• [De1CTF 2019]SSRF Me


    [De1CTF 2019]SSRF Me

    考察,代码审计

    源码如下

    关键处加了些许注释

    #! /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):#python得构造方法
            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):#定义的命令执行函数,此处调用了scan这个自定义的函数
            result = {}
            result['code'] = 500
            if (self.checkSign()):
                if "scan" in self.action:#action要写scan
                    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:#action要加read
                    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):#!!!这个应该是本题关键点,此处注意顺序先是param后是action
        return hashlib.md5(secert_key + param + action).hexdigest()
    
    
    def md5(content):
        return hashlib.md5(content).hexdigest()
    
    
    def waf(param):#这个waf比较没用好像
        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')
    
    

    先解释部分函数:

    所在行 函数 解释
    第20行 init(self, action, param, ...) 构造方法self代表对象,其他是对象的属性
    71 request.args.get(param) 提取get方法传入的,参数名叫param对应得值
    70 request.cookies.get("action") 提取cookie信息中的,名为action得对应值
    92 hashlib.md5().hexdigest() hashlib.md5()#获取一个md5加密算法对象,hexdigest()是获得加密吼的16进制字符串
    70,71,72 urllib.unquote() 将url编码解码
    87 urllib.urlopen() 读取网络文件参数可以是url
    77 json.dumps Python 对象编码成 JSON 字符串

    大致流程:

    image-20200401193522563

    Exec关键代码

    看完这段代码可以理解整个

    1.我们需要过checkSign的校验,getSign(self.action, self.param) == self.sign

    ​ 当secert_key + param + action散列后的16进制内容和sign相同即可通过校验。

    2.action中有read和scan,

    3.param是flag.txt即可通过scan函数读取flag。

    image-20200401191940039

    难点是checkSign函数的校验,也就是getSign的校验。

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

    由于我们并没有secert_key的值,所以本来是没得办法的。

    但是/geneSign路由,暴露了getSign函数,

    我们可以根据路由getSign去得到正确的sign值。

    image-20200401192348317

    用geneSign路由得到sign

    action的值是强制的scan的,所以填不填都可。

    sign: 911db68eab96df16a18bc3521fb10d3d

    get传参:

    ​ param=flag.txt

    cookie:

    ​ action=action

    ​ sign=911db68eab96df16a18bc3521fb10d3d

    image-20200401193217290

  • 相关阅读:
    MySQL-基本sql命令
    Java for LeetCode 203 Remove Linked List Elements
    Java for LeetCode 202 Happy Number
    Java for LeetCode 201 Bitwise AND of Numbers Range
    Java for LeetCode 200 Number of Islands
    Java for LeetCode 199 Binary Tree Right Side View
    Java for LeetCode 198 House Robber
    Java for LeetCode 191 Number of 1 Bits
    Java for LeetCode 190 Reverse Bits
    Java for LeetCode 189 Rotate Array
  • 原文地址:https://www.cnblogs.com/h3zh1/p/12615062.html
Copyright © 2020-2023  润新知