[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): # 是一个简单的赋值函数
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if (not os.path.exists(self.sandbox)): # 如果没有该文件夹,则创立一个文件夹
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') # 注意w,可以对result.txt文件进行修改
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print
resp
tmpfile.write(resp) # 这个将resp中的数据写入result.txt中,可以利用为将flag.txt中的数据放进result.txt中
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() # 读取result.txt中的数据
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
@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']) # 注意这个绑定,接下来的几个函数都很重要,这个相当于c语言里面的主函数,接下来是调用其他函数的过程
def challenge():
action = urllib.unquote(request.cookies.get("action")) # cookie传递action参数,对应不同的处理方式
param = urllib.unquote(request.args.get("param", "")) # 传递get方式的参数param
sign = urllib.unquote(request.cookies.get("sign")) # cookie传递sign参数sign
ip = request.remote_addr # 获取请求端的ip地址
if (waf(param)): # 调用waf函数进行过滤
return "No Hacker!!!!"
task = Task(action, param, sign, ip) # 创建Task类对象
return json.dumps(task.Exec()) # 以json的形式返回到客户端
@app.route('/')
def index():
return open("code.txt", "r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50] # 这个可以利用为访问flag.txt。读取然后为下一步将flag.txt文件中的东西放到result.txt中做铺垫
except:
return "Connection Timeout"
def getSign(action, param): # getSign的作用是拼接secret_key,param,action,然后返回拼接后的字符串的md5加密值
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content): # 将传入的字符串进行md5加密
return hashlib.md5(content).hexdigest()
def waf(param): # 防火墙的作用是判断开头的几个字母是否是gopher 或者是file 如果是的话,返回true
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', port=9999)
@app.route('/De1ta', methods=['GET', 'POST'])
从这个路由开始审计,因为这个路由东西最多
里面包含一个challenge()函数
点击查看代码
def challenge():
action = urllib.unquote(request.cookies.get("action")) # cookie传递action参数,对应不同的处理方式
param = urllib.unquote(request.args.get("param", "")) # 传递get方式的参数param
sign = urllib.unquote(request.cookies.get("sign")) # cookie传递sign参数sign
ip = request.remote_addr # 获取请求端的ip地址
if (waf(param)): # 调用waf函数进行过滤
return "No Hacker!!!!"
task = Task(action, param, sign, ip) # 创建Task类对象
return json.dumps(task.Exec()) # 以json的形式返回到客户端
然后param这个参数经过waf函数过滤
点击查看代码
def waf(param): # 防火墙的作用是判断开头的几个字母是否是gopher 或者是file 如果是的话,返回true
check = param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
点击查看代码
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)): # 如果没有该文件夹,则创立一个文件夹
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') # 注意w,可以对result.txt文件进行修改
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print
resp
tmpfile.write(resp) # 这个将resp中的数据写入result.txt中,可以利用为将flag.txt中的数据放进result.txt中
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() # 读取result.txt中的数据
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
result = {} result['code'] = 500
这两句就是创建了一个叫result的字典。给了一个键值对,code对500
然后直接执行了checkSign()函数
看看
点击查看代码
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
点击查看代码
def getSign(action, param): # getSign的作用是拼接secret_key,param,action,然后返回拼接后的字符串的md5加密值
return hashlib.md5(secert_key + param + action).hexdigest()
点击查看代码
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w') # 注意w,可以对result.txt文件进行修改
resp = scan(self.param)
点击查看代码
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
点击查看代码
def getSign(action, param): # getSign的作用是拼接secret_key,param,action,然后返回拼接后的字符串的md5加密值
return hashlib.md5(secert_key + param + action).hexdigest()
点击查看代码
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w') # 注意w,可以对result.txt文件进行修改
resp = scan(self.param)
print resp
tmpfile.write(resp) # 这个将resp中的数据写入result.txt中,可以利用为将flag.txt中的数据放进result.txt中
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() # 读取result.txt中的数据

记住这个

感觉和ssrf没什么关系呀,可能是我太菜了

浙公网安备 33010602011771号