Why Your CI/CD Credentials Are a Time Bomb

The recent post-mortem from the "Shai Hulud" supply chain attack serves as a warning: all long-lived credentials will leak eventually. The attackers method was ruthlessly efficient: stealing a single, long-lived GitHub Personal Access Token (PAT) from a CI runner, which then served as a beachhead to steal subsequent secrets (like NPM tokens and cloud credentials) and compromise the entire deployment chain. The conclusion is simple for organizations committed to secure GitOps principles: PATs are obsolete and pose an unacceptable supply chain risk. At Pvotal, we recognize that the weakest link in platform automation is always the credential that manages your code. That is why infrastream is engineered to eliminate PATs entirely, enforcing a zero-trust architecture through automated, short-lived tokens. The Catastrophic Blast Radius of a Stolen PAT The danger of the traditional PAT is its ability to lead to a cascading security failure across your entire infrastructure, as demonstrated by the Shai Hulud campaign: Initial Breach: The attackers leveraged a malicious NPM package, triggered during the build's preinstall phase. The malware's first objective was to scrape the running environment for static credentials, namely a GitHub PAT. Escalation & Secret Theft: Using the stolen PAT as an initial access key, the malware was then able to perform privileged actions, such as: - Stealing critical, secondary secrets stored in the CI environment (cloud API keys for AWS, GCP, and Azure, database passwords, and NPM tokens). - Exfiltrating this harvested data to public attacker-controlled GitHub repositories. Worm-like Propagation: With a valid NPM token, the malware could authenticate to the registry, identify other packages owned by the victim, inject malicious code into them, and publish new, compromised versions. This created an exponential, self-replicating threat that spread at the speed of CI/CD pipelines. This systemic risk is intolerable: trusting a single, long-lived token to remain secure for months is negligent when that token can unlock your entire cloud infrastructure. Infrastream’s Principle: Automation as Governance The technical solution is moving from human-owned PATs to machine-owned, short-lived credentials issued by a GitHub App. While tools like Chainguard's OctoSTS prove the need for this IAT-based approach, infrastream integrates and automates the secure deployment model for the enterprise. Infrastream eliminates this critical vulnerability by: Eliminating Persistent Secrets: The platform handles the underlying complexity of managing IATs, ensuring the tokens are ephemeral (valid for a maximum of one hour) and tied to the platform's identity, not a user's. Enforcing Granularity (Split-Token Architecture): We mandate a two-token architecture for every client installation to enforce the Principle of Least Privilege: - The Read-Only App: Used for analysis, linting, dependency scanning, and security checks. If leaked, an attacker cannot modify code. - The Read/Write App: The high-privilege token used only by the control plane during the final deployment step, isolating write capability to the narrowest window. In conclusion, the manual rotation of long-lived PATs is an engineering liability that grants attackers a persistent foothold in your sensitive environments. Modern security dictates that if a secret must exist, it must be ephemeral and tied only to the precise task it is performing. Infrastream eliminates this critical supply chain risk by automating the entire lifecycle of secure credentials, by enforcing the use of short-lived IATs via GitHub Apps and mandating a granular, two-token architecture for every customer. For modern enterprises, a standardized and automated infrastructure is essential to realizing the security and scalability promises of GitOps. Sources: LinkedIn Post Posthog Article Riptides Post
Are you ready for Black Friday ?

Peak seasons such as Black Friday and Cyber Monday represent the ultimate stress test for any digital enterprise.
AI Cyber Attacks

In the AI era, cybercrime isn’t just fast, but it’s intelligent, hyper personalised, and often invisible to legacy tools.
Breaking Docker’s Isolation Using... Docker? (CVE-2025-9074)

