cscg ist super
This commit is contained in:
1
2026/cscg/misc/table-madness/.python-version
Normal file
1
2026/cscg/misc/table-madness/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.13
|
||||
0
2026/cscg/misc/table-madness/README.md
Normal file
0
2026/cscg/misc/table-madness/README.md
Normal file
19
2026/cscg/misc/table-madness/challenge.py
Normal file
19
2026/cscg/misc/table-madness/challenge.py
Normal 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")
|
||||
|
||||
|
||||
13
2026/cscg/misc/table-madness/docker-compose.yml
Normal file
13
2026/cscg/misc/table-madness/docker-compose.yml
Normal 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
|
||||
97
2026/cscg/misc/table-madness/parse.py
Normal file
97
2026/cscg/misc/table-madness/parse.py
Normal 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")
|
||||
9
2026/cscg/misc/table-madness/pyproject.toml
Normal file
9
2026/cscg/misc/table-madness/pyproject.toml
Normal 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",
|
||||
]
|
||||
43
2026/cscg/misc/table-madness/service-entry/Dockerfile
Normal file
43
2026/cscg/misc/table-madness/service-entry/Dockerfile
Normal 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"]
|
||||
13
2026/cscg/misc/table-madness/service-entry/entry.service
Normal file
13
2026/cscg/misc/table-madness/service-entry/entry.service
Normal 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
|
||||
15
2026/cscg/misc/table-madness/service-entry/entrypoint.sh
Normal file
15
2026/cscg/misc/table-madness/service-entry/entrypoint.sh
Normal 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 \
|
||||
5
2026/cscg/misc/table-madness/service-entry/vmentry.sh
Normal file
5
2026/cscg/misc/table-madness/service-entry/vmentry.sh
Normal 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
|
||||
46
2026/cscg/misc/table-madness/service-target/Dockerfile
Normal file
46
2026/cscg/misc/table-madness/service-target/Dockerfile
Normal 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"]
|
||||
@@ -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
|
||||
14
2026/cscg/misc/table-madness/service-target/entrypoint.sh
Normal file
14
2026/cscg/misc/table-madness/service-target/entrypoint.sh
Normal 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 \
|
||||
1
2026/cscg/misc/table-madness/service-target/flag.txt
Normal file
1
2026/cscg/misc/table-madness/service-target/flag.txt
Normal file
@@ -0,0 +1 @@
|
||||
CSCG{dummy}
|
||||
23
2026/cscg/misc/table-madness/service-target/flagger.py
Normal file
23
2026/cscg/misc/table-madness/service-target/flagger.py
Normal 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()
|
||||
|
||||
25170
2026/cscg/misc/table-madness/service-target/ipset.save
Normal file
25170
2026/cscg/misc/table-madness/service-target/ipset.save
Normal file
File diff suppressed because it is too large
Load Diff
58364
2026/cscg/misc/table-madness/service-target/iptables.save
Normal file
58364
2026/cscg/misc/table-madness/service-target/iptables.save
Normal file
File diff suppressed because it is too large
Load Diff
34
2026/cscg/misc/table-madness/solve.py
Normal file
34
2026/cscg/misc/table-madness/solve.py
Normal 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
29
2026/cscg/misc/table-madness/uv.lock
generated
Normal 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" },
|
||||
]
|
||||
Reference in New Issue
Block a user