<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>

    【技術分享】Writeup for Web-Checkin in CyBRICS CTF 2021

    VSole2021-08-06 14:03:14

    這是 CyBRICS CTF 2021 中的一個難度為 Hard 的 Web 題(其實是 Crypto 密碼題)。由于作者的某些原因,這個題目在比賽結束都是零解。在比賽結束之后,跟主辦方 battle 了半天,作者終于意識到這個題目有問題,在原題正常的情況下,只有理論上才有解…于是,主辦方在比賽結束后也承認了自己的錯誤:”looks like we’ve shit our pants”,也在直播解題的時候對于此題無解進行了解釋,并放出了一個修復的版本,而且為了表示自己的誠意,主辦方也表示前三個能夠解決這個問題的人可以獲得 bounty 。

    于是,我跟我的密碼學小伙伴努力了兩天,終于最后成功地解決了這個題目拿到了二血,也是僅有的兩支獲得獎勵的隊伍之一。以下是這個題目修復后版本的 WriteUp 。

    TL;DR

    Padding Oracle Attack + Bit Flip Attack + XSS

    Reconnaissance

    這個題目模擬了一個航班預訂網站,我們可以在那里根據用戶信息生成機票,并上傳機票進行登記。

    題目主要有三個 API 接口:

    • /order:填表單的一個界面,只有做前端頁面展示,表單數據提交到下面這個 API
    • /finalize:根據傳入的參數生成 Aztec Code (一個類似二維碼的碼)。通過在線識別的工具可以知道得到的是一串密文,密文大概長這樣:GLESujRnvL1DfBzExFRDKQ0ZjTrqQgOPuDHKWyu5qhlOpFzn2hw3Dc5dsGLT1jMdwzo24z8h8f2vW6sNINRZa70MLB+mrqY5JVPg5DFygnDmVIUEI6yqkiqaB3fg5RCGeTE6gApiuxZSneallm7kCzIt+au5fZG/f9XXypDLWqM= , base64 解碼不能直接得到 ASCII 明文,從密文格式來看暫時沒有其他更多信息。
    • /upload:用來上傳得到的 Aztec Code ,正常情況下如果成功就返回 “you are now registered“ 的響應,表示成功注冊。

    后來,我們通過改變 base64 編碼數據的一些字節后發現了一些有意思的現象,比如我們通過修改數據的某個字節得到了一個 “PADDING_ERROR” 的響應。所以我們立即想到,這個題目很可能考的是 Padding Oracle Attack (以下簡稱 POA )。

    為了證實我們的想法,我們隨便使用題目生成的一個 Aztec Code ,用 base64 解碼成密文,在倒數第二個密碼文本塊的最后一個字節中 XOR 每一個可能的字節值(0~256),后用 base64 編碼這些修改后的密文,并用 Aztec Code 生成圖片上傳到題目的 upload 接口( python 可以用 aztec_code_generator lib )。在我們收到的 256 個響應中,255 個的狀態碼是 200 ,只有一個響應的狀態碼是 500 。并且在這 255 個響應中,在最后一個字節中用x00進行 XOR 得到一個 “Success“ 的響應,其余254個都是 “PADDING_ERROR“ 的響應。

    所以這意味著只有 “Success“ 的響應和 500 的響應在服務器端解密后得到了正確填充的明文。響應 “Success“ 是因為得到的明文是未經修改的原始明文,而返回 500 的響應則是因為解密后的明文經過一定程度的修改而被正確填充,我們可以通過利用這一點獲得原始明文的最后一個字節。

    通過不斷向服務器發送修改的密碼文本,然后區分服務器是否回應 “PADDING_ERROR”,我們可以逐個恢復整個明文。這也就是所謂的 Padding Oracle Attack。

    Padding Oracle Attack

    我們簡單回顧一下 POA 攻擊的相關知識。

    首先,我們需要了解什么是 Padding 。

    眾所周知,分組加密可以將一個明文/密文分成多個等長的block進行加密/解密操作。在 AES 的情況下, 16 個字節的數據為一個block。使用一些分組加密的操作模式,我們可以重復使用分組密碼的加密/解密操作一些長度超過一個塊的數據。例如,AES-CBC模式可以加密/解密長度為16的倍數的數據。但是如果數據的長度不是分組長度的倍數呢?我們可以使用某種填充方法(padding method),在最后一個塊的末尾添加一些數據,使其成為一個完整的塊。

    PKCS#7#PKCS#5_and_PKCS#7)就是最廣泛使用的一種填充方法。PKCS#7 首先計算要填充的字節數(pad_length),然后將 pad_length 個字節附加到最后一個明文塊中,每個字節值都是 pad_length 。解除填充后,解密結果的最后一個字節被提取并解析為 pad_length ,并根據 pad_length 來截斷最后一個組的字節數。這里簡單舉個例子,在 “aaaab\x03\x03\x03” 當中,解除填充后為 “aaaab” 。下面是一個 PKCS#7 填充和解填充的Python實現。

    def pad(pt):    pad_length = 16 - len(pt)%16    pt += bytes([pad_length]) * pad_length    return pt
    def unpad(pt):    pad_length = pt[-1]    if not 1 <= pad_length <= 16:        return None    if pad(pt[:-pad_length]) != pt:        return None    return pt[:-pad_length]
    

    我們需要注意的是,在解除填充后會對填充進行合法性檢查。這意味著最后一個塊只有存在 16 種情況是可以被認為是有效的,所有其他格式的數據都是無效的,將產生 “PADDING_ERROR“ 響應,這是一個我們將在后面會利用到的一個 Padding Oracle 的表現。

    另一點要注意的是,即使明文的長度是區塊大小的倍數,仍然需要填充。在這種情況下,將追加 0x10 個字節,每個字節值為b"x10"。

    另外,我們還需要與熟悉一下AES-CBC,這是POA最常見的攻擊場景。

    在 CBC 模式下,明文填充后被分成若干個明文塊,每個明文塊在 AES 加密前都會與前一個密碼文本塊進行 XOR ,第一個明文塊與一個隨機生成的初始化向量(IV)進行 XOR ,最后的加密結果是以IV為首、其他密文塊連接而成的密文,解密只是逆序進行了這些操作。

    AES-CBC 的一個重要缺點是,它并不提供完整性保護。換句話說,攻擊者可以通過某種方式(如字節翻轉)修改密文并將修改后的密碼文本發送到服務器而不被發現,這就為 POA 攻擊提供了條件。

    現在,我們可以深入了解 POA 是如何具體進行攻擊的。

    假設攻擊者擁有一個密碼文本,它可以分為一個 IV 和 3 個密碼文本塊 c1 、 c2 、 c3 ,攻擊者的目的是要解密最后一個密碼文本塊 c3 。

    攻擊者可以改變 c2 的最后一個字節(XOR 上一些字節值),然后將其發送給服務器,我們應當可以得到兩種響應,一種是 200 響應,內容為 “PADDING_ERROR“ ,另一種是 500 響應。如果我們得到一個 500 的響應,說明我們就成功了,這意味著解除填充的檢查通過了,最后一個純文本塊必須以b"\x01"結尾,因為這是 16 種有效的填充格式之一。

    在恢復了最后一個字節后,我們可以繼續解密最后一個明文塊之前所有的字節。例如,為了解密倒數第二個字節,我們可以利用b"\x02\x02"的填充格式。由于我們已經知道了明文的最后一個字節,我們可以通過在c2中 XOR 一些字節值將最后一個字節修改成我們想要的任何值。目前,我們想讓最后一個字節變成b"\x02",我們將c2的最后一個字節與明文的最后一個字節進行 XOR ,使其變成b"\x00",然后再 XOR 上b"\x02",結果就是b"\x02"。隨后,嘗試每一個可能的 255 個字節值guess_byte XOR b"\x02"(除了b"\x00")與c2的倒數第二個字節進行XOR,并將修改后的密碼文本發送到 Padding Oracle ,直到得到 500 響應,從而恢復最后第二個明文字節,這正好是guess_byte

    以下是Python代碼,可用于解密最后一個明文塊。

    import requestsimport base64
    import aztec_code_generator
    
    # padding_oracle recovers the last 16 plaintext bytes of the given ciphertextdef padding_oracle(cipher):    plaintext = b""    for index in range(1, 17):        print(f"[*] index: {index}")        for byte in range(0, 256):            bytes_xor = b"\x00"*(16-index)+bytes([byte^index])+xor(plaintext,bytes([index]*(index-1)))            new_cipher = cipher[:-32] + xor(cipher[-32:-16], bytes_xor) + cipher[-16:]
                b64data = base64.b64encode(new_cipher)            code = aztec_code_generator.AztecCode(b64data)            code.save(f"./pics/{byte}.png", module_size=4)
                f = open(f"./pics/{byte}.png", "rb").read()            paramsMultipart = [('file', ('1.png', f, 'application/png'))]            response = session.post("http://207.154.224.121:8080/upload", files=paramsMultipart)
                if response.status_code == 200:                body = response.content.split(b'
    ')[1].split(b"div")[0]
                    if b"PADDING" in response.content:                    print(f"[{byte:>3d}] Status code: {response.status_code}, PADDING ERROR")                else:                    print(f"[{byte:>3d}] Status code: {response.status_code}, {body}")            else:    # response.status_code == 500                print(f"[{byte:>3d}] Status code: {response.status_code}")                plaintext = bytes([byte]) + plaintext                print(f"plaintext: {plaintext}")                break    return plaintext
    

    Recovering the Entire Plaintext

    通過利用 Padding Oracle ,我們能夠逐字節解密最后的明文塊。我們還能再進一步利用嗎?答案是肯定的。

    一旦我們恢復了最后一個明文塊,我們就可以扔掉最后一個密文塊,并繼續利用 Padding Oracle 來恢復倒數第二個明文塊,以此類推,我們將恢復整段明文數據。

    我們按照上述思路實施了攻擊,并成功地恢復了整個明文,并發現這是一個json格式的數據。

    b'{"name": "12321", "surname": "123", "middle": "1", "time": "2021-07-26 13:37:00", "dest": "", "dep": "", "flight": "BLZH1337"}\x02\x02'
    

    到這里,我們就可以猜到服務器端是如何處理上傳的 Aztec Code 。在收到圖片數據后,服務器將其解碼為密文,對密文進行解密,并對解密結果解除填充。如果在解除填充過程中發生了錯誤,服務器會以 “PADDING_ERROR“ 的響應來回答。在解除填充后,明文會被進一步處理,可能會通過類似JSON.parse() 的處理。如果處理過程中產生任何錯誤,服務器會以 500 狀態碼來響應;如果一切正常,服務器會給我們發回一個 “Success“ 的 200 響應。

     Arbitrary Plaintext Encryption

    恢復整個明文并不足以解決這道題目,我們需要進一步構造我們想要的任意明文的密文,也就是說構造一個密文,讓解密結果得到任意我們想要的明文。

    為了實現這一目標,我們需要將字節翻轉與 POA 相結合,字節翻轉攻擊使我們能夠將明文改變成我們想要的,而 Padding Oracle 可以作為一個解密器使用,幫助我們解密任何密文。

    假設密文 IV || c1 || c2 || c3 解密為 p1 || p2 || p3 ,我們想得到 p1'|| p2'|| p3 的密文。

    我們首先將 c1 與 p2 XOR p2' 進行XOR,得到 c1' 。這樣,IV || c1'|| c2 || c3'將被解密為junk || p2' || p3'

    生成的垃圾數據是完全隨機的,這對我們來說是不可控的,而且含有不可控的垃圾數據會影響到 JSON.parse() 的解析,如果有不可見字符服務器會解析出錯,并返回500響應碼。那么,我們能用它做什么呢?還記得恢復最后一個明文塊的 POA 嗎?我們可以重新使用 POA 來恢復垃圾數據塊junk。之后,我們再用 junk XOR p1 來XOR “IV”,得到一個新的 “IV”。這樣,IV'||c1'||c2||c3'將被解密為p1' || p2' || p3,這正是我們想要的!

    The XSS Part

    后面的 XSS 部分就是白給了。目前我們現在可以加密我們任何想要的東西,接下來我們應該怎么做呢?根據題目的描述,我們必須去獲取一個監控系統的內容,并從中獲得 Mr.Flag Flagger 的信息。并且結合題目給了一個上傳掃碼的接口以及明文塊是 JSON ,很明顯的一個 XSS 題目了,接下來就是我們如何構造這個 XSS Payload 了。

    首先,我們得把生成密文的 API 參數與 JSON 參數的對應關系找出來。這個我們可以通過在 API 參數傳入一些易于區分的數據即可,如下:

    URL:http://207.154.224.121:8080/finalize?lastName=1&firstName=2&origin=3&Gender=4&destination=5
    CipherText:8BAHi37U69MYAnP4O4cHrpRIJrT3dKwv7uRCoLYzU2vnxEOCb6vT0LffcAROX3jPZ+p4yDtKRXwcxYF9B22a3PH3m9tIiEDc3OrwR9W/ACyIcPw7XEJKAyB3QlHiFn2j0HC8P8SpwFqe4A/NRCESLI996IzP9Rkw066eGSuK0MxhpBXGV2gqfm4FAgqTLE3N
    PlainText:b'{"name": "2", "surname": "1", "middle": "4", "time": "2021-07-26 13:37:00", "dest": "5", "dep": "3", "flight": "BLZH1337"}'
    

    所以我們基本可以得到如下的對應關系:

    • lastName: surname
    • firstName: name
    • origin: dep
    • Gender: middle
    • destination: dest

    但是我們應該把 XSS Payload 放在哪兒呢?雖然我們可以一個一個嘗試,但是畢竟太麻煩了,仔細觀察題目頁面內容我們可以大概找到如下一個提示(雖然看起來并不算什么提示):

    所以,我們可以嘗試把 XSS Payload 注入到 JSON 的 Name 參數當中,例如:

    {"name": "", "surname": "1", "middle": "4", "time": "2021-07-26 13:37:00", "dest": "5", "dep": "3", "flight": "BLZH1337"}
    

    但是我們需要注意的是,根據以上密碼學知識,我們首先要在對應的生成密文的 API 處,生成一個 Name 長度與我們 XSS Payload 長度相同的密文,這樣才能不至于解密出錯。例如我們這里的 XSS Payload 長度為 40 ,所以我們也要生成一個 Name 參數長度為 40 的密文,也就是需要我們首先在生成密文的 API 傳入一個長度為 40 的 firstName 參數,并且為了保證其他參數加載到頁面時保證頁面正常,我們最好不要改動其他字段,讓其他字段保持默認值即可。(血淚教訓

    URL:http://207.154.224.121:8080/finalize?lastName=1&firstName=0000000000000000000000000000000000000000&origin=3&Gender=4&destination=5
    PlainText:b'{"name": "0000000000000000000000000000000000000000", "surname": "1", "middle": "4", "time": "2021-07-26 13:37:00", "dest": "5", "dep": "3", "flight": "BLZH1337"}'
    

    在我們得到密文后,我們接下來就需要使用 Padding Oracle 和字節翻轉來改變密文對應的明文, 然后使用 base64 編碼一下,再用 Aztec Code 編碼轉成圖片,并通過 upload API 上傳圖片即可。最后,終于打到了 Admin !

    拿到 Admin Cookie 以及對應頁面內容之后,我們可以直接用 Admin Cookie 登錄到該頁面。登錄之后,發現該頁面只有一個搜索的功能,一開始我還以為是個套娃題,還要 SQL 注入,結果最后按照題目提示搜索了一下 Flagger 就拿到 flag 了…

    整體來說是個密碼題,跟 Web 沒多大關系~希望密碼學選手看完后有所收獲23333(因為 Web 部分純白給,Web 選手就是幫檢查檢查哪里出錯了,然后跑跑 exp ,等等 exp 就行了,該說不說,跑個 exp 還得跑個 1.5 小時,確實折磨~

    paddingctf
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    這是 CyBRICS CTF 2021 中的一個難度為 Hard 的 Web 題(其實是 Crypto 密碼題)。由于作者的某些原因,這個題目在比賽結束都是零解。
    前言在Teaser Dragon CTF 2019中有2道考察Crypto方向的題目,一道Crypto題目,一道Web+Crypto題目,在這里對題目進行一下分析。
    看雪論壇作者ID:roadicing
    現在的CTF比賽中很難在大型比賽中看到棧溢出類型的賽題,而即使遇到了也是多種利用方式組合出現,尤其以棧遷移配合其他利用方式來達到組合拳的效果,本篇文章意旨通過原理+例題的形式帶領讀者一步步理解棧遷移的原理以及在ctf中的應用。
    0x01 苦逼的測試任務 某一天,我照常在學校的CTF群和學長吹水,突然管事的學長在群里發了一張圖,這個月輪到我們學校對省內的某旅游相關企業進行漏洞測試。上面的老師自然而然把這個任務分配給我們CTF戰隊,要求是找到漏洞,能Getshell的點證明能Getshell即可,不要深入利用。
    CDN繞過技術總匯
    2022-05-06 15:41:45
    在HVV培訓以及面試中,有人問了CDN該如何繞過找到目標真實IP,這向來是個老生常談的問題,而且網上大多都有,但是有些不夠全面,今天把繞過CDN全理一理。
    前言感謝前些天D^3CTF的lonely_server出題人,這題很有意思第一次遇見擬態題是在強網杯,雖然只是簡單入門級別的棧溢出,但當時一臉懵逼,完全不了解擬態防御機制。這對需要泄露動態加載庫地址、堆地址、程序基址的方法是扼住咽喉的一個防御方式,使得目標系統的安全性大幅度提升。而要突破這種防御機制,也不是沒有辦法,可以采用逐字節爆破、partial write等技巧不泄露信息來getshell。
    前言:__autoload魔術方法從PHP7.2.0開始被廢棄,并且在PHP8.0.0以上的版本完全廢除。
    請勿利用文章內的相關技術從事非法測試,如因此產生的一切不良后果與文章作者和本公眾號無關。0x02 ASP執行命令腳本這個腳本會被網站安全狗查殺,特征:Shell.Application,當Wscript.shell組件被卸載不可用時就會提示:[Err] ActiveX 部件不能創建對象,這時可嘗試用這個組件來執行命令。<%Set SA = CreateObjectSA.ShellExecute "cmd.exe"," /c set > C:\NpointSoft\npointhost2.2.0\web\1.txt","C:\NpointSoft\npointhost2.2.0\web","",0%>
    mruby是一個Ruby語言的輕量級實現,mruby工作方式類似CPython,它可以將Ruby源碼編譯為字節碼,再在虛擬機中解釋運行。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类