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

Several issue with Java command shell payloads (and a proposed fix) #18845

Open
sfewer-r7 opened this issue Feb 16, 2024 · 2 comments
Open

Several issue with Java command shell payloads (and a proposed fix) #18845

sfewer-r7 opened this issue Feb 16, 2024 · 2 comments
Labels
bug confirmed Issues confirmed by a committer

Comments

@sfewer-r7
Copy link
Contributor

sfewer-r7 commented Feb 16, 2024

Note: I made a ninja edit to this on 19/2/24 to change the proposed fix.

When developing an exploit module that targets ARCH_JAVA for several platforms (win, linux, osx) that are not the java platform, i.e the exploit wants to execute a JSP payload, I noticed several problems occurring in the command shell payload.

The Issue(s)

Issue 1

The session has the wrong platform reported, it will always default to linux as that is the first platform listed in the JSP payload:

# ~/git/metasploit-framework/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb

  def initialize(info = {})
    super(merge_info(info,
      'Name'          => 'Java JSP Command Shell, Reverse TCP Inline',
      'Description'   => 'Connect back to attacker and spawn a command shell',
      'Author'        => [ 'sf' ],
      'License'       => MSF_LICENSE,
      'Platform'      => %w{ linux osx solaris unix win },
      'Arch'          => ARCH_JAVA,

And then later in Msf::Sessions::CommandShellOptions this happens:

  def on_session(session)
    super

    # Configure input/output to match the payload
    session.user_input  = self.user_input if self.user_input
    session.user_output = self.user_output if self.user_output

    platform = nil
    if self.platform and self.platform.kind_of? Msf::Module::PlatformList
      platform = self.platform.platforms.first.realname.downcase
    end
    if self.platform and self.platform.kind_of? Msf::Module::Platform
      platform = self.platform.realname.downcase
    end


    # a blank platform is *all* platforms and used by the generic modules, in that case only set this instance if it was
    # not previously set to a more specific value through some means
    session.platform = platform unless platform.blank? && !session.platform.blank?

Causing session.platform to always be set to linux (because of self.platform.platforms.first being used). So after my exploit gets a session, I see this:

msf6 exploit(multi/http/redacted) > sessions -l

Active sessions
===============

  Id  Name  Type              Information                                               Connection
  --  ----  ----              -----------                                               ----------
  1         shell java/linux  Shell Banner: Microsoft Windows [Version 10.0.20348.1547  192.168.86.42:4444 -> redacted
                              ] (c) Microsoft Corp...

We can see the session type is java/linux but the banner clearly shows we have Windows shell session.

Issue 2

The code above that updates the session.platform is called after the FileDropper mixin runs, because of the call to super at the start of on_session.

So if we update the session.platform in CommandShellOptions::on_session, then FileDropper will have already run, and it needed to know the session platform to run correctly.

So if the session.platform is to be updated, it should occur before the call to super

Issue 3

This looks like a logic bug, I expect this was the intended behavior:

-    session.platform = platform unless platform.blank? && !session.platform.blank?
+    session.platform = platform unless platform.blank? || !session.platform.blank?

Proposed Fix

This works for me and could be the basis of resolving the issues mentioned above.

  • For a shell session with no platform, where the platform would have been selected as the first platform in a list of supported platforms, we can grep the banner and determine if it is Windows. This will enable FileDropper mixin to run, as well as the session being reported as the correct platform
  • We move the logic that updates the session platform/arch above the call to super, so things like FileDropper can benefit from the platform being correct.
  • What seems like a logic bug is resolved to ensure the platform is not overwritten if we did set one.
--- a/lib/msf/base/sessions/command_shell_options.rb
+++ b/lib/msf/base/sessions/command_shell_options.rb
@@ -32,7 +32,6 @@ module CommandShellOptions
   end
 
   def on_session(session)
-    super
 
     # Configure input/output to match the payload
     session.user_input  = self.user_input if self.user_input
@@ -40,7 +39,25 @@ module CommandShellOptions
 
     platform = nil
     if self.platform and self.platform.kind_of? Msf::Module::PlatformList
-      platform = self.platform.platforms.first.realname.downcase
+
+      # Update a sessions platform if we can, based on the shell's banner.
+      # Do this before the call to super, so other on_session methods (like FileDropper.on_new_session called via
+      # Payload.on_session) can benefit from the session platform being known.
+      unless session.banner.blank?
+        [
+          'Microsoft Windows [Version ', # Server 2022, 11, 10, 7, Vista
+          'Microsoft Windows XP [Version ', # XP
+          'Copyright Microsoft Corp', # 95, 98
+          'Microsoft(R) Windows NT(TM)', # NT4
+        ].each do |windows_banner|
+          if session.banner.include? windows_banner
+            platform = 'windows'
+            break
+          end
+        end
+      end
+
+      platform = self.platform.platforms.first.realname.downcase if platform.nil?
     end
     if self.platform and self.platform.kind_of? Msf::Module::Platform
       platform = self.platform.realname.downcase
@@ -49,7 +66,7 @@ module CommandShellOptions
 
     # a blank platform is *all* platforms and used by the generic modules, in that case only set this instance if it was
     # not previously set to a more specific value through some means
-    session.platform = platform unless platform.blank? && !session.platform.blank?
+    session.platform = platform unless platform.blank? || !session.platform.blank?
 
     if self.arch
       if self.arch.kind_of?(Array)
@@ -59,6 +76,7 @@ module CommandShellOptions
       end
     end
 
+    super
   end
 
 end

Finally, if I run my exploit with this fix (and the pull request from #18844 to address a separate issue in the FileDropper mixin). I see the session correctly reported as java/windows:

msf6 exploit(multi/http/redacted) > sessions -l

Active sessions
===============

  Id  Name  Type                Information                                              Connection
  --  ----  ----                -----------                                              ----------
  1         shell java/windows  Shell Banner: Microsoft Windows [Version 10.0.20348.154  192.168.86.42:4444 -> redacted
                                7] (c) Microsoft Corp...

And the FileDropper mixin now correctly deletes the files I was expecting it to (because it now knowns the session is for windows).

Open question(s)

Dumping some final thoughts I had when looking into this:

  1. Can we identify other platforms by grepping their banners too?
@adfoster-r7
Copy link
Contributor

Copy link

Hi!

This issue has been left open with no activity for a while now.

We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 30 days since the last update here.
If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

@github-actions github-actions bot added the Stale Marks an issue as stale, to be closed if no action is taken label Mar 22, 2024
@adfoster-r7 adfoster-r7 added confirmed Issues confirmed by a committer and removed Stale Marks an issue as stale, to be closed if no action is taken labels Mar 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug confirmed Issues confirmed by a committer
Projects
Status: No status
Development

No branches or pull requests

2 participants