【技术分享】手把手教你在PE文件中植入无法检测的后门(下)

http://p2.qhimg.com/t01f2934d0f79023cde.jpg

译者:eridanus96

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

传送门

【技术分享】如何在PE文件中植入完全无法检测的后门(上)

前言

在上篇中,我们已经成功创建了一个新的Section Header,并将我们的Shellcode放在其中,劫持了执行流到我们的Shellcode,运行后再返回到应用程序的正常功能。而在本文中,我们在此基础上继续讨论如何来实现反病毒软件软件的低检测率,真正做到标题所说的“完全无法检测的后门”。

7. 如何通过用户交互和Codecave来触发Shellcode

在这一小节中,我们将结合两种方法来实现低检测率,并有效改善新增Section Header方法的诸多缺点。具体要讨论的技术如下:

如何基于用户和特定功能的交互,触发我们的Shellcode。

如何查找和使用Codecave。

7.1 Codecave

Codecave(代码洞)是指在程序运行时内存中的死块(Dead block)或空块(Empty block),可以用来注入我们自己的代码。相比于创建一个新的Section,我们完全可以使用现有的Codecave注入我们的Shellcode。几乎在任何PE中都能找到不同大小的Codecave。并且,Codecave的大小十分关键,我们希望能在找到一个比Shellcode大的Codecave,这样我们就能顺利注入Shellcode,而不必再将其分成多个小块。

第一步,是要找到一个Codecave。Cave Miner是一个非常好用的Python脚本,可以方便地帮助我们找到Codecave,该脚本需要我们提供所需的大小,随后就会自动查找并显示大于该值的全部Codecave。

http://p5.qhimg.com/t011a8e7fc8a0683aeb.png

在这里,我们发现有两个Codecave大于700字节,这两处足够让我们注入Shellcode。我们需要记下虚拟地址(vaddress),虚拟地址就是其起始地址,随后我们将通过跳转到虚拟地址来实现执行流的劫持。

然而,如图中所示,现在找到的这两个Codecave都仅仅是可读的。为了让它能执行我们的Shellcode,就必须要让它可读、可写以及可执行,这一点我们使用LordPE来实现。

http://p6.qhimg.com/t01f4fe52bbbbcfab68.jpg

7.2 通过用户交互触发Shellcode

进展到这里,我们已经有了一个可以跳转到的Codecave,接下来需要找到一种方法去通过用户的交互将执行流重定向到我们的Shellcode上。与前面的方法不同,我们现在并不希望在程序一运行后就劫持执行流。我们希望的是,让程序正常运行,并在用户进行特定功能的交互操作时再执行Shellcode,例如在点击某个选项卡的时候。

为了实现这一点,我们需要在应用程序中查找引用字符串。然后,我们可以通过修改一个特定的引用字符串的地址,来跳转到Codecave。这就意味着,每当在内存中访问一个特定的引用字符串的地址时,执行流都会被重定向到我们的Codecave。让我们具体来研究一下如何去实现。

在Ollydbg中打开7zip程序,右键点击,选择“Search for”,选择“All reference text strings”。

http://p0.qhimg.com/t01e8dae7461b8a4042.jpg

在引用字符串中,我们发现了一个有趣的字符串,一个域名:http://www.7-zip.org。当用户点击“About(关于)”——“Domain(网站)”时,该域的内存地址就会被访问。

http://p9.qhimg.com/t013ffd87787873ef15.jpg

在这里,我们可以在单个程序中设置多个用户交互触发器。举例来说,我们使用上图中的“网址”按钮,该按钮的正常功能是点击后在浏览器中打开7zip的官网。而我们的目标则是,用户在点击该按钮后,能触发我们的Shellcode。

现在,我们必须要在域名字符串的地址添加一个断点,借此来修改其操作码,让用户在点击按钮的时候能跳转到我们的Codecave中。我们复制域名字符串的地址0044A8E5并添加一个断点。然后,我们点击7zip程序中的域名按钮。随后,执行就会在断点处停止,如下图所示:

http://p9.qhimg.com/t01ec6745c16e538b1a.jpg

现在,我们可以修改这个地址,让它能跳转到Codecave,这样一来,当用户点击该按钮时,执行流会跳转到我们的Codecave,再然后会执行我们的Shellcode。

此外,我们还复制0044A8E5后面的指令,因为我们希望在执行完Shellcode后执行流能返回这里,继续运行其正常的功能。

http://p9.qhimg.com/t012e49ebb31a8f686e.jpg

在修改为JMP 00477857之后,我们将可执行文件另存为7zFMUhijacked.exe。请注意,地址00477857是Codecave1的起始地址。

我们在Ollydbg中加载7zFMUhijacked.exe,让其正常执行,随后点击该域名按钮,我们就被重定向到了一个空的Codecave中。

http://p4.qhimg.com/t0144b3bfa2d006ea70.jpg

接下来,为了保持文章的简洁,我们在这里略过“添加Shellcode”和“修改Shellcode”这两个步骤,因为这和之前6.2、6.3中所讲解的方法一致。

7.3 生成Shell

在我们添加和修改Shellcode,并将执行流恢复到我们此前劫持的0044A8E5位置后,将其保存为7zFMUhijackedShelled.exe。该Shellcode使用的是Stageless Windows reverse TCP。我们设置一个netcat监听器,运行7zFMUhijackedShelled.exe,并点击网站按钮。

http://p9.qhimg.com/t012799f81f43be7c4a.png

