Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >linux无文件执行— fexecve 揭秘

linux无文件执行— fexecve 揭秘

作者头像
七夜安全博客
发布于 2020-02-26 03:48:26
发布于 2020-02-26 03:48:26
5.3K00
代码可运行
举报
文章被收录于专栏:七夜安全博客七夜安全博客
运行总次数:0
代码可运行

前言

良好的习惯是人生产生复利的有力助手。

继续2020年的flag,至少每周更一篇文章,今天讲linux无文件执行。

无文件执行

之前的文章中,我们讲到了无文件执行的方法以及混淆进程参数的方法,今天我们继续讲解一种linux无文件执行的技巧,是后台朋友给我的提醒,万分感谢,又学到了新的东西。

linux无文件执行,首先要提到两个函数:memfd_create 和 fexecve。

memfd_create 和 fexecve

1 . memfd_create:允许我们在内存中创建一个文件,但是它在内存中的存储并不会被映射到文件系统中,至少,如果映射了,我是没找到,因此不能简单的通过ls命令进行查看,现在看来这的确是相当隐蔽的。事实上,如果一个文件存在,那么我们还是可以去发现它的,谁会去调用这个文件呢?使用如下的命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
lsof | grep memfd

2 . 第二个函数,fexecve同样的功能很强大,它能使我们执行一个程序(同execve),但是传递给这个函数的是文件描述符,而不是文件的绝对路径,和memfd_create搭配使用非常完美!

但是这里有一个需要注意的地方就是,因为这两个函数相对比较新,memfd_create 是在kernel3.17才被引进来,fexecve是glibc的一个函数,是在版本2.3.2之后才有的, 没有fexecve的时候, 可以使用其它方式去取代它,而memfd_create只能用在相对较新的linux内核系统上。

fexecve的实现

今天不谈memfd_create,这是linux的新特性,没有什么好玩的,本人对fexecve 的实现很有兴趣,因为fexecve是glibc中的函数,而不是linux的系统调用。先看一下fexecve的用法,下面的fexecve_test.c 代码是实现ls -l /dev/shm 功能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

static char *args[] = {
    "hic et nunc",
    "-l",
    "/dev/shm",
    NULL
};

extern char **environ;

int main(void) 
{
    struct stat st;
    void *p;
    int fd, shm_fd, rc;

    shm_fd = shm_open("wurstverschwendung", O_RDWR | O_CREAT, 0777);
    if (shm_fd == -1) {
    perror("shm_open");
    exit(1);
    }

    rc = stat("/bin/ls", &st);
    if (rc == -1) {
    perror("stat");
    exit(1);
    }

    rc = ftruncate(shm_fd, st.st_size);
    if (rc == -1) {
    perror("ftruncate");
    exit(1);
    }

    p = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
         shm_fd, 0);
    if (p == MAP_FAILED) {
    perror("mmap");
    exit(1);
    }

    fd = open("/bin/ls", O_RDONLY, 0);
    if (fd == -1) {
    perror("openls");
    exit(1);
    }

    rc = read(fd, p, st.st_size);
    if (rc == -1) {
    perror("read");
    exit(1);
    }
    if (rc != st.st_size) {
    fputs("Strange situation!\n", stderr);
    exit(1);
    }

    munmap(p, st.st_size);
    close(shm_fd);

    shm_fd = shm_open("wurstverschwendung", O_RDONLY, 0);
    fexecve(shm_fd, args, environ);
    perror("fexecve");
    return 0;
}

代码中主要是分为了三步:

  1. 首先通过shm_open函数在 /dev/shm中创建了wurstverschwendung文件
  2. 将ls 命令文件写入到wurstverschwendung文件
  3. 通过fexecve执行wurstverschwendung文件,因为/dev/shm在内存中,因此fexecve实际上是在内存中执行文件。

对fexecve_test.c 进行编译并执行,可以看到/dev/shm下面确实生成了wurstverschwendung文件。

调试角度

fexecve是如何执行内存中的文件呢?一般可以从调试和源码的角度来探究其中的原理。首先使用strace调试一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
strace -f -tt -T ./fexecve_test

从打印的日志中,找到open系统调用,从创建文件开始关联:

大家可以看到shmopen 其实是在/dev/shm创建文件,而execve的执行文件为/proc/self/fd/3,为进程中打开的文件符号链接,这个指向的就是shm_open创建的文件,但是从监控execve的角度来说, execve无法获取执行文件的路径,从而实现了混淆。

