flask框架的SSRF

来自

[De1CTF 2019]SSRF Me

打开就是flask源码。

cv到编辑器审计一下:

#! /usr/bin/env python
#encoding=utf-8
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", ""))
    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]
    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')

捏麻麻的还挺长。

看名字SSRF应该就是服务器端请求伪造,但是切入口有点难找,很考验代码审计能力。

遇到flask框架首先找路由@app.route(),这里有三个:

@app.route("/geneSign", methods=['GET', 'POST'])
@app.route('/De1ta',methods=['GET','POST'])
@app.route('/')

/Delta有点复杂,但是应该最重要,先放一边,从这个/geneSign分析:

意思就是这个方法可以get传一个param的参,action的值是字符串scan,然后给到了getSign函数。

再看getSign函数:

把一个secret_key与传进来的参数param和值为scan的action合起来并进行MD5加密然后返回。

接下来看看这个/De1ta:

这里吧action赋值为cookie里的action参数,param是get传的param参数,sign也是cookie里的sign参数,ip是本地地址,这里有个waf防火墙,看看:

意思就是开头不能用gopher和file读了,算是ban掉了SSRF的常规方法。

接着分析/De1ta,经过防火墙后是丢进了task对象里,然后用json格式dump出来它的Exec函数返回值。

看看Exec:

开始直接调用了checkSign函数,这里就用到了前面getSign函数的MD5返回值,必须与对象里丢进来的那个cookie的sign相等。

接着是两个判断条件

绕进去了后scan,read都必须在这个对象丢进来的action里,我们要用第一个判断里的resp=scan(self.param),要用第二个判断里的f.read(),那么这个action呼之欲出了,要么是scanread,要么是readscan。

接下来就会执行scan函数,赋值给resp。

提示说了flag在./flag.txt,那么一定是这个param在De1ta路由下传flag.txt。

现在首要目的就是绕开这个MD5加密的玩意,但是我们不知道这个密钥。

然而这个getSign就有用武之地了,直接拿来弄出我们想要的MD5,然后我们就在cookie伪造这个MD5传sign上去,就绕过了这个checkSign的相等判断。

param值可控,我们就直接传flag.txtread进行构造,这样在getSign函数里后面加上那个scan刚好凑成readscan,后面再传action的时候就匹配上了!

所以我们在cookie里应该传action=readscan,借用这个flask带密钥的MD5梭一下:

这个就是我们sign的cookie值。

bp一把梭了:

posted @ 2023-10-06 17:30  Eddie_Murphy  阅读(680)  评论(0编辑  收藏  举报