一切如我们所料,现在又得到了一个Shell。接下来再看看杀毒软件的检测情况如何?

http://p7.qhimg.com/t01fc18ec4906dc8182.jpg

还不错,这回检测率从16/36下降到了3/38。这完全要归功于通过用户在特定功能内的交互行为和Codecave来触发Shellcode的方法。同时也暴露了大部分反病毒检测原理的弱点——如果一个已知且未经编码的msfvenom Shellcode,位于Codecave中并且在用户交互过程中触发,它们就不能再被检测出来。

3/38的检测率确实不错,但还没有达到我们在标题中所说的“完全无法检测”,因此我们还要继续进行探索。还记得在最开始,我们自行加了一些限制,基于这些限制,我们目前似乎只能对Shellcode进行自定义编码,并在内存中执行时对其进行解码。


8. 自定义编码Shellcode

在上述这些尝试与成果的基础之上,我们想要使用XOR编码器去对Shellcode进行编码。

那么,为什么会想到XOR呢?有下面几个原因。首先,XOR相对容易实现。其次,我们并不需要为其编写一个解码器,只需要XOR两次,就能得到原始值。

所以,我们对Shellcode进行一次XOR操作,并将其保存。然后,我们只要在运行过程中,在内存里再对编码后的值执行一次XOR操作,就能得到原始的Shellcode。由于这个过程是在内存中完成的,所以反病毒软件将无法捕捉到这一行为!

为此,我们就需要两个Codecave了,一个用来放Shellcode,另一个用来放编码/解码器。在此前7.1的寻找过程中,我们恰好发现了两个大于700字节的Codecave,它们都有足够的空间来放Shellcode和编码/解码器。下面是执行流的流程图:

http://p8.qhimg.com/t0120ab866274575e26.jpg

因此,在程序执行后,当用户点击域名按钮时,执行流就会被劫持到CC2的起始地址0047972E,将执行编码/解码器的XOR操作,并将编码/解码后的shellcode存放在CC1的起始地址00477857。在CC2执行完成后,将会跳转到CC1开始执行,会产生一个Shell。当CC1执行完成后,我们将通过CC1跳转到最初劫持执行流的地方,也就是点击域名按钮的操作,以此来确保7zip的程序功能与之前仍然是一样的。上述这些,听起来就像是一次长途旅行。

接到消息后,我们从地址0044A8E5(单击域名按钮)劫持执行流到CC2的起始地址0047972E,并在磁盘上保存修改后的文件。我们再在Ollydbg中运行修改后的7zip文件,并通过单击“域名”按钮触发劫持过程。

http://p8.qhimg.com/t012f79e3d187f27704.jpg

现在我们在CC2的位置,在写XOR编码器之前,我们首先要跳转到CC1的起始地址并且植入我们的Shellcode,这样我们就能够得到XOR编码器所需要用到的准确地址。请注意,第一步中的劫持到CC2也可以在最后执行,因为它不会影响到上面流程图所示的整个执行流程。

我们跳转到CC1,采用相同的操作,植入、修改Shellcode并恢复执行流到0044A8E5。由于此前已经解释了植入、修改Shellcode和恢复执行流的方法,在此就不再赘述。

http://p2.qhimg.com/t01d078745243119424.jpg

如上图所示,这是CC1中Shellcode的最后几行,我们记下了Shellcode结束的地址是0047799B,接下来的指令就是恢复执行流。因此,我们必须对起始地址00477859到结束地址0047799B之间的Shellcode进行编码。

我们将CC2的起始地址移动到00477857,随后开始编写XOR编码器,以下就是XOR编码器的具体实现:

PUSH ECX, 00477857               // Push the starting address of shellcode to ECX.
XOR BYTE PTR DS:[EAX],0B         // Exclusive OR the contents of ECX with key 0B
INC ECX                          // Increase ECX to move to next addresses
CMP ECX,0047799B                 // Compare ECX with the ending address of shellcode
JLE SHORT 00479733               // If they are not equal, take a short jump back to address of XOR operation
JMP 7zFM2.00477857               // if they are equal jump to the start of shellcode

当我们在CC1中进行编码操作时,我们必须要确保CC1所在的Header Section是可写的,否则Ollydbg将会产生访问冲突错误。使其可写、可执行的方法已经在此前的7.1中详细讲解过。

在进行编码后,我们在JMP 7zFM2.00477857处添加一个断点,就会跳转到编码的Shellcode。如果我们此时回到CC1,就可以看到Shellcode目前均已经被编码。

http://p5.qhimg.com/t01b2806e74bdd1528c.jpg

接下来,我们将CC1中的Shellcode与CC2中的编码/解码器全部保存,将文件命名为7zFMowned.exe。最后,让我们来共同看看它是否可以按照预想的那样来工作。

8.1 生成Shell

我们在Kali Box的8080端口上设置一个监听器,在Windows环境中运行7zFMbackdoored.exe,点击“域名”按钮。此时,一切都按预想的那样工作,7zip的官网页面可以打开,我们也随之得到了Shell。

http://p8.qhimg.com/t01849fc65e63b47b77.png

接下来,我们再来看看反病毒软件的检测率如何。

http://p9.qhimg.com/t01a5671e195ca106c7.jpg

9. 总结

通过本文的讲解,我们已经清楚知道了在保证功能相同、大小不变的情况下,如何植入一个完全无法检测的后门。希望本文的讲解能对大家有所帮助!

(完)