Skip to content

Latest commit

 

History

History

Simple_Flag_Checker

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Simple Flag Checker:Rev:146pts

A simple flag checker :)

checker

Solution

謎のバイナリが渡される。
実行すると、問題名の通りフラグチェッカーのようだ。

$ ./checker
flag? Satoki
Wrong...

IDAでデコンパイルするとmainは以下であった。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rbx
  int v4; // r12d
  int result; // eax
  __int64 v6[4]; // [rsp+0h] [rbp-98h] BYREF
  int v7; // [rsp+20h] [rbp-78h]
  char s[56]; // [rsp+30h] [rbp-68h] BYREF
  unsigned __int64 v9; // [rsp+68h] [rbp-30h]

  v9 = __readfsqword(0x28u);
  __printf_chk(1LL, "flag? ", envp);
  fgets(s, 50, _bss_start);
  memset(v6, 0, sizeof(v6));
  v7 = 0;
  v3 = 0LL;
  LOBYTE(v4) = 1;
  do
  {
    update(v6, (unsigned __int8)s[v3]);
    v4 = (memcmp(v6, (char *)&table + 16 * v3++, 0x10uLL) == 0) & (unsigned __int8)v4;
  }
  while ( v3 != 49 );
  if ( v4 )
  {
    __printf_chk(1LL, "Correct! Your flag is: %s\n", s);
    result = 0;
  }
  else
  {
    puts("Wrong...");
    result = 1;
  }
  if ( v9 != __readfsqword(0x28u) )
    return term_proc();
  return result;
}

フラグは49文字で、正解であればCorrect! Your flag is: %s\nが出力されるようだ。
以下のように文字数分ループしている個所に注目する。

  do
  {
    update(v6, (unsigned __int8)s[v3]);
    v4 = (memcmp(v6, (char *)&table + 16 * v3++, 0x10uLL) == 0) & (unsigned __int8)v4;
  }
  while ( v3 != 49 );

updateに一文字ずつ渡しているようで、あらかじめmemsetした領域に何らかの更新処理を行っている。
その後にmemcmpで比較処理を行い、結果をフラグの正誤判定に利用する変数にANDしている。
つまり一文字でも間違えると、この変数が偽になり不正解と判定される。
updateはとてつもなく複雑な処理をしているので読みたくない。
memcmpにブレークポイントを設定し、xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxのような誤ったフラグを入力して挙動を見る。

$ gdb ./checker
~~~
pwndbg> start
~~~
pwndbg> disass main
Dump of assembler code for function main:
=> 0x0000555555555980 <+0>:     endbr64
~~~
   0x0000555555555a27 <+167>:   mov    rdi,rbp
   0x0000555555555a2a <+170>:   call   0x5555555550b0 <memcmp@plt>
   0x0000555555555a2f <+175>:   test   eax,eax
~~~
pwndbg> b *0x0000555555555a2a
Breakpoint 2 at 0x555555555a2a
pwndbg> c
Continuing.
flag? xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

~~~
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
 ► 0x555555555a2a <main+170>    call   memcmp@plt                  <memcmp@plt>
        s1: 0x7fffffffdb90 ◂— 0x77aefc95befb0983
        s2: 0x555555558020 (table) ◂— 0xac050fa321f13c42
        n: 0x10

   0x555555555a2f <main+175>    test   eax, eax
   0x555555555a31 <main+177>    sete   al
   0x555555555a34 <main+180>    movzx  eax, al
   0x555555555a37 <main+183>    and    r12d, eax
   0x555555555a3a <main+186>    add    rbx, 1
   0x555555555a3e <main+190>    cmp    rbx, 0x31
   0x555555555a42 <main+194>    jne    main+136                    <main+136>

   0x555555555a44 <main+196>    test   r12d, r12d
   0x555555555a47 <main+199>    je     main+260                    <main+260>

   0x555555555a49 <main+201>    lea    rdx, [rsp + 0x30]
~~~

s1s2が異なっていることがわかる。
このことから、一文字目の判定が失敗していることがわかる。
次に、入力フラグの先頭をAにし、判定を成功させてみる。

pwndbg> c
Continuing.
flag? Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

~~~
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
 ► 0x555555555a2a <main+170>    call   memcmp@plt                  <memcmp@plt>
        s1: 0x7fffffffdb90 ◂— 0xac050fa321f13c42
        s2: 0x555555558020 (table) ◂— 0xac050fa321f13c42
        n: 0x10

s1s2が一致していることがわかる。
二文字目までをAlにした場合や、三文字目までをAlpにした場合でも同様に、正しいフラグ文字まではs1s2が一致した。
このふるまいを利用して、先頭からフラグを導出できる。
以下のplzflag.pyようにpwndbg導入済みgdbを直で実行し、ptrlibで自動化してやる。

import re
import string
from ptrlib import *

logger.level = 0

flag = ["x"] * 49

for i in range(49):
    for c in string.printable:
        sock = Process("gdb ./checker")
        sock.sendlineafter("pwndbg> ", "start")
        sock.sendlineafter("pwndbg> ", "b *0x0000555555555a2a")
        sock.sendlineafter("pwndbg> ", "c")
        flag[i] = c
        sock.sendline("".join(flag))
        for j in range(i):
            sock.sendlineafter("pwndbg> ", "c")
        sock.recvuntil("s1")
        result = sock.recvuntil("pwndbg> ").decode()
        sock.close()
        result = re.sub(r"\x1b\[[0-9;]*m", "", result)
        s1_s2 = re.findall(r"◂—\s*(0x[0-9a-fA-F]+)", result)
        if s1_s2[0] == s1_s2[1]:
            print(f"flag[{i}] = {c}")
            break

print(f"flag = {''.join(flag)}")

実行する。

$ python plzflag.py
flag[0] = A
flag[1] = l
flag[2] = p
~~~
flag[45] = c
flag[46] = a
flag[47] = k
flag[48] = }
flag = Alpaca{h4sh_4lgor1thm_1s_b4s3d_0n_MD5_4nd_keccak}
$ ./checker
flag? Alpaca{h4sh_4lgor1thm_1s_b4s3d_0n_MD5_4nd_keccak}
Correct! Your flag is: Alpaca{h4sh_4lgor1thm_1s_b4s3d_0n_MD5_4nd_keccak}

flagが得られた。

Alpaca{h4sh_4lgor1thm_1s_b4s3d_0n_MD5_4nd_keccak}