当前位置: 首页 > news >正文

CVE_2020_26259 任意文件删除

为什么要用c语言搓个shellcode出来,为什么不用msfvenom?因为这玩意生成的shellcode是基于winsocket的,注进去还要启动个监听,我仅仅想要验证一下可行性而已,不如自己搓个弹出messagebox版本的shellcode

环境

windows 11,amd64, 编译器用的x64 Native Tools Command Prompt for VS 2022(MSVC)

原理

要写一个能在 Windows 上的 Shellcode,最大的挑战在于 PIC(Position Independent Code,位置无关代码)。你不能硬编码 API 的地址(因为重启或不同机器上地址会变),也不能直接引用数据段的字符串和全局变量,全局变量依赖重定位表,Shellcode 没有这东西。所有数据必须在栈(Stack)上。更不能用库函数,因为代码没有导入表(IAT)。

我们以 弹出MessageBox为例,需要解决四个主要问题:

找到 kernel32.dll 的基地址

在 kernel32 中找到 GetProcAddress 和 LoadLibraryA 的地址

使用 LoadLibraryA 加载 user32.dll(MessageBox 在这里面)

解析出 MessageBoxA 的地址并调用

1.找 kernel32.dll 的基地址

我们利用 TEB (Thread Environment Block) 和 PEB (Process Environment Block) 来查找。

_WIN64 和 32 位使用不同的寄存器:64 位:gs:[0x60]32 位:fs:[0x30]

获取 PEB后,找到peb下的Ldr,再获取InMemoryOrderModuleList。链表顺序通常是: .exe -> ntdll.dll -> kernel32.dll. LDR_DATA_TABLE_ENTRY 结构体比较复杂,但在 InMemoryOrderLinks 偏移处 DllBase 通常在 entry 之后特定的偏移位置。 可以利用CONTAINING_RECORD的技巧或者直接偏移计算。

HMODULE GetKernel32() {

#ifdef _WIN64

PEB *peb = (PEB*)__readgsqword(0x60);

#else

PEB *peb = (PEB*)__readfsdword(0x30);

#endif

PEB_LDR_DATA *ldr = peb->Ldr;

LIST_ENTRY *head = &ldr->InMemoryOrderModuleList;

LIST_ENTRY *entry = head->Flink;

entry = entry->Flink;

entry = entry->Flink;

LDR_DATA_TABLE_ENTRY *ldrEntry = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

return (HMODULE)ldrEntry->DllBase;

}

2.手动解析导出表

获取 DOS Header 和 NT Header。从 OptionalHeader.DataDirectory 获取导出表 RVA。然后获取三个主要数组的地址(addressOfFunctions、addressOfNames、addressOfNameOrdinals),遍历 AddressOfNames 找函数名。使用 ordinals 和 AddressOfFunctions 得到函数实际地址。

其实就是等价于 Windows API 的 GetProcAddress,但是自己实现了,不依赖任何导入表。

FARPROC MyGetProcAddress(HMODULE hModule, const char *lpProcName) {

PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule;

PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)hModule + dos->e_lfanew);

DWORD exportDirRVA = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

if (exportDirRVA == 0) return NULL;

PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule + exportDirRVA);

DWORD *names = (DWORD*)((BYTE*)hModule + exportDir->AddressOfNames);

WORD *ordinals = (WORD*)((BYTE*)hModule + exportDir->AddressOfNameOrdinals);

DWORD *funcs = (DWORD*)((BYTE*)hModule + exportDir->AddressOfFunctions);

for (DWORD i = 0; i < exportDir->NumberOfNames; i++) {

char *name = (char*)((BYTE*)hModule + names[i]);

if (MyStrCmp(name, lpProcName) == 0) {

WORD ordinal = ordinals[i];

return (FARPROC)((BYTE*)hModule + funcs[ordinal]);

}

}

return NULL;

}

这里因为不能依赖库函数,所以 MyStrCmp是自己实现的

