[HCTF 2018]Hide and seek
Posted keelongz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HCTF 2018]Hide and seek相关的知识,希望对你有一定的参考价值。
题目一览
进去,一个登陆界面:
发现除了admin登录不了,剩下的用户名密码都可以登录:
出现一个上传点
P1 构造软链接读取文件
题目让我们传一个压缩包,随便试一下,发现会把压缩包里文件的内容读出来:猜测是先解压缩然后再cat
这里就有一种利用方式可行——软链接,来源于FB的一个实际漏洞:http://www.vuln.cn/8132
软链接又叫符号链接,用ln -s
创建。这个链接包含了另一个文件的路径名:它可以是任意文件或目录,可以链接不同文件系统的文件。
换而言之和,就是win下的快捷方式。软链接的用法是:
ln -s 源文件 目标文件
比如我们要读/etc/passwd,可以创建一个软链接test1:
ln -s /etc/passwd ./test1
然后cat之,可以看到成功输出了文件内容:
所以我们直接压缩一下,然后上传试试看,发现只会输出我们本机的/etc/passwd:
这说明直接压缩的话,软链接就断掉了,取而代之的是压缩了链接的文件的副本。
这里就要引入一个新的命令,symlinks
,kali/Ubuntu下需要安装才能使用:
这个命令可以维持软链接(符号链接):
我们试一下:
zip --symlinks te1.zip test1
上传,成功读取:
P2 信息收集
参见之前做过的CISCN华东南Web4,可以知道那些系统变量可以暴露信息,比如:
/proc/self/environ Web工作目录
发现uwsgi的配置文件位置,这里介绍一下uwsgi:
知道这个差不多就知道了Web应用部署的目录,一看/app
估计又是Flask:
成功读取到运行模块。看来就是很简单的/app/main.py
:
读一下,???
后来看别人的WP,可能是docker的环境变量配置有问题,已经email赵师傅。
如果环境是正确(?)的话,我们可以读到源码,
[uwsgi] module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main callable=app
# -*- 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=‘127.0.0.1‘, debug=True, port=10008)
注意这一段:
@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‘)
看来是用了session,如果session里面有username的值,会把结果反映在模板index.html
里,那么我们读一下/app/hard_t0_guess_n9f5a95b5ku9fg/templates/index.html
:
可以看到:
{% if user == ‘admin‘ %}
Your flag: <br>
{{ flag }}
看来要构造username=admin
的session才行:
我们来看下session怎么生成的:
random.seed(uuid.getnode())
app = Flask(__name__)
app.config[‘SECRET_KEY‘] = str(random.random()*100)
和CISCN华东南Web4基本一模一样的思路,这里就直接写过过程了。
P3 Flask session伪造
1.random.seed(uuid.getnode())
说明是按MAC地址生成种子的,我们先读一下:
/sys/class/net/eth0/address
处理一下,开始写脚本,这里注意uuid.getnode()
返回的是十进制,需要转一下:
import random
ma="02:42:ae:01:d8:c5"
mac=ma.replace(":", "")
random.seed(int(mac,16))
key = str(random.random() * 100)
print(key)
这样获得了key:
2.用flask-session-cookie-manager来完成伪造session:
先decode目前的session,看一下结构:
然后decode一下:
那么改成把username改成admin,encode回去:
抓包改包发送,成功获得flag:
以上是关于[HCTF 2018]Hide and seek的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1941 [Sdoi2010]Hide and Seek
P2951 [USACO09OPEN]捉迷藏Hide and Seek
洛谷 P2951 [USACO09OPEN]捉迷藏Hide and Seek
[bzoj1941][Sdoi2010]Hide and Seek