SECCON Beginners CTF 2021 please_not_trace_me Writeup Using Ghidra

SECCON Beginners CTF 2021 reversing 03 please_not_trace_me を Ghidra で静的解析した。
当日は，RC4 の KSA部分だけを見て，暗号化は RC4 と決めつけ，ソルバーを組んだが，flag に戻らずパニックになった。
（keyが違う？encrypted flagが違う？自分のrc4が違う？rc4以外に見落とした暗号化がある？等々）
復習中，焦らずよく見ると，RC4 と同じ部分は KSA だけで，PRGA 部分は RC4 ではないことがわかった。
作問者の方がそういうところまで狙っていたとしたら，まんまと術中にはまったということになる。
これを糧にしてもっと強くなる。

# 問題

please_not_trace_me
フラグを復号してくれるのは良いけど，表示してくれない!!
chall 

# Ghidra初期デコンパイル結果

```c:main
void main(undefined4 param_1,undefined8 param_2,undefined8 param_3)

{
  int local_54;
  int local_50;
  void *local_48;
  long local_40;
  long local_38;
  undefined8 local_30;
  
  megaInit();
  local_30 = 9;
  _global_argv = param_2;
  _global_argc = param_1;
  _global_envp = param_3;
  do {
    switch(local_30) {
    case 0:
      local_50 = 2;
      local_30 = 0xb;
      break;
    case 2:
      local_30 = _1_main_flag_func_0(local_38,0xffffffffffffffff,0x10,5);
      break;
    case 5:
      if (local_50 == 6) {
        local_30 = 0x12;
      }
      else {
        local_30 = 8;
      }
      break;
    case 6:
      if (local_40 == 0) {
        local_30 = 0;
      }
      else {
        local_30 = 0xb;
      }
      break;
    case 8:
      fwrite("prease not trace me...\n",1,0x17,stderr);
                    /* WARNING: Subroutine does not return */
      exit(1);
    case 9:
      local_54 = 0;
      local_30 = 10;
      break;
    case 10:
      switch(local_54) {
      case 0:
        local_30 = 0x16;
        break;
      case 1:
        local_30 = 0xf;
        break;
      case 2:
        local_30 = 0x13;
        break;
      case 3:
        local_30 = 0x14;
        break;
      case 4:
        local_30 = 0x11;
        break;
      case 5:
        local_30 = 0x15;
        break;
      default:
        local_30 = 0x12;
      }
      break;
    case 0xb:
      local_38 = ptrace(PTRACE_TRACEME,0,1,0);
      local_30 = 2;
      break;
    case 0xf:
      local_48 = malloc(0x10);
      local_30 = 0x12;
      break;
    case 0x10:
      local_50 = local_50 * 3;
      local_30 = 5;
      break;
    case 0x11:
      puts("flag decrypted. bye.");
      local_30 = 0x12;
      break;
    case 0x12:
      local_54 = local_54 + 1;
      local_30 = 10;
      break;
    case 0x13:
      generate_key(local_48);
      local_30 = 0x12;
      break;
    case 0x14:
      rc4(e,local_48);
      local_30 = 0x12;
      break;
    case 0x15:
                    /* WARNING: Subroutine does not return */
      exit(0);
    case 0x16:
      local_50 = 0;
      local_40 = ptrace(PTRACE_TRACEME,0,1,0);
      local_30 = 6;
    }
  } while( true );
}
```

```c:rc4（名前だけ）
 void * rc4(char *param_1,char *param_2)

{
  int iVar1;
  size_t sVar2;
  void *pvVar3;
  size_t sVar4;
  uint uVar5;
  long in_FS_OFFSET;
  int local_158;
  int local_154;
  int local_150;
  int local_14c;
  byte local_118 [264];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  sVar2 = strlen(param_1);
  pvVar3 = malloc((long)(int)sVar2);
  sVar4 = strlen(param_2);
  local_158 = 0;
  while (local_158 < 0x100) {
    local_118[local_158] = (byte)local_158;
    local_158 = local_158 + 1;
  }
  local_154 = 0;
  local_150 = 0;
  while (local_154 < 0x100) {
    iVar1 = (int)param_2[local_154 % (int)sVar4] + (uint)local_118[local_154] + local_150;
    uVar5 = (uint)(iVar1 >> 0x1f) >> 0x18;
    local_150 = (iVar1 + uVar5 & 0xff) - uVar5;
    swap(local_118 + local_154,local_118 + local_150,local_118 + local_150);
    local_154 = local_154 + 1;
  }
  local_14c = 0;
  while (local_14c < (int)sVar2) {
    swap(local_118,local_118,local_118,local_118);
    *(byte *)((long)pvVar3 + (long)local_14c) =
         param_1[local_14c] ^ local_118[(int)(uint)(byte)(local_118[0] * '\x02')];
    local_14c = local_14c + 1;
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return pvVar3;
}
```

# rc4っぽい処理のKey

main()  -->  generate_key()  -->  _2_stringEncoder()

