178 lines
3.6 KiB
Go
178 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const probeText = `
|
|
BEGIN {
|
|
printf("ready\n")
|
|
}
|
|
|
|
fentry:vmlinux:security_create_user_ns {
|
|
signal(KILL);
|
|
}
|
|
|
|
fentry:vmlinux:security_file_open {
|
|
$inode = args->file->f_inode;
|
|
$d = $inode->i_sb->s_dev;
|
|
$i = $inode->i_ino;
|
|
|
|
if ($d == $1 && $i == $2) {
|
|
signal(KILL);
|
|
}
|
|
}
|
|
`
|
|
|
|
func getProbeParams(filename string) (uint64, uint64, error) {
|
|
info, err := os.Stat("/flag.txt")
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
if !ok {
|
|
return 0, 0, fmt.Errorf("expected Stat_t, but was: %s", info.Sys())
|
|
}
|
|
|
|
return stat.Dev, stat.Ino, nil
|
|
}
|
|
|
|
func runTracer(ctx context.Context, dev, ino uint64, rdy chan struct{}, errs chan error) {
|
|
cmd := exec.CommandContext(ctx, "/bin/bpftrace", "--unsafe", "-e", probeText, strconv.FormatUint(dev, 10), strconv.FormatUint(ino, 10))
|
|
// cmd.Wait will close the reader automatically
|
|
reader, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
errs <- err
|
|
return
|
|
}
|
|
|
|
// wait for the probe to be ready
|
|
scanner := bufio.NewScanner(reader)
|
|
for scanner.Scan() {
|
|
if strings.Contains(scanner.Text(), "ready") {
|
|
break
|
|
}
|
|
}
|
|
|
|
close(rdy)
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func mountFilesystems() error {
|
|
if err := syscall.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
|
|
return fmt.Errorf("error mounting /dev: %s", err)
|
|
}
|
|
|
|
if err := os.Mkdir("/proc", 0555); err != nil {
|
|
return fmt.Errorf("error creating /proc: %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("proc", "/proc", "proc", 0, ""); err != nil {
|
|
return fmt.Errorf("error mounting /proc: %s", err)
|
|
}
|
|
|
|
if err := os.Mkdir("/sys", 0555); err != nil {
|
|
return fmt.Errorf("error creating /sys: %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
|
|
return fmt.Errorf("error mounting /sys: %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("tracefs", "/sys/kernel/tracing", "tracefs", 0, ""); err != nil {
|
|
return fmt.Errorf("error mounting /sys/kernel/tracing: %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("debugfs", "/sys/kernel/debug", "debugfs", 0, ""); err != nil {
|
|
return fmt.Errorf("error mounting /sys/kernel/debug: %s", err)
|
|
}
|
|
|
|
if err := os.Mkdir("/tmp", 0555); err != nil {
|
|
return fmt.Errorf("error creating /tmp: %s", err)
|
|
}
|
|
|
|
if err := syscall.Mount("tmpfs", "/tmp", "tmpfs", syscall.MS_NOEXEC|syscall.MS_NODEV|syscall.MS_NOSUID, ""); err != nil {
|
|
return fmt.Errorf("error mounting /tmp: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func shutdown() error {
|
|
return syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
|
}
|
|
|
|
func spawnShell(ctx context.Context) error {
|
|
withTimeout, cancel := context.WithTimeout(ctx, time.Minute)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(withTimeout, "/bin/sh")
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{Uid: 99999, Gid: 99999},
|
|
Setpgid: true,
|
|
Pdeathsig: syscall.SIGKILL,
|
|
}
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
fmt.Println("command failed:", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if err := mountFilesystems(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
dev, ino, err := getProbeParams("/flag.txt")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
rdy := make(chan struct{})
|
|
errs := make(chan error)
|
|
go runTracer(ctx, dev, ino, rdy, errs)
|
|
|
|
select {
|
|
case err = <-errs:
|
|
panic(err)
|
|
case <-rdy:
|
|
}
|
|
|
|
if err := spawnShell(ctx); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := shutdown(); err != nil {
|
|
panic(err)
|
|
}
|
|
panic("unreachable")
|
|
}
|