侵删
以下绝大部分内容来自
https://mochazz.github.io/2018/11/08/PHP-Audit-Labs题解之Da13-16/#Day-13题解-By-l1nk3r
知识点
对于传入的非法的 $_GET 数组参数名,PHP会将他们替换成 下划线 。
有以下这些字符
这里CTF碰到过几次用.代替_
当我们使用HPP(HTTP参数污染)传入多个相同参数给服务器时,PHP只会接收到后者的值。(这一特性和中间件有关系)
通过 $_SERVER[‘REQUEST_URI’] 方式获得的参数,并不会对参数中的某些特殊字符进行替换。
分析代码
http://127.0.0.1/index.php?submit=&i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/ctf.users&i.d=123
这里直接分析源码,怎么绕过waf的,不对具体payload分析。
很明显,我们要传入类似submit=&i_d=xxx
我们看这个数据怎么处理的
第一个waf dowith_sql
过滤掉注入查询中的很多关键字
第二个waf
通过$_SERVER['REQUEST_URI']调用
这里先来看 explode函数
这里的作用就是从?之后开始针对 & 进行分割,获取到每个参数的参数名和参数值。
最后经过了
dhtmlspecialchars(addslashes($_value[1]));过滤
我们来看
dhtmlspecialchar
function dhtmlspecialchars($string) {//对PHP内置函数htmlspecialchars的二次封装和补充,使得不仅可以处理字符串还可以递归处理数组;它的作用是是可以把一个数组或字符串中的字符转化为html实体,可以防止页面的跨站问题,那么我们看到他的转换就是将‘&’,‘”’,‘<’,‘>’转化为'&', '"', '<', '>'。
(addslashes就是对'进行转义: '->')
来看个例子:
第一次 $_REQUEST 仅仅只会输出 i_d=2 的原因是因为php自动将 i.d 替换成了 i_d 。而根据我们前面说的第二个特性,PHP取最后一个参数对应的值,因此第一次 $_REQUEST 输出的是2。
第二次 $_REQUEST 会输出 i_d=select&i.d=2 是因为 $_SERVER[‘REQUEST_URI’] 并不会对特殊的符号进行替换,因此结果会原封不动的输出。所以这题的payload可以根据下面这个思维导图进行构造:
我们通过页面请求 i_d=padyload&i.d=123 。
当数据流到达第一个WAF时,php会将参数中的某些特殊符号替换为下划线。因此便得到了两个 i_d ,所以此时的payload变成了 i_d=payload&i_d=123 。
前面我们介绍了,如果参数相同的情况下,默认 第二个参数传入的值 会覆盖 第一个参数传入的值 。因此此时在第一个WAF中 i_d=123 ,不存在其他特殊的字符,因此绕过了第一个WAF。
当数据流到达进入到第二个WAF时,由于代码是通过 $_SERVER[‘REQUEST_URI’] 取参数,而我们前面开头的第三个知识点已经介绍过了 $_SERVER[‘REQUEST_URI’] 是不会将参数中的特殊符号进行转换,因此这里的 i.d 参数并不会被替换为 i_d ,所以此时正常来说 i.d 和 i_d 都能经过第二个WAF。
第二个WAF中有一个 dhtmlspecialchars() 函数,这里需要绕过它,其实很好绕过。绕过之后 i_d=payload&i.d=123 便会进入到业务层代码中,执行SQL语句,由于这里的SQL语句采用拼接的方式,因此存在SQL注入。
这里因为看源码是拼接的i_d,所以后面的在最后也就无所谓了。