算是我第一次正兒八經的分析python代碼了
from flask import Flask, request
import socket
import hashlib
import urllib
import sys
import os
import jsonreload(sys)
sys.setdefaultencoding('latin1')app = Flask(__name__) # 創建一個Flask應用實例
secret_key = os.urandom(16) # 生成一個16字節的隨機密鑰# 定義一個名為Task的類,用于處理任務
class Task:def __init__(self, action, param, sign, ip):self.action = action # 任務動作self.param = param # 參數self.sign = sign # 簽名self.sandbox = md5(ip) # 根據IP生成一個唯一的沙盒目錄名if not os.path.exists(self.sandbox):os.mkdir(self.sandbox) # 如果沙盒目錄不存在,創建它def Exec(self):result = {}result['code'] = 500 # 默認響應碼為500if self.checkSign(): # 檢查簽名是否有效if "scan" in self.action: # 如果任務動作是"scan",執行掃描操作tmpfile = open("./%s/result.txt" % self.sandbox, 'w') #寫到result.txt文件resp = scan(self.param) if resp == "Connection Timeout":result['data'] = respelse:print resptmpfile.write(resp)tmpfile.close()result['code'] = 200 # 執行成功,響應碼為200if "read" in self.action: #如果任務動作是"read"f = open("./%s/result.txt" % self.sandbox, 'r') #讀result.txt文件result['code'] = 200result['data'] = f.read() # 讀取結果if result['code'] == 500:result['data'] = "Action Error" # 如果動作無效,設置響應數據else:result['code'] = 500result['msg'] = "Sign Error" # 如果簽名無效,設置響應消息return resultdef checkSign(self):if getSign(self.action, self.param) == self.sign: # 驗證簽名是否匹配return Trueelse:return False# 創建路由"/geneSign",用于生成簽名
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():param = urllib.unquote(request.args.get("param", ""))action = "scan"return getSign(action, param)# 創建路由"/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"))ip = request.remote_addrif waf(param): # 檢查是否觸發Web應用防火墻(WAF)return "No Hacker!!!!"task = Task(action, param, sign, ip) # 創建任務對象return json.dumps(task.Exec()) # 返回任務執行結果的JSON表示# 創建根路由"/",用于返回文本文件內容
@app.route('/')
def index():return open("code.txt", "r").read()# 定義一個用于掃描URL的函數
def scan(param):socket.setdefaulttimeout(1) # 設置超時時間try:return urllib.urlopen(param).read()[:50] # 打開URL并讀取前50個字符except:return "Connection Timeout"# 生成簽名的函數
def getSign(action, param): return hashlib.md5(secret_key + param + action).hexdigest()# 計算MD5哈希的函數
def md5(content):return hashlib.md5(content).hexdigest()# waf
def waf(param):check = param.strip().lower()if check.startswith("gopher") or check.startswith("file"): return True else:return Falseif __name__ == '__main__':app.debug = Falseapp.run(host='0.0.0.0', port=80) # 啟動Flask應用,監聽在0.0.0.0的80端口上
這里出flag的邏輯就是通過scan掃到flag.txt然后寫入result.txt文件,再用read把result.txt的文件內容讀出來
如果我們想要讀flag.txt,就需要進行scan和read操作,而這兩個操作在Tack類中需要checkSign檢查(對比md5值),那我們就可以用/geneSign知道該md5值(有·return)
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():param = urllib.unquote(request.args.get("param", ""))action = "scan"return getSign(action, param)
此時action為scan,所以我們param為flag.txtread
(這里加個read是因為我們在/De1ta中操作的時候需要read,來構建我們需要的sign)
這樣拼接之后就是 secret_keyflag.txtreadscan
because——> return hashlib.md5(secret_key + param + action).hexdigest()
def checkSign(self):
??????? if getSign(self.action, self.param) == self.sign:? # 驗證簽名是否匹配
??????????? return True
??????? else:
??????????? return False
這里就是把我們的action(readscan)和param(flag.txt)傳進去得到的結果和我們傳進去的sign比較
下一步:
讀flag:
這里param=flag.txt與action=readscan 拼接完后為 secret_keyflag.txtreadscan
和上文得到的md5值一致