zoukankan      html  css  js  c++  java
  • [De1CTF 2019]SSRF Me

    今天做的这道题比较偏审计,当时拿道题目的确发慌,也是看着大佬的wp一步步做出来的,这题就是需要你扎实的基本功,要有耐心,一步步跟着回溯,就可以做出来

    题目

    拿到题目代码很乱,可放在pycharm里Ctrl+Alt+L将代码格式化一下

      1 #! /usr/bin/env python
      2 #encoding=utf-8
      3 from flask import Flask
      4 from flask import request
      5 import socket
      6 import hashlib
      7 import urllib
      8 import sys
      9 import os
     10 import json
     11 reload(sys)
     12 sys.setdefaultencoding('latin1')
     13 
     14 app = Flask(__name__)
     15 
     16 secert_key = os.urandom(16)
     17 
     18 
     19 class Task:
     20     def __init__(self, action, param, sign, ip):
     21         self.action = action
     22         self.param = param
     23         self.sign = sign
     24         self.sandbox = md5(ip)
     25         if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
     26             os.mkdir(self.sandbox)
     27 
     28     def Exec(self):
     29         result = {}
     30         result['code'] = 500
     31         if (self.checkSign()):
     32             if "scan" in self.action:
     33                 tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
     34                 resp = scan(self.param)
     35                 if (resp == "Connection Timeout"):
     36                     result['data'] = resp
     37                 else:
     38                     print resp
     39                     tmpfile.write(resp)
     40                     tmpfile.close()
     41                 result['code'] = 200
     42             if "read" in self.action:
     43                 f = open("./%s/result.txt" % self.sandbox, 'r')
     44                 result['code'] = 200
     45                 result['data'] = f.read()
     46             if result['code'] == 500:
     47                 result['data'] = "Action Error"
     48         else:
     49             result['code'] = 500
     50             result['msg'] = "Sign Error"
     51         return result
     52 
     53     def checkSign(self):
     54         if (getSign(self.action, self.param) == self.sign):
     55             return True
     56         else:
     57             return False
     58 
     59 
     60 #generate Sign For Action Scan.
     61 @app.route("/geneSign", methods=['GET', 'POST'])
     62 def geneSign():
     63     param = urllib.unquote(request.args.get("param", ""))
     64     action = "scan"
     65     return getSign(action, param)
     66 
     67 
     68 @app.route('/De1ta',methods=['GET','POST'])
     69 def challenge():
     70     action = urllib.unquote(request.cookies.get("action"))
     71     param = urllib.unquote(request.args.get("param", ""))
     72     sign = urllib.unquote(request.cookies.get("sign"))
     73     ip = request.remote_addr
     74     if(waf(param)):
     75         return "No Hacker!!!!"
     76     task = Task(action, param, sign, ip)
     77     return json.dumps(task.Exec())
     78 @app.route('/')
     79 def index():
     80     return open("code.txt","r").read()
     81 
     82 
     83 def scan(param):
     84     socket.setdefaulttimeout(1)
     85     try:
     86         return urllib.urlopen(param).read()[:50]
     87     except:
     88         return "Connection Timeout"
     89 
     90 
     91 
     92 def getSign(action, param):
     93     return hashlib.md5(secert_key + param + action).hexdigest()
     94 
     95 
     96 def md5(content):
     97     return hashlib.md5(content).hexdigest()
     98 
     99 
    100 def waf(param):
    101     check=param.strip().lower()
    102     if check.startswith("gopher") or check.startswith("file"):
    103         return True
    104     else:
    105         return False
    106 
    107 
    108 if __name__ == '__main__':
    109     app.debug = False
    110     app.run(host='0.0.0.0')

    分析

    先从路由入手,本题一共有3个路由

    我们先看/De1ta这个路由(因为它涵盖的面比较广)

    @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"))

    可以看到在/De1ta页面我们get方法传入param参数值,在cookie里面传递action和sign的值

    然后将传递的param通过waf这个函数。

    if(waf(param)):
            return "No Hacker!!!!"

    于是我们先去看waf函数

    waf函数找到以gopher或者file开头的,所以在这里过滤了这两个协议,使我们不能通过协议读取文件

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

    接着在challenge里面,用我们传进去的参数构造一个Task类对象,并且执行它的Exec方法

    我们接着去看Exec方法

    task = Task(action, param, sign, ip)
        return json.dumps(task.Exec())

    这是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方法检测登录。

    到checkSign方法里面去看看

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

    当我们传入的参数action和param经过getSign这个函数之后与sign相等,就返回true

    返回true之后则进入if语句里面

    我们来追踪一下getSign这个函数,它主要是三个东西拼接然后进行md5

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

     

    我发现/geneSign这个路由可以生成我们需要的md5(其实它也是调用了getSign这个函数)

    @app.route("/geneSign", methods=['GET', 'POST'])
    def geneSign():
        param = urllib.unquote(request.args.get("param", ""))
        action = "scan"
        return getSign(action, param)

    但是它的action只有"scan",我们回到之前 if (self.checkSign()):中,当它为真会执行它下面的两条if语句

            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

    代码也很好懂,就是action中必须要有"scan"和"read"两个才能读取flag

    试着访问了一下 /geneSign?param=flag.txt ,给出了一个 md5 4699ef157bee078779c3b263dd895341 ,但是只有 scan 的功能,想加入 read 功能就要另想办法了

    def geneSign():
        param = urllib.unquote(request.args.get("param", ""))
        action = "scan"
        return getSign(action, param)

    看了一下逻辑,在 getSign 处很有意思,这个字符串拼接的就很有意思了

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

    不妨假设 secert_key 是 xxx ,那么在开始访问 /geneSign?param=flag.txt 的时候,返回的 md5 就是 md5('xxx' + 'flag.txt' + 'scan') ,在 python 里面上述表达式就相当于 md5(xxxflag.txtscan) ,这就很有意思了。

    直接构造访问 /geneSign?param=flag.txtread ,拿到的 md5 就是 md5('xxx' + 'flag.txtread' + 'scan') ,等价于 md5('xxxflag.txtreadscan') ,这就达到了目标。

    直接访问 /De1ta?param=flag.txt 构造 Cookie: sign=7a2a235fcc9218dfe21e4eb400a11b5e;action=readscan 即可

  • 相关阅读:
    JS中的各种函数
    JS循环 && JS语法
    对JS基础学习的重新理解
    vue系列---【vue项目如何使用less语法?】
    vue系列---【vue项目如何利用图形化界面安装axios依赖?】
    vue系列---【vue项目如何使用图形化界面自动安装element-ui,实现按需导入?】
    vue系列---【如何使用vue ui图形化界面创建一个vue项目?(注意:适用于3.0.x版本)】
    vue系列---【vue项目如何使用element-ui?】
    vue系列---【如何安装vue脚手架?】
    Oracle系列---【PL/SQL Developer如何连接远程oracle数据库?】
  • 原文地址:https://www.cnblogs.com/zzjdbk/p/13685940.html
Copyright © 2011-2022 走看看