2021 Google CTF - ADSPAM
題目:ADSPAM
ADSPAM
Challenge Info
We’ve intercepted this demo build of a new ad spam bot, see if you can find anything interesting.
adspam.2021.ctfcompetition.com 1337
app-release.apk
Solution
實際執行只看到一條訊息:

apk 總之先反編譯(APKLab 還不錯用)
在 a/a/e.java 發現有用到 socket
tcpdump 聽封包
先搞好一台 root VM,ARM 架構的話,這邊可以直接下載,我的是 i386 只好自己編:
| |
編好之後丟進 VM
| |
聽封包:
| |
發現會跟 adspam.2021.ctfcompetition.com 1337 溝通,並且送了很 base64 的東西出去,但解出來不是明文:

靜態分析
在 ad/spam/NativeAdapter.java 看到有 load libnative-lib.so 進來,所以把 librart 丟 IDA:
| |
順帶一提 load library 的方法有兩種,用第二種的話就要自己處理架構問題:
| |
但無論使用哪種方式,library load 進來之後會呼叫 JNI_OnLoad,並且要使用 library function 的話,宣告方式如下:
| |
而 function linking 有分兩種方式:
1. Dynamic Linking:使用 JNI Native Method Name Resolving
比如說 class com.android.interesting.Stuff 有個 function
| |
在 library 內的名稱要設定為 Java_com_android_interesting_Stuff_doThingsInNativeLibrary
2. Static Linking:使用 RegisterNatives API
API 長這樣,需要 class instance, function name, function pointer 還有 JNI Type Signatures
| |
實際逆向的時候 JNI Functions 會是 JNIEnv + offset 的方式呈現,不過 IDA 支援度很好,把 type 修正回 JNIEnv* 就可以了
找到相關 function 之後逆一逆,這邊就簡單說明結果:
rick_decode_445A0:用'cFUgdW9ZIGV2aUcgYW5ub0cgcmV2ZU4='做 xor decode(base64 解碼後是倒過來的 rick rollpU uoY eviG annoG reveN= =)declicstr:拿來 RSA pubkey 來解密decrypt:AES/ECB/PKCS5Padding解密encrypt:AES/ECB/PKCS5Padding加密oktorun:沒認真看,可能是 anti-debug,檢查有沒有用frida.server之類的transform:把 byte array 拿去rick_decode_445A0
Java 加解密用 python 實作實在不習慣 QQ,這邊簡單紀錄一下
declicstr()
1 2 3 4 5 6 7 8byte[] declicstr(byte[] enc_lic) { X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedPubKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey RSA_pubkey = keyFactory.generatePublic(keySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, RSA_pubkey); return cipher.doFinal(enc_lic); }轉換成:
1 2 3def declicstr(encodedPubKey): key = RSA.import_key(encodedPubKey) res = pow(int.from_bytes(secret, 'big'), key.e, key.n)
如此一來能解密(AES)封包內容了,發送的內容:
| |
server 回傳資料:
| |
看來 data 是顯示通知時的訊息,嘗試改 name, is_admin 會回傳這類訊息,網址應該是隨機的:
| |
程式核心邏輯在 ad/spam/MainActivity.java 跟 a/a/f.java,可以知道要把 res/law/lic 每行取出來 base64 decode,然後 declicstr
res/raw/lic 長這樣:
| |
每行 license 會解成 4 bytes 資料,串起來長這樣:
| |
這邊感覺要用通靈的?總之發現它是由長度跟資料組成:
| |
看起來三組分別代表 name, 某個 ID 跟 is_admin,所以能利用現有的 license 片段把 is_admin 的值改成非 0
好像要黑箱測試 server 規則,在嘗試時有收到這個回傳訊息,所以能確定 is_admin 要是一個十進位數字
| |
我最後構造出來的 license 長這樣,讓第三個 block 是十進位數字就好,後面有沒有符合格式好像不影響,甚至內容跟 payload 的資料不一樣也沒關係,頗奇妙
| |
最後的腳本:
| |
Flag
CTF{n0w_u_kn0w_h0w_n0t_t0_l1c3n53_ur_b0t}
意外的沒說到很難