int MyStrCmp(const char *s1, const char *s2) {

while (*s1 && (*s1 == *s2)) {

s1++;

s2++;

}

return *(const unsigned char*)s1 - *(const unsigned char*)s2;

}

3.EntryPoint

首先函数名和 DLL 名手动写成 char 数组,避免直接引用字符串。获取 Kernel32 基址,再获取 GetProcAddress 函数指针、获取 LoadLibraryA 和 ExitProcess 函数指针,就可以加载 user32.dll, 获取 MessageBoxA 函数指针了

void EntryPoint() {

char sLoadLib[] = {'L','o','a','d','L','i','b','r','a','r','y','A',0};

char sGetProc[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};

char sExit[] = {'E','x','i','t','P','r','o','c','e','s','s',0};

char sUser32[] = {'u','s','e','r','3','2','.','d','l','l',0};

char sMsgBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};

char sText[] = {'H','e','l','l','o',' ','F','r','o','m',' ','C',0};

char sTitle[] = {'T','e','s','t','.',0};

HMODULE hKernel32 = GetKernel32();

P_GetProcAddress pGetProcAddress = (P_GetProcAddress)MyGetProcAddress(hKernel32, sGetProc);

if (!pGetProcAddress) return;

P_LoadLibraryA pLoadLibraryA = (P_LoadLibraryA)pGetProcAddress(hKernel32, sLoadLib);

P_ExitProcess pExitProcess = (P_ExitProcess)pGetProcAddress(hKernel32, sExit);

HMODULE hUser32 = pLoadLibraryA(sUser32);

P_MessageBoxA pMessageBoxA = (P_MessageBoxA)pGetProcAddress(hUser32, sMsgBox);

if (pMessageBoxA) {

pMessageBoxA(NULL, sText, sTitle, MB_OK);

}

if (pExitProcess) {

pExitProcess(0);

}

}

编译

编译shellcode,这里建议开启O1优化把函数内联进去

cl.exe /c /nologo /Gy /O1 /GS- /Tc shellcode.c /Fo:shellcode.obj

链接shellcode

link.exe /nologo /ENTRY:EntryPoint /SUBSYSTEM:WINDOWS /NODEFAULTLIB /ALIGN:16 /ORDER:@order.txt shellcode.obj /OUT:shellcode.exe

注意这里因为shellcode必须要保证入口函数偏移为0,所以要指定函数的顺序(order.txt)

我采用的顺序如下:

EntryPoint

GetKernel32

MyGetProcAddress

MyStrCmp

image

这里虽然error但是其实在用户态,shellcode.exe就已经可以执行了

然后把shellcode.exe的.text段抠出来,这里我们用python比较方便

import pefile

import os

def extract_shellcode(file_path, out_file="shellcode.bin"):

try:

pe = pefile.PE(file_path)

except FileNotFoundError:

print(f"Error: File '{file_path}' not found!")

return

except pefile.PEFormatError as e:

print(f"Error: Invalid PE file: {e}")

return

shellcode = b""

for section in pe.sections:

if b".text" in section.Name:

shellcode = section.get_data()

break

if not shellcode:

print("Error: .text section not found!")

return

print(f"Shellcode Length: {len(shellcode)} bytes\n")

c_array = ''.join(f"\\x{byte:02x}" for byte in shellcode)

print(f"// C String Format:\n\"{c_array}\"")

print("\n// Hex Format:")

print(shellcode.hex())

try:

with open(out_file, "wb") as f:

f.write(shellcode)

print(f"\nShellcode written to '{out_file}'")

except Exception as e:

print(f"Error writing shellcode to file: {e}")

if __name__ == "__main__":

exe_path = "shellcode.exe"

out_path = "shellcode.bin"

extract_shellcode(exe_path, out_path)

image

就得到了hex串

执行

然后简单写个加载器试试

#include

#include

#include

