# CS2022 補交 writeup ## hackmd url https://hackmd.io/@Chtsai873/Hyiu52oKo * Crypto * [[HW] DH](#[HW]-DH) * Reverse * [[HW] dropper](#[HW]-dropper) ## [HW] DH * 題目 Hint :::success **[HW] DH [200]** nc edu-ctf.zoolab.org 10104 Something might be helpful [Quadratic_residue](https://en.wikipedia.org/wiki/Quadratic_residue#Prime_modulus) [is_square](https://doc.sagemath.org/html/en/reference/finite_rings/sage/rings/finite_rings/integer_mod.html#sage.rings.finite_rings.integer_mod.IntegerMod_abstract.is_square) [sqrt](https://doc.sagemath.org/html/en/reference/finite_rings/sage/rings/finite_rings/integer_mod.html#sage.rings.finite_rings.integer_mod.IntegerMod_abstract.sqrt) you can solve this challenge without the links above, but some of you might want to use these functions. ::: * 題目程式碼 ```python= #! /usr/bin/python3 from Crypto.Util.number import bytes_to_long, getPrime import random from secret import FLAG # nc edu-ctf.zoolab.org 10104 p = getPrime(1024) assert bytes_to_long(FLAG) < p print(p) g = int(input().strip()) g %= p if g == 1 or g == p - 1: print("Bad :(") exit(0) a = random.randint(2, p - 2) A = pow(g, a, p) if A == 1 or A == p - 1: print("Bad :(") exit(0) b = random.randint(2, p - 2) c = pow(A, b, p) * bytes_to_long(FLAG) % p print(c) ``` * 解題思路 1. 這題明顯是有關 Diffie_Hellman 的題目,但不同於先前的 Dlog 那題,這題的大質數 p ,是直接呼叫 getPrime(1024) 產出的,因此無法再使用先前的手法嘗試,接著繼續往下可以看到,雖然底數 g 是由我們自己輸入的,但題目有特別對可能產生的特例進行防堵,加上了 `g != 1 or g != p-1` 和 `A != 1 or A != p-1` 這兩個限制,加上這兩樣限制導致我們幾乎不太可能對 g 和 A 做手腳,剩下有辦法嘗試的點就只有 `B=pow(g, b, p)` 了。 2. 由於題目並沒有對 B 進行確認,所以我想說可以嘗試著讓`B == 1`,這樣做會導致 `c = pow(A, b, p) * bytes_to_long(FLAG) % p` 中的 `pow(A, b, p) = 1`,因為 pow(A, b, p) = A^b^ mod p = g^ab^ mod p = B^a^ mod p = 1。想著這樣在還原的時候可能可以避免大量的計算,但最後大失敗,不知道如何滿足。 3. 後來才有查到一些資訊,這種題型貌似是需要透過給定 g 來產生一個很小的 subgroup,因此解題前先限定 `p%3==1`,若沒有抽到我們想要的 p 則 exit(0) 重來。再來則是set `g = pow(3, (p - 1) // 3, p)`(前方的底數3似乎可以隨意給一個數?),這樣做則一定會符合`g != 1 or g != p-1`、但有機會剛好使`A == 1 or A == p-1`而失敗,因此需要多試幾次,總共會有兩種失敗的可能,一種是撞到`A != 1 or A != p-1`、另一種則是`p%3!=1`。 4. 最後則會產生 3個可能的模逆元,一一塞進去還原看看就可以找到對的值。 * 解題程式碼 ```python= from pwn import * from Crypto.Util.number import long_to_bytes if __name__=="__main__": r = remote("edu-ctf.zoolab.org", 10104) p = int(r.recvline().decode()) if(p%3 != 1): print("Bad prime :(") exit(0) g = pow(2, (p-1)//3, p) r.sendline(str(g).encode()) c = int(r.recvline().decode()) r.close() for i in range(3): flag = c * pow(g, -i, p) % p print(long_to_bytes(flag)) # f = c % p # print(long_to_bytes(flag)) ``` * 執行結果 ![](https://i.imgur.com/7Z9jOSh.png) ## [HW] dropper * 解題思路 1. 解題首先使用 die 來查看檔案是否有被加殼過,發現似乎有被 UPX 給加殼過,查了一下發現 linux 本身就有指令可以用來解 UPX。 ![](https://i.imgur.com/UFDIsiR.png) ![](https://i.imgur.com/1cVtbTg.png) ![](https://i.imgur.com/d5BD7c0.png) 2. 然後使用 IDA 來查看程式碼,一點開就可以看到主程式,接下來可以看到很多重複做的事,不斷的給陣列賦值然後呼叫 function `sub140001030`,查看後發現 `sub140001030` 會將先前賦值的陣列進行 decode。 ![](https://i.imgur.com/NULkMkT.png) ![](https://i.imgur.com/6ZBExkE.png) 人工搬運還原 ![](https://i.imgur.com/XmOtdTx.png) 3. 實際執行的時候會失敗,用 IDA 動態分析可以看到程式甚至還沒進到 main function 就會發生 exception,在執行 _initterm_e 會報錯,貌似是給的 Last funtion pointer 有誤,但查了很久也不知道要怎麼更改,最後想到有可能是跟 packer 有關係。 ![](https://i.imgur.com/p3ojtsx.png) ![](https://i.imgur.com/Ekq05Ee.png) 4. 後來改成使用 x64dbg 來跑動態分析,跑的是沒有去殼的 dropper,發現跑到一個地方就停住了,下方有紀錄顯示已經載入了 cryptbase.dll,於是再回到 IDA 查看,發現停住的地方是因為被呼叫了一段時間很長的 sleep()。 ![](https://i.imgur.com/4iK4lbC.png) ![](https://i.imgur.com/ahSHI78.png) 而在 x64dbg 有功能可以跳過某段程式碼,所以我就試試讓他不執行 sleep syscall 直接 return,讓程式可以成功跑完。 ![](https://i.imgur.com/nNYNun1.png) 5. 最後回頭來分析整理一下程式碼,大概的內容是會透過呼叫 api 來對 flag 進行加密的流程,結束後會再呼叫 RegCreateKeyA 儲存 key,名稱則是叫做 `CS_2022`,`win+R`打開 regedit 後就會看到剛剛成功執行的FLAG已經跑出來了。 ![](https://i.imgur.com/3OXADgf.png) * 執行結果 ![](https://i.imgur.com/8N1Sf3Q.png)