看雪.WiFi萬能鑰匙 CTF 2017第十二 點評及解題思路

Editor發表於2017-06-28


看雪CTF 2017 比賽進行至第十二題

截止至今天中午12點,第十二題破解人數為 5 人!

防守方 hsadkhk 依然位居首位~,第十二題作者衝進第二位!

此題過後,風間仁依然處於第一位,前十名排名基本保持不變。


比賽進入尾聲,僅剩 3 題。

期待ing......

接下來我們來回顧一下第十二題

看看 看雪評委和出題者是怎麼說的ヾ(๑╹◡╹)ノ"。


看雪評委 netwind 點評


作為一道安卓平臺破解題,作者在反除錯和加密演算法上對apk檔案進行了很好的防護。透過對dex檔案進行hash校驗,隱藏函式名,在so層檢查是否被附加除錯來進行反除錯。在演算法上採用了DES和RC6演算法,需要暴力列舉來破解此題。


作者簡介


liuluping,四川大學資訊電子資訊學院在讀研究生,目前研究方向為軟體安全和漏洞分析,正在進行符號執行和程式自動化分析分析相關的研究,從碩士開始接觸軟體安全,從事程式分析,惡意程式碼分析和檢測的相關方向的研究。

在程式分析上比較擅長協議逆向分析,常見加密演算法識別以及惡意程式碼分析等,在實驗室的日常工作中,接觸較多各種零散類的工作,在安全研究路上還有待成長。


看雪 CTF 2017 第十二題設計思路


題目Flag

Flag:kxuectf{D3crypted1sV3rylntere5tin91}

題目設定時需要說明flag格式:kxuectf{},否則後面涉及到暴力破解,可能會出現題目無解。

安全策略

1. DEX 修改和反除錯檢測

(1)對DEX檔案的SHA-1值進行檢測:從/data/dalvik-cache/目錄中讀出應用安裝儲存的DEX檔案,獲取裡面DEX檔案的SHA-1值,並與應用原始的SHA-1值比較,不相等則退出,隱藏在動態註冊中;

(2)為了防止解題者直接nop掉exit()函式,在進入so中的check函式後首先再進行一次DEX是否修改和反除錯檢測,如果檢測到任意一個就將DES演算法金鑰的第5個和第6個值改掉。

2. 隱藏MainActivity,使得程式不執行配置檔案中的MainActivity 。

安全策略

1.採用符號隱藏策略,將函式名隱藏

2.動態註冊

使用動態註冊將java層的check函式動態註冊為ab函式

Java層演算法

1. 在配置檔案的MainActivity 即Main類中新增了多餘的迷惑解題者的簽名驗證,實際並沒有執行。

2. Java層演算法一共三個,ab.a()、ba.a()、ca.a(),但是真正有用的是ab.a(),一個簡單的移位加密。

Main類中的呼叫順序是:

  str=ca.a(input)+ba.a(input)+ab.a(input)

uv類中的呼叫順序是:

  str= ab.a(input)+ba.a(input)+ca.a(input)

ab.a()、ba.a()函式密文和輸入長度一樣,ca.a()函式密文與輸入長度有關,所以演算法需要對輸入位數的判斷,來確定輸入為多少位。

3. 真正用於so中驗證的只取前36位,也就是真正有用的加密函式只有ab.a()。


Native 層演算法

1. Native層演算法思路簡單,使用兩層加密演算法,分別是RC6和DES。

2. 演算法基本步驟如下:

3. 首先將輸入利用DES進行加密,其中DES金鑰硬編碼在程式中,需要進行5輪。

4. 將加密後的資料分成兩部分,前4輪組成0x20個位元組作為第一部分,而後面一輪的前4個位元組作為另外一部分。

5. 將步驟2中得到的最後一輪前4個位元組替換掉硬編碼的0x10個位元組後作為下一步驟進行加密的RC6金鑰。

6. 利用步驟3中得到的RC6金鑰對步驟1中加密後得到的前半部分(0x20)進行加密後和硬編碼的資料進行比較。

7. 最後根據比較結果得到True或者False並返回。

8. Native層的加密演算法整個流程見PPT第二頁。


相關思路

1. 在步驟1中使用的DES加密演算法為標準的加密演算法,使用的Paddting填充模式,填充資料為(8-(加密長度%8))

