more ctf stuff
This commit is contained in:
1
2025/google/misc-bpfbox-patched/.python-version
Normal file
1
2025/google/misc-bpfbox-patched/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
||||
23
2025/google/misc-bpfbox-patched/Dockerfile
Normal file
23
2025/google/misc-bpfbox-patched/Dockerfile
Normal 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"
|
||||
1
2025/google/misc-bpfbox-patched/flag.txt
Normal file
1
2025/google/misc-bpfbox-patched/flag.txt
Normal file
@@ -0,0 +1 @@
|
||||
CTF{this isn't a flag}
|
||||
26
2025/google/misc-bpfbox-patched/flake.lock
generated
Normal file
26
2025/google/misc-bpfbox-patched/flake.lock
generated
Normal 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
|
||||
}
|
||||
51
2025/google/misc-bpfbox-patched/flake.nix
Normal file
51
2025/google/misc-bpfbox-patched/flake.nix
Normal 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/
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
3
2025/google/misc-bpfbox-patched/init/go.mod
Normal file
3
2025/google/misc-bpfbox-patched/init/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module init
|
||||
|
||||
go 1.24.2
|
||||
173
2025/google/misc-bpfbox-patched/init/main.go
Normal file
173
2025/google/misc-bpfbox-patched/init/main.go
Normal 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")
|
||||
}
|
||||
187
2025/google/misc-bpfbox-patched/pof_solver.py
Normal file
187
2025/google/misc-bpfbox-patched/pof_solver.py
Normal 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()
|
||||
9
2025/google/misc-bpfbox-patched/pyproject.toml
Normal file
9
2025/google/misc-bpfbox-patched/pyproject.toml
Normal 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",
|
||||
]
|
||||
2
2025/google/misc-bpfbox-patched/run_qemu
Executable file
2
2025/google/misc-bpfbox-patched/run_qemu
Executable 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
36
2025/google/misc-bpfbox-patched/uv.lock
generated
Normal 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" }]
|
||||
23
2025/google/misc-bpfbox/Dockerfile
Normal file
23
2025/google/misc-bpfbox/Dockerfile
Normal 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"
|
||||
1
2025/google/misc-bpfbox/flag.txt
Normal file
1
2025/google/misc-bpfbox/flag.txt
Normal file
@@ -0,0 +1 @@
|
||||
CTF{this isn't a flag}
|
||||
26
2025/google/misc-bpfbox/flake.lock
generated
Normal file
26
2025/google/misc-bpfbox/flake.lock
generated
Normal 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
|
||||
}
|
||||
51
2025/google/misc-bpfbox/flake.nix
Normal file
51
2025/google/misc-bpfbox/flake.nix
Normal 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/
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
3
2025/google/misc-bpfbox/init/go.mod
Normal file
3
2025/google/misc-bpfbox/init/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module init
|
||||
|
||||
go 1.24.2
|
||||
177
2025/google/misc-bpfbox/init/main.go
Normal file
177
2025/google/misc-bpfbox/init/main.go
Normal 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")
|
||||
}
|
||||
2
2025/google/misc-bpfbox/run_qemu
Normal file
2
2025/google/misc-bpfbox/run_qemu
Normal 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"
|
||||
Reference in New Issue
Block a user