checksec
└─# checksec cpp_string
[*] '/root/dream/cpp_string/cpp_string'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
문제풀이
//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;
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) {
    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" 파일을 읽고 
flag[64]에 저장 →read_flag() - "test" 파일을 읽고 
readbuffer[64]에 저장 →read_file() - "test" 파일 쓰기 → 
write_file() readbuffer[64]에 저장된 데이터 출력 →show_contents()
cpp의 std::ifstream 객체에서 사용하는 read()에 대한 취약점을 다룬 문제다.
read()의 경우 기존 C언어의 read()와 동일한 작업을 수행한다. 하지만 버퍼에 입력을 받을 때 NULL 바이트를 추가하지 않기 때문에 메모리릭이 발생할 수 있다.
예를 들어 readbuffer[64]와 flag[64]가 메모리상에서 연결됐다고 가정하자.
std::ifstream 객체의 read()를 통해 readbuffer[64]를 "A"로 가득 채우면 어떻게 될까?
"A"*64+"DH{this_is_flag?}"
메모리에서는 위와 같이 저장될 것이다. 그렇다면 readbuffer[]를 출력하면 어떻게 될까?
NULL 바이트가 추가되지 않았기 때문에, readbuffer[64] 뿐만 아니라 뒤에 연결된 flag 변수의 데이터까지 출력될 것이다.
정리하면
std::ifstream객체의read()를 사용하여readbuffer[64]를 입력한다면, 메모리 릭 발생 가능이 존재readbuffer[64]와flag[64]가 메모리에서 연결되어 할당된 상황
위 2가지 조건을 만족한다면 메모리릭 취약점을 이용해서 flag를 읽을 수 있다.
○ read_file()
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);
    }
}
read_file()은 "test" 파일에 값을 readbuffer[64]에 저장하는 함수다. 코드를 보면 is.read()로 readbuffer[64]에 데이터를 저장하는 것을 확인할 수 있다.
- 즉, 위에서 언급한 것처럼 버퍼를 입력 받은 후 널 바이트를 별도로 추가하지 않기 때문에 메모리 릭이 발생할 수 있다.
 - 1번 조건 만족!
 
pwndbg> info var
All defined variables:
Non-debugging symbols:
/* other var */
0x0000000000602380  readbuffer
0x00000000006023c0  flag
0x0000000000602400  writebuffer[abi:cxx11]
0x0000000000602420  std::__ioinit
0x0000000000602428  _end
다음으로 flag[64]와 readbuffer[64]의 위치를 확인해보자.
전역변수 주소는 info var 명령어로 구할 수 있다. flag[64]와 readbuffer[64]의 주소를 연산하면 64바이트 차이임을 알 수 있다.
- 2번 조건 만족!
 
그렇다면 공격시나리오는 다음과 같이 작성할 수 있다.
- "test" 파일에 
"a"*64를 저장 read_file(),read_flag()실행show_contents()실행 후,readbuffer[]와flag[]확인
공격 시나리오대로 진행해보자.

reference
[dreamhack] Memory Corruption - C++