1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SECCON Beginners CTF 2021 please_not_trace_me Writeup Using Ghidra

Last updated at Posted at 2021-05-25

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初期デコンパイル結果

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 );
}
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()

_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()

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
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

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
こっちだと動くじゃん
image.png

keyってこれか?

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?