面向新手的CTF實戰教學(一)

2019-06-02 +8 89018人圍觀 ,發現 7 個不明物體 網絡安全

參加CTF(Capture The Flag)是提高網絡安全技術水平的一種有效途徑,其解題模式中主要提供了逆向、漏洞挖掘與利用、Web滲透等題目,作為業余愛好者,這不失為一種鍛煉自己思路的好方法。2019年03月10日開始的“2019看雪CTF(第一季)”已經落下帷幕,本文就從第一關開始,詳細解析本季CTF的比賽題目。本文面向讀者為初學者,所以部分分析過程可能會詳細一些, 高手可以略過 。

一、初窺門徑

首先,我們登錄ctf.pediy.com,找到“2019看雪CTF晉級賽Q1”,然后就從第1關開始我們的奪旗之路吧。

第一關“流浪者”,題目要求輸入正確的注冊碼。我們將程序下載下載后雙擊,會出現一個輸入注冊碼的界面。此時我們隨意輸入一串字符并點擊“驗證”,程序會彈出一個消息框,提示我們輸入錯誤。如圖:

2.png

如果你曾有些逆向的入門知識的話,這算是逆向工程中最常見的場景了。按照常規的軟件運行邏輯(獲取輸入的字符串 –> 對字符串進行處理 –> 與真正的注冊碼做比較  –> 如果正確則進行下一步、錯誤則彈出消息框),我們只需進行反向操作,即先根據消息框上面的文字,找到調用該消息框的函數,再通過層層回溯,找到處理字符串的關鍵函數,進而窺探出注冊碼的廬山真面目。那么在這里,我們需要記下消息框上的關鍵線索:“錯了!加油!”,然后用反匯編軟件對目標程序進行詳細剖析。

二、庖丁解牛

在這里,我們使用著名的反匯編軟件IDA ,首先打開IDA,載入目標程序。經過一番載入后,界面停在了程序的入口點: 

6.png

那么,我們下一步的任務,就是從目標程序里面找到我們記下的關鍵線索——那條叫做“錯了!”的字符串。我們在 IDA 菜單欄中點選“View”–>“Open subview”–>“Strings”:

7.png

然后IDA界面上就顯示出了目標程序內的字符串???,里面有我們尋找的線索:

8雙擊.png

此時,我們雙擊該字符串,就來到了該字符串對應的位置(先不要管這個紅色線框):

12.png

看到上圖紅色線框里面的字符“sub_4017B0”了嗎?這就是引用“關鍵線索”字符串的函數,我們雙擊這個函數,來到了它的地址: 

13.png

從圖中的匯編代碼中可知,這是一個彈出消息框的函數,并不參與注冊碼的相關計算。再看紅色線框的內容,是調用這個函數的“上家”函數sub_4017F0,我們且雙擊一下它,看看“上家”在搞什么鬼?

在雙擊過后,界面顯示了sub_4017F0函數的地址:

14.png

哦……看看紅色線框里面的內容,是在操作某些字符串!是不是感覺有了點眉目了?但這一堆壓棧、讀寫寄存器等匯編指令看得人頭大,我們在這個界面上按下鍵盤上的“F5”,IDA會將這個函數的匯編代碼翻譯為C語言風格的偽代碼:

15.png

這下真相大白了,原來“上家”都干了這些事情!我們分析一下代碼:

int __cdecl sub_4017F0(int a1)//傳入一個int變量a1
{
  int result; // eax@6
  char Str1[28]; // [sp+D8h] [bp-24h]@4
  int v3; // [sp+F4h] [bp-8h]@1
  int v4; // [sp+F8h] [bp-4h]@1
 
  v4 = 0;
  v3 = 0;

//以a1的值為基礎地址,每次累加4字節,讀取對應的內存數據。由此可推斷出a1是一個字符串的首地址,該字符串數
//據類型為DWORD,而1個DWORD所占空間為4字節,所以要以4的倍數來移動
  while ( *(_DWORD *)(a1 + 4 * v4) < 62 && *(_DWORD *)(a1 + 4 * v4) >= 0 )
  {
//將依次讀取的a1[ ]的值作為aAbcdefghiabcde[ ]數組的下標,取得相應的值存入數組Str1[ ]中。
    Str1[v4] = aAbcdefghiabcde[*(_DWORD *)(a1 + 4 * v4)];
    ++v4;
  }
  Str1[v4] = 0;

//將Str1[ ]與"KanXueCTF2019JustForhappy"進行對比,如果相同則調用函數sub_401770(),如果不同則調用函數
//sub_4017B0(注意,這個就是之前彈報錯消息框的函數)
  if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") )
    result = sub_401770();
  else
    result = sub_4017B0();
  return result;
}

看到這里,我們就知道了,原來函數sub_4017F0是對某個叫a1[ ]的字符串當成了索引,用它來查閱aAbcdefghiabcde[ ]的內容,然后把查閱出來的內容與”KanXueCTF2019JustForhappy”進行對比。

那么,aAbcdefghiabcde[ ]數組里面的內容是啥呢?我們再次回顧到函數sub_4017F0的反匯編代碼處:

