Description

    랜덤한 값을 맞혀 get_flag() 함수를 호출하세요! 플래그는 64자리입니다.

    플래그 형식은 DH{…} 입니다.

    Files

    ubuntu@WSL2:~/CTF/dreamhack.io$ tree randzzz
    randzzz
    └── chall
    
    0 directories, 1 file
    
    ubuntu@WSL2:~/CTF/dreamhack.io$ file randzzz/chall
    randzzz/chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=144322b4d0870425ad69846991455114d0197396, for GNU/Linux 3.2.0, not stripped

    64비트 리눅스용 실행 파일 하나.

    분석

    main

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      __int64 v4[3]; // [rsp+0h] [rbp-A0h]
      __int64 v5[3]; // [rsp+18h] [rbp-88h] BYREF
      __int64 v6[3]; // [rsp+30h] [rbp-70h]
      int v7; // [rsp+48h] [rbp-58h]
      char v8[68]; // [rsp+50h] [rbp-50h] BYREF
      unsigned int seconds; // [rsp+94h] [rbp-Ch] BYREF
      int j; // [rsp+98h] [rbp-8h]
      int i; // [rsp+9Ch] [rbp-4h]
    
      puts("----Start----");
      sleep(1u);
      puts("fall asleep from now on.");
      seconds = rand() + 1;
      sleep(seconds);
      rand();
      rand();
      printf("Can you guess the rand num?: ");
      __isoc99_scanf("%d", &seconds);
      if ( rand() % 10 == seconds )
      {
        v6[0] = 0x386C2C39364C396CLL;
        v6[1] = 0x30383338AC4C4C39LL;
        v6[2] = 0x353330354CCCCC34LL;
        v7 = 0xCC6C37AC;
        for ( i = 0; i <= 27; ++i )
          v8[i] = get_flag((unsigned int)*((char *)v6 + i), seconds);
      }
      if ( rand() % 10 == seconds )
      {
        v4[0] = 0x1B323838330B1335LL;
        v4[1] = 0xB332323361B2333LL;
        v4[2] = 0x23391B0B38370B13LL;
        qmemcpy(v5, "3539458369+8", 12);
        for ( j = 0; j <= 35; ++j )
          v8[j + 28] = get_flag((unsigned int)*((char *)v4 + j), seconds);
      }
      printf("DH{%s}", v8);
      return 0;
    }

    rand() 함수는 랜덤한 숫자를 반환하는데,
    실제로는 프로그램을 매번 처음 실행시킬 때마다 같은 값이 나온다.

    그리고, “fall asleep from now on.” 문구를 출력하면서
    sleep 함수가 작동되어 거의 무한으로 기다려야된다.
    따라서 NOP 패치를 시키거나, 건너뛸 필요가 있다.

    rand 함수를 통해 값을 확인하여 get_flag를 호출하는 제어문이 2번 나오는데,

    첫번째에서는 rand 값이 1714636915이였고,
    두번째에서는 rand 값이 1957747793였다.

    따라서 첫 제어문에서는 seconds가 5,
    두번째 제어문에서는 seconds가 3이여야 한다.

    풀이

    sleep 함수를 건너뛴다.

    NOP으로 직접 바이너리를 패치해도 되지만, 필자는 rip 레지스터를 그냥 조작해서 건너뛰었다.

    ubuntu@WSL2:~/CTF/dreamhack.io/randzzz$ gdb ./chall
    ...
    gdb-peda$ b *main+89
    Breakpoint 1 at 0x12fe
    gdb-peda$ disas main
    Dump of assembler code for function main:
    ...
       0x00005555555552fe <+89>:    call   0x5555555550d0 <sleep@plt>
       0x0000555555555303 <+94>:    call   0x5555555550e0 <rand@plt>
    ...
    gdb-peda$ r
    ...
    Breakpoint 1, 0x00005555555552fe in main ()
    gdb-peda$ set $rip=*main+94
    gdb-peda$ c
    Continuing.
    Can you guess the rand num?:

    Can you guess the rand num?에서 5를 입력하고,
    두번째 제어문에서 seconds를 불러오는 명령어 주소를 breakpoint시켜 값을 수정한다.

    ...
    gdb-peda$ set $rip=*main+94
    gdb-peda$ b *main+354
    Breakpoint 2 at 0x555555555407
    gdb-peda$ c
    Continuing.
    Can you guess the rand num?: 5
    ...
    => 0x555555555407 <main+354>:   mov    eax,DWORD PTR [rbp-0xc]
       0x55555555540a <main+357>:   cmp    ecx,eax
    ...
    Breakpoint 2, 0x0000555555555407 in main ()
    gdb-peda$ x/wx $rbp-0xc
    0x7fffffffdd84: 0x00000005
    gdb-peda$ set *0x7fffffffdd84=3
    gdb-peda$ x/wx $rbp-0xc
    0x7fffffffdd84: 0x00000003
    gdb-peda$ c
    Continuing.
    DH{c8b48ac08bbe00068ffb6606e2cf6ba0002c0dc4dd0aba20ac8d0608860048e0}
    [Inferior 1 (process 3449) exited normally]

    FLAG

    DH{c8b48ac08bbe00068ffb6606e2cf6ba0002c0dc4dd0aba20ac8d0608860048e0}


    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다