Rebuilding the Agriculture Office's Satellite Check | Subsidy Claim | GaianSpace CTF 2026
đ ä¸ćçďźçšéŤä¸ć
Fourth post in the series, and the finale of the âfarm arcâ (Babbâs AgriLink world). The first three posts got the LoRa demodulator in shape; this one adds a layer of GIS and classifier reconstruction on top of the demodulation.
In a way, itâs everything from the earlier posts applied together.
Rest of the series: Channel HopăTrue Color Is a LieăIsodata SevenăTwo VoicesăNo Name on the DoorăHopscotch
What the challenge is about

At the county agriculture office, the area-payment claim for Babbâs north parcel was rejected: âparcel already declared as pasture, satellite check passed.â Someone filed a claim using Babbâs corn field, and the officeâs satellite check actually sided with them.
A clerk lets something slip: this year the officeâs classification stats left out one class, because ânobody around here declares that crop, and field surveys are expensive.â Separately, at 12:05 the shared SDR recorded a stranger transmitting this batch of claims on âyet another channel.â
Goal: find all the false claims in the strangerâs batch, and join their claim codes with hyphens in the order they were sent.
The files given are the SDR recording, the officeâs check imagery (5-channel GeoTIFF), the class statistics, the training-field and parcel registry, and the rejection letter.
First, demodulate the claim batch from the SDR
The radio this time is 434.65 MHz, SF9, BW125k, preamble 8, explicit header, CR4/5.
Reusing the demodulator, 64 bursts decode to: one batch header + 30 claims + one short packet, and then the whole batch is sent again (so every record has two copies to cross-check).

Each claim has the format <seq>;<parcel>;<declared crop>;<area>;<claim code>, e.g. 01;MZH-8140;legelo;44.67;CUVE (legelo = pasture).
The key: the class that got deleted
The officeâs .sta file uses Gaussian maximum-likelihood classification with equal priors, but it has only four classes: pasture, stubble, cropland, settlement. You can see that corn (kukorica) was removed, and only its training field is still there.
A supervised classifier can only output classes it was trained on, so with no corn statistics, a corn field gets forced into the most similar class (pasture), and the check âpasses.â Thatâs the core vulnerability of the whole challenge.
I first rasterized the training-field polygons onto the image grid and recomputed each classâs mean and covariance. They matched the .sta file byte for byte, confirming my coordinate and sampling conventions were correct.
Then I used training field T-01 to restore cornâs statistics, ran a 5-class classification, and compared record by record:

Under the officeâs 4-class classification, all 30 records âpass.â
After restoring corn, only 3 records donât match, and all three are âdeclared pasture, actually cornâ:
| # | Parcel | Declared | After restoring corn | Code |
|---|---|---|---|---|
| 1 | MZH-8140 (Babbâs parcel) | pasture | corn 93% | CUVE |
| 10 | MZH-9183 | pasture | corn 84% | UKEJ |
| 28 | MZH-1402 | pasture | corn 80% | TRP6 |
Flag
1 | GAIANSPACE{CUVE-UKEJ-TRP6} |
Takeaways
- âOne class is missingâ is the whole key. A supervised classifier can only output classes it was trained on; with no corn statistics it crams corn into the most similar class, pasture, so âcheck passedâ doesnât mean the claim is honest. Itâs a very realistic remote-sensing fraud scenario.
- Validate your own pipeline against known data first. Only after the training-field pixel counts and the
.stameans and covariances matched byte for byte did I go restore the missing class, which is what makes the conclusion hold up. - Make use of the re-send when decoding SDR batches. The two transmissions are identical, so you can cross-confirm every character instead of guessing.
- By this point the farm arcâs LoRa demodulator is quite mature. Next the series jumps to a different storyline, a night shift, a hijacked satellite, where the same demodulator meets trickier situations.

