Elelzedel's Projects

Security and hardware enthusiast

Hacking PentAGI [Part 2] - Dual Node


Introduction

In the last post, we hacked PentAGI by using prompt injection to hijack PentAGI’s task scheduling and then escaping the command container through the host’s Docker socket. The explanation from the developer was that this was “expected behavior for single-node deployment”.

Sigh, well let’s hack a dual-node deployment.

Setup

First I created two fresh Ubuntu 26.04 Desktop VMs:

both installed

I started with the official worker node guide and followed it as closely as I could, but I ran into several issues. First, passing alt_names as an EASYRSA_EXTRA_EXTS was broken in my version of EasyRSA::

san fixed

After that, I added my OpenAI key through the installer:

OpenAI key

According to the docs, the rest of the Docker container environment is set up through the web interface:

After the installer completes and PentAGI is running, manually configure the Docker environment through the web interface:

Okay cool, we’ll just go into settings and…

you cannot

The docs are wrong: you cannot set up the Docker environment from the web interface. You have to do it from the installer. If dual-node is the recommended approach, and apparently the only way to run PentAGI securely, why is it so poorly documented?

Okay whatever, we’ll tear it down and set up the Docker environment from the installer:

docker environment settings 1 docker environment settings 2 docker environment settings 3

Launching a Scan

Let’s start a flow and then pause it so we can look at how terminal containers are configured this time:

start flow

Docker Containers

Running docker ps on the master, we can see the core containers from last time, but there is no terminal container this time.

master docker ps

Now it is on the worker node:

master docker ps

The worker node is also running a container: docker-dind. We’ll come back to that in a second; first let’s check for Docker sockets mounted in pentagi-terminal-1:

dind mount

This time, rather than the host’s docker socket, this docker-dind socket is mounted in it. Inspecting that docker:dind container, we can see that it has that folder mounted.

dind mounted dind

Basically, the /var/run/dind/docker.sock socket in docker:dind is getting shared with pentagi-terminal-1. Doing some reading on Docker Hub, we learn that dind stands for “docker in docker”. It’s a way of running subcontainers within this dind container. Once again, no idea why the pentagi-terminal-1 environment needs to spin up its own containers, but this does feel like an improvement over passing the host’s Docker socket in.

Unfortunately, it’s not. Continuing on the dind page, we read the following:

Note: just like the regular dind images, –privileged is required for Docker-in-Docker to function properly (docker-library/docker#151 & docker-library/docker#281), which is a security issue that needs to be treated appropriately.

As described in this Snyk article, if a container is running in privileged mode, it has excessive privileges over the host system. There are a lot of ways to abuse this, but one of the simplest is using the privileged container’s access to the host’s /dev directory to mount the host’s entire filesystem inside a container’s filesystem.

Prove It

We already know we can get remote code execution by hijacking the task queue, so let’s spawn a shell inside pentagi-terminal-1:

shell

Now for the magic: we’ll create a --privileged Debian subcontainer and run bash inside it:

privileged debian subcontainer

This container is now inside the dind container, but because both have been launched with --privileged, this subcontainer has root access to the host’s filesystem:

root access

Tags: