Skip to content

Commit

Permalink
Merge pull request #11 from X1r0z/feature/encryption
Browse files Browse the repository at this point in the history
feat: TLS encryption support
  • Loading branch information
X1r0z authored Dec 21, 2024
2 parents 25bcf63 + 639e7ea commit 1975e5c
Show file tree
Hide file tree
Showing 10 changed files with 949 additions and 140 deletions.
473 changes: 473 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ license = "MIT"

[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
rcgen = "0.13.1"
rustls = "0.23.20"
tokio = { version = "1.42.0", features = ["full"] }
tokio-rustls = "0.26.1"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cargo build --release
- TCP/UDP port forwarding
- Unix domain socket forwarding (e.g. `/var/run/docker.sock`)
- Multi network layer support
- TLS encryption support
- Socks5 proxy

## Usage
Expand Down Expand Up @@ -164,6 +165,38 @@ Reverse socks proxy.
# now attacker can use socks proxy on vps:8888
```

### TLS Encryption

TLS encryption is supported for TCP, Unix domain socket forwarding and socks proxy.

To enable encryption, simple add `+` sign in front of the address or port.

For ease of use, the server uses a self-signed TLS certificate by default, and the client trusts all certificates (no verify).

Example of a TLS encrypted TCP port forwarding.

```bash
# on attacker's machine
./rsproxy fwd -l +7777 -l 33890

# on victim's machine
./rsproxy fwd -r 127.0.0.1:3389 -r +vps:7777

# now attacker can access 3389 through vps:33890, and the traffic on port 7777 will be encrypted
```

Example of a TLS encrypted reverse socks proxy.

```bash
# on attacker's machine
./rsproxy socks -l +7777 -l 8888

# on victim's machine
./rsproxy socks -r +vps:7777

# now attacker can use socks proxy on vps:8888, and the traffic on port 7777 will be encrypted
```

## Reference

[https://github.com/EddieIvan01/iox](https://github.com/EddieIvan01/iox)
94 changes: 94 additions & 0 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::sync::Arc;

use rcgen::{generate_simple_self_signed, CertifiedKey};
use rustls::{
client::danger::ServerCertVerifier,
pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer},
ClientConfig, ServerConfig, SignatureScheme,
};
use tokio_rustls::{TlsAcceptor, TlsConnector};
use tracing::info;

pub fn get_tls_acceptor(host: &str) -> TlsAcceptor {
info!("Generate self-signed tls certificate for {}", host);

let subject_alt_names = vec![host.into()];
let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names).unwrap();

let cert_chain = CertificateDer::pem_slice_iter(cert.pem().as_bytes())
.collect::<std::result::Result<Vec<_>, _>>()
.unwrap();
let key_der = PrivateKeyDer::from_pem_slice(key_pair.serialize_pem().as_bytes()).unwrap();
let config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key_der)
.unwrap();

TlsAcceptor::from(Arc::new(config))
}

pub fn get_tls_connector() -> TlsConnector {
let config = ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoCertVerifier))
.with_no_client_auth();

TlsConnector::from(Arc::new(config))
}

#[derive(Debug)]
struct NoCertVerifier;

#[allow(unused_variables)]
impl ServerCertVerifier for NoCertVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &rustls::pki_types::ServerName<'_>,
ocsp_response: &[u8],
now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}

fn requires_raw_public_keys(&self) -> bool {
false
}

fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
vec![
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP521_SHA512,
SignatureScheme::ECDSA_SHA1_Legacy,
SignatureScheme::ED25519,
SignatureScheme::ED448,
SignatureScheme::RSA_PKCS1_SHA1,
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
]
}
}
102 changes: 88 additions & 14 deletions src/forward.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
use std::io::Result;
use std::{io::Result, sync::Arc};

use tokio::net::{TcpListener, TcpStream, UdpSocket, UnixStream};
use tracing::{error, info};

use crate::{tcp, udp};
use crate::{crypto, tcp, udp};

pub struct Forward {
local_addrs: Vec<String>,
remote_addrs: Vec<String>,
socket: Option<String>,
udp: bool,
local_opts: Vec<bool>,
remote_opts: Vec<bool>,
}

impl Forward {
pub fn new(
local_addrs: Vec<String>,
remote_addrs: Vec<String>,
local_opts: Vec<bool>,
remote_opts: Vec<bool>,
socket: Option<String>,
udp: bool,
) -> Self {
Self {
local_addrs,
remote_addrs,
local_opts,
remote_opts,
socket,
udp,
}
Expand Down Expand Up @@ -71,8 +77,18 @@ impl Forward {
let listener1 = TcpListener::bind(&self.local_addrs[0]).await?;
let listener2 = TcpListener::bind(&self.local_addrs[1]).await?;

info!("Bind to {} success", self.local_addrs[0]);
info!("Bind to {} success", self.local_addrs[1]);
info!("Bind to {} success", listener1.local_addr()?);
info!("Bind to {} success", listener1.local_addr()?);

let acceptor1 = Arc::new(match self.local_opts[0] {
true => Some(crypto::get_tls_acceptor(&self.local_addrs[0])),
false => None,
});

let acceptor2 = Arc::new(match self.local_opts[1] {
true => Some(crypto::get_tls_acceptor(&self.local_addrs[1])),
false => None,
});

loop {
let (stream1, addr1) = listener1.accept().await?;
Expand All @@ -81,7 +97,13 @@ impl Forward {
info!("Accept connection from {}", addr1);
info!("Accept connection from {}", addr2);

tokio::spawn(async {
let acceptor1 = acceptor1.clone();
let acceptor2 = acceptor2.clone();

tokio::spawn(async move {
let stream1 = tcp::NetStream::from_acceptor(stream1, acceptor1).await;
let stream2 = tcp::NetStream::from_acceptor(stream2, acceptor2).await;

if let Err(e) = tcp::handle_forward(stream1, stream2).await {
error!("Failed to forward: {}", e)
}
Expand All @@ -91,16 +113,32 @@ impl Forward {

async fn local_to_remote_tcp(&self) -> Result<()> {
let listener = TcpListener::bind(&self.local_addrs[0]).await?;
info!("Bind to {} success", self.local_addrs[0]);
info!("Bind to {} success", listener.local_addr()?);

let acceptor = Arc::new(match self.local_opts[0] {
true => Some(crypto::get_tls_acceptor(&self.local_addrs[0])),
false => None,
});

let connector = Arc::new(match self.remote_opts[0] {
true => Some(crypto::get_tls_connector()),
false => None,
});

loop {
let (stream, addr) = listener.accept().await?;
let remote = TcpStream::connect(&self.remote_addrs[0]).await?;

let acceptor = acceptor.clone();
let connector = connector.clone();

info!("Accept connection from {}", addr);
info!("Connect to {} success", self.remote_addrs[0]);
info!("Connect to {} success", remote.peer_addr()?);

tokio::spawn(async move {
let stream = tcp::NetStream::from_acceptor(stream, acceptor).await;
let remote = tcp::NetStream::from_connector(remote, connector).await;

tokio::spawn(async {
if let Err(e) = tcp::handle_forward(stream, remote).await {
error!("failed to forward: {}", e)
}
Expand All @@ -109,12 +147,28 @@ impl Forward {
}

async fn remote_to_remote_tcp(&self) -> Result<()> {
let connector1 = Arc::new(match self.remote_opts[0] {
true => Some(crypto::get_tls_connector()),
false => None,
});

let connector2 = Arc::new(match self.remote_opts[1] {
true => Some(crypto::get_tls_connector()),
false => None,
});

loop {
let stream1 = TcpStream::connect(&self.remote_addrs[0]).await?;
let stream2 = TcpStream::connect(&self.remote_addrs[1]).await?;

info!("Connect to {} success", self.remote_addrs[0]);
info!("Connect to {} success", self.remote_addrs[1]);
info!("Connect to {} success", stream1.peer_addr()?);
info!("Connect to {} success", stream2.peer_addr()?);

let connector1 = connector1.clone();
let connector2 = connector2.clone();

let stream1 = tcp::NetStream::from_connector(stream1, connector1).await;
let stream2 = tcp::NetStream::from_connector(stream2, connector2).await;

tcp::handle_forward(stream1, stream2).await?;
}
Expand All @@ -124,7 +178,12 @@ impl Forward {
let socket_path = self.socket.as_ref().unwrap();

let local_listener = TcpListener::bind(&self.local_addrs[0]).await?;
info!("Bind to {} success", self.local_addrs[0]);
info!("Bind to {} success", local_listener.local_addr()?);

let acceptor = Arc::new(match self.local_opts[0] {
true => Some(crypto::get_tls_acceptor(&self.local_addrs[0])),
false => None,
});

loop {
let (local_stream, addr) = local_listener.accept().await?;
Expand All @@ -133,8 +192,13 @@ impl Forward {
info!("Accept connection from {}", addr);
info!("Connect to {} success", socket_path);

let acceptor = acceptor.clone();

tokio::spawn(async move {
if let Err(e) = tcp::handle_unix_socket_forward(unix_stream, local_stream).await {
let local_stream = tcp::NetStream::from_acceptor(local_stream, acceptor).await;
let unix_stream = tcp::NetStream::Unix(unix_stream);

if let Err(e) = tcp::handle_forward(unix_stream, local_stream).await {
error!("Failed to forward: {}", e)
}
});
Expand All @@ -144,14 +208,24 @@ impl Forward {
async fn socket_to_remote_tcp(&self) -> Result<()> {
let socket_path = self.socket.as_ref().unwrap();

let connector = Arc::new(match self.remote_opts[0] {
true => Some(crypto::get_tls_connector()),
false => None,
});

loop {
let unix_stream = UnixStream::connect(socket_path).await?;
let remote_stream = TcpStream::connect(&self.remote_addrs[0]).await?;

info!("Connect to {} success", socket_path);
info!("Connect to {} success", self.remote_addrs[0]);
info!("Connect to {} success", remote_stream.peer_addr()?);

let connector = connector.clone();

let unix_stream = tcp::NetStream::Unix(unix_stream);
let remote_stream = tcp::NetStream::from_connector(remote_stream, connector).await;

tcp::handle_unix_socket_forward(unix_stream, remote_stream).await?;
tcp::handle_forward(unix_stream, remote_stream).await?;
}
}

Expand Down
Loading

0 comments on commit 1975e5c

Please sign in to comment.