看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

Editor發表於2021-12-03

看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


看雪·眾安 2021 KCTF秋季賽的第七題《聲名遠揚》已於今天中午12點截止答題,經統計,本題圍觀人數多達1179人,共計20支戰隊成功破解。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


恭喜金左手用時4002秒拿下“一血”,接下來和我一起來看看該賽題的設計思路和相關解析吧~



出題團隊簡介


第七題《聲名遠揚》出題方 洋洋不得意 戰隊:


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析



賽題設計思路


在win64中,程式碼段暫存器0x23和0x33所對應的GDT表項中CPU的模式分別為32位與64位。


具體原理參考連結:https://bbs.pediy.com/thread-221236.htm


設計思路:

1、exe程式編譯為32位程式,把核心判斷程式碼放入64位程式碼中。

2、加密用base64改了一下編碼表,編碼表特別好找,單步跟就能看到。

3、校驗flag的流程是線性的,沒有反除錯,加了一丟丟的垃圾指令(可能你們都注意不到)。只要找到onclick函式,一路單步就可以看到輸出的結果。

4、把關鍵字串隱藏起來,執行時解密出來。比如:"正確","錯誤",flag編碼後的字串。隱藏字串演算法是異或。


流程如下:

1、把輸入的flag進行base64編碼;

2、把編碼結果丟到64位程式碼中對比,接收對比結果;

3、把對比結果輸出。


破解思路:

1、找到64位程式碼中flag加密後的資料。

2、找到base64編碼表。

3、通過base64演算法還原flag。



賽題解析


本賽題解析由看雪論壇sunfishi給出:

看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

考察C++逆向。

 

總體思路:動態除錯,黑盒測試。

 

Windows 32位程式,無殼。

 

注:以下分析如未作特殊說明,預設基址為0x251000。

 

題目存在一些花指令,不多做闡述,nop修復即可。

 

比較多的虛擬函式,同時能看到關鍵詞DuiLib。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


有關DuiLib能夠從網上找到N篇文章介紹切入點,隨便掛一個。

 

