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 8
byte[] 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 3
def 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}
意外的沒說到很難