Skip to content

Commit

Permalink
new: added AMQP plugin (ActiveMQ, RabbitMQ, Qpid, JORAM and Solace)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilsocket committed Oct 24, 2023
1 parent bb2cbee commit bf73ef7
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ default = [
"rdp",
# "oracle", optional as it requires libclntsh that's a pain to install and configure
"stomp",
"amqp",
]
http = ["dep:url", "dep:regex", "dep:reqwest", "dep:base64", "dep:ntlmclient"]
dns = ["dep:trust-dns-resolver"]
Expand All @@ -118,6 +119,7 @@ mongodb = ["dep:mongodb"]
oracle = ["dep:sibyl"]
rdp = ["dep:rdp-rs"]
stomp = []
amqp = []

[profile.release]
debug = true
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Currently supported protocols / plugins (use `legba --list-plugins` to print thi

| Plugin Name | Description |
| ------------- | ------------- |
| amqp | AMQP password authentication (ActiveMQ, RabbitMQ, Qpid, JORAM and Solace). |
| dns | DNS subdomain enumeration. |
| ftp | FTP password authentication. |
| http | HTTP request for custom web logins supporting CSRF. |
Expand Down Expand Up @@ -343,7 +344,7 @@ legba vnc \

#### STOMP Password Authentication:

The STOMP protocol allows interaction with message queueing services like ActiveMQ, RabbitMQ, HornetQ and OpenMQ.
The STOMP text protocol allows interaction with message queueing services like ActiveMQ, RabbitMQ, HornetQ and OpenMQ.

```sh
legba stomp \
Expand All @@ -352,6 +353,17 @@ legba stomp \
--password data/passwords.txt
```

#### AMQP Password Authentication:

The AMQP binary protocol allows interaction with message queueing services like ActiveMQ, RabbitMQ, Qpid, JORAM and Solace.

```sh
legba amqp \
--target localhost:5672 \
--username admin \
--password data/passwords.txt
```

## License

Legba was made with ♥ by [Simone Margaritelli](https://www.evilsocket.net/) and it's released under the GPL 3 license.
Expand Down
122 changes: 122 additions & 0 deletions src/plugins/amqp/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use std::time::Duration;

use async_trait::async_trait;
use ctor::ctor;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;

use crate::session::{Error, Loot};
use crate::utils;
use crate::Options;
use crate::Plugin;

use crate::creds::Credentials;

const PROTOCOL_HEADER_091: &[u8] = &[b'A', b'M', b'Q', b'P', 0, 0, 9, 1];

#[ctor]
fn register() {
crate::plugins::manager::register("amqp", Box::new(AMQP::new()));
}

#[derive(Clone)]
pub(crate) struct AMQP {
host: String,
port: u16,
address: String,
}

impl AMQP {
pub fn new() -> Self {
AMQP {
host: String::new(),
port: 5672,
address: String::new(),
}
}
}

#[async_trait]
impl Plugin for AMQP {
fn description(&self) -> &'static str {
"AMQP password authentication (ActiveMQ, RabbitMQ, Qpid, JORAM and Solace)."
}

fn setup(&mut self, opts: &Options) -> Result<(), Error> {
(self.host, self.port) = utils::parse_target(opts.target.as_ref(), 5672)?;
self.address = format!("{}:{}", &self.host, self.port);
Ok(())
}

async fn attempt(&self, creds: &Credentials, timeout: Duration) -> Result<Option<Loot>, Error> {
// TODO: SSL
let mut stream = tokio::time::timeout(timeout, TcpStream::connect(&self.address))
.await
.map_err(|e| e.to_string())?
.map_err(|e| e.to_string())?;

// send proto header
stream
.write_all(PROTOCOL_HEADER_091)
.await
.map_err(|e| e.to_string())?;

// read connection.start header
let mut conn_start_header = [0_u8; 7];
stream
.read_exact(&mut conn_start_header)
.await
.map_err(|e| e.to_string())?;
let size_raw: [u8; 4] = conn_start_header[3..].try_into().unwrap();
let payload_size = u32::from_be_bytes(size_raw) + 1;
// read connection.start body
let mut conn_start_body = vec![0_u8; payload_size as usize];
stream
.read_exact(&mut conn_start_body)
.await
.map_err(|e| e.to_string())?;

// send connection.start-ok
let auth = [
&[0],
creds.username.as_bytes(),
&[0],
creds.password.as_bytes(),
]
.concat();

let frame_args = [
&vec![0x00, 0x00, 0x00, 0x00], // 0 client properties
&vec![0x05, b'P', b'L', b'A', b'I', b'N'], // mechanism
(auth.len() as u32).to_be_bytes().as_ref(), // auth len + auth
&auth,
&vec![0x05, b'e', b'n', b'_', b'U', b'S'], // locale
]
.concat();

let frame = [
&[0x01, 0, 0], // type:method + channel: 0
((frame_args.len() + 4) as u32).to_be_bytes().as_ref(), // length
&[0, 0x0a], // class: connection
&[0, 0x0b], // method: start-ok
&frame_args,
&[0xce], // frame end
]
.concat();

stream.write_all(&frame).await.map_err(|e| e.to_string())?;

// read response
let mut buffer = [0_u8; 16];
stream.read(&mut buffer).await.map_err(|e| e.to_string())?;

if buffer[0] == 0x01 {
Ok(Some(Loot::from([
("username".to_owned(), creds.username.to_owned()),
("password".to_owned(), creds.password.to_owned()),
])))
} else {
Ok(None)
}
}
}
2 changes: 2 additions & 0 deletions src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub(crate) use plugin::Plugin;
// TODO: SNMP
// TODO: SMB

#[cfg(feature = "amqp")]
pub(crate) mod amqp;
#[cfg(feature = "dns")]
pub(crate) mod dns;
#[cfg(feature = "ftp")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
docker run -it \
--name rabbitmq \
-p 61613:61613/tcp \
-p 5672:5672/tcp \
-e RABBITMQ_ADMIN_USER=admin666 \
-e RABBITMQ_ADMIN_PASSWORD=test12345 \
-e RABBITMQ_API_USER=user666 \
Expand Down

0 comments on commit bf73ef7

Please sign in to comment.