[{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/","section":"/dev/brain","summary":"","title":"/dev/brain","type":"page"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/categories/blog/","section":"Categories","summary":"","title":"Blog","type":"categories"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/claude-code/","section":"Tags","summary":"","title":"Claude Code","type":"tags"},{"content":"This is year two of building custom CTF challenges for NoCo Hackers, our local security meetup in Fort Collins. Last year I built Horsetooth Liquidators \u0026ndash; a vulnerable e-commerce web app with eight challenges. This year I wanted to try something a bit different.\nCold Signal is a text adventure CTF. Players SSH into an abandoned Arctic research station, explore rooms, interact with the station\u0026rsquo;s AI, and find flags by exploiting vulnerabilities embedded in the game. No browser, no web forms. Just a terminal and a mystery. You can try it in your browser here.\nThe idea was to make a CTF that was a little \u0026ldquo;outside the box\u0026rdquo;. Ultimately, it worked in some ways and didn\u0026rsquo;t in others, and I think the lessons are worth sharing.\nMaking CTFs Fun (and Where That Gets Complicated) # I grew up on video games, with things like GameSharks and Action Replays, allowing you to modify some memory values, and suddenly you could walk through walls. Those along with things like hardware modifications, automation bots, and \u0026ldquo;lag switching\u0026rdquo; were probably my first real exposure to the idea that systems have rules, and if you understand how the system works underneath, you can break those rules (or use the tools made by people that understood these things, at least). I think a lot of security people got their start through the same exposure. I\u0026rsquo;ve contemplated giving talks on the influence and overlap of security in gaming culture (and vice versa), and it definitely played a role in my decision to make this years CTF as much about a game as it is about the CTF challenges.\nI also had also experienced some more inspirational and innovative CTFs in 2025 that not only achieved being a traditional CTF under the hood, but were fronted with well written, rich atmosphere and narrative, making the CTF more interesting to play and explore. Johnny Reina\u0026rsquo;s CTF from LASCON 2025 was a good example of this with IRC bots that added \u0026ldquo;life\u0026rdquo; to the CTF with distinct personalities across channels, a fake corporate website to investigate, and a world that was interesting to explore.\nI\u0026rsquo;ve always been impressed by text adventures since Zork and Colossal Cave Adventure \u0026ndash; the idea that you can build real atmosphere with nothing but words. So I built Cold Signal: an abandoned research station, a crew that disappeared under mysterious circumstances, and an AI that\u0026rsquo;s helpful but clearly hiding something.\nThe challenges were more so simulations of real vulnerability classes such as session fixation, path traversal, configuration manipulation, that were embedded in the game world. I also included a few extra flags like hiding a spectrogram of the landing page\u0026rsquo;s background audio for a steganography challenge outside the game itself.\nChallenge Type The Archive Chamber Session fixation \u0026ndash; load a preserved session to inherit elevated access VESPERA\u0026rsquo;s Hidden Files Path traversal \u0026ndash; bypass access control via ../ before path normalization The Listening Array Config manipulation \u0026ndash; disable safety filters to reveal unfiltered signal data Broadcast 783 Steganography \u0026ndash; decode a spectrogram hidden in the landing page\u0026rsquo;s background audio The reception was lukewarm # Our annual CTFs are still small, maybe 10-20 participants and the people playing the most are experienced CTF players who are there to score points and win. For those players, the narrative was more decoration. The reality is CTF challenges should seek to teach people something. Cold Signal was built to have people experience something. Those aren\u0026rsquo;t the same thing, and I didn\u0026rsquo;t fully reckon with that distinction going in.\nWhere things broke down:\nNarrative as friction \u0026ndash; Competitive players didn\u0026rsquo;t want to read room descriptions or parse dialogue for hints. They just wanted to find a vulnerability, exploit it, and move on. Unfamiliar mechanics \u0026ndash; Save states and access levels are natural game concepts, but they were confusing in a CTF context. Players didn\u0026rsquo;t intuitively understand the systems they needed to exploit. Red herrings \u0026ndash; I added an interactive access card as atmospheric flavor. Players saw a tangible item that matched their problem (need elevated access) and spent 20+ minutes trying to use it instead of discovering the actual solution. Classic game design tool. Terrible CTF design tool. Wrong audience \u0026ndash; My hope was to appeal to newer players who\u0026rsquo;d find a game more approachable than a challenge board. But I didn\u0026rsquo;t market it that way, and the beginners I was designing for didn\u0026rsquo;t show up. Red herrings work in adventure games. Not so much in CTFs.\nThe best CTFs I\u0026rsquo;ve played strike a balance where there\u0026rsquo;s both atmosphere and narrative, but the underlying challenges still involve real technologies. Players learn real tools, read real documentation, interact with real systems. The story makes it interesting and the technology makes it educational. Too much narrative and you lose the core audience that plays CTFs to sharpen real skills. Too little and you\u0026rsquo;re back to security homework. My game simulated everything inside a single Python app, and that tipped the scale too far toward game and not far enough toward CTF.\nBuilding It with AI Agents # The other big experiment this year was the development process itself. Last year I used Cursor with Claude 3.5 Sonnet and ChatGPT. It worked, but I was constantly experiencing context rot, having to re-explain context every time I switched between writing narrative, coding the game engine, or setting up deployment. The AI would lose track of earlier decisions, and I\u0026rsquo;d waste time getting it back up to speed.\nThis year I used Claude Code with five specialized subagents, each defined as a markdown file in .claude/agents/ with its own system prompt, tool access, and domain boundaries:\n╔═══════════════════════════════════════════════════════╗ ║ CLAUDE CODE ║ ╠═══════════════════════════════════════════════════════╣ ║ ║ ║ \u0026gt; agents --status ║ ║ ║ ║ [●] ctf-narrative-agent ····· story \u0026amp; atmosphere ║ ║ [●] python-game-dev ········· engine \u0026amp; architecture ║ ║ [●] challenge-designer ······ vulns \u0026amp; difficulty ║ ║ [●] tech-stack-architect ···· docker \u0026amp; security ║ ║ [●] project-coordinator ····· tasks \u0026amp; coordination ║ ║ ║ ║ \u0026gt; _ ║ ╚═══════════════════════════════════════════════════════╝ Each agent file is just YAML frontmatter and a system prompt \u0026ndash; a name, description, tool allowlist, and the role definition. I\u0026rsquo;ve found using an LLM to help generate agent system prompts on high-level requirements or role-description can really speed things up as well. When Claude Code invokes a subagent, it spins up a fresh context scoped to that domain, so switching from narrative to infrastructure doesn\u0026rsquo;t carry over stale context from the previous task. I also used a project-level CLAUDE.md to give all agents shared context about the game\u0026rsquo;s structure, the flag format, and the file layout \u0026ndash; things every agent needed regardless of domain.\nI only have rough dev metrics from last year, but using Claude Code cut development time from roughly 65 hours to about 42. But the bigger win was simply the consistency of everything from dialogue to challenges. The code stayed clean because one agent enforced the architecture. Security boundaries between intentional game vulnerabilities and real flaws stayed clear because the tech-stack-architect\u0026rsquo;s prompt included an explicit security boundary checklist separating educational exploits from actual risk.\nThe agents weren\u0026rsquo;t fully autopilot, though. I found myself still haven\u0026rsquo;t to actively act as a human in the loop to provide clarification, additional context, or even flat out reject features and enhancements. Fortunately that is pretty well baked into the Claude Code UI and experience.\nIf I were starting over, I\u0026rsquo;d probably begin with 2-3 agents and add more only when the scope demanded it \u0026ndash; the setup overhead for five was real. But for any project that crosses multiple domains, I\u0026rsquo;d use this approach again without hesitation.\nWhere This Goes # Cold Signal was an experiment with mixed results. The AI development workflow was awesome, but the narrative-heavy CTF format needs still needs work.\nBut I still think there\u0026rsquo;s something here. CTFs have come a long way since DEF CON first ran one in 1996. We\u0026rsquo;ve gone from attack-defense wargames to jeopardy-style platforms to education-first formats designed specifically for learners. There\u0026rsquo;s definitely a convergence happening between security education and game design, and I think we\u0026rsquo;re still early in figuring out what it looks like.\nAI-assisted development makes the experimentation cheaper, too. Building a text adventure CTF, deploying it via Docker and SSH, and iterating on it over the course of a few weeks would have been totally impractical with my personal life a couple of years ago. The barrier to trying creative approaches to security education is lower than it\u0026rsquo;s ever been. More people should take advantage of that.\nI don\u0026rsquo;t want to stop trying to be innovative or creative in this space, though. I think there\u0026rsquo;s a huge opportunity for creatives to get involved in building CTF experiences. It would be great to see more traditional game developers and designers participating in this area, and I feel like it\u0026rsquo;s a space for non-traditional security people to help build cool things.\nIf you\u0026rsquo;re interested in this space, or want to chat more about my experiencing developing CTFs, please don\u0026rsquo;t hesistate to reach out, I\u0026rsquo;m always happy to chat!\nResources # Play Cold Signal in your browser Cold Signal Web GitHub Repository Cold Signal GitHub Repository Horsetooth Liquidators (2024 CTF) ","date":"1 February 2026","externalUrl":null,"permalink":"/blog/posts/cold-signal/","section":"Posts","summary":"","title":"Cold Signal: A Text Adventure CTF Postmortem","type":"posts"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/ctf/","section":"Tags","summary":"","title":"CTF","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/game-design/","section":"Tags","summary":"","title":"Game Design","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/categories/post/","section":"Categories","summary":"","title":"Post","type":"categories"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/security/","section":"Tags","summary":"","title":"Security","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/blog/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":" Intro # The Horsetooth Liquidators project is something I put together as part of my involvement with NoCo Hackers, a local security meetup here in Fort Collins. We ran our capture the flag (CTF) event for the second time, and with a significantly bigger crowd, I thought it was a good chance to try building a custom vulnerable web application.\nHaving used apps like Juice Shop, DVMA, Gruyere, CRAPI, etc., I have always wanted to dive deeper into what it takes to develop something similar. Having benefited from CTFs myself, I wanted to give back by creating a platform that provides challenges, but is also fun, immersive, and thematic. The idea was to see what I could create over a couple of months, using the skills I\u0026rsquo;ve picked up from developing web applications over the past few years. I was also curious about how Gen AI tools like ChatGPT, Claude, and Cursor could assist in building larger projects.\nWe designed the event around a fictional Fort Collins business that needed a security check-up. Participants took on the role of security engineers, assessing the business\u0026rsquo;s security and submitting flags in the CTF. It was all about crafting a unique experience for everyone involved.\nHere's the CTF narrative prompt we used for the event Sly, a tired and overworked fox, was the lead developer and sysadmin at Horsetooth Liquidators Inc., a rapidly growing liquidation company in Fort Collins. When he first joined, he was excited about the challenge of building the company’s e-commerce platform from scratch. But as the company expanded, so did Sly’s responsibilities. With no extra help in the IT department, he was stretched thin, juggling new feature requests, server maintenance, and keeping the company’s web infrastructure running smoothly.\nOver time, the system began to show cracks. Sly knew there were vulnerabilities he hadn’t had time to fix—SQL injections, outdated encryption, and exposed API endpoints. Each day he feared an attack was looming, but with every plea to the CEO, Mr. Badger, for more resources, he was met with the same response: “No budget for extra staff. Just keep things running, Sly.”\nSly did his best, patching what he could while trying to meet deadlines for new features. But the stress was catching up with him. His fur was frazzled, and he felt a sinking pit in his stomach whenever a new security alert popped up. Finally, after an especially close call when an attempted attack almost broke through, Sly had enough. He confronted Mr. Badger, insisting that the company needed help before a real disaster struck. After much grumbling and hand-wringing, Mr. Badger relented. “We’ll bring in a penetration tester,” Mr. Badger said. “But it better not cost too much.”\nEnter Thumper, a sharp and resourceful rabbit from Briarwood Security, a well-known firm that specialized in uncovering vulnerabilities. Thumper had a reputation for being thorough, and he wasted no time setting up his tools. His mission was clear: test the infrastructure at Horsetooth Liquidators from top to bottom, identify every flaw, and recommend fixes.\nSly, feeling a mix of relief and nervousness, sat nearby, ready to assist but also anxious about what Thumper might uncover. He had done his best, but he knew there were gaps.\nThumper, calm but focused, ran his initial scans and began probing the system. The fate of the company\u0026rsquo;s security now lay in the hands of the rabbit—and the vulnerabilities he was about to expose.\nNow it’s your turn! As Thumper, the skilled penetration tester, you’ve been brought in to identify and exploit vulnerabilities in the company’s infrastructure. Your task is to expose these weaknesses before an actual attacker does. Can you uncover the flaws that Sly hasn’t been able to fix?\nOh, and Horsetooth is in reference to the Horsetooth Rock (and Horsetooth Reservoir) here in Fort Collins, CO: Horsetooth Rock The Goal # At the end of the day, my goal was to create a fun and immersive web experience for CTF players, packed with a variety of challenges and flags. I wanted it to feel like a real business, Horsetooth Liquidators, and make it as thematic as possible. The first year we had a small turnout, so I focused on making the platform approachable and enjoyable for all skill levels. The challenges needed to be balanced - not too hard, but not too easy either. It would have been easy to recycle one of the many other vulnerable web app platforms, but I wanted to create something new and thematic specifically for our event.\nHere\u0026rsquo;s what I came up with: Technical Architecture # Tech Stack # I chose Vue 3 for the frontend, Express and Node for the backend, and PostgreSQL for the database. I primarily chose this stack since I had previous experience with it and it easily met the project requirements. Hosting # I went with Google Cloud for hosting as an opportunity to get more familiar with the GCP native services since most of my past experience has been with both Azure and AWS, but not so much GCP. For the project, I leveraged GCP Run Services, Google Cloud SQL, Google Cloud Storage, Google Cloud Functions, and Google Cloud Secret Manager. Docker was used for containerization to help make environment management more consistent across development and production. This is what the final GCP architecture looked like:\nGCP Architecture Security Considerations and Requirements: # I didn\u0026rsquo;t want to limit the tooling people could use for the CTF, so I had to design the infrastructure to be able to handle brute forcing tools like dirbuster and hydra as well as other tools like sqlmap and Burpsuite. Fortunately, I did not encounter any performance degradation or disruptions/outages, and the infrastructure scaled and held up well overall. Securing the flags from players was a challenge, which meant that I needed to prioritize adequate authentication/authorization between the frontend and backend. This included using validated JWT tokens as access tokens and secrets (stored in Google Cloud Secret Manager) to secure the backend endpoints. This held up well during the event and prevented users from abusing the backend to get flags. This is pretty much the auth pattern I used for flag retrieval:\n// Backend (Express) import jsonwebtoken // JWT secret generated during deployment jwtSecret = environment.JWT_SECRET // $(openssl rand -base64 32) // Middleware to verify JWT token function verifyToken(request, response, next) { token = request.headers.authorization.split(\u0026#39; \u0026#39;)[1] if (token is empty) { return response.error(401, \u0026#34;No token provided\u0026#34;) } // Verify token using JWT library result = jwt.verify(token, jwtSecret) if (result has error) { return response.error(401, \u0026#34;Invalid token\u0026#34;) } // ... attach decoded token to request next() } // Protected flag route function getFlagById(request, response) { challengeId = request.params.challengeId if (isChallengeCompleted(challengeId)) { flag = getFlag(challengeId) return response.success({ flag }) } else { return response.error(403, \u0026#34;Access denied\u0026#34;) } } // Register route with middleware router.get(\u0026#39;/api/flags/:challengeId\u0026#39;, verifyToken, getFlagById) // Frontend (Vue) // Function to retrieve the flag for a specific challenge function getFlag(challengeId) { token = localStorage.get(\u0026#39;token\u0026#39;) try { response = apiCall.get(\u0026#39;/api/flags/\u0026#39; + challengeId, { headers: { Authorization: \u0026#39;Bearer \u0026#39; + token } }) // ... handle success response } catch (error) { // ... handle error response } } Flag Development \u0026amp; Philosophy # Narrative \u0026amp; Challenge Progression # I really wanted to craft a compelling narrative to maximize player engagement and enjoyment. Players assumed the role of a security engineer hired to assist a business, progressing through challenges that mirrored real-world scenarios. Using CTFd\u0026rsquo;s challenge dependencies, my goal was to create a logical progression, but this limited the number of available challenges, affecting player motivation which I\u0026rsquo;ll touch more later.\nSomething for Everyone # I aimed to create CTF challenges that would appeal to a wide range of skill levels. We had participants for whom this was their first CTF event, as well as veteran CTF players. My goal was to cater to everyone if possible. Ultimately, I had to make a decision to preioritize one or the other, which I\u0026rsquo;ll touch on later in the Lessons Learned section.\nSomething New # I wanted to create challenges that were relevant to modern security practices, but also straightforward to solve. My hope was to include some modern CVE examples and dabble with AI/LLM vulnerabilities to provide something fresh to the player. Though since this CTF included brand new players with minimal web security experience, it was a good opportunity to sprinkle in some of the more basic OWASP Top 10 type challenges as well.\nChallenges # Ultimately, I ended up developing 8 web challenges:\nBackup Protocol (25pts) - CWE-312 Admin Access Requires (75pts) - CWE-347 Inventory Overflow (50pts) - CWE-20 Rookie Mistake (25pts) - CWE-312 Order Up! (50pts) - CWE-639 Confused Fox (25pts) - CWE-74 Late Night Easter Egg (25pts) - CWE-312 Support Ticket Snooping (100pts) - CWE-79 You can find the full descriptions of challenges on the GitHub project at horsetooth-liqudators/CHALLENGES.md.\nHow It Went\u0026hellip; # A few thoughts on what went well and what could have gone better\nBuilding the Thing # Tech Stack \u0026amp; Infra # Overall, everything was \u0026#x1f44d;, but a few things worth highlighting:\nI had to pivot from originally having everything deployable with Docker Compose to building and serving individual images to Google Cloud Run. I also had to move away from deploying Postgres via Docker to using Google Cloud SQL, which was fine but required a bit more setup in GCP. Quickly abandoned the VM route. For a moment, I was going to pivot to using IaaS/VMs so I could retain my Docker Compose setup, but I quickly realized how much of a pain reployment was going to be, and I\u0026rsquo;m glad I went serverless. Overall spend during 2-3 weeks of hosting was only ~$35 which isn\u0026rsquo;t that bad for the number of services and user traffic received. I set up a budget alert to notify me if the spend exceeded $50 and was happy to see it never went over. Deployment # I never fully automated the deployments to Google Cloud, and I would have liked to. Fortunately I didn\u0026rsquo;t have to deal with any major outages or bug fixes, so I wasn\u0026rsquo;t ever overwhelmed with the re-deployment process. The GCP CLI tool made redeploying a breeze, so once I was able to test in dev using Docker Compose, I just ran the gcloud builds like:\n# DEPLOY BACKEND gcloud builds submit --config cloudbuild.yaml \\ --substitutions=_INSTANCE_CONNECTION_NAME=\u0026#34;GCP_SQL_INSTANCE_NAME\u0026#34;,\\ _DB_USER=\u0026#34;postgres\u0026#34;,\\ _DB_PASSWORD=\u0026#39;SUPER_SECRET_PASSWORD\u0026#39;,\\ _DB_NAME=\u0026#34;horsetooth_liquidators\u0026#34;,\\ _FRONTEND_URL=\u0026#34;FRONTEND_URL\u0026#34;,\\ _JWT_SECRET=\u0026#34;$(openssl rand -base64 32)\u0026#34; # DEPLOY FRONTEND gcloud builds submit --config cloudbuild.frontend.yaml More Data # I would have liked to invest more time into telemetry and observability data. I was very curious about how people were interacting using and evaluating the application. Google Cloud actually offers decent analytics, telemetry, and observability services (ie. Google Cloud Monitoring, Google Cloud Logging, and Google Cloud Trace). I would have liked to use these services to achieve better visibility and understanding of user interactions and behaviors during the CTF event but unfortunately this fell to the wayside.\nOne of the benefits of using a CTF hosting platform like CTFd, is that you do get decent metrics on solves, challenges, point distribution, and more. I spent a bit of time analyzing this data after the event, and was able to put together a few visuals like the Points vs. Solves chart below.\nFlag Development # Validation and Security # I initially overlooked securing the flags from players. Midway through development, I moved all flag delivery to the backend, making them inaccessible from the frontend. Browser features like the developer console can easily detect hard-coded flags. The Chatbot had a bug with the drop-down prompts, which broke while I focused on the freeform text option. I had to push a hotfix to fix the issue, but fortunately this did not break any of the challenges themselves.\nChallenge Progression and Difficulty Curves # Looking at the correlation of # of points to % solved, I\u0026rsquo;m generally happy with the difficulty curve with the exception of Iventory Overflow, which no one solved unfortunately and was supposed to be more of a \u0026ldquo;Medium\u0026rdquo; difficulty challenge. Unfortunately, this is the result of having a very specific expected solution method in mind, and not being flexible enough to allow for other strategies. I assumed users would leverage something like Burpsuite to enumerate the inventory endpoint and brute force inventory values. This proved to be more elusive than I had hoped, and led to players ultimately missing out on the challenge.\nTesting (oops) # Honestly, I completely dropped the ball on testing and developing test cases for challenges, which fortunately only led to 1-2 minor bugs. One challenge in particular involved cracking a password hash, and I was confident the hash could easily be cracked using a tool like Hashcat or John, but I was wrong. I ended up having to providing players with a custom password file to use, which significantly simplified the challenge and made it less fun. This could have be avoided if I would have tested the hash myself, but unfortunately I was short on time. In the future, I will definitely write unit tests for all of the challenges which actually would also be a fun challenge in itself.\nNext Time - More Modular! # Assuming I\u0026rsquo;m developing something brand new and not just forking this project, I want to take a more modular approach to the project and challenges. I would like develop a \u0026ldquo;shell\u0026rdquo; of an ecommerce web application, for example, and make each challenge a potentially added feature of the platform, but not have them be dependencies or related to one another. Ideally, I could develop something that I could continue to recycle and add on to each year.\nAI Tooling - not perfect, but pretty damn good # This was by far the largest project I have developed using AI tooling. This was my first time using Cursor to help develop the majority of features, specifically using Claude 3.5 Sonnet. These AI models still have trouble keeping up with large code bases, and I ran into several instances where the model would lose context and start deviating from the original prompt, so in the later stages of development, it could be a headache getting the model back on track and re-feeding it sufficient context to continue progressing. For specific re-occuring patterns and features, it would be super helpful to be able to define a \u0026ldquo;recipe\u0026rdquo; for the model to follow. Things as simple as the preferred flag generation pattern:\npython -c \u0026#39;from secrets import token_hex; print(f\u0026#34;noco{{{token_hex(16)}}}\u0026#34;)\u0026#39; I noticed that because of the contextual limitations, it really requires the developer to be able to identify deviations and be familiar enough with web development (or general software development) to get the AI back on track. While the fixes typically involve clever prompting, I did have to revert to previous commits at times.\nWhen it came to generating flavor text for the web application and challenges, AI was extremely helpful. By feeding it the initial Horsetooth Liquidators story prompt, I had it build me the About Page, the Hiring Page, the Inbox messages, and the challenge descriptions with ease. This really helped speed up development and is what primarily allowed me to keep the challenges and narrative on-theme and engaging.\nPlayer Experience # We went from 3 users to over 15 users this year, which makes me tremendously proud and excited for future events. To me, the amount of participation and feedback reinforced the value of the event and the opportunity to learn and grow.\nI had several participants reach out to me with feedback and appreciation for the web app and challenges, which was super cool. I had a few instances of unexpected solutions and approahces which was also fun to see. One player accidentally completed the Support Ticket Snooping challenge while trying to solve the Inventory Overflow challenges which is awesome. Support Ticket Snooping was a XSS challenge that involved devising a payload that would expose an admin-panel iframe to the user. The CTF player was using Burpsuite which helped them identify the solution while also trying to brute force our inventory endpoint to find the flag.\nIt\u0026rsquo;s also worth noting that we leveraged our community Discord to host comms for the event. This live communication stream actually helped pull more players into the event, and gave players the opportunity to work with the development team, report issues, and more. We had a private channel for discussing challenge development as well, which helped with coordination.\nConclusion # While I feel like I could have prioritized more time on designing the challenges themselves, I learned a lot about the value of hands-on security education. Unfortunately, because I spent so much time developing the platform, I didn\u0026rsquo;t get to spend any time actually playing the CTF. But I\u0026rsquo;m a big fan of the \u0026ldquo;learn by doing\u0026rdquo; approach, and I think this CTF event was a great opportunity to do just that, so I\u0026rsquo;m glad I was able to learn something myself, andprovide a fun and engaging experience for everyone involved. My goal for future events is to develop a re-usable UI that can be easily modified with relevant flavor text and content, but then support modular challenge development so that I can continue to recycle the platform each year and focus more on the challenges themselves.\nYou can do it too! Seriously, AI assisted dev makes developing these types of projects accessible. It\u0026rsquo;s given me the confidence to build ambitious projects that I would have otherwise been prohibitive in terms of time and resources.\nI\u0026rsquo;m planning to do something similar for next year\u0026rsquo;s event, as well as finding other creative ways to build games around CTF-type challenges. In my opinion, there is a lot of untapped potential for creativity in this space and for building more immersive, thematic CTF platforms.\nResources # Checkout the official GitHub project at GitHub Project ","date":"5 January 2025","externalUrl":null,"permalink":"/blog/posts/horsetooth-ctf/","section":"Posts","summary":"","title":"Building a Vulnerable Web Application: Lessons from Horsetooth Liquidators","type":"posts"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/blog/tags/cloud-development/","section":"Tags","summary":"","title":"Cloud Development","type":"tags"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/blog/tags/node.js/","section":"Tags","summary":"","title":"Node.js","type":"tags"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/blog/tags/vue.js/","section":"Tags","summary":"","title":"Vue.js","type":"tags"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/blog/tags/web-security/","section":"Tags","summary":"","title":"Web Security","type":"tags"},{"content":"","date":"1 July 2024","externalUrl":null,"permalink":"/blog/tags/appsec/","section":"Tags","summary":"","title":"AppSec","type":"tags"},{"content":" Securing the Ship: Tackling Docker Image Flaws # I\u0026rsquo;ve spent some time delving into the complexities of finding and remediating vulnerabilities in Docker container images over the past several months. This article explores various detection mechanisms, including references to specific tooling, and provides remediation processes for addressing vulnerabilities in Docker container images. I\u0026rsquo;ll discuss the differences between software composition analysis (SCA) vulnerabilities and first-party vulnerabilities, and the challenges associated with remediation efforts. Additionally, I\u0026rsquo;ll share personal observations and insights from my experience in the field.\nDetection Mechanisms # Software Composition Analysis (SCA) Tools # SCA tools are designed to analyze the components within your Docker images and identify known vulnerabilities. Some popular SCA tools include:\nSnyk: Snyk integrates with Docker to scan images for known vulnerabilities and provides actionable remediation advice. I found Snyk particularly useful during a recent project where we needed to ensure the security of our containerized applications without disrupting the development workflow. Aqua Security: Aqua Security offers comprehensive scanning for vulnerabilities in Docker images and integrates seamlessly with CI/CD pipelines. In my experience, Aqua Security\u0026rsquo;s deep integration with various DevOps tools makes it a go-to solution for continuous monitoring. Clair: Clair is an open-source project that scans Docker images for vulnerabilities in the packages installed in them. Clair’s flexibility and ease of integration into custom CI/CD pipelines were invaluable in a recent security assessment I conducted. # Example: Scanning a Docker image with Snyk snyk container test my-docker-image First-Party Vulnerabilities # First-party vulnerabilities refer to issues introduced by the custom code and configurations within your Docker images. Detecting these requires a combination of static analysis, dynamic analysis, and manual review.\nStatic Code Analysis: Tools like SonarQube and Bandit can analyze your source code for security issues before it\u0026rsquo;s built into a Docker image. During one code audit, using SonarQube revealed a critical flaw in our custom middleware, which could have led to a significant data breach if not addressed. Dynamic Analysis: Tools like OWASP ZAP can be used to perform dynamic testing against running containers to identify vulnerabilities. I\u0026rsquo;ve seen dynamic analysis catch runtime issues that static analysis tools missed, highlighting the importance of a multi-faceted approach. # Example: Using Bandit for static code analysis bandit -r /path/to/your/code Personal Observations # During my time threat hunting in Docker environments, I noticed several patterns and common issues. One frequent observation was the over-reliance on outdated base images, which often contained numerous vulnerabilities. In a collaborative work environment, I found that regular security training for developers significantly reduced the number of first-party vulnerabilities introduced into our Docker images.\nA lot of the popular IaC scanning tools recommend adding additional lines to the Dockerfile to update individual dependencies. While this approach works, I feel that it is not as efficient as it could be. These tools often lack the context and consideration of all dependencies being used, and common transitive dependencies within multiple components. For example, if you have 5 transitive dependencies in your Dockerfile, you could update explicity update those depedencies, or simply update the transitive top-level dependency which could resolve all the vulnerable transitives.\nRemediation Processes # Updating Base Images # One of the most straightforward remediation steps is to update the base images used in your Dockerfiles. Ensure you\u0026rsquo;re using the latest, secure versions of base images.\n# Example: Updating a Dockerfile to use a secure base image FROM ubuntu:20.04 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y \\ nodejs \\ npm COPY . /app RUN cd /app \u0026amp;\u0026amp; npm install CMD [\u0026#34;node\u0026#34;, \u0026#34;/app/index.js\u0026#34;] Patching Dependencies # Ensuring that all dependencies are up to date is crucial. Tools like Dependabot can automate dependency updates, making it easier to keep your Docker images secure.\n# Example: Using Dependabot to automate dependency updates version: 2 updates: - package-ecosystem: \u0026#34;npm\u0026#34; directory: \u0026#34;/\u0026#34; schedule: interval: \u0026#34;daily\u0026#34; Implementing Security Best Practices # Adopt best practices for Docker image security, such as minimizing the attack surface by using minimal base images like Alpine, and ensuring proper user permissions within containers. This was a game-changer in a project where we had to harden our containers for a financial services client.\n# Example: Using a minimal base image FROM alpine:3.12 RUN apk add --no-cache nodejs npm COPY . /app RUN cd /app \u0026amp;\u0026amp; npm install USER node CMD [\u0026#34;node\u0026#34;, \u0026#34;/app/index.js\u0026#34;] Challenges in Docker Vulnerability Remediation # Remediating vulnerabilities in Docker images is not without its challenges. Some key difficulties include:\nDependency Hell: Managing and updating dependencies can be complex, especially when dealing with large, multi-layered Docker images. I remember a particular instance where conflicting dependencies between two essential services caused significant delays in our deployment schedule. Continuous Monitoring: Docker images need continuous monitoring for new vulnerabilities, requiring integration with CI/CD pipelines and constant vigilance. At my workplace, setting up automated scans and integrating them with our CI/CD pipeline helped catch vulnerabilities early in the development cycle. Balancing Security and Functionality: Ensuring security without breaking functionality can be a delicate balance, often requiring in-depth testing and validation. This balance is critical, as I’ve seen how overly restrictive security measures can stifle productivity and lead to workarounds that introduce new risks. Key Takeaways # Regular Scanning: Regularly scan your Docker images using both SCA tools and first-party vulnerability detection methods. This proactive approach has saved me from countless potential incidents. Automate Updates: Automate dependency and base image updates to reduce the risk of vulnerabilities. Automation tools have significantly reduced the manual overhead and error rate in our processes. Follow Best Practices: Implement Docker security best practices to minimize the attack surface and enhance overall security. Best practices, when consistently followed, create a robust baseline security posture. Continuous Vigilance: Continuously monitor and update Docker images to stay ahead of emerging threats. Staying updated with the latest security trends and vulnerabilities is part of my daily routine. Recent research from the application security industry highlights the critical importance of proactive vulnerability management in Docker environments. Studies by organizations like Snyk and Aqua Security emphasize that regular scanning and prompt remediation are essential to maintaining secure containerized applications. The dynamic nature of threats makes continuous learning and adaptation crucial.\n","date":"1 July 2024","externalUrl":null,"permalink":"/blog/posts/2024-07-01-securing-the-ship/","section":"Posts","summary":"","title":"Securing the Ship: Tackling Docker Image Flaws","type":"posts"},{"content":"","date":"1 July 2024","externalUrl":null,"permalink":"/blog/tags/vulnerability-management/","section":"Tags","summary":"","title":"Vulnerability Management","type":"tags"},{"content":"","date":"20 June 2024","externalUrl":null,"permalink":"/blog/tags/azure/","section":"Tags","summary":"","title":"Azure","type":"tags"},{"content":" Azure Subdomain Takeover \u0026amp; Dangling DNS # As a security researcher and application security engineer, I frequently encounter the issue of dangling DNS or subdomain takeovers, particularly within Microsoft Azure environments. This occurs when web applications are deleted, but their associated CNAME records on custom domains are not removed. This leaves the domain vulnerable to malicious actors who can redirect traffic to their own infrastructure.\nUnderstanding the Problem # When you create a web application in Azure and assign it a custom domain using a CNAME record, you establish a link between your domain and Azure\u0026rsquo;s resources. If these resources are later deleted without removing the CNAME record, the domain remains pointed to a now non-existent resource. This creates a \u0026ldquo;dangling\u0026rdquo; DNS entry.\nWhat Happens Next? # Domain Availability Check: Attackers can periodically check for available subdomains. Resource Re-creation: They can then create a resource in Azure with the same name, effectively taking over the subdomain. Malicious Redirection: The hijacked subdomain can be used to serve malicious content, phish for credentials, or spread malware. Real-world Example # Let\u0026rsquo;s say you have a CNAME record pointing app.example.com to myapp.azurewebsites.net. If myapp is deleted but the CNAME record remains, an attacker can claim myapp in Azure, and app.example.com will now point to their resource.\nDNS Provider Configuration # For demonstration, we\u0026rsquo;ll use a common DNS provider, such as GoDaddy, to illustrate how to check and remove these records.\nexample.com ├── www.example.com (CNAME -\u0026gt; myapp.azurewebsites.net) ├── app.example.com (CNAME -\u0026gt; myapp.azurewebsites.net) └── blog.example.com (A -\u0026gt; 192.0.2.1) Mitigation and Remediation Strategies # Regular Audits # Perform regular audits of your DNS records to ensure they are still valid. Tools and scripts can automate this process.\n# Simple script to check DNS records for domain in $(cat domains.txt); do nslookup $domain | grep \u0026#34;can\u0026#39;t find\u0026#34; \u0026amp;\u0026amp; echo \u0026#34;$domain is dangling\u0026#34; done Automated Cleanup # Implement automated cleanup of DNS records when deleting Azure resources. Azure Resource Manager (ARM) templates and scripts can be configured to remove DNS entries.\n{ \u0026#34;resources\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;Microsoft.Network/dnszones/CNAME\u0026#34;, \u0026#34;apiVersion\u0026#34;: \u0026#34;2018-05-01\u0026#34;, \u0026#34;properties\u0026#34;: { \u0026#34;TTL\u0026#34;: 3600, \u0026#34;CNAMERecord\u0026#34;: { \u0026#34;cname\u0026#34;: \u0026#34;myapp.azurewebsites.net\u0026#34; } }, \u0026#34;dependsOn\u0026#34;: [\u0026#34;[resourceId(\u0026#39;Microsoft.Web/sites\u0026#39;, \u0026#39;myapp\u0026#39;)]\u0026#34;] } ] } Use Azure Managed Services # Where possible, use Azure-managed services that handle DNS records for you. Azure Front Door, for example, can manage your custom domains and reduce the risk of dangling DNS.\nConclusion # Dangling DNS records pose a significant security risk, especially in dynamic cloud environments like Azure. By understanding the problem and implementing the strategies outlined above, you can mitigate and remediate these risks effectively. Regular audits, automated cleanups, and utilizing managed services are key steps to securing your domains.\nStay vigilant and keep your infrastructure secure!\n","date":"20 June 2024","externalUrl":null,"permalink":"/blog/posts/2024-06-20-azure-subdomain-takeover/","section":"Posts","summary":"","title":"Azure Subdomain Takeover \u0026 Dangling DNS","type":"posts"},{"content":"","date":"20 June 2024","externalUrl":null,"permalink":"/blog/tags/dns/","section":"Tags","summary":"","title":"DNS","type":"tags"},{"content":"","date":"29 September 2023","externalUrl":null,"permalink":"/blog/tags/alerting/","section":"Tags","summary":"","title":"Alerting","type":"tags"},{"content":" Introduction # Proxmox Virtual Environment (Proxmox VE) is a powerful open-source virtualization platform that combines two virtualization technologies: KVM (Kernel-based Virtual Machine) for virtual machines and LXC (Linux Containers) for lightweight container-based virtualization. Monitoring your Proxmox environment is crucial for ensuring its reliability and performance. In this guide, we\u0026rsquo;ll explore how to set up automated Proxmox monitoring and alerting to keep your virtual infrastructure in check.\nPrerequisites # Before we begin, make sure you have the following prerequisites:\nA Proxmox Virtual Environment (Proxmox VE) installation. Access to the Proxmox web interface. A server or virtual machine running an operating system that supports Docker. Step 1: Install Docker and Docker Compose # Proxmox monitoring and alerting can be simplified by using Docker containers. Start by installing Docker and Docker Compose on a server or VM that can communicate with your Proxmox host. You can follow the official Docker installation instructions for your chosen operating system.\nStep 2: Create a Docker Compose Configuration # Create a directory for your Proxmox monitoring configuration files. Inside this directory, create a docker-compose.yml file with the following content:\nversion: \u0026#34;3\u0026#34; services: prometheus: image: prom/prometheus container_name: prometheus ports: - 9090:9090 volumes: - /path/to/prometheus-config:/etc/prometheus command: - \u0026#34;--config.file=/etc/prometheus/prometheus.yml\u0026#34; alertmanager: image: prom/alertmanager container_name: alertmanager ports: - 9093:9093 volumes: - /path/to/alertmanager-config:/etc/alertmanager command: - \u0026#34;--config.file=/etc/alertmanager/config.yml\u0026#34; Replace /path/to/prometheus-config and /path/to/alertmanager-config with the paths to your configuration directories.\nStep 3: Configure Prometheus # Create a prometheus.yml file inside your /path/to/prometheus-config directory with the following content:\nglobal: scrape_interval: 15s scrape_configs: - job_name: \u0026#34;proxmox\u0026#34; static_configs: - targets: [\u0026#34;proxmox-host-ip:9100\u0026#34;] Replace \u0026lsquo;proxmox-host-ip\u0026rsquo; with the IP address or hostname of your Proxmox host.\nStep 4: Configure AlertManager # Create a config.yml file inside your /path/to/alertmanager-config directory. Here\u0026rsquo;s a sample configuration for AlertManager:\nroute: group_by: [\u0026#34;alertname\u0026#34;] group_wait: 30s group_interval: 5m repeat_interval: 1h receiver: \u0026#34;default\u0026#34; receivers: - name: \u0026#34;default\u0026#34; email_configs: - to: \u0026#34;your-email@example.com\u0026#34; Configure the email recipient address in the to field.\nStep 5: Start Docker Containers # Navigate to the directory containing your docker-compose.yml file and run the following command to start Prometheus and AlertManager in Docker containers:\ndocker-compose up -d Step 6: Configure Proxmox Alerts # In the Proxmox web interface, navigate to \u0026ldquo;Datacenter\u0026rdquo; \u0026gt; \u0026ldquo;Nodes\u0026rdquo; \u0026gt; \u0026ldquo;Your Node\u0026rdquo; \u0026gt; \u0026ldquo;System\u0026rdquo; \u0026gt; \u0026ldquo;Email.\u0026rdquo; Set up email notifications, and configure alerts to send emails to your AlertManager.\nConclusion # Automated Proxmox monitoring and alerting using Docker containers, Prometheus, and AlertManager ensures that your Proxmox virtual environment stays healthy and responsive. You can receive timely alerts about system health and take proactive measures to maintain a reliable virtual infrastructure. Monitoring and alerting are essential components of managing your Proxmox VE effectively, ensuring your virtual machines and containers run smoothly.\n","date":"29 September 2023","externalUrl":null,"permalink":"/blog/posts/2023-09-29-proxmox-monitoring/","section":"Posts","summary":"","title":"Automated Monitoring and Alerting for Proxmox","type":"posts"},{"content":"","date":"29 September 2023","externalUrl":null,"permalink":"/blog/tags/monitoring/","section":"Tags","summary":"","title":"Monitoring","type":"tags"},{"content":"","date":"29 September 2023","externalUrl":null,"permalink":"/blog/tags/proxmox/","section":"Tags","summary":"","title":"Proxmox","type":"tags"},{"content":"","externalUrl":null,"permalink":"/blog/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/blog/series/","section":"Series","summary":"","title":"Series","type":"series"}]