3 个版本
0.0.4 | 2023年5月28日 |
---|---|
0.0.3 | 2023年5月27日 |
0.0.2 | 2023年5月27日 |
0.0.1 |
|
500 在 Unix API 中排名
每月 下载 33 次
47KB
531 行
greenhook
https://crates.io/crates/greenhook
Greenhook 是一个基于seccomp-unotify的系统调用钩子库。它是由 https://github.com/pdlan/binder 改编而来的。
如果你想要寻找除 LD_PRELOAD
和 ptrace
之外的替代方案,可以尝试它。但是请注意,seccomp unotify 并不完全 替代这些技术,在开始之前请花一些时间阅读 seccomp_unotify(2)
。
要充分利用这个库,你需要有一个 >= 5.9.0 的内核版本。如果你想在Docker或其他容器中运行它(允许 process_vm_readv()
和 pidfd_getfd()
在没有能力的情况下运行),你需要 一个特殊的seccomp策略文件,如下所示
# docker run --security-opt seccomp=assets/seccomp.json ...
还需要安装libseccomp头文件和库
$ sudo apt install libseccomp-dev
示例
你可以在测试代码中找到一些示例。这里有一个简单的示例,它通过钩子 geteuid(2)
来使程序像 whoami(1)
一样工作,即使你不是root用户
use std::process::Command;
use greenhook::{Supervisor, UNotifyEventRequest};
use libseccomp::ScmpSyscall;
fn geteuid_handler(req: &UNotifyEventRequest) -> libseccomp::ScmpNotifResp {
req.return_syscall(0)
}
fn main() {
env_logger::init();
// Get argv[1]
let program = std::env::args().nth(1).unwrap();
let mut supervisor = Supervisor::new(2).unwrap();
supervisor.insert_handler(ScmpSyscall::new("geteuid"), geteuid_handler);
let mut cmd = Command::new(program);
let (mut child, thread_handle, pool) = supervisor.exec(&mut cmd).unwrap();
let _ = Supervisor::wait(&mut child, thread_handle, pool).unwrap();
}
运行此命令
> cargo run --example geteuid -- whoami
root
> whoami
user
一个更复杂的示例,通过钩子 openat(2)
将 /etc/passwd
替换为 /etc/resolv.conf
use std::{process::Command, ffi::CStr, fs::File, os::fd::AsRawFd};
use greenhook::{Supervisor, UNotifyEventRequest, RemoteProcess};
use libseccomp::ScmpSyscall;
use log::info;
use nix::{unistd::Pid, libc};
fn openat_handler(req: &UNotifyEventRequest) -> libseccomp::ScmpNotifResp {
let path = req.get_request().data.args[1];
let remote = RemoteProcess::new(Pid::from_raw(req.get_request().pid as i32)).unwrap();
let mut buf = [0u8; 256];
remote.read_mem(&mut buf, path as usize).unwrap();
// debug!("open (read from remote): {:?}", buf);
let path = CStr::from_bytes_until_nul(&buf).unwrap();
if !req.is_valid() {
return req.fail_syscall(libc::EACCES);
}
info!("open (path CStr): {:?}", path);
if path.to_str().unwrap() == "/etc/passwd" {
// open /etc/resolv.conf instead
let file = File::open("/etc/resolv.conf").unwrap();
let fd = file.as_raw_fd();
let remote_fd = req.add_fd(fd).unwrap();
req.return_syscall(remote_fd as i64)
} else {
unsafe { req.continue_syscall() }
}
}
fn main() {
env_logger::init();
// Get argv[1..]
let args = std::env::args().skip(1).collect::<Vec<_>>();
if args.len() == 0 {
panic!("Usage: {} <program> [args...]", std::env::args().nth(0).unwrap());
}
let mut supervisor = Supervisor::new(2).unwrap();
supervisor.insert_handler(ScmpSyscall::new("openat"), openat_handler);
let mut cmd = Command::new(args[0].clone());
let cmd = cmd.args(&args[1..]);
let (mut child, thread_handle, pool) = supervisor.exec(cmd).unwrap();
let _ = Supervisor::wait(&mut child, thread_handle, pool).unwrap();
}
运行此命令
> RUST_LOG=info cargo run --example openat -- cat /etc/passwd
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/etc/ld.so.cache"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/usr/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/usr/lib/locale/locale-archive"
[2023-05-27T14:39:57Z INFO openat] open (path CStr): "/etc/passwd"
# Generated by NetworkManager
...
请参阅examples/binder.rs
以获取更复杂的示例。它类似于Rust中https://github.com/pdlan/binder的逻辑。
限制
- 您的钩子函数是由管理进程(线程)执行的,而不是被管理的进程!这意味着当您需要代表被管理进程执行某些操作时,可能会遇到困难。
- 小心TOCTOU攻击!Seccomp unotify在处理系统调用时不会停止整个进程,因此存在这样的可能性:在管理进程检查它们之后,被管理进程可能会更改系统调用参数,而
continue_syscall
可能是有危险的(因此在这里标记为unsafe
)。 - 处理信号可能很麻烦。信号可能会中断系统调用或重新启动它们,但管理进程对此一无所知。尝试在您的函数中检查请求的有效性以减轻这个问题。更多详细信息请参阅
seccomp_unotify(2)
。
依赖
~4–16MB
~184K SLoC