[題目板] [計分板] [解題概要] [回首頁]

HIT 2011 Wargame 解題概要


Web3

不能暴錯顯示資料 不能union顯示資料 不能Blind取得資料

只會顯示空白頁面 or "500 Error"

完整SQL語法 sql = "Select a.id, " + s + ", c.user, d.pwd, e.mail from id as a, name as b, user as c, pwd as d, _mail as e" s是使用者可以控制的部分

但是可以透過此種方式 (select top 1 case len(name) when 3 then cast(user as int) end from sysobjects where xtype='U') 強制判斷式成立顯示 "500 Error" 來取得資料


Web4

利用Mysql Error base的方法取得資料內容 關鍵字 "Duplicate Column name" 以及關鍵字 "Duplicate Entry"

取出的資料,密碼是用Mysql old_password加密,此加密法存在碰撞(collision),碰撞出明文不同但hash相同的密碼即可登入


Forensics 1

K 島用的幾乎都是日本 futaba 系統改良,這個系統的發文者 ID 產生類似 DES(IP Addr + DATE + SALT) ( C.F. http://pixmicat.wikidot.com/pmcfeature ) 提示有說,發文者在相同 IP Addr、相同日期、不同板,發了好幾篇跟 HIT 有關的文章 每個板的 SALT 可能會不同,本題關鍵在至少要解出一個板的 SALT 提示又說是台灣 ISP,那就把 TWNIC 的台灣 IP 列表帶進去跑 根據關主測試,MacBook Air 用 php crypt() 大約跑 5 分鐘可以找到正確的發文者 IP Addr,也就是 ans key.

Komica has many board system derived from futaba. They generate author ID similar to DES(IP Addr + DATE + SALT). Hint#1 says the same author (same IP Addr) posted several articles on different boards on the same date. The essential concept is to obtain at least one SALT of any of these boards, since every board might use different SALT. Hint#2 says it's a Taiwan major ISP, therefore we need Taiwan IP range list, which can be found on TWNIC.net. According to my test on MacBook Air, php crypt() took about 5 min to find the correct author's IP Addr (which is the ans key).


Forensics 4

不懂 FFS 格式沒關係,看檔案裡面的字串好像是 base64,有些解回來怪怪的可能還需 rot13。那麼它們是先 rot13 還是先 base64?值得嘗試。


Crypto 2

So, how to solve the question? 1. Look into the packet capture. We found: (1).Client -> Server: AUTH admin (AUTH_REQ) (2).Server -> Client: 100 continue\r\n (AUTH_RSP) (3).Client -> Server: DATA (DATA_REQ) (4).Server -> Client: 200 OK\r\n (DATA_RSP)

That's a homemade authentication protocol. The question is to break the authentication mechanism to get the token.

Replay-attack!

  1. Decode the . They're binary data and 16-bytes aligned and the last 16 bytes of AUTH_REQ, AUTH_RSP and DATA_REQ packet are the same.

  2. strings mycleint => We saw “aes-128-ecb”. The program used AES with 128bits key and ECB mode! That means the block size is 16 byes and the same plaintext will always produce the same cipher-text. The should be encrypted by AES.

  3. Re-playing the AUTH_REQ in demo.cap to server; we got "100 continue", but the are different than the origin (e.g. the first and the third 16 bytes are the same, but the second 16 bytes are different.). Re-playing the DATA_REQ to server; we got "403 Forbidden"

  4. Modify the first 16 bytes of the AUTH_REQ and replay to server; we got "403 Forbidden". Modify the second 16 bytes of the AUTH_REQ and replay to server; we got "100 continue" with different (e.g. only the third 16 bytes are same as original, the first and the second are different.)

The second 16-bytes of AUTH_REQ will affect the first 16-bytes of AUTH_RSP. Could they be a challenge (client) and a response (server)?

Guessing-1: Client -> Server: Ek[Unknown1, Client challenge, Unknown2]; Server -> Client: Ek[Re Client_ challenge, Unknown3, Unknown2]; Client -> Server: Ek[Unknown4, Unknown5, Unknown2]; Server -> Client: Ek[];

Will the second 16-bytes of the second packet (server response) be server’s challenge?

Guessing-2: Client -> Server: Ek[Unknown1, Client challenge, Unknown2]; Server -> Client: Ek[Re_Client challenge, Server_challenge, Unknown2]; Client -> Server: Ek[Re_Server_challenge, Unknown5, Unknown2]; Server -> Client: Ek[];

  1. Verify our guessing. To generate the correct data request, we need to generate the correct encrypted “Re_Server_challenge”. We didn’t know the “Server_challenge” and didn’t know the key to encrypt “Re_Server_challenge”. How can we do that?

We knew the system is replay-able. We’re able to ask server to tell us the answer (the encrypted version of “Re_Server_challenge”). (1): Client -> Server: AUTH admin Ek[Unknown1, Client challenge, Unknown2]; (2): Server -> Client: 100 continue\r\nEk[Re_Client challenge, Server_challenge, Unknown2];

(1): Client -> Server: AUTH admin Ek[Unknown1, Server_challenge, Unknown2]; (2): Server -> Client: 100 continue\r\nEk[Re_Server_challenge, Other_server_challenge, Unknown2];

(3): Client -> Server: DATA Ek[Re_Server_challenge, Unknown5, Unknown2]; (4): Server->Client: 200 OK\r\nEk[]

Good! We passed the authentication, but how can we decrypt the response?

  1. What’s the next step? What are the Unknown-1, Unknown-2 and Unknown-5? Why unknown-2 are the same in the first three packets?

The answer is padding. OpenSSL’s crypto uses PKCS padding. “PKCS padding works by adding n padding bytes of value n to make the total length of the encrypted data a multiple of the block size”. In our case, it should be 16 0x10.

  1. Why the DATA_REQ has different padding? Does it indicate the key was different? The unknown-5 may be a session key and the DATA_RSP may be encrypted by the session-key.

Guessing-3: Client -> Server: Ek[Unknown1, Client challenge, Padding]; Server -> Client: Ek[Re_Client challenge, Server_challenge, Padding]; Client -> Server: Ek[Re_Server_challenge, SessionKey, Padding]; Server -> Client: Esk[];

  1. To decrypt the token, we need to known the session key and its encrypted version. Luckily, we had known the plain-text and cipher-text of the 16-bytes padding.

(1): Client -> Server: AUTH admin Ek[Unknown1, Client challenge, Padding]; (2): Server -> Client: 100 continue\r\nEk[Re_Client challenge, Server_challenge, Padding];

(1): Client -> Server: AUTH admin Ek[Unknown1, Server_challenge, Padding]; (2): Server -> Client: 100 continue\r\nEk[Re_Server_challenge, Other_server_challenge, Padding];

(3): Client -> Server: DATA Ek[Re_Server_challenge, Padding, Padding]; (4): Server->Client: 200 OK\r\nEpadding[]

Dpadding[] = “Well, the designer knew something about replay-attack, but he should use non-ECB-mode and HMAC.”


Binary 3

條件斷點,斷點Keydown事件在處理 Windows Queue的地方 找到輸入按鍵比對字串的地方會發現輸入特定的按鍵即會跳出Key 關鍵地址在記憶體位置 0x0040394A


Binary 4

開進 EFI shell 跑過一次程式,再想辦法取出 EFI variable "HIT2011" 即可。EDK 有提供 nt32 模擬環境,所以也可在 Windows 環境下進行。


Binary 5

這是一個正常的 Windows 程式 EXE 檔,這種檔案除了能在 Windows 環境中執行以外,還可以在哪些系統直接執行呢?


Binary 7

我們Trace該程式碼發現在按下OK鍵之後由00401079開始處理, 其中取得ID, Key, 檢查長度後分別存入004038AC+418與004038AC+438, 之後呼叫004011B0, 初始化一些記憶體, 之後跳到, 0040120D, 再到00401235, 之後可以看到他重覆呼叫00401257, Trace進去發現其存取004038AC+[EAX+28]之地址, 並將其內容對應至一位於00403000的Jump Table. 由此可見此題為VM架構.

要解析VM架構就必須了解其指令集, 我們可以透過00403000的Jump Table, 與其指向的函數了解其指令集, 大致發現其指令集類似Java, 但所有運算皆為128bit.

從初始化記憶體那段可以發現VM內部程式碼開始點為004038AC, 解析出來VM內部的程式邏輯為:

char OutMsg[20]; char AuthMsg[48]; char InMsgID[32]; char InMsgKey[32];

int128 MsgDec_m = 340282366920938463463374607431761032153;

void start() { int128 ID = B64Int( InMsgID ); int128 Key = B64Int( InMsgKey ); int128 res = modexp( 5, Key, 19596767773127812424957864437383827 ); ((int128)OutMsg)[0] = ((res==ID)?(105301654095313299654048370191391060467199703652194577528229132243031796280551): (197680031326451432482662674009500026802*310064492916542235219739349638128050902))%MsgDec_m; // 若正確, 將"Correct!"寫入OutMsg, 若錯誤, 將"Wrong!"寫入OutMsg

((int128)AuthMsg)[0] = (195766984014281786082357898559783470710254470429003189982004899291357099989685)%MsgDec_m; ((int128)AuthMsg)[1] = (266135957779003895313225526866976331010272238919995820160251614705487299352748)%MsgDec_m; ((int128)AuthMsg)[2] = (81991844101130129097540507137711880334242030473429073614653007885595652833609)%MsgDec_m; };

char B64Tbl[256] = { 略 }; int128 B64Int( char s ) { int128 i; int128 res = 0; int128 cd = 1; int128 conv; for ( i = 0;; i++ ) { if ( s[i] == 0 ) { break; } conv = B64Tbl[s[i]]; if ( conv == 255 ) { ((int128)OutMsg)[0] = (245342629657944979472966687916456702819134346232835026316012844649565736484219)%340282366920938463463374607431761032153; } res = res + cdconv; cd = cd * 64; } return res; };

int128 modexp( int128 b, int128 p, int128 m ) { b = b % m; if ( p == 1 ) { return b; } int hp = p/2; int rv = modexp( b, hp, m ); rv = ( rv * rv ) % m; if ( p%2 != 0 ) rv = ( rv * b ) % m; return rv; }

其中, (x*y)%z的形式, z大多為MsgDec_m, 這是用簡單的乘法來將字串加碼, 避免被字串搜尋.

modexp是Modulo Exponentiation函數. B64Int是將Base64字串轉換為數字的函數.

從程式邏輯可以看出, 我們要求解Log5 B64Int("HITCON2011") (mod 19596767773127812424957864437383827). 這是一個離散對數的問題.

經計算得知 B64Int("HITCON2011") = 969913718232658439

在求解之前, 我們先將(19596767773127812424957864437383827-1)拿去因數分解 (19596767773127812424957864437383827-1) = 2×314621297×314621339×314621959×314622029

這可以看出整數模19596767773127812424957864437383827的乘法群的元素個數為一個Smooth Number, 因為他是多個差不多大小的直數所組成, 而不是少數個(i.e 3)很大的質數所組成.

這樣的乘法群中求離散對數, 可以使用Pohlig Hellman方法, 上網搜尋有一Java寫的線上離散對數計算機, 其使用Pohlig Hellman方法. 使用其可得以下結果: 51809532300910690594095483870361979 (mod 19596767773127812424957864437383827 ) = 969913718232658439

將答案 1809532300910690594095483870361979轉成Base64 格式得到: 7lbC7Fap6azDaUjg3kF