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

Editor發表於2017-06-28


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

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

防守方 hsadkhk 依然位居首位~,第十題作者位居第五位。

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

此題過後,風間仁依然處於第一位,lacoucou再次進入前十。

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

比賽進入倒數計時,接下來還會有什麼變化呢?

期待ing......

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

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


看雪評委 netwind 點評


此題是一道演算法為主的題目,需要攻方熟練掌握大數運算以及RSA演算法。要解此題,首先要列舉e,然後再根據e、d、n求得p、q。


作者簡介


看雪ID:海風月影

職業:老牌打醬油人員
聯絡方式:無
興趣愛好:逛逛論壇,吹吹牛
特長:忽悠
研究方向:人工智慧


看雪 CTF2017 第十題設計思路


參賽內容:Windows 64位系統 CrackMe

檔名:cm.exe

CRC32:13F73C04

MD5:31A4B4FF1ABFE9F0C9A11CF4E37C2B86

SHA-1:F38DE836BE531F436ACFD04100FFA52848FEFF15

設計思路

題目:RSA 密碼學演算法,窮舉 e,快速分解 N

cm 執行流程:

  1.  輸入全大寫 70 個字元,前 6 個,後 64 個,分別為兩個 16 進位制大數:e, p

  2.  e、p 都是質數(Miller Rabin 測試演算法)

  3.  初始化 N、D 兩個大數,N 為 512 位
    N=6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE94
    1C7AA288095F33BC4B255FD983114D480EFFBEE2E313E6218A57F9CCC8189
    D=2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D3
    81925EB40CFED8FBE894570155E41569B4EC69B26CB0320105A29651CB4B
  4. p == 0 (mod N),則設 q = N / p,p < q
  5. 驗證D = ( - 1)( - 1)
  6. 上面全通過,完畢。

解題思路

  1. N 是 512 位,分解 N = pq,e = D ( - 1)( - 1),拼接 ep 完畢(如果是 768
    以上的,目前就不能用這種方法了,為了放水。。。所以 N 設計成 512 位)
    分解方法,使用 ggnfs 和 msieve 工具即可,這裡不多說了。2 天內分解完,可能有點難度。

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


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


1. PEID 掃描

又是大數運算,IDA開啟:

F5,好直觀:


查詢字串:


有 PEID 和上邊的字串資訊可知,是用GMP實現的大數運算,接著就是猜每個函式的意義了。

由 MPN 的官方文件可知:

1. 所有物件使用前要初始化,通過呼叫函式 mpz_init來完成

2. 一般的函式,第一個引數為輸出函式,後邊的為輸入引數

剛開始,使用的預設值來計算,進展緩慢,後來把mpn_set_str_140007990 處的大數改成0x10之後,進展飛快。

__int64 sub_140006F50()
{
  signed __int64 v0; // rax@1
  __int64 v1; // r8@4
  __int64 v2; // rdx@4
  char v3; // cl@5
  char v4; // al@9
  int v5; // eax@14
  const char *v6; // rcx@14
  char v8; // [sp+20h] [bp-18h]@12
  printf_140006EA0("=========================================================================\n");
  printf_140006EA0("                     看雪CTF2017 CrackMe by 海風月影\n\n");
  printf_140006EA0("    請輸入序列號:");
  scanf_140006F00("%s", &g_dwInput_140051F20);
  printf_140006EA0(&unk_14004BF30);
  v0 = -1i64;
  do
    ++v0;
  while ( *((_BYTE *)&g_dwInput_140051F20 + v0) );
  if ( (_DWORD)v0 == 70 )
  {
    v1 = 0i64;
    v2 = 0i64;
    while ( 1 )
    {
      v3 = *((_BYTE *)&g_dwInput_140051F20 + v2);
      // 大寫字母加數字
      if ( (unsigned __int8)(v3 - 48) > 9u && (unsigned __int8)(v3 - 65) > 0x19u )
        break;
      if ( ++v2 >= 70 )
      {
        dword_140052F98 = g_dwInput_140051F20;
        word_140052F9C = word_140051F24;
        do
        {
          v4 = *((_BYTE *)&g_dwInput_140051F20 + v1 + 6);
          byte_140052F30[v1++] = v4;
        }
        while ( v4 );
        // 初始化
        mpn_set_str_140007990(&mpn_str_left_6_140051F10, &dword_140052F98, 16i64);
        mpn_set_str_140007990(&mpn_str_right_64_140052F20, byte_140052F30, 16i64);
        // 判斷是否為質數
        if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_right_64_140052F20, 500u) )
        {
          if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_left_6_140051F10, 500u) )
          {
            // 初始化
            mpz_set_si_140007AD0(&unk_140051EF0, 0i64);
            mpn_set_str_140007990(
              &big_1,
              "6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE"
              "2E313E6218A57F9CCC8189",
              16i64);
            mpn_set_str_140007990(
              &big_2,
              "2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69"
              "B26CB0320105A29651CB4B",

完整函式如上,流程如下:

1.假設輸入的字串為szInput


2.首先判斷szInput的長度是否為70個位元組


1
2
3
4
5
  v0 = -1i64;
  do
    ++v0;
  while ( *((_BYTE *)&g_dwInput_140051F20 + v0) );
  if ( (_DWORD)v0 == 70 )

3.接著判斷這些字元是否由 0-9 和A-Z的字元組成:


1
2
3
4
 v3 = *((_BYTE *)&g_dwInput_140051F20 + v2);
      // 大寫字母加數字
      if ( (unsigned __int8)(v3 - 48) > 9u && (unsigned __int8)(v3 - 65) > 0x19u )
        break;

4.將輸入字串分成兩段,第一段為原字串的0-6自己,第二段為後64位元組



  dword_140052F98 = g_dwInput_140051F20;
        word_140052F9C = word_140051F24;
        do
        {
          v4 = *((_BYTE *)&g_dwInput_140051F20 + v1 + 6);
          byte_140052F30[v1++] = v4;
        }
        while ( v4 );

5.用這兩個字串來初始化兩個mpz結構題:


1
2
3
// 初始化
        mpn_set_str_140007990(&mpn_str_left_6_140051F10, &dword_140052F98, 16i64);
        mpn_set_str_140007990(&mpn_str_right_64_140052F20, byte_140052F30, 16i64);


結構體定義:


1
2
3
4
5
6
7
typedef unsigned long mp_limb_t
struct __mpz_struct
{
int _mp_alloc;     //申請的大小  _mp_alloc=申請位元組數/8
int _mp_size;      //當前佔用的大小  _mp_size=使用位元組數/8
mp_limb_t * _mp_d; //資料指標
};


初始化前6個字元的例子:


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


記憶體中,rcx地址的值:


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

執行之後:

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

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


6. 判斷是否為素數


1
2
3
        if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_right_64_140052F20, 500u) )
        {
          if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_left_6_140051F10, 500u) )


