more ctf stuff

This commit is contained in:
2025-09-20 21:10:36 +02:00
parent 443a793e6b
commit e0ed1e486b
92 changed files with 14630 additions and 7 deletions

View File

@@ -0,0 +1 @@
3.12

View File

@@ -0,0 +1,23 @@
FROM nixos/nix AS build
RUN mkdir -p /build
WORKDIR /build
COPY flag.txt flake.nix flake.lock /build/
COPY ./init /build/init/
RUN nix --extra-experimental-features nix-command --extra-experimental-features flakes build
FROM gcr.io/kctf-docker/challenge@sha256:9f15314c26bd681a043557c9f136e7823414e9e662c08dde54d14a6bfd0b619f
RUN apt-get update && apt-get install -y qemu-system-x86
WORKDIR /
COPY --from=build /build/result/bzImage /build/result/initrd.gz /
COPY run_qemu /
CMD kctf_setup && \
chmod 0777 /dev/kvm && \
chmod 0777 /dev/vhost-vsock && \
kctf_drop_privs \
socat \
TCP-LISTEN:1337,reuseaddr,fork \
EXEC:"kctf_pow /run_qemu"

View File

@@ -0,0 +1 @@
CTF{this isn't a flag}

View File

@@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1749857119,
"narHash": "sha256-tG5xUn3hFaPpAHYIvr2F88b+ovcIO5k1HqajFy7ZFPM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5f4f306bea96741f1588ea4f450b2a2e29f42b98",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -0,0 +1,51 @@
{
description = "bpfbox";
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05";
};
outputs = { self, nixpkgs }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
lib = pkgs.lib;
init = pkgs.buildGoModule {
name = "init";
vendorHash = null;
src = ./init;
};
initrdEnv = pkgs.buildEnv {
name = "initrd-env";
paths = with pkgs; [
pkgsStatic.busybox
bpftrace
init
];
pathsToLink = [
"/bin"
];
};
kernel = pkgs.linuxPackages.kernel;
initrd = pkgs.makeInitrdNG {
name = "initramfs";
contents = [
{ source = "${initrdEnv}/bin"; target = "/bin"; }
{ source = ./flag.txt; target = "/flag.txt"; }
{ source = "${initrdEnv}/bin/init"; target = "/init"; }
];
};
in
{
packages.x86_64-linux.default = pkgs.stdenvNoCC.mkDerivation {
name = "bpfbox";
phases = [ "installPhase" ];
installPhase = ''
mkdir -p $out
cp ${initrd}/initrd.gz $out/
cp ${kernel}/bzImage $out/
'';
};
};
}

View File

@@ -0,0 +1,3 @@
module init
go 1.24.2

View File

@@ -0,0 +1,173 @@
package main
import (
"bufio"
"context"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
)
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 {
cmd := exec.CommandContext(ctx, "/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")
}

View File

