[CISCN2019 华东南赛区]Web4
文章目录
- [CISCN2019 华东南赛区]Web4
- 掌握知识
- 解题思路
- 代码分析
- 正式解题
- 关键paylaod
掌握知识
根据url
地址传参结构来判断php
后端还是python
后端;uuid.getnode()
函数的了解,可以返回主机MAC地址十六进制;python网站源码多半处于/app.app.py
;代码审计,session
欺骗,session
的加解密
解题思路
- 打开题目链接,发现有一个超链接可以点击,点击之后发现实现了跳转,来到了百度界面,看url地址发现是远程文件包含的可能,但懒得开服务器测试了,试一试是否可以本地文件包含。包含
/etc/passwd
成功回显内容,确认可疑文件包含。尝试包含/flag
文件直接显示黑客
-
尝试查看网站源码,一开始还以为依旧是
php
的后端,查看/var/www/html/index.php
发现页面返回无响应,想着去看一看url给出的文件名,发现只有read
并没有后缀,了解了一下,发现这是写路由形式,是python
后端的标志。记住一个知识点url/read?id=xxxx这种在url和参数中间又会有一段字符串的,可以考虑是写了路由,不是php后端,可能是python后端。 -
那就去读取一下python网站的默认源码文件,
/app/app.py
成功回显源码内容,简单看了一下,感觉是session欺骗的题目,又生成了session私钥,又给了session字段信息,获得flag还需要修改其中的字段信息,很明显的session欺骗考察的逻辑
# encoding:utf-8
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"
)
代码分析
- 又是老朋友随机数了,根据随机数种子生成随机数,由于伪随机数漏洞,只要确保随机数种子一样,版本一样,次序一样,使用相同的随机数生成代码就能生成同样的随机数,所以这里关键的就是
uuid.getnode()
函数的返回值了。搜索了一下发现函数返回结果为MAC地址的十六进制形式,那也很好办,之前学习ssti
计算PIN码也就遇到读取MAC文件,继续文件包含读取MAC地址/sys/class/net/eth0/address
。根据源码的文件头注释猜测是python2
的可能性大一点,因为去在线网站查看了一下相应的头部信息。
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
/read
路劲现在已经没什么用处了,这里过滤了flag
也就不能直接文件包含读取flag.txt
了。/
路径是给session字段赋值用户信息的
@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'
- 其实
/flag
字段也很好理解,就是要修改session字段的用户为fuck
,只需要抓取/flag
的请求包,将其的session字段使用加解密脚本和私钥进行加解密,修改好用户信息之后将新的session字段替换一下,发送包即可得到flag
。这道题关键也就在确定python
后端,读取/app/app.py
的网站源码信息
@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
正式解题
- 整体分析结束,其实还可以,后面也就是简单的session欺骗过程了。开始抓包获得
/flag
界面的session字段,其实session也都一样,为了直接替换的flag
方便。直接使用加解密脚本进行对session伪造的操作。由于加密脚本使用命令行会报错,所以就使用pycharm
自带的功能,可以传递形参来替换命令行执行的方式,得到加密的session
字段
- 替换原来的session字段,发送包成功拿下
flag
关键paylaod
python网站源码 /app/app.py
MAC地址文件路径 /sys/class/net/eth0/address
session解密脚本传参 encode -s 82.684855651 -t "{'username':'fuck'}"
私钥生成脚本
import random
mac="6ed1b6c952d6"
random.seed(int(mac,16))
key = str(random.random() * 233)
print key