int main() {

unsigned char shellcode[] =

"";

void* exec_mem = VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (exec_mem == NULL) {

std::cerr << "[-] VirtualAlloc failed." << std::endl;

return -1;

}

std::cout << "[+] Memory allocated at: 0x" << exec_mem << std::endl;

memcpy(exec_mem, shellcode, sizeof(shellcode));

std::cout << "[+] Shellcode copied to memory." << std::endl;

DWORD oldProtect = 0;

BOOL vpResult = VirtualProtect(exec_mem, sizeof(shellcode), PAGE_EXECUTE_READ, &oldProtect);

if (!vpResult) {

std::cerr << "[-] VirtualProtect failed." << std::endl;

return -1;

}

std::cout << "[+] Memory protection changed to RX (Read/Execute)." << std::endl;

std::cout << "[*] Executing shellcode..." << std::endl;

((void(*)())exec_mem)();

VirtualFree(exec_mem, 0, MEM_RELEASE);

return 0;

}

这里使用VirtualProtect赋予可执行权限,不然 VirtualAlloc的东西其实是在堆上的,无法执行。

然后把loader编译

image峦卑刂郴

http://www.jsqmd.com/news/549131/

相关文章:

  • 为什么你需要KKS-HF_Patch?解锁Koikatsu Sunshine完整游戏体验的终极指南
  • 飞牛NAS上不用Docker也能玩转Zerotier?SSH直装教程+避坑指南
  • 如何选择高性价比的企业号码认证公司?手机/座机来电名片服务商选型方案 - 企业服务推荐
  • 从‘山峰’与‘山谷’理解拉普拉斯锐化:一个给视觉派程序员的MATLAB教程
  • PID算法进阶:从经典到变种的深度解析与应用指南
  • OpenClaw定时任务专家:Qwen3-32B-Chat实现凌晨自动数据备份
  • 边缘AI时代的流片风险:GPU IP的优势所在
  • GTE-large多任务效果惊艳展示:同一段‘碳中和’政策文本输出目标事件+责任主体+情感倾向
  • MedGemma-X模型压缩:基于TensorRT的推理加速
  • Node.js环境配置与PyTorch模型服务:打造高性能AI推理网关
  • 为什么你的STM32F103项目需要切换到内部晶振?配置教程与性能对比
  • 2026年浙江好用的FSB认证公司,涵盖俄罗斯、白俄罗斯、哈萨克斯坦认证 - 工业设备
  • 高效获取B站资源的完全指南:BiliTools跨平台下载工具详解
  • 如何用AI将复杂代码库转化为结构化学习教程
  • Z-Image-Turbo-辉夜巫女创意工坊:惊艳二次元角色与场景概念设计作品集
  • Cosmos-Reason1-7B效果展示:对用户模糊提问自动补全约束条件,再展开严谨推理
  • 探讨2026年宜昌电竞网咖,费用低且服务好的有哪些 - mypinpai
  • Uncle小说:一站式PC端数字阅读解决方案
  • 2026郑州热门发酵苹果醋品牌排名,好用的发酵苹果醋品牌选哪家 - mypinpai
  • macOS窗口管理终极解决方案:AltTab完整指南,让你告别混乱的多任务界面
  • Vsync中的ArmingInfo和FrameTimeline
  • 探讨和丝露苹果醋,其市场认可度排名如何 - 工业品网
  • payload-dumper-go:重新定义Android OTA包解压效率的并行处理引擎
  • 别再只盯着YOLO了!盘点2024年那些专为无人机AI设计的宝藏数据集(附下载与使用指南)
  • OpenClaw语音交互方案:Qwen3-32B对接Whisper实现会议语音转写
  • 美锦墅精造联系方式查询:高端私宅健康精造服务联系渠道与业主决策参考指南 - 品牌推荐
  • 烁智 AI 云入门|部署 OpenClaw保姆级教程
  • 树莓派5变身家庭自动化中枢:用Docker部署n8n,零代码搞定智能家居联动
  • 从SRResNet到WDSR:剖析残差网络在图像超分辨率中的演进与实战
  • WuliArt Qwen-Image Turbo真实作品:用户投稿精选——从概念草图到成品交付全链路