1
1

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.

picoCTF Investigative Reversing 0 Writeup Using Ghidra

Last updated at Posted at 2021-03-03

Ghidraのデコンパイル誤りを訂正する訓練になるので記録に残しておく。

問題としては,ELFバイナリ「mystery」と PNG画像「mystery.png」が渡される。

mystery.png を確認すると,フッターの後ろにフラグっぽいのが見える。
フラグの長さは26バイト。
が, 0x80 など ASCII 外のコードがある。
image.png

Ghidraでデコンパイルしてみる

void main(void)
{
  FILE *__stream;
  FILE *__stream_00;
  size_t sVar1;
  long in_FS_OFFSET;
  int local_54;
  int local_50;
  char local_38 [4];
  char local_34;
  char local_33;
  char local_29;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  __stream_00 = fopen("mystery.png","a");
  if (__stream == (FILE *)0x0) {
    puts("No flag found, please make sure this is run on the server");
  }
  if (__stream_00 == (FILE *)0x0) {
    puts("mystery.png is missing, please run this on the server");
  }
  sVar1 = fread(local_38,0x1a,1,__stream);
  if ((int)sVar1 < 1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("at insert");
  fputc((int)local_38[0],__stream_00);
  fputc((int)local_38[1],__stream_00);
  fputc((int)local_38[2],__stream_00);
  fputc((int)local_38[3],__stream_00);
  fputc((int)local_34,__stream_00);
  fputc((int)local_33,__stream_00);
  local_54 = 6;
  while (local_54 < 0xf) {
    fputc((int)(char)(local_38[local_54] + '\x05'),__stream_00);
    local_54 = local_54 + 1;
  }
  fputc((int)(char)(local_29 + -3),__stream_00);
  local_50 = 0x10;
  while (local_50 < 0x1a) {
    fputc((int)local_38[local_50],__stream_00);
    local_50 = local_50 + 1;
  }
  fclose(__stream_00);
  fclose(__stream);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

fputc をネットで調べるとファイルに追記する関数とわかる。
よって,この ELF は, flag.txt を読んで,フラグに何らかの細工(暗号化)をして, mystery.png の末尾に付加するものと予測する。

この処理を見ていく中で最大の問題は,変数 local_29 に対する代入が無く,値が不明ということだ。
ピンク先生は「アサインされていない変数」という言い方をされていた。
英語で書くと local_29 is not assigned という感じ。

こういうのはたいていFunction Signatureを変更するか,別の変数の型を変更すると自動的に直る場合が多い。

Ghidra のデコンパイル誤りを正していく。
local_38 は flag.txt の内容が入るバッファなので,変数名を flag に。
そして一番重要なのが,型。
前記 mystery.png をバイナリエディタで確認した時,フラグに長さが 26バイト だったので,
local_38 の型を char[26] に変更する。

void main(void)

{
  long lVar1;
  FILE *__stream;
  FILE *__stream_00;
  size_t sVar2;
  long in_FS_OFFSET;
  int local_54;
  int local_50;
  char flag [26];
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  __stream_00 = fopen("mystery.png","a");
  if (__stream == (FILE *)0x0) {
    puts("No flag found, please make sure this is run on the server");
  }
  if (__stream_00 == (FILE *)0x0) {
    puts("mystery.png is missing, please run this on the server");
  }
  sVar2 = fread(flag,0x1a,1,__stream);
  if ((int)sVar2 < 1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("at insert");
  fputc((int)flag[0],__stream_00);
  fputc((int)flag[1],__stream_00);
  fputc((int)flag[2],__stream_00);
  fputc((int)flag[3],__stream_00);
  fputc((int)flag[4],__stream_00);
  fputc((int)flag[5],__stream_00);
  local_54 = 6;
  while (local_54 < 0xf) {
    fputc((int)(char)(flag[local_54] + '\x05'),__stream_00);
    local_54 = local_54 + 1;
  }
  fputc((int)(char)(flag[15] + -3),__stream_00);
  local_50 = 0x10;
  while (local_50 < 0x1a) {
    fputc((int)flag[local_50],__stream_00);
    local_50 = local_50 + 1;
  }
  fclose(__stream_00);
  fclose(__stream);
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

ビンゴ!
アサインされていない変数 local_29 が消えた。

さらに読みやすくする
local_54 の変数名を i に
__stream_00 の変数名を __stream_png に
local_50 の変数名を j に

void main(void)

{
  long lVar1;
  FILE *__stream;
  FILE *__stream_png;
  size_t sVar2;
  long in_FS_OFFSET;
  int i;
  int j;
  char flag [26];
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  __stream_png = fopen("mystery.png","a");
  if (__stream == (FILE *)0x0) {
    puts("No flag found, please make sure this is run on the server");
  }
  if (__stream_png == (FILE *)0x0) {
    puts("mystery.png is missing, please run this on the server");
  }
  sVar2 = fread(flag,0x1a,1,__stream);
  if ((int)sVar2 < 1) {
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("at insert");
  fputc((int)flag[0],__stream_png);
  fputc((int)flag[1],__stream_png);
  fputc((int)flag[2],__stream_png);
  fputc((int)flag[3],__stream_png);
  fputc((int)flag[4],__stream_png);
  fputc((int)flag[5],__stream_png);
  i = 6;
  while (i < 0xf) {
    fputc((int)(char)(flag[i] + '\x05'),__stream_png);
    i = i + 1;
  }
  fputc((int)(char)(flag[15] + -3),__stream_png);
  j = 0x10;
  while (j < 0x1a) {
    fputc((int)flag[j],__stream_png);
    j = j + 1;
  }
  fclose(__stream_png);
  fclose(__stream);
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

コードが読みやすくなったのであとはソルバーを書くだけ

# enc_string was extracted from behind the footer of mystery.png
enc_string = "picoCTK\x80k5zsid6q_3d659f57}"

enc = list(enc_string)
# print(enc)
    
ans = []
    
i = 0
while i < 0x6:
    ans.append(ord(enc[i]))
    i = i + 1
    
while i < 0xf:
    ans.append(ord(enc[i]) - 0x5)
    i = i + 1
    
ans.append(ord(enc[15]) + 0x3)

i = 0x10
while i < 0x1a:
    ans.append(ord(enc[i]))
    i = i + 1

print(''.join(map(chr,ans)))
1
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?