cscg ist super

This commit is contained in:
2026-04-10 03:31:12 +02:00
parent 7a9dfeda60
commit db0324c43d
99 changed files with 92358 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update --fix-missing && apt-get install -y openssh-server qemu-system git wget cpio curl sudo
RUN mkdir /var/run/sshd
#password for user login
RUN sed -i 's/#Port 22/Port 3000/' /etc/ssh/sshd_config \
&& sed -i 's/#PermitEmptyPasswords no/PermitEmptyPasswords yes/' /etc/ssh/sshd_config \
&& printf "\n\nAuthenticationMethods none\n" >> /etc/ssh/sshd_config
RUN groupadd --system ctf --gid 1000 \
&& useradd --uid 1000 --system --gid ctf --home-dir /home/ctf --create-home --comment "CTF image user" -s /bin/bash ctf \
&& chown -R ctf:ctf /home/ctf \
&& passwd -d ctf && mkdir -p /chall/initfs
WORKDIR /chall
COPY initfs/ ./initfs
RUN git clone --recurse-submodules -j8 https://github.com/MyEyes/basic_linux_env && cd basic_linux_env && ./build.sh && mkdir host
COPY adjust_initramfs.sh run.sh ./
COPY run_qemu.sh ./basic_linux_env
RUN ./adjust_initramfs.sh && chmod -R go-rwx basic_linux_env/* && chmod -R go+rwx basic_linux_env/host && rm -rf initfs
COPY sudoers /etc/sudoers
COPY bzImage ./basic_linux_env
EXPOSE 3000
# Start SSH server
CMD ["/usr/sbin/sshd", "-D"]

View File

@@ -0,0 +1,7 @@
## General notes
* The challenge really uses a stock `v5.15.201` kernel with the given `.config` and the applied `kernel.diff`
* The `chall_config.diff` is just there for your convenience, the `9p` stuff is so the `host` dir can be mounted in QEMU.
* You can download the bzImage or build it yourself, but you shouldn't really need it.
* The `basic_linux_env` is just a minimal shell environment to run a linux kernel, you can clone it yourself for testing.
* The flag is accessible in the qemu VM at /flag

View File

@@ -0,0 +1,16 @@
#!/bin/sh
cp -r initfs/* basic_linux_env/basic_initfs/root/
cd basic_linux_env/basic_initfs/root/bin
ln -s ./busybox ./cat
ln -s ./busybox ./uname
ln -s ./busybox ./modprobe
ln -s ./busybox ./chmod
ln -s ./busybox ./grep
ln -s ./busybox ./unshare
ln -s ./busybox ./setuidgid
cd -
cd basic_linux_env/basic_initfs && ./build.sh
cd -

Binary file not shown.

View File

@@ -0,0 +1,41 @@
192c192
< # CONFIG_USER_NS is not set
---
> CONFIG_USER_NS=y
782a783
> CONFIG_BLK_MQ_VIRTIO=y
1233c1234,1236
< # CONFIG_NET_9P is not set
---
> CONFIG_NET_9P=y
> CONFIG_NET_9P_VIRTIO=y
> # CONFIG_NET_9P_DEBUG is not set
1443a1447
> # CONFIG_VIRTIO_BLK is not set
1720a1725
> # CONFIG_VIRTIO_NET is not set
2397a2403
> # CONFIG_HW_RANDOM_VIRTIO is not set
3183a3190
> # CONFIG_SND_VIRTIO is not set
3669a3677,3678
> CONFIG_VIRTIO=y
> CONFIG_VIRTIO_PCI_LIB=y
3671,3672c3680,3685
< # CONFIG_VIRTIO_PCI is not set
< # CONFIG_VIRTIO_MMIO is not set
---
> CONFIG_VIRTIO_PCI=y
> CONFIG_VIRTIO_PCI_LEGACY=y
> # CONFIG_VIRTIO_BALLOON is not set
> # CONFIG_VIRTIO_INPUT is not set
> CONFIG_VIRTIO_MMIO=y
> # CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
3803a3817
> # CONFIG_VIRTIO_IOMMU is not set
4080a4095,4097
> CONFIG_9P_FS=y
> # CONFIG_9P_FS_POSIX_ACL is not set
> # CONFIG_9P_FS_SECURITY is not set
4388a4406
> # CONFIG_CRYPTO_DEV_VIRTIO is not set

View File

@@ -0,0 +1 @@
CSCG{dummy}

View File

@@ -0,0 +1,13 @@
#!/bin/sh
mount -t devtmpfs devtmpfs /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /tmp
mount -t 9p -o trans=virtio,version=9p2000.L host /mnt/host
chmod 600 flag
chmod +s /bin/busybox
setuidgid 1000 sh
echo "Init exiting, shutting down system"
poweroff -f

View File

@@ -0,0 +1,12 @@
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 740dac1012ae..b974b868278a 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -934,6 +934,7 @@ static struct file_system_type bm_fs_type = {
.name = "binfmt_misc",
.init_fs_context = bm_init_fs_context,
.kill_sb = kill_litter_super,
+ .fs_flags = FS_USERNS_MOUNT,
};
MODULE_ALIAS_FS("binfmt_misc");

3
2026/cscg/misc/one-bit/run.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
cd /chall/basic_linux_env && sudo /chall/basic_linux_env/run_qemu.sh

View File

@@ -0,0 +1,10 @@
#!/bin/sh
qemu-system-x86_64 \
-m 2048\
-kernel /chall/basic_linux_env/bzImage\
-append 'console=ttyS0 noapic'\
-monitor none\
-initrd /chall/basic_linux_env/basic_initfs/initramfs\
-virtfs local,path=/chall/basic_linux_env/host,mount_tag=host,security_model=passthrough,id=host\
-nographic
#-serial mon:stdio

View File

@@ -0,0 +1 @@
ctf ALL=(ALL) NOPASSWD: /chall/basic_linux_env/run_qemu.sh

View File

@@ -0,0 +1 @@
3.13

View File

View File

@@ -0,0 +1,19 @@
iptables = []
ipset = []
# [...]
with open("iptables.save", "w") as fd:
fd.write("""*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]""")
fd.write("\n".join(iptables))
fd.write("\nCOMMIT")
with open("ipset.save", "w") as fd:
for ipset in ipsets:
fd.write(f"create {ipset} hash:ip family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x{os.urandom(4).hex()}\n")

View File

@@ -0,0 +1,13 @@
services:
service-target:
privileged: true
build:
context: service-target
service-entry:
privileged: true
build:
context: service-entry
ports:
- "1024:1024"
depends_on:
- service-target

View File

@@ -0,0 +1,97 @@
import sys
import re
from z3 import *
IPTABLES_FILE = "./service-target/iptables.save"
def solve_circuit(iptables_path: str) -> list[int]:
rules_raw = []
with open(iptables_path) as f:
for line in f:
line = line.strip()
if line.startswith("-A INPUT"):
rules_raw.append(line)
input_knock: dict[str, int] = {} # ipset_bit -> source_port
eval_del: list[str] = [] # bits cleared unconditionally on sport=1338
eval_gate: list[str] = [] # conditional gate rules on sport=1338
for rule in rules_raw:
# Input knock: --sport 2XXXX without any match-set condition
m = re.search(r"--sport (2\d+) -j SET --add-set (i\d+) src", rule)
if m and "match-set" not in rule:
input_knock[m.group(2)] = int(m.group(1))
continue
# Eval del: unconditional clear on sport=1338
if "--sport 1338" in rule and "--del-set" in rule and "match-set" not in rule:
m = re.search(r"--del-set (i\d+)", rule)
if m:
eval_del.append(m.group(1))
continue
# Gate rule: conditional add on sport=1338
if "--sport 1338" in rule and "--add-set" in rule:
eval_gate.append(rule)
print(f"[*] Circuit: {len(input_knock)} inputs, {len(eval_gate)} gates, {len(eval_del)} resets")
# Build symbolic Z3 state
bits: dict[str, object] = {}
# Input bits = free boolean variables
for bit in input_knock:
bits[bit] = Bool(bit)
# Cleared bits start as False (reset each eval cycle)
for bit in eval_del:
bits[bit] = BoolVal(False)
def get_bit(b):
return bits.get(b, BoolVal(False))
# Process gate rules in file order (sequential iptables evaluation)
for rule in eval_gate:
# Double-condition: AND gate
m2 = re.search(
r"-m set (!?) ?--match-set (\S+) src -m set (!?) ?--match-set (\S+) src -j SET --add-set (\S+) src",
rule,
)
if m2:
neg1, a, neg2, b, out = m2.group(1) == "!", m2.group(2), m2.group(3) == "!", m2.group(4), m2.group(5)
expr_a = Not(get_bit(a)) if neg1 else get_bit(a)
expr_b = Not(get_bit(b)) if neg2 else get_bit(b)
bits[out] = Or(get_bit(out), And(expr_a, expr_b))
continue
# Single-condition: OR feed
m1 = re.search(
r"-m set (!?) ?--match-set (\S+) src -j SET --add-set (\S+) src",
rule,
)
if m1:
neg, a, out = m1.group(1) == "!", m1.group(2), m1.group(3)
expr_a = Not(get_bit(a)) if neg else get_bit(a)
bits[out] = Or(get_bit(out), expr_a)
# Solve: i00002 must be True (the ACCEPT gate)
print("[*] Running Z3 solver...")
s = Solver()
s.add(get_bit("i00002") == True)
if s.check() != sat:
print("[!] UNSAT — circuit has no solution")
sys.exit(1)
model = s.model()
knock_ports = []
for bit, port in input_knock.items():
if is_true(model.eval(Bool(bit))):
knock_ports.append(port)
knock_ports.sort()
print(f"[+] Solution found: {len(knock_ports)} ports to knock")
return knock_ports
if __name__ == "__main__":
knock_ports = solve_circuit(IPTABLES_FILE)
print(f"[*] Knock ports: {knock_ports}\n")

View File

@@ -0,0 +1,9 @@
[project]
name = "table-madness"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"z3-solver>=4.16.0.0",
]

View File

@@ -0,0 +1,43 @@
FROM debian:bookworm AS builder
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
libguestfs-tools cloud-image-utils curl linux-image-amd64 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
RUN chmod 0644 /boot/vmlinuz*
RUN curl -Lo user.qcow2 https://cloud.debian.org/images/cloud/bookworm/20260316-2418/debian-12-nocloud-amd64-20260316-2418.qcow2 && \
echo "661a11d853616ba72913ace404961a28350c58eb4041edd2c847aec932d5b99b user.qcow2" | sha256sum --check
COPY entry.service vmentry.sh .
RUN virt-customize -a user.qcow2 \
--update \
--install socat \
--install python3 \
--upload vmentry.sh:/vmentry.sh \
--upload entry.service:/etc/systemd/system/entry.service \
--run-command 'systemctl daemon-reload && systemctl enable entry.service' \
--run-command 'chmod +x /vmentry.sh' \
--run-command 'rm -rf /var/lib/apt/lists/*'
FROM debian:bookworm-slim
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
qemu-system-x86 qemu-utils qemu-kvm \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/user.qcow2 /vm/
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

View File

@@ -0,0 +1,13 @@
[Unit]
Description=Challenge
After=network.target
Wants=network.target
[Service]
Type=simple
ExecStart=/bin/bash /vmentry.sh
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,15 @@
#!/bin/bash
qemu-system-x86_64 \
-drive file=/vm/user.qcow2,format=qcow2 \
-m 512M \
-smp 2 \
-enable-kvm \
-nographic \
-display none \
-monitor none \
-snapshot \
-serial null \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::1024-:1024 \
-netdev socket,id=net1,connect=service-target:5555 \
-device e1000,netdev=net1,mac=52:54:00:AA:BB:CC \

View File

@@ -0,0 +1,5 @@
#!/bin/bash
IFACE=$(ip -o link | grep -i 52:54:00:AA:BB:CC | cut -d':' -f2 | xargs)
ip addr add 192.168.100.1/24 dev "$IFACE"
ip link set "$IFACE" up
socat TCP-LISTEN:1024,reuseaddr,fork EXEC:"/bin/bash -li",pty,stderr,setsid,sane

View File

@@ -0,0 +1,46 @@
FROM debian:bookworm AS builder
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
libguestfs-tools cloud-image-utils curl linux-image-amd64 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
RUN chmod 0644 /boot/vmlinuz*
RUN curl -Lo debian.qcow2 https://cloud.debian.org/images/cloud/bookworm/20260316-2418/debian-12-nocloud-amd64-20260316-2418.qcow2 && \
echo "661a11d853616ba72913ace404961a28350c58eb4041edd2c847aec932d5b99b debian.qcow2" | sha256sum --check
COPY challenge.service flagger.py ipset.save iptables.save .
RUN virt-customize -a debian.qcow2 \
--update \
--install iptables \
--install ipset \
--upload iptables.save:/iptables.save \
--upload ipset.save:/ipset.save \
--upload challenge.service:/etc/systemd/system/challenge.service \
--upload flagger.py:/flagger.py \
--run-command 'systemctl daemon-reload && systemctl enable challenge.service' \
--run-command 'rm -rf /var/lib/apt/lists/*'
FROM debian:bookworm-slim
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
qemu-system-x86 qemu-utils qemu-kvm \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /build/debian.qcow2 /vm/
COPY flag.txt /vm/
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Challenge
After=network.target
Wants=network.target
[Service]
Type=simple
ExecStartPre=/usr/sbin/iptables --flush
ExecStartPre=/usr/sbin/ipset destroy
ExecStartPre=/usr/sbin/ipset restore -f /ipset.save
ExecStartPre=/usr/sbin/iptables-restore /iptables.save
ExecStart=/usr/bin/python3 /flagger.py
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
#!/bin/bash
qemu-system-x86_64 \
-drive file=/vm/debian.qcow2,format=qcow2 \
-drive file=/vm/flag.txt,format=raw,if=virtio \
-m 1G \
-smp 4 \
-enable-kvm \
-nographic \
-display none \
-monitor none \
-snapshot \
-serial null \
-netdev socket,id=net0,listen=:5555 \
-device e1000,netdev=net0,mac=52:54:00:DD:EE:FF \

View File

@@ -0,0 +1 @@
CSCG{dummy}

View File

@@ -0,0 +1,23 @@
import socket
import os
PORT = 1337
os.system("""
IFACE=$(ip -o link | grep -i 52:54:00:DD:EE:FF | cut -d':' -f2 | xargs);
ip addr add 192.168.100.2/24 dev "$IFACE";
ip link set "$IFACE" up);
""")
os.system("dd if=/dev/vda of=/flag")
FLAG = open("/flag", "rb").read()
s = socket.socket()
s.bind(("0.0.0.0", PORT))
s.listen(1)
while True:
c, _ = s.accept()
c.send(FLAG)
c.close()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
import socket
import time
TARGET = "192.168.100.2"
PORTS = [20054,20097,20103,20123,20132,20137,20141,20225,20229,20251,20255,20303,20321,20326,20348,20363,20369,20392,20436,20465,20520,20538,20544,20600,20639,20695,20715,20744,20755,20801,20855,20917,20969,20981,21013,21100,21109,21121,21167,21171,21176,21262,21267,21331,21356,21373,21465,21515,21519,21553,21644,21688,21806,21863,22069,22253,22290,22365,22385,22422,22584,22596,22634,22664,22765,22808,22814,22872,22904,22951,22998,23060,23081,23175,23222,23374,23453,23553,23700,23911,24176,25083]
def knock(sport):
try:
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", sport))
s.settimeout(0.5)
s.connect((TARGET, 1337))
except:
pass
finally:
s.close()
print(f"[*] Knocking {len(PORTS)} ports...")
for p in PORTS:
print(f" sport={p}")
knock(p)
time.sleep(0.05)
print("[*] Sending eval packet (sport=1338)...")
knock(1338)
time.sleep(0.3)
print("[*] Getting flag...")
s = socket.socket()
s.settimeout(5)
s.connect((TARGET, 1337))
print(s.recv(4096).decode())
s.close()

29
2026/cscg/misc/table-madness/uv.lock generated Normal file
View File

@@ -0,0 +1,29 @@
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "table-madness"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "z3-solver" },
]
[package.metadata]
requires-dist = [{ name = "z3-solver", specifier = ">=4.16.0.0" }]
[[package]]
name = "z3-solver"
version = "4.16.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/93/3b/2b714c40ef2ecf6d8aa080056b9c24a77fe4ca2c83abd83e9c93d34212ac/z3_solver-4.16.0.0.tar.gz", hash = "sha256:263d9ad668966e832c2b246ba0389298a599637793da2dc01cc5e4ef4b0b6c78", size = 5098891, upload-time = "2026-02-19T04:14:08.818Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2d/5d/9b277a80333db6b85fedd0f5082e311efcbaec47f2c44c57d38953c2d4d9/z3_solver-4.16.0.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:cc52843cfdd3d3f2cd24bedc62e71c18af8c8b7b23fb05e639ab60b01b5f8f2f", size = 36963251, upload-time = "2026-02-19T04:13:44.303Z" },
{ url = "https://files.pythonhosted.org/packages/1c/c4/fc99aa544930fb7bfcd88947c2788f318acaf1b9704a7a914445e204436a/z3_solver-4.16.0.0-py3-none-macosx_15_0_x86_64.whl", hash = "sha256:e292df40951523e4ecfbc8dee549d93dee00a3fe4ee4833270d19876b713e210", size = 47523873, upload-time = "2026-02-19T04:13:48.154Z" },
{ url = "https://files.pythonhosted.org/packages/f6/e6/98741b086b6e01630a55db1fbda596949f738204aac14ef35e64a9526ccb/z3_solver-4.16.0.0-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:afae2551f795670f0522cfce82132d129c408a2694adff71eb01ba0f2ece44f9", size = 31741807, upload-time = "2026-02-19T04:13:52.283Z" },
{ url = "https://files.pythonhosted.org/packages/e7/2e/295d467c7c796c01337bff790dbedc28cf279f9d365ed64aa9f8ca6b2ba1/z3_solver-4.16.0.0-py3-none-manylinux_2_38_aarch64.whl", hash = "sha256:358648c3b5ef82b9ec9a25711cf4fc498c7881f03a9f4a2ea6ffa9304ca65d94", size = 27326531, upload-time = "2026-02-19T04:13:55.787Z" },
{ url = "https://files.pythonhosted.org/packages/34/df/29816ce4de24cca3acb007412f9c6fba603e55fcc27ce8c2aade0939057a/z3_solver-4.16.0.0-py3-none-win32.whl", hash = "sha256:cc64c4d41fbebe419fccddb044979c3d95b41214547db65eecdaa67fafef7fe0", size = 13341643, upload-time = "2026-02-19T04:13:58.88Z" },
{ url = "https://files.pythonhosted.org/packages/86/20/cef4f4d70845df24572d005d19995f92b7f527eb2ffb63a3f5f938a0de2e/z3_solver-4.16.0.0-py3-none-win_amd64.whl", hash = "sha256:eb5df383cb6a3d6b7767dbdca348ac71f6f41e82f76c9ac42002a1f55e35f462", size = 16419861, upload-time = "2026-02-19T04:14:03.232Z" },
{ url = "https://files.pythonhosted.org/packages/e1/18/7dc1051093abfd6db56ce9addb63c624bfa31946ccb9cfc9be5e75237a26/z3_solver-4.16.0.0-py3-none-win_arm64.whl", hash = "sha256:28729eae2c89112e37697acce4d4517f5e44c6c54d36fed9cf914b06f380cbd6", size = 15084866, upload-time = "2026-02-19T04:14:06.355Z" },
]