WaniCTF'21-spring secret Writeup Using Ghidra

Reversing の王道を行く stackstrings の問題ですね。
Ghidraを使えば，静的解析で解けます。

## 問題
secret
150pt Beginner
この問題では Linux の ELF 実行ファイル(バイナリ)である「secret」が配布されています。
このバイナリを実行すると secret key を入力するように表示されます。
試しに「abcde」と入力してみると「Incorrect」と言われました。

```
$ file secret
secret: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1daf4ab43cfa357911806c3ccae34a1b6e027913, for GNU/Linux 3.2.0, not stripped

$ sudo chmod +x secret

$ ./secret
...
Input secret key : abcde
Incorrect

$ ./secret
...
Input secret key : ??????
Correct! Flag is ??????
```

このバイナリが正しいと判断する secret key を見つけて読み込んでみましょう！
(secret key とフラグは別の文字列です)
(このファイルを実行するためには Linux 環境が必要となりますので WSL や VirtualBox で用意してください)
ヒント :「表層解析」や「静的解析」を行うことで secret key が見つかるかも...?
表層解析ツール strings
静的解析ツール Ghidra

## Solution

GhidraでStringsを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/10a62a12-db02-2d31-95cd-e53c909497cf.png)
Correct! Flag isの文字が見えます。
xrefで関数に
main関数でした。

```c:main
undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  undefined8 uVar3;
  undefined4 local_128 [44];
  byte local_78 [41];
  undefined local_4f;
  byte local_48 [56];
  ulong local_10;
  
  local_128[0] = 0x31;
  local_128[1] = 0x2d;
  local_128[2] = 0x2f;
  local_128[3] = 0x2e;
  local_128[4] = 0x24;
  local_128[5] = 8;
  local_128[6] = 0x1d;
  local_128[7] = 0x3e;
  local_128[8] = 0x45;
  local_128[9] = 0x11;
  local_128[10] = 0x1f;
  local_128[11] = 0x3a;
  local_128[12] = 0x3c;
  local_128[13] = 0x5b;
  local_128[14] = 1;
  local_128[15] = 8;
  local_128[16] = 0x3a;
  local_128[17] = 0;
  local_128[18] = 0;
  local_128[19] = 0x2d;
  local_128[20] = 8;
  local_128[21] = 0;
  local_128[22] = 0x5f;
  local_128[23] = 0x1e;
  local_128[24] = 0x3e;
  local_128[25] = 0xd;
  local_128[26] = 0x1d;
  local_128[27] = 0x3b;
  local_128[28] = 0x36;
  local_128[29] = 0xb;
  local_128[30] = 0x27;
  local_128[31] = 0x11;
  local_128[32] = 0xb;
  local_128[33] = 0x10;
  local_128[34] = 0x68;
  local_128[35] = 0x12;
  local_128[36] = 0x30;
  local_128[37] = 0x44;
  local_128[38] = 5;
  local_128[39] = 10;
  local_128[40] = 0x5c;
  show_description();
  printf("Input secret key : ");
  __isoc99_scanf(&DAT_001022cf,local_48);
  sVar2 = strlen((char *)local_48);
  if (sVar2 == 0x29) {
    iVar1 = strncmp((char *)local_48,"wani_is_the_coolest_animals_in_the_world!",0x29);
    if (iVar1 == 0) {
      local_10 = 0;
      while (local_10 < 0x29) {
        local_78[local_10] = local_48[local_10] ^ (byte)local_128[local_10];
        local_10 = local_10 + 1;
      }
      local_4f = 0;
      printf("Correct! Flag is %s ",local_78);
      uVar3 = 0;
    }
    else {
      puts("Incorrect");
      uVar3 = 1;
    }
  }
  else {
    puts("Incorrect");
    uVar3 = 1;
  }
  return uVar3;
}
```

stackstringsが見えます。
コードを少し見やすくします。

local_10 -> i
local_48 -> key
local_78 -> flag
local_128 -> enc

```c:main
undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  undefined8 uVar3;
  undefined4 enc [44];
  byte flag [41];
  undefined local_4f;
  byte key [56];
  ulong i;
  
  enc[0] = 0x31;
  enc[1] = 0x2d;
  enc[2] = 0x2f;
  enc[3] = 0x2e;
  enc[4] = 0x24;
  enc[5] = 8;
  enc[6] = 0x1d;
  enc[7] = 0x3e;
  enc[8] = 0x45;
  enc[9] = 0x11;
  enc[10] = 0x1f;
  enc[11] = 0x3a;
  enc[12] = 0x3c;
  enc[13] = 0x5b;
  enc[14] = 1;
  enc[15] = 8;
  enc[16] = 0x3a;
  enc[17] = 0;
  enc[18] = 0;
  enc[19] = 0x2d;
  enc[20] = 8;
  enc[21] = 0;
  enc[22] = 0x5f;
  enc[23] = 0x1e;
  enc[24] = 0x3e;
  enc[25] = 0xd;
  enc[26] = 0x1d;
  enc[27] = 0x3b;
  enc[28] = 0x36;
  enc[29] = 0xb;
  enc[30] = 0x27;
  enc[31] = 0x11;
  enc[32] = 0xb;
  enc[33] = 0x10;
  enc[34] = 0x68;
  enc[35] = 0x12;
  enc[36] = 0x30;
  enc[37] = 0x44;
  enc[38] = 5;
  enc[39] = 10;
  enc[40] = 0x5c;
  show_description();
  printf("Input secret key : ");
  __isoc99_scanf(&DAT_001022cf,key);
  sVar2 = strlen((char *)key);
  if (sVar2 == 0x29) {
    iVar1 = strncmp((char *)key,"wani_is_the_coolest_animals_in_the_world!",0x29);
    if (iVar1 == 0) {
      i = 0;
      while (i < 0x29) {
        flag[i] = key[i] ^ (byte)enc[i];
        i = i + 1;
      }
      local_4f = 0;
      printf("Correct! Flag is %s ",flag);
      uVar3 = 0;
    }
    else {
      puts("Incorrect");
      uVar3 = 1;
    }
  }
  else {
    puts("Incorrect");
    uVar3 = 1;
  }
  return uVar3;
}
```

key と stackstrings を xor したのが flag ですね
ソルバー書きます。

```python:solver.py
key = getBytes(toAddr(0x001022e0),0x29)
print(key)
print("key="+''.join(map(chr,key)))

enc=[]
inst = getInstructionAt(toAddr(0x0010124f))
i = 0
while i < 0x29:
    enc.append(inst.getOpObjects(1)[0].getValue())
    inst = inst.getNext()
    i = i + 1
print(enc)
print("enc="+''.join(map(chr,enc)))

ans=[]
i = 0
while i < 0x29:
    ans.append(enc[i] ^ key[i])
    i = i + 1
print(ans)
print("ans="+''.join(map(chr,ans)))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/2dcc6ba4-5e56-fa4f-6b10-0a7659b07bd6.png)

Ghidra Scriptを書くとstackstringsを転記しなくて楽です。
しかもELFを動かさずに解析（静的解析）できます。
　　
　
　
作問者writeup

https://github.com/wani-hackase/wanictf21spring-writeup/tree/main/rev/secret