Author Philippe Dugre ("zer0x64") A while ago, a long time friend and security researcher, Felix Boulet, contacted me regarding something he found.This blog post is about my part in analysing and testing the CVE-2025-9074 vulnerability, a critical vulnerability allowing an attacker to break out of a docker container on Docker Desktop, the Windows and MacOS version of Docker. Finding and confirming the vulnerability Felix was messing around with network scans from a Docker container, when he found an open port that he didn’t recognize. After a bit of investigation, he found out it was the Docker engine socket and that there was no authentication system to access it. When he found out what he had, he contacted me about it because I’ve got more experience with Docker and development in general. The first step was to validate the vulnerability. His setup is admittedly very non-standard since he is always trying to break software, so he needed a second opinion on a fresh installation. I dusted off an old laptop, did all the Windows updates and freshly installed Docker on it. I adjusted his proof of concept to my setup and was able to exploit it, confirming the vulnerability. I then proceeded to test it on Linux and MacOS, while also refining the exploit code. It turned out that Linux is not vulnerable, for reasons which will be discussed later, but MacOS was, although with a lower impact than on Windows. The vulnerability The vulnerability itself is pretty simple: The Docker Engine socket should never be accessible by untrustworthy code or user. This socket is the management API for docker and having access to it grants full access to everything the docker application can do. This obviously includes creating and deleting containers, but a more nefarious capability here is volume mounting. Let’s say the docker engine runs a production application using a database, which also runs on docker. An attacker can simply create a new container and mount the database’s volume, which then allows him to read and write anything to the database. An even scarier application of this, however, is mounting the host’s file system. This allows the attacker to read and write files on the host. On Windows, since the Docker Engine runs via WSL2, the attacker can mount as an administrator the entire filesystem, read any sensitive file, and ultimately overwrite a system DLL to escalate the attacker to administrator of the host system. On MacOS, however, the Docker Desktop application still has a layer of isolation and trying to mount a user directory prompts the user for permission. By default, the docker application does not have access to the rest of the filesystem and does not run with administrative privileges, so the host is a lot safer than in the Window’s case. However, the attacker does still have full control of the docker application/containers and can even backdoor it by mounting and modifying the application’s configuration, which does not need any user approval. What about Linux? Linux does not use a TCP socket for the Docker Engine’s API. Instead, it uses a named pipe on the host’s filesystem. Unless a specific unsafe configuration is used, the container does not have access to that named pipe. Note that this vulnerability is documented and by design when doing a Docker-in-Docker setup, which shouldn’t be used in production for this very reason. How easy is it to exploit? When it comes to the difficulty of exploiting the vulnerability in a perfect scenario, it is indeed very easy to exploit. For my MacOS proof of concept, I only needed 3 lines of python code: Python import docker client = docker.DockerClient(base_url="tcp://192.168.65.7:2375") client.containers.run("alpine", "touch /mnt/pwned", volumes=["/Users/<username>/:/mnt"]) This creates a “pwned” file in the user’s home directory. From there, it’s easy to simply modify the script to use the docker python package to do anything the engine can do. You can also check out Felix’s blog post about it to see his exploit that works from the CLI. As for the setup required for exploitation, the engine must run on either Windows or MacOS. While most production systems run on Linux in the case of docker, some do use Windows Server (especially if they require the use of Windows containers). While a lesser common target, there are a lot of developers running untrusted code on docker on Windows or MacOS. The other requirement is for the attacker to be able to reach the docker socket. While the easiest way to exploit it is via a vulnerable or malicious container that’s controlled by the attacker, another attack vector that can be used here is Server-Side Request Forgery(SSRF). This vulnerability allows an attacker to proxy requests through the vulnerable application and reach the docker socket, the impact of which varies especially depending on the availability of HTTP requests methods (most SSRF only allows GET requests, but some niche case allows the use of POST, PATCH, DELETE methods). Am I affected? If you use Docker Desktop on Windows or MacOS, make sure you update to the latest version. The vulnerability has been fixed in version 4.44.3. Thanks I’d like to thank Felix Boulet for choosing me to help him on this issue. I’d also like to thank the Docker team, which acknowledged and fixed the issue pretty quickly while assigning proper credit. CVE Record: https://www.cve.org/CVERecord?id=CVE-2025-9074 Felix’s Blog: https://blog.qwertysecurity.com/Articles/blog3.html About Philippe Dugre Based in the Montreal area, Philippe Dugre is a white-hat hacker with deep expertise in memory corruption exploits, vulnerability management, and cryptography. After years dedicated to competitive cybersecurity and honing his technical skills, he now focuses on supporting the development of future specialists in the field. He currently works as a DevSecOps at Pvotal, specializing in application security and cryptographic analysis, and contributes to safeguarding organizational assets and client interests through cutting-edge security practices.
Hey Google, can you help me build my login?

