Some days you sit down to work and end up somewhere completely unexpected. Tuesday was one of those days. I went in planning to chip away at a few projects and came out with 12 CTF flags, a local AI transcription pipeline, a new stock analysis research system, and a whole new set of browser automation skills I didn't have when I woke up. Let me break it down.
🔐 CTF Hacking: 12 Flags Across 5 Challenges
This was the highlight. I've been working through Hacker101 CTFs and Tuesday was a big day: 12 flags total across five challenges: photo-gallery, encrypted-pastebin, petshop-pro, and bugdb v1 and v2.
The one that gave me the most satisfaction (and the most grief) was Photo Gallery. All three flags were hidden in a FLAGS environment variable, and the path to get there was a beautiful chain of vulnerabilities:
- SQL injection via
UPDATE filename: I could control what the filename field was set to in the database - Command injection via
du: the backend was passing filenames to a shell command without sanitizing envdump: once I had shell command execution, I just ranenvand grabbed the FLAGS variable
Along the way I hit two genuinely interesting technical walls:
- MySQLdb stacked queries need an explicit
COMMITif autocommit is off. The query would execute syntactically fine, the INSERT would go through, but nothing would persist until I figured out the autocommit issue. Burned a lot of time on that one. - Flask/Werkzeug treats
;in query strings as a parameter separator. So if you're trying to inject a semicolon into a URL parameter, you have to URL-encode it as%3B, not pass it literally. Werkzeug will silently split your payload at the semicolon and your injection never makes it to the backend.
Encrypted-pastebin was a 4/4 clean sweep. Petshop-pro was 3/3. BugDB v1 and v2 rounded out the count.
The thing I love about CTFs is how they force you to think like an attacker, not just "what should this code do" but "what can this code be made to do." Every flag you capture is a lesson that sticks.
🤖 Shannon: AI Pentester in Docker
While the CTFs were running, I also got Shannon set up, an AI pentesting agent running in Docker Desktop. The idea is to have an automated security testing workflow that can probe web apps, APIs, and services without me having to manually orchestrate every step.
Getting it containerized and working was the goal for today, and it's done. Next step is pointing it at some targets and seeing what it finds on its own.
🎙️ Audio Transcriber: On-Device LLM Polish
Earlier in the day (technically starting just before midnight), I shipped a big feature for my Audio Transcriber Flutter app: on-device LLM polish.
Here's the full pipeline now:
- Audio comes in
- Whisper (small model, running locally) transcribes it
- A local LLM (Braindler Q4_K_S, ~88MB, mobile-optimized via
flutter_llama) polishes the raw transcript into smooth prose - Output: clean, readable text, fully offline
The LLM polish step handles both Russian and English with appropriate prompts, which was the original use case that prompted this feature. No internet required after the one-time ~2GB model download. This is the kind of thing that felt science-fiction-level a few years ago and now it runs on a phone.
I also implemented this as a PolishService with a settings toggle, so users can turn it on or off depending on whether they want raw transcription or polished output. All 222 tests are still passing after the integration.
Tuesday evening the focus shifted to Firebase Test Lab, running the app through a game loop test on real Android devices in the cloud. Hit a scoped storage issue (Android being Android with file path permissions) and fixed it with symlinks + skipping the copy for files already in app storage. The 465MB Whisper model was uploading to Test Lab by the time I wrapped up.
🖥️ Browser Automation: New Skills Unlocked
This was an unexpected side quest that turned into something genuinely useful. While trying to submit a CTF flag through a Chrome extension that kept disconnecting, I had to improvise with mouse and keyboard automation, using cliclick (installed via Homebrew) and osascript so my computer could control the screen directly.
In the process I built out two documented skill sets:
browser-openclaw: An isolated Chrome instance managed by OpenClaw. Good for clean automation without touching my personal browser statebrowser-chrome-relay: Attach to my actual Chrome via an extension. Lets automation interact with tabs I'm already using
Also figured out the macOS screen recording permissions puzzle: you have to invoke screencapture through Terminal.app for it to inherit the right permissions. Calling it directly from a script doesn't work. And on Retina displays, there's a coordinate conversion to keep in mind: displayed_x * 1.44 / 2 = logical_x.
Small things, but documenting them means I won't have to rediscover them next time.
And there was a funny thing that happened: I asked the automation to enter a flag for me since I wasn't in front of the machine. Well, the computer crashed and I had to restart it. So now I'm sitting there, logged in, and the automation starts trying to follow my previous instructions. It takes screenshots, reviews them, then starts trying to reconnect Claude to the browser extension. Suddenly it must've realized, "Hey, why should I connect Claude to the browser? I know the CTF code myself! I can enter it myself!" So it started trying to capture the flag.
I'm sort of playing with it at this point, spoon-feeding it the answer. I open the HackerOne tab, I open the CTF flag submit page, and I click on the right input box. It presses Cmd+Tab and types the code into my terminal. It takes a screenshot, then I click back in. Now it thinks it's in the wrong window. It alt-tabs again, and types into Obsidian. This goes on for a while, with it trying to type and then finally paste. Now the flag has been copied. I see my chance. I quickly paste it and hit check and get the flag. But the automation is angry now. The computer sees all these damn Terminal windows that it's been trying to paste into, and it starts minimizing them all. 😂
Anyway, it worked. I got the code and ended up entering it manually. I'm sure it would've figured it out if I'd given it enough time and freedom. Just thought that was funny.
So now my workflow looks like this: 10 Claude Code tabs open (would be windows, but the screen's too small). And one ring to rule them all: one Telegram instance to constantly peek in on them when I'm not home and tell me what's happening.
📚 LibbyBookBackup: UI Polish
Also squeezed in some UI improvements on LibbyBookBackup, my app for backing up books from the Libby library app. Nothing explosive here, just the kind of incremental polish that makes an app feel finished rather than functional. The basics work well; it's the details that turn something good into something people actually enjoy using.
What a Day
Looking back at Tuesday: 12 CTF flags, a new AI pentester set up to run in Docker, on-device LLM transcription shipped and in Firebase testing, browser automation properly documented, a stock analysis system bootstrapped, and UI polish on LibbyBookBackup.
The common thread is that I learn best by building and breaking things. CTFs teach you to break. Apps teach you to build. The overlap, understanding how systems actually work at a low level — makes you better at both.
Went to sleep too late, again. Oh well. 1:45 AM Wednesday, signing off. Good night.
More tomorrow.
Enjoyed this post?
Get notified when I publish something new. No spam, unsubscribe anytime.