dualflow
dualflow
Platform: Umdctf | Category: ML | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-25 | Status: Solved Techniques: coordinate_perturbation, likelihood_margin_optimization, white_box_adversarial_example
Summary
Task: submit a float32 window close to a provided reference so it passes three RealNVP-based acceptance checks. Solution: use a white-box adversarial perturbation, changing one coordinate of the reference by +0.03 to push the flow_1 likelihood and margin far above the required thresholds while staying inside the L-infinity budget.
Recon
Port scan
bash read-only • copy allowednmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
umdctf| ID:20260425_umdctf_dualflow - Tags: float32, realnvp, normalizing_flow, adversarial_example, white_box
- Indicators: two flow log-probabilities are compared as a classifier, submission is constrained only by a small L-infinity budget, acceptance depends on likelihood margin and Jacobian band checks, reference sample is provided locally as a numpy array
- Source:
20260425_umdctf_dualflow.md
Foothold
Vulnerability / Misconfiguration
- coordinate_perturbation
- likelihood_margin_optimization
- white_box_adversarial_example
bash read-only • copy allowed<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
bash read-only • copy allowedsudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
bash read-only • copy allowed<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- coordinate_perturbation
- likelihood_margin_optimization
- white_box_adversarial_example
- Tags: float32, realnvp, normalizing_flow, adversarial_example, white_box
Original Writeup
<details><summary>Click to expand original content</summary>Description
submit base64(numpy float32 array, shape (5, 64)):
The service loads a reference window and checks whether our submitted float32 array stays within EPS = 0.08 in L-infinity distance. It then evaluates the sample under two RealNVP flows and only prints the flag if the sample looks realistic to flow_1 and strongly prefers class 1 over class 0.
Analysis
challenge.py exposes the full decision rule:
- shape must be
(5, 64) - values are compared to
reference_window.npy linf <= 0.08margin = log q1 - log q0 >= 30log q1 >= 932.8031log_det1must lie in[1367.1451, 1595.0465]
The important point is that this is a white-box setting. We have the reference input, the model code, and both flow checkpoints. The reference sample itself is not close to the target class boundary in the right direction: its margin is about -1.90, so we must meaningfully increase log q1 - log q0 while keeping the perturbation tiny.
In practice, no numerical trick was needed. A simple coordinate search was enough: flatten the reference array and add +0.03 at index 205, which is element [3, 13] in the original (5, 64) layout.
That single change already satisfies every condition:
linf = 0.03margin = 105.70log_q1 = 986.23log_det1 = 1526.48
So the challenge is essentially a straightforward adversarial-example task against a flow-based discriminator.
Solution
Load the reference window, perturb one coordinate, save the result as a NumPy array, then base64-encode the .npy payload for submission.
python read-only • copy allowed#!/usr/bin/env python3 import base64 import io import numpy as np ref = np.load("reference_window.npy").astype(np.float32) sub = ref.copy().reshape(-1) sub[205] += np.float32(0.03) # same as ref[3, 13] += 0.03 sub = sub.reshape(5, 64).astype(np.float32) buf = io.BytesIO() np.save(buf, sub) print(base64.b64encode(buf.getvalue()).decode())
Submitting that array to the remote service returns the success line and the flag.
</details>signed by XESXOR