這裡花了點時間,後來通過連續測試0-0x10基本可以確定是判斷素數。


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


跟官方文件似乎不太一樣。


7. 初始化幾個值



            // 初始化
            mpz_set_si_140007AD0(&unk_140051EF0, 0i64);
            mpn_set_str_140007990(
              &big_1,
              "6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE"
              "2E313E6218A57F9CCC8189",
              16i64);
            mpn_set_str_140007990(
              &big_2,
              "2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69"
              "B26CB0320105A29651CB4B",
              16i64);
            mpz_set_si_140007AD0(&v8, 0i64);


8.用大數0x62..... 模上我們輸入的64位然後與0比較:


1
2
mpz_mod_140007B10((__int64)&v8, (__int64)&big_1, (__int64)&mpn_str_right_64_140052F20);
if ( !mpz_cmp_si_1400075D0(&v8, 0i64) )


9.用大數0x62..... 除以我們輸入的64位然後與商做比較,商大於我們輸入的數:


1
2
mpz_div_1400071E0(&unk_140051EF0, &big_1, &mpn_str_right_64_140052F20);
if ( mpz_cmp_140007560(&mpn_str_right_64_140052F20, &unk_140051EF0) <= 0 )

10.最後一段,p=input[6:64] 商q=big_1/p  v8=(p-1)(q-1)


mpn_sub_1400079F0(&mpn_str_right_64_140052F20, &mpn_str_right_64_140052F20, 1i64);
mpn_sub_1400079F0(&unk_140051EF0, &unk_140051EF0, 1i64);
mpn_mul_140007610(&v8, &mpn_str_right_64_140052F20, &unk_140051EF0);
// 猜不出來
sub_1400073B0((__int64)&v8, (__int64)&mpn_str_left_6_140051F10, (__int64)&v8);
 v5 = mpz_cmp_140007560(&big_2, &v8);
 v6 = "註冊成功!!!\n\n";
 if ( !v5 )
    goto LABEL_16;

有了以上資訊,結合上一題的坑,基本猜到是RSA,不然還能有啥。。。。。。。。

RSA 演算法:

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

RSA金鑰生成步驟:


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

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

根據以上資訊,以及計算過程:

1
2
3
4
5
6
7
8
mpz_mod_140007B10((__int64)&v8, (__int64)&big_1, (__int64)&mpn_str_right_64_140052F20);
if ( !mpz_cmp_si_1400075D0(&v8, 0i64) )
mpz_div_1400071E0(&unk_140051EF0, &big_1, &mpn_str_right_64_140052F20);
if ( mpz_cmp_140007560(&mpn_str_right_64_140052F20, &unk_140051EF0) <= 0 )
{
 mpn_sub_1400079F0(&mpn_str_right_64_140052F20, &mpn_str_right_64_140052F20, 1i64);
mpn_sub_1400079F0(&unk_140051EF0, &unk_140051EF0, 1i64);
mpn_mul_140007610(&v8, &mpn_str_right_64_140052F20, &unk_140051EF0);

猜測:

1
2
3
4
big_1即為N
big_2可能是D
N=0x6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE2E313E6218A57F9CCC8189L
D=0x2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69B26CB0320105A29651CB4B

線上分解N,達到P,Q:

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

1
2
p =64111581030502014729907148975807553274150008894301323553363399183151805372611
q =80290597658186981463023471970341877717671071586519890660723213036500314978243

把P轉成十六進位制作為後64位,果然可以直接執行到:

1
2
3
// 猜不出來
 sub_1400073B0((__int64)&v8, (__int64)&mpn_str_left_6_140051F10, (__int64)&v8);
 v5 = mpz_cmp_140007560(&big_2, &v8);

那麼肯定是要根據以下公式求解E

1
2
3
4
5
ed - 1 = kφ(n)
e:不知道,待求
d:私鑰,似乎是big_2 0x 2476.....
K:為整數
φ(n)=(p-1)*(q-1)

根據以上條件,寫python,計算:

1
2
3
4
5
6
7
8
9
def calc_e()
    ola_n=(p-1)*(q-1
    for in range(1,10000000):
        e=(ola_n*x+1)/big_num_2
        if ola_n*x+1==big_num_2*e:  #驗證e是否為整數(是否捨棄了小數部分 10/3=3  3×3!=10)
            print e,hex(e)
 
輸出:
16077491 0xf552b3L

完整註冊碼:

F552B38DBDDE72E2E693B2AED5C769C0DCB3DA83534480A80E652FFE53544CD91A18C3

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


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

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

比心 ❤

贊助商

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



相關文章