• [De1CTF 2019]SSRF Me


    [De1CTF 2019]SSRF Me

    题目放了hint

    hint:flag is in ./flag.txt

    代码审计先,看下源代码

    #! /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.xists(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", "")) #urllib.unquote 是url解码   ----urlib.urlencode 是url编码  #request.args.get获取单个值
    
        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 #获取request的ip
    
        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')
    
    # generate Sign For Action Scan.
    @app.route("/geneSign", methods=['GET', 'POST'])
    def geneSign():
    
        # urllib.unquote 是url解码   ----urlib.urlencode 是url编码  #request.args.get获取单个值
        param = urllib.unquote(request.args.get("param", ""))
    
        action = "scan"
    
        return getSign(action, param)
    

    返回geneSign页面

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

    看下De1ta页面,接受了action和sign的cookie和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  # 获取request的ip
    
        if (waf(param)):
    
            return "No Hacker!!!!"
    
        task = Task(action, param, sign, ip)
    
        return json.dumps(task.Exec())
    

    param要过waf,看一下waf。

    移除头尾空格,转为小写,检查是否存在gopher和file,过滤了这两个协议,不能直接通过param传参来读文件

    def waf(param):
        check = param.strip().lower()
        if check.startswith("gopher") or check.startswith("file"):
            return True
        else:
            return False
    

    过waf后,调用exec方法

    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
    

    先通过checkSign.

    需要action和param通过getSign方法后的值与sign相同,如果scan再action里面,会将param传入scan函数调用

    def checkSign(self):
    
            if (getSign(self.action, self.param) == self.sign):
    
                return True
    
            else:
    
                return False
    

    看一下scan

    def scan(param):
    
        socket.setdefaulttimeout(1)
    
        try:
    
            return urllib.urlopen(param).read()[:50]
    
        except:
    
            return "Connection Timeout"
    

    scan函数会抓取param这个页面并读取

    再往后看,如果有read就可以进行读取,并返回结果

    这里应该就是利用点了,题目一开始提醒了,flag在/flag.txt,我们就可以将param的值变为flag.txt,首先要考虑checkSign,要使

    需要构造aciton和param经过md5加密后等于sign

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

    secret_key的值不知道,没办法手工构造,在geneSign页面

    # generate Sign For Action Scan.
    @app.route("/geneSign", methods=['GET', 'POST'])
    def geneSign():
    
        # urllib.unquote 是url解码   ----urlib.urlencode 是url编码  #request.args.get获取单个值
        param = urllib.unquote(request.args.get("param", ""))
    
        action = "scan"
    
        return getSign(action, param)
    

    这里调用了getSign,并且返回了他的值

    action被写为scan,少一个read,在exc方法中action必须有read和scan,

    然后hint又告诉我们在./flag.txt下

    所以构造

    http://ca643316-afc2-4476-afd0-5263a319e2c6.node3.buuoj.cn/geneSign?param=flag.txtread
    

    获得cookie后

    a918564b3ef3310d56199a0039bf5b60
    

    访问Delta页面,传参param=flag.txt

    image-20200730195759371

    代码审计类的题目总体上讲没有什么特别的套路,尤其是这种体量较小的代码,也不需要很大的脑洞,只要有较为扎实的基本功,认真的审计代码,找出关键点,耐心回溯与跟进,搞清楚流程,构建一个大体的思路,去实践去验证它的可行性,即使没有接触题目,也一定有所提升。

  • 相关阅读:
    vue项目打包发布
    jap和mybatis比较
    前端常用js插件
    【知乎问题】如何让不懂编程的人感受到编程的魅力
    「MoreThanJava」Day 3:构建程序逻辑的方法
    「MoreThanJava」Day 1:环境搭建和程序基本结构元素
    PHPExecl导出大量数据卡顿问题解决(Laravel实现)
    互联网协议入门(二)(转)
    互联网协议入门(一)(转)
    Mysql创建用户与授权
  • 原文地址:https://www.cnblogs.com/LEOGG321/p/13406418.html
Copyright © 2020-2023  润新知