[De1CTF 2019]SSRF Me


題目提示我們flag在./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.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')

對python寫的后端程序我們首先看路由。

可以看到在/De1ta頁面我們get方法傳入param參數值,在cookie里面傳遞action和sign的值,將傳遞的param通過waf這個函數。

於是我們先去看waf函數

waf函數找到以gopher或者file開頭的,所以在這里過濾了這兩個協議,使我們不能通過協議讀取文件

接着在challenge里面,用我們傳進去的參數構造一個Task類對象,並且執行它的Exec方法

我們接着去看Exec方法

先通過checkSign方法檢測登錄。

到checkSign方法里面去看看

當我們傳入的參數action和param經過getSign這個函數之后與sign相等,就返回true

返回true之后則進入if語句里面

這里可以看到,如果scan在action里面,則我們可以讓param進入scan這個函數。值得注意的是,這里使用的是in而不是==,於是在這里必定是破題的地方。

跳轉到scan函數

這里我們很容易注意到,此處傳入到scan里面的param沒有被過濾,對於param參數的過濾,僅僅在於最開始的waf函數,意於阻止我們使用gopher協議和file協議讀取服務器本地的文件。於是如果我們能夠令param為flag.txt,我們就能讀取它的內容。

大概捋了一下思路之后,我們反向進行構造。

首先我們需要是self.checkSign()函數返回為真,所以我們需要讓經過了getSign函數的action和param返回值等於sign

需要secert_key+param+action三個字符串連接起來之后進行md5加密作為返回值,但是這里我們不知道secert_key的值。

但是這里的字符串是連接起來的,舉個例子:

param=abc;
action=def;
param+action=abcdef;
param=a;
action=bcdef;
param+action=abcdef;

又因為我們需要讓param能夠讀取flag.txt

而又因為

所以我們需要令action里面有scan和read這兩個參數

所以我們現在就需要

md5(key+flag.txt+scanread/readscan)

雖然key我們不知道,但是這里

訪問這個頁面,我們可以得到一個getSign返回的md5值,不過action已經被寫死為scan了。

但是最后的時候我們還是需要讀取flag.txt,所以我們令param為flag.txtread

這樣就能得到對應的用來登錄的md5值了。

可以看到我們獲得了

0f10b9dfb586adf81d993acd6af48f84

這就是我們登錄需要的md5

接着我們訪問De1ta頁面,傳遞參數

按照之前的思路,獲取到了flag

 

這道題目的收獲:(直接抄的別的師傅的,我覺得說的很對hhh)代碼審計類的題目總體上講沒有什么特別的套路,尤其是這種體量較小的代碼,也不需要很大的腦洞,只要有較為扎實的基本功,認真的審計代碼,找出關鍵點,耐心回溯與跟進,搞清楚流程,構建一個大體的思路,去實踐去驗證它的可行性,即使沒有接觸題目,也一定有所提升。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM