Skip to content

Commit

Permalink
new: refactored redis module in order to support both legacy and ACL
Browse files Browse the repository at this point in the history
  • Loading branch information
evilsocket committed Oct 30, 2023
1 parent 12c35f9 commit 23ddeb3
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 77 deletions.
44 changes: 0 additions & 44 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ sibyl = { version = "0.6.16", optional = true, features = [
rdp-rs = { version = "0.1.0", optional = true }
hex = "0.4.3"
glob = "0.3.1"
redis = { version = "0.23.3", features = [
"tokio-comp",
"tokio-native-tls-comp",
], optional = true }

[dev-dependencies]
tempfile = "3.8.0"
Expand Down Expand Up @@ -126,7 +122,7 @@ oracle = ["dep:sibyl"]
rdp = ["dep:rdp-rs"]
stomp = []
amqp = []
redis = ["dep:redis"]
redis = []

[profile.release]
debug = true
2 changes: 1 addition & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ pub(crate) struct Options {
#[clap(flatten, next_help_heading = "RDP")]
pub rdp: crate::plugins::rdp::options::Options,
#[cfg(feature = "redis")]
#[clap(flatten, next_help_heading = "Redis")]
#[clap(flatten, next_help_heading = "REDIS")]
pub redis: crate::plugins::redis::options::Options,
}
73 changes: 49 additions & 24 deletions src/plugins/redis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ 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::Plugin;
Expand All @@ -20,7 +22,7 @@ pub(crate) struct Redis {
host: String,
port: u16,
ssl: bool,
command: String,
address: String,
}

impl Redis {
Expand All @@ -29,50 +31,73 @@ impl Redis {
host: String::new(),
port: 6379,
ssl: false,
command: String::new(),
address: String::new(),
}
}

async fn attempt_with_stream<S>(
&self,
creds: &Credentials,
mut stream: S,
) -> Result<Option<Loot>, Error>
where
S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
{
let auth = format!("AUTH {} {}\n", &creds.username, &creds.password);

stream
.write_all(auth.as_bytes())
.await
.map_err(|e| e.to_string())?;

let mut buffer = [0_u8; 128];

stream.read(&mut buffer).await.map_err(|e| e.to_string())?;

if buffer.starts_with(&[b'+', b'O', b'K']) {
Ok(Some(Loot::from([
("username".to_owned(), creds.username.to_owned()),
("password".to_owned(), creds.password.to_owned()),
])))
} else {
Ok(None)
}
}
}

#[async_trait]
impl Plugin for Redis {
fn description(&self) -> &'static str {
"Redis ACL password authentication."
"Redis legacy and ACL password authentication."
}

fn setup(&mut self, opts: &Options) -> Result<(), Error> {
(self.host, self.port) = utils::parse_target(opts.target.as_ref(), 6379)?;
self.ssl = opts.redis.redis_ssl;
self.command = opts.redis.redis_command.to_owned();
self.address = format!("{}:{}", &self.host, self.port);

Ok(())
}

async fn attempt(&self, creds: &Credentials, timeout: Duration) -> Result<Option<Loot>, Error> {
let url = format!(
"{}://{}:{}@{}:{}",
if self.ssl { "rediss" } else { "redis" },
&creds.username,
&creds.password,
&self.host,
self.port
);

let client = redis::Client::open(url).map_err(|e| e.to_string())?;

let mut conn = tokio::time::timeout(timeout, client.get_async_connection())
let tcp_stream = tokio::time::timeout(timeout, TcpStream::connect(&self.address))
.await
.map_err(|e| e.to_string())?
.map_err(|e| e.to_string())?;

redis::cmd(&self.command)
.query_async(&mut conn)
.await
.map_err(|e| e.to_string())?;
if !self.ssl {
self.attempt_with_stream(creds, tcp_stream).await
} else {
let tls = async_native_tls::TlsConnector::new()
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true);

let stream = tokio::time::timeout(timeout, tls.connect(&self.host, tcp_stream))
.await
.map_err(|e| e.to_string())?
.map_err(|e| e.to_string())?;

Ok(Some(Loot::from([
("username".to_owned(), creds.username.to_owned()),
("password".to_owned(), creds.password.to_owned()),
])))
self.attempt_with_stream(creds, stream).await
}
}
}
3 changes: 0 additions & 3 deletions src/plugins/redis/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,4 @@ pub(crate) struct Options {
#[clap(long, default_value_t = false)]
/// Enable SSL for Redis.
pub redis_ssl: bool,
#[clap(long, default_value = "PING")]
/// Redis command to execute in order to test ACL.
pub redis_command: String,
}

0 comments on commit 23ddeb3

Please sign in to comment.