@@ -0,0 +1,187 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import base64
import os
import secrets
import socket
import sys
import hashlib
try:
import gmpy2
HAVE_GMP = True
except ImportError:
HAVE_GMP = False
sys.stderr.write("[NOTICE] Running 10x slower, gotta go fast? pip3 install gmpy2\n")
VERSION = 's'
MODULUS = 2**1279-1
CHALSIZE = 2**128
SOLVER_URL = 'https://goo.gle/kctf-pow'
def python_sloth_root(x, diff, p):
exponent = (p + 1) // 4
for i in range(diff):
x = pow(x, exponent, p) ^ 1
return x
def python_sloth_square(y, diff, p):
for i in range(diff):
y = pow(y ^ 1, 2, p)
return y
def gmpy_sloth_root(x, diff, p):
exponent = (p + 1) // 4
for i in range(diff):
x = gmpy2.powmod(x, exponent, p).bit_flip(0)
return int(x)
def gmpy_sloth_square(y, diff, p):
y = gmpy2.mpz(y)
for i in range(diff):
y = gmpy2.powmod(y.bit_flip(0), 2, p)
return int(y)
def sloth_root(x, diff, p):
if HAVE_GMP:
return gmpy_sloth_root(x, diff, p)
else:
return python_sloth_root(x, diff, p)
def sloth_square(x, diff, p):
if HAVE_GMP:
return gmpy_sloth_square(x, diff, p)
else:
return python_sloth_square(x, diff, p)
def encode_number(num):
size = (num.bit_length() // 24) * 3 + 3
return str(base64.b64encode(num.to_bytes(size, 'big')), 'utf-8')
def decode_number(enc):
return int.from_bytes(base64.b64decode(bytes(enc, 'utf-8')), 'big')
def decode_challenge(enc):
dec = enc.split('.')
if dec[0] != VERSION:
raise Exception('Unknown challenge version')
return list(map(decode_number, dec[1:]))
def encode_challenge(arr):
return '.'.join([VERSION] + list(map(encode_number, arr)))
def get_challenge(diff):
x = secrets.randbelow(CHALSIZE)
return encode_challenge([diff, x])
def solve_challenge(chal):
[diff, x] = decode_challenge(chal)
y = sloth_root(x, diff, MODULUS)
return encode_challenge([y])
def can_bypass(chal, sol):
from ecdsa import VerifyingKey
from ecdsa.util import sigdecode_der
if not sol.startswith('b.'):
return False
sig = bytes.fromhex(sol[2:])
with open("/kctf/pow-bypass/pow-bypass-key-pub.pem", "r") as fd:
vk = VerifyingKey.from_pem(fd.read())
return vk.verify(signature=sig, data=bytes(chal, 'ascii'), hashfunc=hashlib.sha256, sigdecode=sigdecode_der)
def verify_challenge(chal, sol, allow_bypass=True):
if allow_bypass and can_bypass(chal, sol):
return True
[diff, x] = decode_challenge(chal)
[y] = decode_challenge(sol)
res = sloth_square(y, diff, MODULUS)
return (x == res) or (MODULUS - x == res)
def usage():
sys.stdout.write('Usage:\n')
sys.stdout.write('Solve pow: {} solve $challenge\n')
sys.stdout.write('Check pow: {} ask $difficulty\n')
sys.stdout.write(' $difficulty examples (for 1.6GHz CPU) in fast mode:\n')
sys.stdout.write(' 1337: 1 sec\n')
sys.stdout.write(' 31337: 30 secs\n')
sys.stdout.write(' 313373: 5 mins\n')
sys.stdout.flush()
sys.exit(1)
def main():
if len(sys.argv) != 3:
usage()
sys.exit(1)
cmd = sys.argv[1]
if cmd == 'ask':
difficulty = int(sys.argv[2])
if difficulty == 0:
sys.stdout.write("== proof-of-work: disabled ==\n")
sys.exit(0)
challenge = get_challenge(difficulty)
sys.stdout.write("== proof-of-work: enabled ==\n")
sys.stdout.write("please solve a pow first\n")
sys.stdout.write("You can run the solver with:\n")
sys.stdout.write(" python3 <(curl -sSL {}) solve {}\n".format(SOLVER_URL, challenge))
sys.stdout.write("===================\n")
sys.stdout.write("\n")
sys.stdout.write("Solution? ")
sys.stdout.flush()
solution = ''
with os.fdopen(0, "rb", 0) as f:
while not solution:
line = f.readline().decode("utf-8")
if not line:
sys.stdout.write("EOF")
sys.stdout.flush()
sys.exit(1)
solution = line.strip()
if verify_challenge(challenge, solution):
sys.stdout.write("Correct\n")
sys.stdout.flush()
sys.exit(0)
else:
sys.stdout.write("Proof-of-work fail")
sys.stdout.flush()
elif cmd == 'solve':
challenge = sys.argv[2]
solution = solve_challenge(challenge)
if verify_challenge(challenge, solution, False):
sys.stderr.write("Solution: \n".format(solution))
sys.stderr.flush()
sys.stdout.write(solution)
sys.stdout.flush()
sys.stderr.write("\n")
sys.stderr.flush()
sys.exit(0)
else:
usage()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,9 @@
[project]
name = "misc-bpfbox"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"gmpy2>=2.2.1",
]

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec qemu-system-x86_64 -serial mon:stdio -nographic -cpu host -m 1024 -accel kvm -kernel /bzImage -initrd /initrd.gz -append "console=ttyS0 quiet"

36
2025/google/misc-bpfbox-patched/uv.lock generated Normal file
View File