源码角度

从上文中,我们大致知道了原理。具体细节还是要看源码:glibc中的代码库中(https://github.com/jeremie-koenig/glibc/blob/master-beware-rebase/sysdeps/unix/sysv/linux/fexecve.c)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>


/* Execute the file FD refers to, overlaying the running program image.
   ARGV and ENVP are passed to the new program, as for `execve'.  */
int
fexecve (fd, argv, envp)
     int fd;
     char *const argv[];
     char *const envp[];
{
  if (fd < 0 || argv == NULL || envp == NULL)
    {
      __set_errno (EINVAL);
      return -1;
    }

  /* We use the /proc filesystem to get the information.  If it is not
     mounted we fail.  */
  char buf[sizeof "/proc/self/fd/" + sizeof (int) * 3];
  __snprintf (buf, sizeof (buf), "/proc/self/fd/%d", fd);

  /* We do not need the return value.  */
  __execve (buf, argv, envp);

  int save = errno;

  /* We come here only if the 'execve' call fails.  Determine whether
     /proc is mounted.  If not we return ENOSYS.  */
  struct stat st;
  if (stat ("/proc/self/fd", &st) != 0 && errno == ENOENT)
    save = ENOSYS;

  __set_errno (save);

  return -1;
}

关键部位代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
char buf[sizeof "/proc/self/fd/" + sizeof (int) * 3];
  __snprintf (buf, sizeof (buf), "/proc/self/fd/%d", fd);

  /* We do not need the return value.  */
  __execve (buf, argv, envp);

fexecve本质上还是调用execve,只不过文件路径是在/proc中。fexecve_test中实现的功能,可以用bash来简单描述,作用是等同的:

总结

写完快12点了,发了发了,睡觉睡觉

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七夜安全博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
linux网络编程之POSIX 共享内存和 系列函数
本文介绍了Linux系统下共享内存的概念、实现方法以及相关的应用,包括共享内存的读写、同步和调试等方面。
s1mba
2017/12/28
1.9K0
Linux进程间通信(四) - 共享内存
共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因
三丰SanFeng
2018/01/16
7.4K0
Linux进程间通信(四) - 共享内存
runc 1.0-rc7 发布之际
在 18 年 11 月底时,我写了一篇文章 《runc 1.0-rc6 发布之际》 。如果你还不了解 runc 是什么,以及如何使用它,请参考我那篇文章。本文中,不再对其概念和用法等进行说明。
Jintao Zhang
2019/04/25
7380
CVE-2022-0847-DirtyPipe原理 | 文件覆写提权
​ CVE-2022-0847 是存在于 Linux内核 5.8 及之后版本中的本地提权漏洞。攻击者通过利用此漏洞,可覆盖重写任意可读文件中的数据,从而可将普通权限的用户提升到特权 root。
h0cksr
2023/05/17
9080
CVE-2022-0847-DirtyPipe原理 | 文件覆写提权
宋宝华:世上最好的共享内存(Linux共享内存最透彻的一篇)
早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU可以在各个进程访问到这片内存。
Linux阅码场
2019/12/10
51.8K6
宋宝华:世上最好的共享内存(Linux共享内存最透彻的一篇)
Linux内核编程--内存映射和共享内存
将一个文件或其它对象映射到进程地址空间,实现文件在磁盘的存储地址和进程地址空间中一段虚拟地址的映射关系。有了这样的映射,进程利用指针直接读写虚拟地址就可以完成对文件的读写操作。这样可以避免进行read/write函数操作。
Coder-ZZ
2022/05/09
6.8K0
Linux内核编程--内存映射和共享内存
【Linux】重定向与缓冲区
这三个函数 stat、fstat 和 lstat 都是 C 语言中用于获取文件的状态信息(如文件大小、权限、修改时间等)的系统调用。它们用于查询文件或目录的元数据,返回一个 struct stat 结构,结构中包含了该文件的详细信息。
用户11029103
2025/02/25
2690
【Linux】重定向与缓冲区
Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例
信号灯概述 什么是信号灯 信号灯用来实现同步,用于多线程,多进程之间同步共享资源(临界资源)。 PV原语:信号灯使用PV原语 P原语操作的动作是: u sem减1。 u sem减1后仍大于或等于零,则进程继续执行。 u 若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。 V原语操作的动作是: u sem加1。 u 若相加结果大于零,则进程继续执行。 u 若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。 信号灯分类 按信号灯实
三丰SanFeng
2018/01/16
2.1K0
Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例
Linux通用GPIO驱动写法与应用
在Linux中,可以对GPIO进行相关的控制,具体的做法就是利用字符设备驱动程序对相关的gpio进行控制。由于操作系统的限制,在Linux上又无法直接在应用程序的层面上对底层的硬件进行操作。本文主要通过一个点亮红外灯的实例,再次理解Linux下的应用程序与驱动程序的交互,同时加深驱动程序编写流程的理解。
bigmagic
2020/03/17
10.2K1
linux系统编程之进程(三):exec系列函数和system函数
一、exec替换进程映象 在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建 了一个进程之后,通常将子进程替换成新
s1mba
2018/01/03
2.3K0
linux系统编程之进程(三):exec系列函数和system函数
【操作系统】进程间的通信——共享内存
进程间的通信-共享内存 共享内存机制 共享内存机制是允许两个或多个进程(不相关或有亲缘关系)访问同一逻辑内存的机制。它是共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。 ---- 两种常用的共享内存方式 System V版本的共享内存 shmm 多进程直接共享内存 文件映射mmap 如果一个文件需要频繁进行读写,那么将它映射到内存中。 将特殊文件进行匿名内存映射,为有关联的进程提供共享内存空间。 为无关联的进程提供共享内存空间,将
半生瓜的blog
2023/05/13
1K0
【操作系统】进程间的通信——共享内存
磁盘文件读性能测试
 Timing buffered disk reads: 2454 MB in  3.00 seconds = 817.84 MB/sec
一见
2018/08/10
1.5K0
UNIX共享内存总结
    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。因此,采用共享内存的通信方式效率是非常高的。
王亚昌
2018/08/03
2.5K0
比ls快8倍?百万级文件遍历的奇技淫巧
1.问题背景 在Linux下当我们操作一个文件数较少的目录时,例如执行ls列出当前目录下所有的文件,这个命令可能会瞬间执行完毕,但是当一个目录下有上百万个文件时,执行ls命令会发生什么呢,带着疑问,我们做了如下实验(实验中使用的存储设备为NVMe接口的SSD): [root@localhost /data1/test_ls]# for i in {1..1000000}; do echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' > $i.txt ; done [ro
腾讯数据库技术
2018/06/05
5.3K3
一种C程序使用IPC多进程共享内存并实现热迁移的方法
这篇文章讨论如何使用CRIU迁移使用了共享内存的程序,主要讨论其中的前两种共享内存方法,最终介绍一种支持热迁移的C程序共享内存使用方法。
宋天伦
2020/12/31
1.4K0
Linux高性能server规划——多进程编程
该函数的每次都用都返回两次,在父进程中返回的是子进程的PID,在子进程中返回的是0.该返回值是兴许代码推断当前进程是父进程还是子进程的根据。
全栈程序员站长
2022/07/06
1.6K0
反弹shell-逃逸基于execve的命令监控(上)
本篇聊一聊 新的主题:《反弹shell-逃逸基于execve的命令监控》,打算写一个专题,预估可以写三篇,内容确实有点多,也是最近研究了一些有意思的东西,想给大家分享一下。喜欢的话,请大家一定点在看,并分享出去,算是对我原创最大的支持了。
七夜安全博客
2019/12/26
3.2K0
反弹shell-逃逸基于execve的命令监控(上)
Linux进程间通信:共享内存 (下)
邹立巍
2017/07/27
8.8K0
【Linux系统调用API】三、进程地址虚拟空间、fcntl函数、stat函数
下面我们写一个程序来测试一下,一次性最多能打开的文件数量,来验证文件描述符的作用和范围。
mindtechnist
2024/08/08
3140
【Linux系统调用API】三、进程地址虚拟空间、fcntl函数、stat函数
Linux 的进程间通信:信号量
本文介绍了Linux信号量、POSIX信号量、Linux条件变量和Linux线程同步基本概念,并通过代码示例展示了如何使用这些技术进行线程同步。
邹立巍
2017/07/25
7K0
相关推荐
linux网络编程之POSIX 共享内存和 系列函数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档