19.png

哦,原來是“KanXueCTF2019JustForhappy”下面的那一串字符??!注冊碼比對的原理搞明白了,勝利就在前方!

但是先別著急,還有最后一個問題:這個用來作對比的a1[ ]數組是從哪兒來的呢?是我們一開始輸入到程序里的那串字符嗎?還是有別的情況?帶著這個疑惑,我們回到sub_4017F0函數的界面,發現該函數還有個“上家”sub_401890在調用它:

16.png

我們還用老辦法,雙擊紅色線框內的sub_401890,跳到這個函數。然后鼠標滾輪往上滾幾下,來到函數的首地址00401890:

17.png

看,從反匯編代碼中可以看出,這個函數要從程序的輸入框中讀取字符了!看來這就是計算注冊碼的第一個關口了。為了方便分析,我們繼續用“F5”來讀(偽)源代碼:

18.png

堅持住,這是最后一步了!我們對代碼進行分析:

//把輸入的字符放到字符串數組Str[ ]中
  Str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v3);

//遍歷整個數組,對數組的值進行處理
 if ( strlen(Str) )
  {
    for ( i = 0; Str[i]; ++i )
    {
      if ( Str[i] > 57 || Str[i] < 48 )
      {
        if ( Str[i] > 122 || Str[i] < 97 )
        {
          if ( Str[i] > 90 || Str[i] < 65 )
            sub_4017B0();
          else
            v5[i] = Str[i] - 29;//如果Str[i]的元素的值在65~90之間,就把它減去29,將結果放到v5[i]中去;
        }
        else
        {
          v5[i] = Str[i] - 87;//如果Str[i]的元素的值在97~122之間,就把它減去29,將結果放到v5[i]中去;
        }
      }
      else
      {
        v5[i] = Str[i] - 48;//如果Str[i]的元素的值在57~48之間,就把它減去29,將結果放到v5[i]中去;
      }
    }
    result = sub_4017F0((int)v5);//將數組v5[]傳遞給函數sub_4017F0 ;
  }
  else
  {
    result = CWnd::MessageBoxA(v8, "請輸入pass!", 0, 0);
  }
  return result;

從上面可以看出,函數sub_401890主要是對輸入的字符的值進行逐個處理。如果字符的值在48~57之間,就將其減去48;如果在65~90之間,就將其減去29;如果在97~122之間,就將其減去87。然后將處理過的數組存放到v5[ ]里面。注意,這里面的值是ASCII碼!通過查閱ASCII碼表可以發現:48-57對應的是數字字符“0”~“9”,65-90對應的是大寫字母“A”~“Z ”,97-122對應的是小寫字母“a”~“z”。

綜上所述,該程序的運行流程為:[雙擊啟動程序 ]–> [輸入注冊碼] –> [函數sub_401890獲取注冊碼并進行處理]–> [將處理后的字符串傳遞給函數sub_4017F0函數,該函數以處理后的字符串數組為索引,從aAbcdefghiabcde[ ]里面查表] –> [如果查出的結果等于 “KanXueCTF2019JustForhappy”則通過,若不相等則報錯]

三、見招拆招

知道了原理,那破譯出注冊碼的算法也就簡單了,我們只需要將其反向操作一下即可。

以“KanXueCTF2019JustForhappy”的第一個字符“K”為例,“K”字符在aAbcdefghiabcde[ ]字符串的第19個位置,(aAbcdefghiabcde[ ] =“abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ”,且數組第一個下標是以0為起點的),而且48~57減去48對應的值是[0~9];65~90減去29對應的值是[36~61];97~122減去87對應的值是[10~35];那么剛才“K”字符對應的19,落在了[10~35]的區間,可以得知“K”對應的注冊碼應該是19+87=106,通過查閱ASCII碼可知,對應的是小寫字母“j”。

那么,我們用編程來實現一下。上Python代碼:

str_A = 'KanXueCTF2019JustForhappy'
str_B = 'abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ'
str_C = []
for i in str_A:
    str_C.append(str_B.index(i))
    
password = ''
for i in str_C:
    if i >= 0 and i <10:
        password += chr(i+48)
    if i >= 9 and i < 36:
        password += chr(i+87)
    if i >= 35 and i < 62:
        password += chr(i+29)
print(password)

運行結果如下:

21.png

看,注冊碼被計算出來了。這時我們把藍色字符輸入到程序中,CTF第一關就通過啦:

22.png

四、后記

其實第一關沒有什么難度,但由于本文主要面向新手,所以重點講解了IDA的基本操作 、字符串及函數的交叉引用、DWORD數據結構的存儲原理 、簡單的算法分析等一些基本功。隨著第一關的順利通過,我們稍事休息,在第二關見!

附本題目下載地址: 2019看雪CTF > 晉級賽Q1 > 第一題 流浪者

*本文作者:張召忠,轉載請注明來自FreeBuf.COM

發表評論

已有 7 條評論

取消
Loading...

特別推薦

推薦關注

填寫個人信息

姓名
電話
郵箱
公司
行業
職位
css.php jizzz