@@ -0,0 +1,36 @@
version = 1
revision = 2
requires-python = ">=3.12"
[[package]]
name = "gmpy2"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/07/bd/c6c154ce734a3e6187871b323297d8e5f3bdf9feaafc5212381538bc19e4/gmpy2-2.2.1.tar.gz", hash = "sha256:e83e07567441b78cb87544910cb3cc4fe94e7da987e93ef7622e76fb96650432", size = 234228, upload-time = "2024-07-21T05:33:00.715Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/ee/8f9f65e2bac334cfe13b3fc3f8962d5fc2858ebcf4517690d2d24afa6d0e/gmpy2-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d03a1be1b1ad3944013fae5250316c3f4e6aec45ecdf189a5c7422d640004d", size = 885231, upload-time = "2024-07-21T05:31:41.471Z" },
{ url = "https://files.pythonhosted.org/packages/07/1c/bf29f6bf8acd72c3cf85d04e7db1bb26dd5507ee2387770bb787bc54e2a5/gmpy2-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd09dd43d199908c1d1d501c5de842b3bf754f99b94af5b5ef0e26e3b716d2d5", size = 696569, upload-time = "2024-07-21T05:31:43.768Z" },
{ url = "https://files.pythonhosted.org/packages/7c/cc/38d33eadeccd81b604a95b67d43c71b246793b7c441f1d7c3b41978cd1cf/gmpy2-2.2.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3232859fda3e96fd1aecd6235ae20476ed4506562bcdef6796a629b78bb96acd", size = 1655776, upload-time = "2024-07-21T05:31:46.272Z" },
{ url = "https://files.pythonhosted.org/packages/96/8d/d017599d6db8e9b96d6e84ea5102c33525cb71c82876b1813a2ece5d94ec/gmpy2-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30fba6f7cf43fb7f8474216701b5aaddfa5e6a06d560e88a67f814062934e863", size = 1707529, upload-time = "2024-07-21T05:31:48.732Z" },
{ url = "https://files.pythonhosted.org/packages/d0/93/91b4a0af23ae4216fd7ebcfd955dcbe152c5ef170598aee421310834de0a/gmpy2-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b33cae533ede8173bc7d4bb855b388c5b636ca9f22a32c949f2eb7e0cc531b2", size = 1634195, upload-time = "2024-07-21T05:31:50.99Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ba/08ee99f19424cd33d5f0f17b2184e34d2fa886eebafcd3e164ccba15d9f2/gmpy2-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:954e7e1936c26e370ca31bbd49729ebeeb2006a8f9866b1e778ebb89add2e941", size = 1656779, upload-time = "2024-07-21T05:31:53.657Z" },
{ url = "https://files.pythonhosted.org/packages/14/e1/7b32ae2b23c8363d87b7f4bbac9abe9a1f820c2417d2e99ca3b4afd9379b/gmpy2-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c929870137b20d9c3f7dd97f43615b2d2c1a2470e50bafd9a5eea2e844f462e9", size = 1204668, upload-time = "2024-07-21T05:31:56.264Z" },
{ url = "https://files.pythonhosted.org/packages/7b/ab/82e4ef7e5b26e2f7bf97c2d46567f1f00cc6a442e995c0e7830025187cdf/gmpy2-2.2.1-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:a3859ef1706bc631ee7fbdf3ae0367da1709fae1e2538b0e1bc6c53fa3ee7ef4", size = 885100, upload-time = "2024-07-21T05:31:58.339Z" },
{ url = "https://files.pythonhosted.org/packages/b4/2d/4d6992ac765c8e5b53c3f4950369e92194d376aef2dd12c950ee9b6bcd70/gmpy2-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6468fc604d5a322fe037b8880848eef2fef7e9f843872645c4c11eef276896ad", size = 696455, upload-time = "2024-07-21T05:32:00.546Z" },
{ url = "https://files.pythonhosted.org/packages/77/be/474784ac57eac28c61cf789e55acea874f115f00757896502f50a5bcd0f0/gmpy2-2.2.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a845a7701217da4ff81a2e4ae8df479e904621b7953d3a6b4ca0ff139f1fa71f", size = 1655890, upload-time = "2024-07-21T05:32:03.08Z" },
{ url = "https://files.pythonhosted.org/packages/a3/03/c59a817ac599043224101dac647e712ebce400394980609646993fcc7787/gmpy2-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0b1e14ef1793a1e0176e7b54b29b44c1d93cf8699ca8e4a93ed53fdd16e2c52", size = 1707719, upload-time = "2024-07-21T05:32:05.378Z" },
{ url = "https://files.pythonhosted.org/packages/24/4a/923e50787dcd7ac7caa14a1c3f15040c16bf9cad6e42d9664070b5d45e7f/gmpy2-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13b0e00170c14ed4cd1e007cc6f1bcb3417b5677d2ef964d46959a1833aa84ab", size = 1634253, upload-time = "2024-07-21T05:32:07.896Z" },
{ url = "https://files.pythonhosted.org/packages/93/39/9aa392f20f5246740529a65385d2a40b7002f1fa98cc3205e708a77da2d7/gmpy2-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:831280e3943897ae6bf69ebd868dc6de2a46c078230b9f2a9f66b4ad793d0440", size = 1656831, upload-time = "2024-07-21T05:32:10.5Z" },
{ url = "https://files.pythonhosted.org/packages/b2/28/335bf8b4a1fc2acacda311f03cdfe87a07585754b95bae2c5331de15726b/gmpy2-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74235fcce8a1bee207bf8d43955cb04563f71ba8231a3bbafc6dd7869503d05c", size = 1204644, upload-time = "2024-07-21T05:32:12.972Z" },
]
[[package]]
name = "misc-bpfbox"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "gmpy2" },
]
[package.metadata]
requires-dist = [{ name = "gmpy2", specifier = ">=2.2.1" }]

