Introduction
A directory traversal (or path traversal) vulnerability allows attackers to access files outside the intended directory by using ../ sequences in file path parameters. When exploited, attackers can read sensitive files like /etc/passwd, /etc/shadow, application configuration files containing database credentials, SSH keys, or source code. Immediate containment, damage assessment, and remediation are required to prevent further unauthorized access.
Symptoms
- Web server access logs show requests with
../patterns: `- 192.168.1.100 - - [09/Apr/2026:03:15:22 +0000] "GET /files/../../../etc/passwd HTTP/1.1" 200 1234
- 192.168.1.100 - - [09/Apr/2026:03:15:25 +0000] "GET /files/../../../etc/shadow HTTP/1.1" 200 892
- 192.168.1.100 - - [09/Apr/2026:03:15:30 +0000] "GET /files/../../../app/.env HTTP/1.1" 200 456
- 192.168.1.100 - - [09/Apr/2026:03:16:01 +0000] "GET /files/../../../home/deploy/.ssh/id_rsa HTTP/1.1" 200 1679
`- Or encoded variants:
`- GET /files/..%2f..%2f..%2fetc/passwd HTTP/1.1
- GET /files/..%252f..%252f..%252fetc/passwd HTTP/1.1
`- Response status 200 indicates the files were successfully read
- Attack IP accessed configuration files, credentials, and SSH keys
Common Causes
- File download endpoint does not sanitize the
filenameparameter - Application uses user input directly in file path construction
- Lack of chroot or sandbox for file serving functionality
- Web server configured to serve files from a directory without path validation
- Application framework's file serving middleware misconfigured
Step-by-Step Fix
Phase 1: Contain the Breach
- 1.Block the attacking IP immediately:
- 2.```bash
- 3.# Block at firewall level
- 4.sudo iptables -A INPUT -s 192.168.1.100 -j DROP
- 5.# Or with ufw:
- 6.sudo ufw deny from 192.168.1.100
- 7.
` - 8.Rotate all potentially exposed credentials:
- 9.```bash
- 10.# The attacker likely read these files:
- 11.# /etc/shadow - all user password hashes
- 12.# /app/.env - database credentials, API keys
- 13.# /home/deploy/.ssh/id_rsa - SSH private key
# Rotate database passwords psql -U postgres -c "ALTER USER app_user WITH PASSWORD 'new-secure-password';"
# Regenerate API keys aws iam create-access-key --user-name deploy # Then deactivate the old key aws iam update-access-key --access-key-id OLD_KEY_ID --status Inactive
# Regenerate SSH keys ssh-keygen -t ed25519 -f /home/deploy/.ssh/id_ed25519 -N "" # Update authorized_keys on all servers ```
Phase 2: Assess the Damage
- 1.Determine what files were accessed:
- 2.```bash
- 3.# Search access logs for traversal patterns
- 4.grep -E '\.\.\/|\.\.%2[fF]|%252[fF]' /var/log/nginx/access.log
# Check if the attacker escalated further grep "192.168.1.100" /var/log/auth.log # Look for SSH login attempts from this IP after the file read
# Check for new SSH keys added to authorized_keys cat /home/*/\.ssh/authorized_keys # Look for keys that do not match known deployments ```
- 1.Check for signs of lateral movement:
- 2.```bash
- 3.# Check for new user accounts
- 4.awk -F: '$3 >= 1000 {print $1}' /etc/passwd
# Check for new cron jobs crontab -l ls -la /etc/cron.d/ cat /var/spool/cron/crontabs/*
# Check for new SUID binaries find / -perm -4000 -type f 2>/dev/null | sort
# Check recently modified files find /etc /home /var/www -mtime -1 -type f 2>/dev/null ```
Phase 3: Patch the Vulnerability
- 1.Fix the file serving code:
- 2.```python
- 3.# BEFORE (vulnerable)
- 4.@app.route('/files/<path:filename>')
- 5.def serve_file(filename):
- 6.return send_file(f'/uploads/{filename}')
# AFTER (secure) import os from werkzeug.utils import safe_join
UPLOAD_DIR = os.path.abspath('/uploads')
@app.route('/files/<path:filename>') def serve_file(filename): # Resolve the full path filepath = os.path.abspath(safe_join(UPLOAD_DIR, filename))
# Verify the resolved path is within the upload directory if not filepath.startswith(UPLOAD_DIR): abort(403) # Forbidden
# Verify the file exists if not os.path.isfile(filepath): abort(404)
return send_file(filepath) ```
- 1.Add WAF rules to block traversal attempts:
- 2.```nginx
- 3.# Nginx - block common traversal patterns
- 4.location ~* \.\./ {
- 5.return 403;
- 6.}
# Or use ModSecurity rule # SecRule ARGS "@contains ../" "id:1001,phase:2,deny,status:403" ```
Prevention
- Use
safe_joinoros.path.realpathto validate file paths - Serve files through a dedicated CDN or object storage (S3) instead of the application
- Implement allowlists for permitted file extensions and directories
- Run the application under a dedicated user with minimal file system access
- Use chroot or containers to limit file system scope
- Monitor logs for
../patterns and alert on detection - Conduct regular security audits of file handling endpoints