```c:_2_stringEncoder
void _2_stringEncoder(int param_1,undefined *param_2)

{
  if (param_1 == 0) {
    *param_2 = 0x6e;
    param_2[1] = 0x69;
    param_2[2] = 99;
    param_2[3] = 0x6b;
    param_2[4] = 0x65;
    param_2[5] = 0x6c;
    param_2[6] = 0x6f;
    param_2[7] = 100;
    param_2[8] = 0x65;
    param_2[9] = 0x6f;
    param_2[10] = 0x6e;
    param_2[0xb] = 0;
  }
  return;
}
```

# rc4っぽい処理のEnc

main()  -->  megaInit()  -->  e_i$nit()

```c:e_i$nit
void e_i$nit(void)

{
  e[0] = 0x80;
  e[1] = 0x97;
  e[2] = 0x85;
  e[3] = 0xd7;
  e[4] = 0x81;
  e[5] = 0x98;
  e[6] = 0x87;
  e[7] = 0xd2;
  e[8] = 0x87;
  e[9] = 0xbc;
  e[10] = 0x9a;
  e[11] = 0xd3;
  e[12] = 0x96;
  e[13] = 0xbc;
  e[14] = 0x87;
  e[15] = 0xd0;
  e[16] = 0x80;
  e[17] = 0x91;
  e[18] = 0x9a;
  e[19] = 0x93;
  e[20] = 0x97;
  e[21] = 0xbc;
  e[22] = 0x91;
  e[23] = 0x80;
  e[24] = 0xd7;
  e[25] = 0xdc;
  e[26] = 0x9e;
  return;
}
```

# デコンパイル結果の修正

変数名の修正

|変更前  |変更後  |
|---|---|
|param_2 | key |
|param_1 | enc |
|sVar4 | keylength |
|sVar2 | enclength |
|pvVar2 | flag |
|local_118 | S |
|local_158 | ix |
|local_154 | i |
|local_150 | j |
|local_14c | k |

```c:rc4（名前だけ）修正後
void * rc4(char *enc,char *key)

{
  int iVar1;
  size_t enclength;
  void *flag;
  size_t keylength;
  uint uVar2;
  long in_FS_OFFSET;
  int ix;
  int i;
  int j;
  int k;
  byte S [264];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  enclength = strlen(enc);
  flag = malloc((long)(int)enclength);
  keylength = strlen(key);
  ix = 0;
  while (ix < 0x100) {
    S[ix] = (byte)ix;
    ix = ix + 1;
  }
  i = 0;
  j = 0;
  while (i < 0x100) {
    iVar1 = (int)key[i % (int)keylength] + (uint)S[i] + j;
    uVar2 = (uint)(iVar1 >> 0x1f) >> 0x18;
    j = (iVar1 + uVar2 & 0xff) - uVar2;
    swap(S + i,S + j,S + j);
    i = i + 1;
  }
  k = 0;
  while (k < (int)enclength) {
    swap(S,S,S,S);
    *(byte *)((long)flag + (long)k) = enc[k] ^ S[(int)(uint)(byte)(S[0] * '\x02')];
    k = k + 1;
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return flag;
}

```

# solver ( Ghidra Script )

RC4 algorithm

https://en.wikipedia.org/wiki/RC4

```python:test_nise_rc4_ctf4b2021.py
#
# SECCON Beginners CTF 2021 please_not_trace_me
#

def init_rc4(key):
    S = range(256)
    j = 0
    for i in range(256):
        j = (key[i % len(key)] + S[i] + j) & 0xFF
        S[i],S[j] = S[j],S[i]
    return S

def rc4(enc,S):
    '''
    x = 0
    y = 0
    '''
    for i in range(len(enc)):
        '''
        x = (x + 1)  & 0xFF
        y = (S[x] + y) & 0xFF
        S[x],S[y] = S[y],S[x]
        enc[i] ^= S[(S[x] + S[y]) & 0xFF]
        '''
        enc[i] ^= S[S[0] * 2 & 0xFF]
    return bytes(enc)

def do_rc4(enc,key):
    S = init_rc4(key)
    return rc4(enc,S)

# Extract encrypted flag
enc=[]
inst = getInstructionAt(toAddr(0x00101873))
i = 0
while i < 27:
    enc.append(inst.getOpObjects(1)[0].getValue())
    inst = inst.getNext()
    i = i + 1

print("enc")
print(enc)

# Extract Key
key=[]
inst = getInstructionAt(toAddr(0x001014e1))
i = 0
while i < 11:
    key.append(inst.getOpObjects(1)[0].getValue())
    inst = inst.getNext()
    inst = inst.getNext()
    inst = inst.getNext()
    inst = inst.getNext()
    inst = inst.getNext()
    inst = inst.getNext()
    i = i + 1

print("key")
print(key)
print(''.join(map(chr,key)))


flag = do_rc4(enc,key)

print("flag")
print(flag)
print(''.join(map(chr,flag)))
```

なんか Ghidra の機嫌が悪くエラーの意味がわからん。（バグかも？）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/0f9cc426-2433-063f-b556-b528a2739b45.png)
こっちだと動くじゃん
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/3c264338-1b06-370e-471d-778ec9b090f7.png)

keyってこれか？

https://www.facebook.com/nickelodeon.japan.official/photos/a.163987254451632/638644063652613/