View File

@@ -0,0 +1,23 @@
FROM nixos/nix AS build
RUN mkdir -p /build
WORKDIR /build
COPY flag.txt flake.nix flake.lock /build/
COPY ./init /build/init/
RUN nix --extra-experimental-features nix-command --extra-experimental-features flakes build
FROM gcr.io/kctf-docker/challenge@sha256:9f15314c26bd681a043557c9f136e7823414e9e662c08dde54d14a6bfd0b619f
RUN apt-get update && apt-get install -y qemu-system-x86
WORKDIR /
COPY --from=build /build/result/bzImage /build/result/initrd.gz /
COPY run_qemu /
CMD kctf_setup && \
chmod 0777 /dev/kvm && \
chmod 0777 /dev/vhost-vsock && \
kctf_drop_privs \
socat \
TCP-LISTEN:1337,reuseaddr,fork \
EXEC:"kctf_pow /run_qemu"

View File

@@ -0,0 +1 @@
CTF{this isn't a flag}

26
2025/google/misc-bpfbox/flake.lock generated Normal file
View File

@@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1749857119,
"narHash": "sha256-tG5xUn3hFaPpAHYIvr2F88b+ovcIO5k1HqajFy7ZFPM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5f4f306bea96741f1588ea4f450b2a2e29f42b98",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -0,0 +1,51 @@
{
description = "bpfbox";
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05";
};
outputs = { self, nixpkgs }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };
lib = pkgs.lib;
init = pkgs.buildGoModule {
name = "init";
vendorHash = null;
src = ./init;
};
initrdEnv = pkgs.buildEnv {
name = "initrd-env";
paths = with pkgs; [
pkgsStatic.busybox
bpftrace
init
];
pathsToLink = [
"/bin"
];
};
kernel = pkgs.linuxPackages.kernel;
initrd = pkgs.makeInitrdNG {
name = "initramfs";
contents = [
{ source = "${initrdEnv}/bin"; target = "/bin"; }
{ source = ./flag.txt; target = "/flag.txt"; }
{ source = "${initrdEnv}/bin/init"; target = "/init"; }
];
};
in
{
packages.x86_64-linux.default = pkgs.stdenvNoCC.mkDerivation {
name = "bpfbox";
phases = [ "installPhase" ];
installPhase = ''
mkdir -p $out
cp ${initrd}/initrd.gz $out/
cp ${kernel}/bzImage $out/
'';
};
};
}

View File

@@ -0,0 +1,3 @@
module init
go 1.24.2

View File

@@ -0,0 +1,177 @@
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")
}

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec qemu-system-x86_64 -serial mon:stdio -nographic -cpu host -m 1024 -accel kvm -kernel /bzImage -initrd /initrd.gz -append "console=ttyS0 quiet"