Dump微信PC端的介面Duilib檔案-軟體逆向-看雪論壇-安全社群|安全招聘|bbs.pediy.com(https://bbs.pediy.com/thread-259443.htm)

 

通過虛擬函式跳轉最終定位到按鈕回撥函式sub_26D2D0。

 

簡單除錯分析後,能夠發現首先是進行了變表的base64編碼,具體位置在0x26E530,虛擬碼分析特徵還是比較明顯的。

 

看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

 

當然最大的特徵當屬編碼表,其特徵位於函式0x26E250,虛擬碼如下:

int __cdecl base64Maps(int a1){  int *v1; // eax  char v3[8]; // [esp+4h] [ebp-68h] BYREF  int v4; // [esp+Ch] [ebp-60h]  _BYTE base64[65]; // [esp+10h] [ebp-5Ch] BYREF  unsigned int v6[2]; // [esp+51h] [ebp-1Bh] BYREF  int v7; // [esp+68h] [ebp-4h]   v4 = 0;  sub_26D5D0((char *)v6 + 3, 8u);  qmemcpy(base64, "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!", sizeof(base64));  v1 = (int *)__FrameHandler3::TryBlockMap::TryBlockMap(                (__FrameHandler3::TryBlockMap *)v3,                (const struct _s_FuncInfo *)base64,                (unsigned int)v6);  sub_26EA90((char *)v6 + 3, *v1, v1[1]);  v7 = 0;  sub_26EAD0((void *)a1, (int)v6 + 3);  v4 |= 1u;  v7 = -1;  sub_26EA70();  return a1;}


下面進行驗證。

 

動態除錯得到編碼串:


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


嘗試變表解密:

import base64 baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!" cipherText = "EHsnG0bjGT44BqIhETj!"cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps))print(base64.b64decode(cipherText.encode('utf-8')).decode())


解得lovectf{mas0n},驗證成功。

 

進入下一步。

 

經過除錯分析確定總體邏輯如下圖:


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


單步除錯過程中會發現在verify執行至一個段間跳轉時出現異常。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


反覆除錯後,最終發現突破點在於其進行原跳轉前,32位至64位模式的切換。

 

只不過在這裡的表現形式與往常有所不同。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


關於32位程式至64位程式的切換,可以參考文章:

 

CTF中32位程式呼叫64位程式碼的逆向方法 - 安全客,安全資訊平臺 (anquanke.com)(https://www.anquanke.com/post/id/171111)

 

知曉其模式切換後,強制指定PE64標識。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


放入IDA,為方便定位函式,Rebase Segment,基址設為0。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


通過除錯得到呼叫函式地址,減去基址後得到偏移量0x146f0。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


定位函式,得到虛擬碼後,看到了函式呼叫。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


彙編下形式為call rdi。


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


翻找之後能夠知道rdi來自於指令mov rdi, [rsp+arg_0]。

 

向上分析


看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析


確定函式偏移為0x145AC。

 

簡單分析虛擬碼,結合程式碼複用,能夠確定其check邏輯如下:

__int64 __fastcall sub_145AC(char *a1, __int64 a2){  unsigned int v4; // edx  char v5; // al  __int64 v6; // rcx  int v7; // edx  char v8; // al  char *v9; // rcx  __int64 v10; // rax  unsigned int v11; // edx  char v12; // al  __int64 v13; // rcx  char v14; // cl  __int64 v15; // r8  int *v16; // rax  char v17; // al  __int64 v18; // rcx  char v19; // cl  __int64 v20; // r8  int *v21; // rax  int v23; // [rsp+4h] [rbp-3Ch] BYREF  char v24; // [rsp+8h] [rbp-38h]  __int16 v25; // [rsp+9h] [rbp-37h]  char v26; // [rsp+Bh] [rbp-35h]  unsigned int v27; // [rsp+Ch] [rbp-34h]  char v28[48]; // [rsp+10h] [rbp-30h] BYREF   v27 = 44;  v4 = 0;  *(__m128i *)v28 = _mm_load_si128((const __m128i *)&xmmword_14408);  *(__m128i *)&v28[32] = _mm_load_si128((const __m128i *)&xmmword_143F8);  *(__m128i *)&v28[16] = _mm_load_si128(xmmword_14418);  do  {    v5 = v4 - 52;    v6 = v4++;    v28[v6] ^= v5;  }  while ( v4 < v27 );                           // 還原base64編碼串  v28[v27] = 0;  v7 = 0;  v8 = *a1;  if ( *a1 )  {    v9 = a1;    do    {      if ( v8 != v9[v28 - a1] )                 // strcmp        break;      ++v9;      ++v7;      v8 = *v9;    }    while ( *v9 );  }  v10 = v7;  v11 = 0;  v24 = -48;  if ( a1[v10] == v28[v10] )                    // bingo  {    v23 = 0x78063019;                           // 正確    v25 = 0;    v26 = 0;    do    {      v12 = v11 - 52;      v13 = v11++;      *((_BYTE *)&v23 + v13) ^= v12;    }    while ( v11 < 4 );    v24 = 0;    v14 = v23;    if ( (_BYTE)v23 )    {      v15 = a2 - (_QWORD)&v23;      v16 = &v23;      do      {        *((_BYTE *)v16 + v15) = v14;        v16 = (int *)((char *)v16 + 1);        v14 = *(_BYTE *)v16;      }      while ( *(_BYTE *)v16 );    }  }  else  {    v23 = 0x3C002078;                           // 錯誤    v25 = 0;    v26 = 0;    do    {      v17 = v11 - 52;      v18 = v11++;      *((_BYTE *)&v23 + v18) ^= v17;    }    while ( v11 < 4 );    v24 = 0;    v19 = v23;    if ( (_BYTE)v23 )    {      v20 = a2 - (_QWORD)&v23;      v21 = &v23;      do      {        *((_BYTE *)v21 + v20) = v19;        v21 = (int *)((char *)v21 + 1);        v19 = *(_BYTE *)v21;      }      while ( *(_BYTE *)v21 );    }  }  return 0i64;}


簡單異或還原明文驗證猜想。

"""v23 = 0x78063019;    v25 = 0;    v26 = 0;    do    {      v12 = v11 - 52;      v13 = v11++;      *((_BYTE *)&v23 + v13) ^= v12;    }    while ( v11 < 4 );""" v11 = 0v23 = bytearray(int.to_bytes(0x78063019, length=4, byteorder="little")) while 1:    v12 = v11 - 52    v13 = v11    v11 += 1    v23[v13] ^= v12 & 0xff     if v11 >= 4:        breakprint(v23.decode('gbk'))# 正確


最終解題指令碼如下:

import base64 """  v27 = 44;  v4 = 0;  *(__m128i *)v28 = _mm_load_si128((const __m128i *)&xmmword_15408);  *(__m128i *)&v28[32] = _mm_load_si128((const __m128i *)&xmmword_153F8);  *(__m128i *)&v28[16] = _mm_load_si128(xmmword_15418);  do  {    v5 = v4 - 52;    v6 = v4++;    v28[v6] ^= v5;  }  while ( v4 < v27 );  v28[v27] = 0;""" xmmArr = [0x0B3E38188BB9CBA9DBAFFB697ABA2948B, 0x0BFDBD9AAD6D4BCA1878490B0B5AE858C, 0x0F8D6D7A7BAB89480B78A94B9AE]v27 = 44v28 = b''for xmm in xmmArr:    v28 += int.to_bytes(xmm, length=16, byteorder="little")v28 = bytearray(v28) v4 = 0while 1:    v5 = v4 - 52    v6 = v4    v4 += 1    v28[v6] ^= v5 & 0xff     if v4 >= v27:        breakv28[v27] = 0print(v28)cipherText = v28.decode() baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!" cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps))print(base64.b64decode(cipherText.encode('utf-8')).decode())



往期解析


1、看雪·眾安 2021 KCTF 秋季賽 | 第二題設計思路及解析


2、看雪·眾安 2021 KCTF 秋季賽 | 第三題設計思路及解析


3、看雪·眾安 2021 KCTF 秋季賽 | 第四題設計思路及解析


4、看雪·眾安 2021 KCTF 秋季賽 | 第五題設計思路及解析


5、看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析



看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

第八題《群狼環伺》正在火熱進行中,

還在等什麼,快來參賽吧!


- End -



看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析

公眾號ID:ikanxue

官方微博:看雪安全

商務合作:wsc@kanxue.com

相關文章