2. 在RC6演算法中,對兩個固定引數中的一個稍微進行了修改,引數Q由0x9E3779B9變成了0x61c88647。

3. 為了增加一點難度,這裡沒有直接將輸入經過兩次加密後直接和硬編碼資料進行比較,而是將第一次加密後的部分作為第二次金鑰的一部分。這裡不會造成無解。因為在程式中已經進行了提示flag開頭為kxuectf{,因此前8個位元組固定,經過java層相關變換以及到了native層第一次DES加密時,由於DES也是8位元組分組加密,因此DES加密後輸出8位元組也是固定的,為“\x4c\xd9\xa3\xe6\xed\xfe\xd1\x05”。

這樣在進行解題中,由於知道最後比較結果,知道RC6金鑰的前12個位元組,以及知道RC6加密前的明文的前8個位元組格式,因此這裡一般思路是可以暴力列舉4個位元組,將比較結果進行解密判斷前8個位元組是否等於上面的結果,從而得到RC6的金鑰,進而逆推還原。

4. 但其實只需要破解3個位元組就ok,透過分析程式可知,flag為36個字元,最後一個為},經過java層加密後得到固定字元‘{’,在進行DES加密時,採用padding模式,因此最後一組肯定是”xxx{\x04\x04\x04\x04”。

從這個地方推過去,由於最後一組進行DES加密後會去前4個位元組來替換掉RC6的金鑰,因此列舉最後一組前的3個字元即可,這樣將最後一組進行列舉並一次進行DES加密然後將結果中的前四個位元組取出來替換到硬編碼的RC6金鑰,然後利用RC6金鑰對最後比較的0x20個位元組進行解密,如果接的得到的前8個位元組為“\x4c\xd9\xa3\xe6\xed\xfe\xd1\x05”則表示最後flag的最後四個字元已經確定,並且RC6的金鑰也確定,這樣在再對硬編碼資料進行解密並依次逆推後即可順利得到最後的flag:kxuectf{D3crypted1sV3rylntere5tin91}。


下面選取攻擊者 loudy 的破解分析



使用工具:IDA6.8(so修改、反彙編分析、動態除錯) notepad(記錄) vc6.0(程式設計解碼) android sdk emulator(android4.4虛擬機器)、jeb(dex反編譯)ApkIDE(修改DEX、重打包)

步    驟:

一、反逆向手段繞過

(1)dex層

直接JEB分析,如下圖3處紅線標註的地方有MD5校驗。

看雪.WiFi萬能鑰匙 CTF 2017第十二 點評及解題思路

繞過方法也很簡單,ApkIDE反編譯,修改下圖61行的if-eqz為if-neq,並重新打包,即可直接跳過MD5檢測。打包時,也許會提示某個styles.xml的幾行有問題,刪除那幾行即可。


(2)so層

用IDA連線,程式直接退出,故肯定有反除錯手段。用winrar對apk檔案解包後,發現lib目錄有3個不同的so檔案,如下,其中armeabi-v7a資料夾下的libenjoy.so檔案才是我的android4.4系統需要的,分析此檔案。


IDA載入,靜態分析,沒有發現.init表,那麼我們重點分析JNI_OnLoad函式。其中sub_24B8和sub_281C是兩個主要的反除錯函式。

看雪.WiFi萬能鑰匙 CTF 2017第十二 點評及解題思路

而sub_2350和sub_2378都是同一種形式的解碼函式,即按位元組依次與0x16異或,解密結果都是字串,這種函式後面還有很多,不一一介紹。

JNI_OnLoad中第14行解碼結果為“check”,也即dex呼叫的本地函式,再看17行,跟蹤到如下位置,發現check函式的實際地址為sub_3748。

而此處作者也用了一個小手段,自己新建了一個名為.mytext的可執行段,sub_3748在其中實現,導致IDA在預設的.text段找不到該函式,有一定的反逆向效果。

再看JNI_OnLoad函式呼叫的sub_24B8函式,該函式的主要作用是讀取/data/dalvik-cache/目錄下的data@app@com.game.kxctf-1.apk@classes.dex檔案或者data@app@com.game.kxctf-2.apk@classes.dex檔案的校驗值,結果與“E07784C2A2923354F18952BBDB5D81916D24D2F5”比較,不等於則exit(0)退出程式。

繞過方法也很簡單直接將JNI_OnLoad中對該函式的呼叫以0x00覆蓋即可。

JNI_OnLoad函式呼叫的sub_281C函式,實際就是呼叫sub_1B04如下,該函式建立了一個執行緒。

跟進後,發現執行緒主體如下。其中sub_25A8函式獲取/proc/%d/status的State,看其中是否包含“R/S/T”。sub_2628獲取/proc/%d/status的TracerPid,看是否為非0值。sub_2688執行cat/proc/%d/wchan,看結果中是否包含sys_epoll或者ptrace_stop。sub_24B8之前已經分析。

從分析可以知道,該執行緒沒有影響演算法流程,因此也可以直接在JNI_OnLoad中將對sub_281C的呼叫以0x00覆蓋,實現繞過。

最後在check函式sub_3748開頭還有一處反除錯,如下,分析發現sub_27B4功能和sub_24B8一模一樣,不再累述,直接將呼叫處用0x00覆蓋實現繞過。


二、演算法流程分析

反逆向手段清除後就可以大膽除錯,跟蹤演算法流程了。流程分dex層和so層兩個方面講。

(1)dex層

JEB分析結果很直觀,將三個函式分別對輸入字串的處理結果連線起來,送入so層處理,如果so層處理結果為1,則正確。

ca.a是base64編碼,ba.a、ab.a也分別是一種編碼,雖然有三種編碼,但實際分析後發現,ca.a和ba.a都不起作用,實際只有ab.a參與了演算法流程,該函式如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
publicstaticStringa(Stringinput) {
returnab.encrypt(input);
    }
publicstaticStringencrypt(Stringinput) {
Stringv3 = "";
intv2;
for(v2 = 0; v2<input.length(); ++v2) {
intv0 = input.charAt(v2);
if(v0<97 v0="">109) {
if(v0>= 65&&v0<= 77) {
v3 = String.valueOf(v3) + (((char)(v0 + 13)));
gotolabel_20;
                }
if(v0>= 110&&v0<= 122) {
v3 = String.valueOf(v3) + (((char)(v0 - 13)));
gotolabel_20;
                }
if(v0>= 78&&v0<= 90) {
v3 = String.valueOf(v3) + (((char)(v0 - 13)));
gotolabel_20;
                }
if(v0>= 48&&v0<= 57) {
v3 = String.valueOf(v3) + (((char)(v0 ^ 7)));
gotolabel_20;
                }
v3 = String.valueOf(v3) + (((char)(v0 ^ 6)));
            }
else {
v3 = String.valueOf(v3) + (((char)(v0 + 13)));
            }
label_20:
        }
returnv3;
    }

此時無需更加深入分析,可以等so反推之後再爆破。

(2)so層

so層流程其實很簡單。

1、   判斷dex層傳來的資料是否大於0x78(其實此時就是0x78),大於則over。

2、   擷取前0x24位(即dex層ab.a函式處理結果),並在結尾補4個4成0x28位。

3、   用金鑰{0xFD, 0xB4, 0x68, 0x54, 0x08, 0xCD, 0x56, 0x4E}對此0x28位進行DES加密(非標準)(實際操作時,金鑰轉換為子金鑰總有一兩位不同,故直接在記憶體提取子金鑰表,取消金鑰生成過程)

4、   DES結果第0x20、0x21、0x22,0x23位替換金鑰{0x65, 0x48, 0x32, 0xEF, 0xBA, 0xCD, 0x56, 0x4E, 0x0F, 0x9B, 0x1D,0x27, 0x0, 0x0, 0x0, 0x0}的最後四位。

5、   用上一步的金鑰對DES結果前0x20位進行RC6加密。

6、   RC6加密(非標準)結果與0x42D3C3C2, 0xF12AE92D, 0x66C92822, 0x2CEB540E,0x9407E577, 0x4A92B792, 0x2E5DFDF0, 0xF3549FC6比較,全部相等則註冊成功。

三、破解過程

流程梳理清楚就好了,首先透過除錯修改標準DES和RC6與程式所用一致。

其中DES修改較小,RC6修改較大,分別如下。一般RC6加密Q值為0x9E3779B9,而靜態分析可知此程式為0x61C88647(即-0x9E3779B9)。

另外RC6的金鑰會有個反饋,不是每次塊(0x10位元組)加密都用同一個金鑰,對照IDA將C程式碼做如下修改,其中kerr為下一次加解密的金鑰。

看雪.WiFi萬能鑰匙 CTF 2017第十二 點評及解題思路

這兩個函式調整好後(這是一個漫長的跟蹤過程),事情就好辦了。

由於DES為ECB模式,8位元組獨立加密。根據已知條件前8位元組為“kxuectf{”得到加密結果0x4C,0xD9, 0xA3, 0xE6, 0xED, 0xFE, 0xD1,0x05。

由最後一個位元組為“}”, ab.a函式處理後為“{”,所以DES加密的最後8位元組為x,x,x,{,4,4,4,4,其中x為未知數。編寫如下程式碼爆破,得到結果“a>6”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for(char i=0x20;i<0x7f;i++)< code="">
         for(char j=0x20;j<0x7f;j++)< code="">
              for(char k=0x20;k<0x7f;k++)< code="">
              {
                       str[0] = i;
                       str[1] = j;
                       str[2] = k;
                       memset ( str2, 0, sizeof ( str2 ) );
                       Des_Run ( str2, str, ENCRYPT );
                       key1[12]=str2[0];
                       key1[13]=str2[1];
                       key1[14]=str2[2];
                       key1[15]=str2[3];
                       rc6_key_setup(key1, 16);
                       rc6_block_decrypt(ct, pt);
                       if(pt[0]==stro[0] &&pt[1]==stro[1])
                       {
                            printf("%c%c%c%c\n",str[0],str[1],str[2],str[3]);
                       }
              }

由此得到正確RC6金鑰{0x65, 0x48, 0x32,0xEF, 0xBA, 0xCD, 0x56, 0x4E, 0x0F, 0x9B, 0x1D, 0x27, 0x13, 0x6a, 0x7e, 0x1F}。

利用此金鑰對0x42D3C3C2, 0xF12AE92D, 0x66C92822, 0x2CEB540E,0x9407E577, 0x4A92B792, 0x2E5DFDF0, 0xF3549FC6解密,得到結果“xkhrpgs}Q4pelcgrq6fI4elyagrer2gv”,加上“a>6{”,即“xkhrpgs}Q4pelcgrq6fI4elyagrer2gv a>6{”為正確的進入so的字串。

透過如下函式爆破。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include 
char test[] = "xkhrpgs}Q4pelcgrq6fI4elyagrer2gva>6{";
char getit(char a){
    if(a<97 a="">109){
            if(a>=48 && a <=57){< code="">
                return  a^7;
            }
            else if(a>=65&&a<=77){< code="">
                return a+13;
            }
            else if(a>=78&&a<=90){< code="">
                return a-13;
            }
            else if(a>=110 && a<=122){< code="">
                return a-13;
            }
            else{
                return a^6;
            }
    }
    else{
            return a+13;
    }   
}
  
int main(){
    for(int j=0;j<36;j++){< code="">
        printf("%d ",j);
        for(char a=0x20;;a++){
            if(getit(a)==test[j]){
                printf("%c ",a);
            }
            if(a==0x7f) break;
        }
        printf("\n");
    }
    return 0;
}

得到結果如下。

如果剔除掉特殊字元,得到註冊碼為

看雪.WiFi萬能鑰匙 CTF 2017第十二 點評及解題思路

“kxuectf{D3crypted1sV3rylntere5tin91}”輸入模擬器,正確。




最後感謝 WiFi 萬能鑰匙安全應急響應中心的贊助支援,

接下來的比賽大家一定要使出洪荒之力哦!↖(^ω^)↗

比心 ❤



贊助商

上海連尚網路科技有限公司成立於 2013 年,是一家專注於提供免費上網和內容服務的移動網際網路企業。連尚網路自主研發的核心產品 WiFi 萬能鑰匙,以分享經濟的模式,透過雲端計算和大資料技術,利用熱點主人分享的閒置WiFi資源,為使用者提供免費、穩定、安全的上網服務,以幫助更多的人上網,找到屬於他們的機會,改變自己的命運。



相關文章