Lazy Galleries
Harvest every image from a lazy-loaded, paginated gallery with an unknown page count — then find the flag hidden where the browser can't show it.
- ▹Lazy-load harvesting: raw-HTML grep vs DOM/HAR routes
- ▹srcset over src, and stripping resizer query params to reach originals
- ▹Loop-until-dry pagination — converge on novelty, don't trust 200/404
- ▹Recognising and surviving out-of-range page clamping
- ▹Recovering data from EXIF that never renders in a browser
The goal
There's a gallery at /labs/scrape-01/target/page/1/. Somewhere in it, one
image carries a flag — but not where you can see it. Your job: pull every image
off the gallery with the command line, find the one hiding the flag, and read it.
The flag looks like BTS{...}. Paste it in the box at the bottom to confirm.
Everything here is hosted on this site on purpose, so practising on it is legal.
That's the whole point of /labs: a real target you're allowed to hit.
What you're up against
This gallery is built to punish the naive one-liner. Five things stand between you and the flag, in roughly the order you'll trip over them:
- The images are lazy-loaded. Grep the raw HTML for
src="..."and you get a 1×1 placeholder, not a photo. The real URLs are elsewhere in the markup. srcsethides a tinysrc. The obvious URL is a 400px thumbnail. The thumbnail is not the original — and the flag does not survive in it.- The URLs look resized. They carry a
?w=400query. A static host ignores the query and serves the same file, but the original lives at a different, un-suffixed URL. - You don't know how many pages there are. No "Page 3 of 10", no
rel="last", no total anywhere in the markup. You have to discover the end. - Out-of-range pages lie. Ask for a page past the end and the server happily
returns
200with page 1's content again. A loop that stops on404will run long past where it should — or forever.
The flag is in the EXIF metadata of one original, on a late page. It never renders in a browser. You will only find it if you pulled every page, kept the originals (not the thumbnails), and looked at the bytes the image carries with it.
The shape of the answer
You don't need a headless browser here — this gallery is server-rendered, so the
image URLs are in the HTML curl already gives you. The skills this teaches
generalise well past scraping:
- Parse the right attribute.
data-srcandsrcset, notsrc. - Normalise before you dedupe. Strip the
?w=and prefer the widestsrcsetcandidate, or the same photo at two sizes looks like two photos. - Converge on novelty, not on a status code. Stop paginating when a page
shows you nothing you haven't already seen — because the server's
200/404can't be trusted to tell you when you're done. - Always cap an unbounded remote loop. A runaway guard is not optional.
Work it yourself first. Hints below reveal one step at a time; the full solution is at the very bottom, collapsed.
Hints
Stuck? Open them one at a time — each reveals a single step.
▸Hint 1 — the images aren't where you think
Grep the page for `src` and you get a placeholder. Look at the other
attributes on the `<img>` tags instead:
curl -s https://bobtheskull.org/labs/scrape-01/target/page/1/ \
| grep -oE 'data-src="[^"]+"|srcset="[^"]+"'▸Hint 2 — thumbnail vs original
The `data-src` points at a `_400.jpg?w=400` thumbnail. The `srcset` lists a
second, wider candidate ending `_1600.jpg` with no query string. That's the
original — and it's the only one that keeps the flag. Grab the `_1600` URLs:
... | grep -oE 'tile-[0-9]+_1600\.jpg'▸Hint 3 — how many pages?
Nothing on the page tells you. Don't guess a number — loop, and watch what comes back. Increment the page in the path (`/target/page/2/`, `/3/`, …). Static hosting means `?page=2` won't work; the number is in the URL path.
▸Hint 4 — don't trust the status code
Ask for a page past the end and you'll still get a `200` — with page 1's images again. Stop when a page shows you **no new URLs**, not when you hit a 404. And cap the loop so a lie can't run it forever.
▸Hint 5 — where the flag actually is
You've got every original. The flag isn't a pixel — it's metadata:
exiftool out/*.jpg | grep -B1 'BTS{'Found it? Paste the flag to verify. Checked in your browser — nothing is sent anywhere.
