今天看了一下2021陇原战役WP,在看web方向的时候,看到pop链,想了解一下,后来又看到了p师傅在15年的一篇文章,在这里记录一下。这里主要是和反序列化一起讲的。
这个漏洞是在反序列化session
这个漏洞是因为libraries/joomla/session/session.php的_validate函数,将ua和xff调用set方法设置到了session中(session.client.browser和session.client.forwarded)
protected function _validate($restart = false)
{
...
// Record proxy forwarded for in the session in case we need it later
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$this->set('session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
}
...
// Check for clients browser
if (in_array('fix_browser', $this->_security) && isset($_SERVER['HTTP_USER_AGENT']))
{
$browser = $this->get('session.client.browser');
if ($browser === null)
{
$this->set('session.client.browser', $_SERVER['HTTP_USER_AGENT']);
}
elseif ($_SERVER['HTTP_USER_AGENT'] !== $browser)
{
// @todo remove code: $this->_state = 'error';
// @todo remove code: return false;
}
}
这里先说一下
cookie和session都是为了解决http无状态的问题,解决了每次登录网站都需要重新去输入账号密码
Step1. 客户端发送带有其资料的请求。
Step2. 服务器会使用 Set-Cookie header 告诉浏览器这些资料要存在 Cookies 资料库,当 Chrome 收到带有 Set-
Cookie 的回复时,会将 Set-Cookie 的 value 存入 Cookies 资料库 (ex: Crome SQLite database)。
Step3. 接着当客户端透过 Chrome 发送请求到 “test.com” 这个 domain 时,Chrome 会从 Cookies 资料库中读取
所有关于 “test.com” 这个 domain 的 Cookies 资料,并放在请求的 Cookie header 中。
Step4. 最后服务端透过解析 request Cookie header 中的资讯,了解客户端目前的状态。
cookie的问题
cookie是存到浏览器里的,安全性较低,但是可以加密后存放
存储的时间要进行设定
每次的访问必须将cookies的资料放到headers里 ,所以当cookies的资料过大时,过大的request会影响网络传输
的速度
为了弥补 Cookies 的不足,我们会在 Server 端使用 Session 的机制去储存这些状态资讯,并产生一组 Session
key 放入 Cookie 中,由于状态资讯是直接存在 Server 端的,所以使用者无法读取,使用者只能看到 session key
但不知道其结构,同时也能避免 Cookie 存入过多资料。
Step1. 客户端发送带有其资料的 request。
Step2. 服务端的 Session database(ex: mysql or redis)会将使用者资料存起来,并产生一组session _id,透过
Set-Cookie header传给客户端。
Step3. 接著当客户端透过 Chrome 发送请求到 “test.com” 这个 domain 时,Chrome 会从 Cookies 资料库中读取
所有关于 “test.com” 这个 domain 的 Cookies 资料,并将session_id放在 request 的 Cookie header 中。
Step4. 最后 Server 可以拿到request Cookie header 中的session_id资讯,去session database查找使用者资
讯。
回归正题
上面的UA和xff进入了数据库
正常情况下我们只是控制了反序列化对象中的一个字符串,不会触发反序列化相关的漏洞。但是p师傅说这里有个
姿势可以使得我们控制整个反序列化对象。
这里又有补充知识
phpcodz/pch-013.md at master · 80vul/phpcodz · GitHub
这里就是利用了php内置了多种处理器用来存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以
下三种,对应三种不同的处理格式:
处理器 | 对应的存储格式 |
---|---|
php | 键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值 |
php_serialize (php>=5.5.4) | 经过 serialize() 函数反序列处理的数组 |
通过上面对存储格式的分析,如果 PHP 在反序列化存储的 $_SESSION 数据时的使用的处理器和序列化时使用的
处理器不同,会导致数据无法正确反序列化,通过特殊的构造,甚至可以伪造任意数据:
文章中还介绍了两种情况
session.auto_start=On和session.auto_start=Off的两种情况,详情请看文章
当配置选项 session.auto_start=On,会自动注册 Session 会话
具体的我就不说了
继续说joomla,joomla也没有采用php自带的session处理机制,而是用多种方式(包括database、memcache等)自己编写了存储session的容器(storage)。
存储方式为『键名 + 竖线 + 经过 serialize() 函数反序列处理的值』,重要的是并没有处理好多个竖线的情况
这里利用的原理和sql注入相似,注入一个|符号,让他认为|前面的是键名,后面的是serialize字符串。这样来构造反序列化漏洞。
这里盗一张p师傅的图
但是由于在我们构造好的反序列化字符串后面,还有它原来的内容,这里必须要截断,但是这里没有注释符可以用。
这里p师傅说了一种办法来截断utf-8的字段
就是利用""(%F0%9D%8C%86)插入数据库
这里我们用同样的方法,在session进入数据库的时候就截断后面的内容,避免对我们反序列化过程造成影响。
这里利用的是php底层对session字符串处理的不合理
代码中通过指针移动判断"|“的位置,获取”|"前面部分为session键名,然后通过php_var_unserialize函数反序列
化"|“后面部门,如果解析成功则把值写入session,如果解析失败则销毁当变量,然后继续移动指针判断”|",如
果"|“存在,继续把”|“前数据作为变量,解析”|"后面的值。
这里利用的是""(%F0%9D%8C%86)来截断
在php>=5.6.13版本中修复此问题,5.6.13版本以前是第一个变量解析错误注销第一个变量,然后解析第二个变
量,但是5.6.13以后如果第一个变量错误,直接销毁整个session。
至于构造pop链就看p师傅的吧,这里只记录一下学习的过程,适合小白了解
F0%9D%8C%86)来截断
在php>=5.6.13版本中修复此问题,5.6.13版本以前是第一个变量解析错误注销第一个变量,然后解析第二个变
量,但是5.6.13以后如果第一个变量错误,直接销毁整个session。
至于构造pop链就看p师傅的吧,这里只记录一下学习的过程,适合小白了解
Joomla远程代码执行漏洞分析(总结) | 离别歌 (leavesongs.com)