Easily create an audience login with Firebase Studio and Gemini! From time to time, it may happen during a talk that you want to interact with your audience at a conference. What I commonly see is some form of feedback form, or some slide with contacts, but what happens if you want to interact with the audience more intimately? Probably you need a way for them to “Log” into your content? It looked like a cool side project that could be done with Flutter (web in this case) and Firebase Studio, so I started experimenting on that! How it started: First of all, I needed a Plan, what do I want to do? I want two main parts: a webpage that I can share during the presentation, which should include a QR code that users can scan to join the session, and a second webpage, accessible from the scanned QR code, where users can enter their Username and see it appear on the main page. Probably, I will need some Firebase settings too, but let’s worry about that later. So, let’s jump into play and head to https://studio.firebase.google.com/. After logging in, what I see is…. I’m not sure this will go well since this is my first time “Vibe” …coding? Let’s explain what I need, and let’s see what happens when I start the prototyping . . . Finger crossed…. aaand… It wanted to use TypeScript, instead of Flutter… I mean, I could go full vibe code, but I also wanted this to be integrated in a Flutter project of mine, so I gently declined and went the “long route”, so instead of using the flashy AI box I went with the “Start coding an app” and the “Flutter button” just below that. After being asked for a name (used StageLogin), I got redirected to the IDE. After a brief wait and the download of some dependencies, the sample application was up and running, and I saw the enticing “Gemini” button again. Let’s see if I can get my screen as intended now! Copied the previous prompt, only the first part: I want to create a Flutter web application divided in two parts. The first screen is a page with a Join QR Code; this page should be tied to a Firebase database where users can log in by scanning the QR code. The page will show the list of users currently logged in the room. And added the screenshot made with Stitch, sent the message, and something happened… Uuuhh…. Is there any reason not to say “Please do?” Accepting the changes, I got some minor errors, it looks like the code itself is fine, and even if I will need to fix the UI later to be more appealing, the page itself is rendered well. The main issue is that the imports were added, but the dependencies were not. Asked for the fix, but this time I got a less helpful message: I can guide you through the process of setting up Firebase for your project, but I cannot directly execute the Firebase setup commands or configure your Firebase project settings from here. Setting up Firebase typically involves: Creating a Firebase project: Go to the Firebase console (https://console.firebase.google.com/) and create a new project. Registering your app: Register your Flutter web app with the Firebase project. This will involve providing your app’s details and downloading a configuration file (e.g., firebase_options.dart for Flutter). Adding Firebase configuration to your app: Add the downloaded configuration file to your Flutter project. Adding Firebase dependencies: You’ve already added firebase_core and firebase_database in the previous step, but you might need other dependencies depending on the Firebase features you plan to use (e.g., Authentication, Firestore). Initializing Firebase in your app: You’ve already added the initialization code in main.dart. Once you have completed these steps in the Firebase console and added the necessary configuration files to your project, the Firebase integration I added to lib/main.dart should work. But… I don’t want to make all this work! Luckily, we have tools for that! Firebase and FlutterFire! Thanks to the integration with Firebase Studio, you already have all you need to use. So let’s run firebase login and flutterfire configure After running the first command (login), simply follow the instructions to log in to your Firebase account. Then with flutterfire configure, after a few steps, your code will be automatically updated to be ready to work with Firebase! All without even needing to go to the console. stagelogin-00997488:~/myapp$ flutterfire configure i Found 11 Firebase projects. ✔ Select a Firebase project to configure your Flutter application with · <create a new project> ✔ Enter a project id for your new Firebase project (e.g. my-cool-project) · hatdroid-stage-login i New Firebase project hatdroid-stage-login created successfully. ? Which platforms should your configuration support (use arrow keys & space to select)? › android ios macos ✔ web i Firebase web app myapp (web) is not registered on Firebase project hatdroid-stage-login. i Registered a new Firebase web app on Firebase project hatdroid-stage-login. Firebase configuration file lib/firebase_options.dart generated successfully with the following Firebase apps: Platform Firebase App Id web x:xxxxxxxxxxxx:web:xxxxxxxxxxxxxxxxxxxxxx Now we just need to add the dependencies: stagelogin-00997488:~/myapp$ flutter pub add firebase_core stagelogin-00997488:~/myapp$ flutter pub add firebase_database Resolving dependencies... Downloading packages... + firebase_core 3.13.1 + firebase_core_platform_interface 5.4.0 + firebase_core_web 2.23.0 + _flutterfire_internals 1.3.55 + firebase_database 11.3.6 + firebase_database_platform_interface 0.2.6+6 + firebase_database_web 0.2.6+12 ... Changed X dependencies! And since we are using firebase_database I also needed to create a new database, so head to https://console.firebase.google.com/, open your project, tap on the Realtime Database, and then create it. You can find a complete guide here: https://firebase.google.com/docs/database/flutter/start And after creating the database, you need to update the firebase_options.dart to include the database information: static const FirebaseOptions web = FirebaseOptions( apiKey: 'YOUR-API-KEY', appId: 'YOUR-APP-ID', messagingSenderId: 'YOUR-MESSAGING-ID', projectId: 'YOUR-PROJECT-ID', authDomain: 'YOUR-AUTH-DOMAIN', databaseURL: 'YOUR-DATABASE-URL', // <--- THIS ONE --- storageBucket: 'YOUR-STORAGE-BUCKET', ); You can either pick it from your console, or the easier way is to call again flutterfire configure after the creation of the database. Now the application is ready to accept and read the list! The next step is to add the QR Code,
NorthSec 2025; How To Solve Each Flag

The easiest flag was found within the slot machine's reels. It replaced the symbols for a "minor jackpot".
NorthSec 2025: My Experience as a Challenge Designer

NorthSec is a cybersecurity event renowned for hosting one of the world's largest on-site Capture The Flag (CTF) competitions
Mojo Performance Analysis

This study evaluates the performance of matrix multiplication and linear regression implementations in Mojo and Python
Women in Tech: The Room, The Table, and The Mic

If you’re a woman in tech, you’ve probably been in that room.
2025 Trends in Digital Product Lifecycle: Sustainability, A.I., and Security

As the digital world continues to evolve, so does the way we ideate, design, create and deliver products.