Skip to content
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

Add Config options for subnet filtering #61

Merged
merged 7 commits into from
Dec 24, 2020
Merged
15 changes: 15 additions & 0 deletions application/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ heartbeat_interval = 30000
heartbeat_timeout = 1000


# If a registration is received with an address in one of these subnets it will
# be ignored and dropped. This is to prevent clients leveraging the outgoing
# connections from the station to connect to sation infrastructure that would
# be othewise firewalled.
covert_blocklist = [
"127.0.0.1/32", # localhost ipv4
"10.0.0.0/8", # reserved ipv4
"172.16.0.0/12", # reserved ipv4
"192.168.0.0/16", # reserved ipv4
"fc00::/7 ", # private network ipv6
"fe80::0/16", # link local ipv6
"::1/128" # localhost ipv6
]


### ZMQ sockets to connect to and subscribe

## Registration API
Expand Down
27 changes: 27 additions & 0 deletions application/lib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lib

import (
"fmt"
"net"
"os"

"github.com/BurntSushi/toml"
Expand All @@ -16,6 +17,10 @@ type Config struct {

// REST endpoint to share decoy registrations.
PreshareEndpoint string `toml:"preshare_endpoint"`

// List of subnets with disallowed covert addresses.
CovertBlocklist []string `toml:"covert_blocklist"`
covertBlocklist []*net.IPNet
}

func ParseConfig() (*Config, error) {
Expand All @@ -25,5 +30,27 @@ func ParseConfig() (*Config, error) {
return nil, fmt.Errorf("failed to load config: %v", err)
}

c.covertBlocklist = []*net.IPNet{}
for _, subnet := range c.CovertBlocklist {
_, ipNet, err := net.ParseCIDR(subnet)
if err != nil {
continue
}

c.covertBlocklist = append(c.covertBlocklist, ipNet)
}

return &c, nil
}

func (c *Config) IsBlocklisted(addr net.IP) bool {
if addr == nil {
return true
}
for _, subnet := range c.covertBlocklist {
if subnet.Contains(addr) {
return true
}
}
return false
}
18 changes: 16 additions & 2 deletions application/lib/config_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lib

import (
"net"
"os"
"testing"
)
Expand All @@ -17,6 +18,19 @@ func TestConjureLibParseConfig(t *testing.T) {
t.Fatalf("No sockets provided to test parse")
}

// t.Logf("%+v", conf)
return
if len(conf.covertBlocklist) == 0 {
t.Fatalf("failed to parse blocklist")
}

if !conf.IsBlocklisted(net.ParseIP("127.0.0.1")) {
t.Fatalf("Blocklist error - 127.0.0.1 should be blocked")
}

if conf.IsBlocklisted(net.ParseIP("1.2.3.4")) {
t.Fatalf("Blocklist error - 1.2.3.4 should not be blocked")
}

if conf.IsBlocklisted(nil) {
t.Fatalf("Not sure what will happen.")
}
}
24 changes: 21 additions & 3 deletions application/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,18 @@ func get_zmq_updates(connectAddr string, regManager *cj.RegistrationManager, con
go func() {
// Handle multiple as receive_zmq_messages returns separate registrations for v4 and v6
for _, reg := range newRegs {
if reg == nil || reg.RegistrationSource == nil {
if reg == nil {
continue
}

// If registration is trying to connect to a dark decoy that is blocklisted continue
covertStr, _, err := net.SplitHostPort(reg.Covert)
covert := net.ParseIP(covertStr)
if covert == nil || err != nil || conf.IsBlocklisted(covert) {
logger.Printf("Dropping reg, malformed or blocklisted covert: %v, %s, %v", reg.IDString(), reg.Covert, err)
continue
}

if !reg.PreScanned() {
// New registration received over channel that requires liveness scan for the phantom
liveness, response := reg.PhantomIsLive()
Expand Down Expand Up @@ -257,6 +266,15 @@ func recieve_zmq_message(sub *zmq.Socket, regManager *cj.RegistrationManager) ([
}

conjureKeys, err := cj.GenSharedKeys(parsed.SharedSecret)
if parsed.GetRegistrationAddress() == nil {
parsed.RegistrationAddress = make([]byte, 16, 16)
}
if parsed.GetDecoyAddress() == nil {
parsed.DecoyAddress = make([]byte, 16, 16)
}

sourceAddr := net.IP(parsed.RegistrationAddress)
decoyAddr := net.IP(parsed.DecoyAddress)

// Register one or both of v4 and v6 based on support specified by the client
var newRegs []*cj.DecoyRegistration
Expand All @@ -269,7 +287,7 @@ func recieve_zmq_message(sub *zmq.Socket, regManager *cj.RegistrationManager) ([
}

// log phantom IP, shared secret, ipv6 support
logger.Printf("New registration: %v\n", reg.String())
logger.Printf("New registration: '%v' -> '%v' %v\n", sourceAddr, decoyAddr, reg.String())

newRegs = append(newRegs, reg)
}
Expand All @@ -282,7 +300,7 @@ func recieve_zmq_message(sub *zmq.Socket, regManager *cj.RegistrationManager) ([
}

// log phantom IP, shared secret, ipv6 support
logger.Printf("New registration: %v\n", reg.String())
logger.Printf("New registration: '%v' -> '%v' %v\n", sourceAddr, decoyAddr, reg.String())
newRegs = append(newRegs, reg)
}

Expand Down
3 changes: 2 additions & 1 deletion application/registration_with_transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ func TestManagerFunctionality(t *testing.T) {
rm.AddTransport(pb.TransportType_Min, min.Transport{})
c2s.Transport = &transport

newReg, err := rm.NewRegistration(c2s, &keys, c2s.GetV6Support())
source := pb.RegistrationSource_Detector
newReg, err := rm.NewRegistration(c2s, &keys, c2s.GetV6Support(), &source)
if err != nil {
t.Fatalf("Registration failed: %v", err)
}
Expand Down
6 changes: 3 additions & 3 deletions src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,11 @@ pub fn c_open_reporter(fname: String)
*/

#[cfg(test)]
pub fn c_write_reporter(msg: String)
pub fn c_write_reporter(_msg: String)
{panic!("YOU ARE TESTING AND THIS FUNCTION IS NOT MOCKED YET!");}
#[cfg(test)]
pub fn c_tcp_send_rst_pkt(saddr: u32, daddr: u32,
sport: u16, dport: u16, seq: u32)
pub fn c_tcp_send_rst_pkt(_saddr: u32, _daddr: u32,
_sport: u16, _dport: u16, seq: u32)
{panic!("c_tcp_send_rst_pkt({}) called", seq);}
/*
pub fn c_get_global_cli_conf() -> *const ClientConf
Expand Down
98 changes: 89 additions & 9 deletions src/flow_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ impl Flow
{
Flow { src_ip: sip, dst_ip: dip, src_port: sport, dst_port: dport }
}

pub fn export_addrs(&self) -> (Vec<u8>, Vec<u8>) {
let src_bytes = match self.src_ip {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};

let dst_bytes = match self.dst_ip {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};

return (src_bytes, dst_bytes)
}
}

// All members are stored in host-order, even src_ip and dst_ip.
Expand All @@ -86,7 +100,9 @@ pub struct FlowNoSrcPort

impl fmt::Display for FlowNoSrcPort {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:* -> {}:{}", self.src_ip, self.dst_ip, self.dst_port)
let socket_src = SocketAddr::new(self.src_ip, 0);
let socket_dst = SocketAddr::new(self.dst_ip, self.dst_port);
write!(f, "{} -> {}",socket_src, socket_dst)
}
}

Expand All @@ -111,6 +127,20 @@ impl FlowNoSrcPort {
FlowNoSrcPort { src_ip: sip, dst_ip: dip, dst_port: dport }
}
pub fn from_flow(f: &Flow) -> FlowNoSrcPort {FlowNoSrcPort{src_ip: f.src_ip, dst_ip: f.dst_ip, dst_port: f.dst_port}}

pub fn export_addrs(&self) -> (Vec<u8>, Vec<u8>) {
let src_bytes = match self.src_ip {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};

let dst_bytes = match self.dst_ip {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};

return (src_bytes, dst_bytes)
}
}

pub struct SchedEvent
Expand Down Expand Up @@ -320,15 +350,14 @@ impl FlowTracker

#[cfg(test)]
mod tests {
use flow_tracker::{FlowNoSrcPort, Flow};
use std::fmt::Write;

#[test]
fn test_flow_display_format() {
use Flow;
use std::fmt::Write;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

let flow6 = Flow {
src_ip: IpAddr::V6(Ipv6Addr::new(0x2601, 0, 0, 0, 0, 0, 0xabcd, 0xef00)),
dst_ip: IpAddr::V6(Ipv6Addr::new(0x26ff, 0, 0, 0, 0, 0, 0, 1)),
src_ip: "2601::abcd:ef00".parse().unwrap(),
dst_ip: "26ff::1".parse().unwrap(),
src_port: 5672,
dst_port: 443,
};
Expand All @@ -340,8 +369,8 @@ mod tests {


let flow4 = Flow {
src_ip: IpAddr::V4(Ipv4Addr::new(10,22,0,1)),
dst_ip: IpAddr::V4(Ipv4Addr::new(128,138,97,6)),
src_ip: "10.22.0.1".parse().unwrap(),
dst_ip: "128.138.97.6".parse().unwrap(),
src_port: 5672,
dst_port: 443,
};
Expand All @@ -350,5 +379,56 @@ mod tests {
write!(&mut output, "{}", flow4)
.expect("Error occurred while trying to write in String");
assert_eq!(output, "10.22.0.1:5672 -> 128.138.97.6:443");


let flow_n6 = FlowNoSrcPort {
src_ip: "2601::abcd:ef00".parse().unwrap(),
dst_ip: "26ff::1".parse().unwrap(),
dst_port: 443,
};

let mut output = String::new();
write!(&mut output, "{}", flow_n6)
.expect("Error occurred while trying to write in String");
assert_eq!(output, "[2601::abcd:ef00]:0 -> [26ff::1]:443");


let flow_n4 = FlowNoSrcPort {
src_ip: "10.22.0.1".parse().unwrap(),
dst_ip: "128.138.97.6".parse().unwrap(),
dst_port: 443,
};

let mut output = String::new();
write!(&mut output, "{}", flow_n4)
.expect("Error occurred while trying to write in String");
assert_eq!(output, "10.22.0.1:0 -> 128.138.97.6:443");
}

#[test]
fn test_flow_export_addrs() {
let flow = Flow {
src_ip: "10.22.0.1".parse().unwrap(),
dst_ip: "128.138.97.6".parse().unwrap(),
src_port: 5672,
dst_port: 443,
};

let (src, dst) = flow.export_addrs();
print!("{:?} {:?}", src, dst);
assert_eq!(vec![10,22,0,1], src);
assert_eq!(vec![128,138,97,6], dst);

let flow6 = Flow {
src_ip: "2601::abcd:ef00".parse().unwrap(),
dst_ip: "26ff::1".parse().unwrap(),
src_port: 5672,
dst_port: 443,
};

let (src, dst) = flow6.export_addrs();
print!("{:?} {:?}", src, dst);
assert_eq!(vec![0x26, 0x01, 0,0,0,0,0,0,0,0,0,0,0xab, 0xcd, 0xef, 0x00], src);
assert_eq!(vec![0x26, 0xff, 0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 1], dst);
}
}
6 changes: 5 additions & 1 deletion src/process_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use flow_tracker::{Flow, FlowNoSrcPort};
use PerCoreGlobal;
use util::IpPacket;
use elligator;
use protobuf::{Message, SingularPtrField};
use protobuf::{Message};
use signalling::{C2SWrapper, RegistrationSource};


Expand Down Expand Up @@ -308,10 +308,14 @@ impl PerCoreGlobal
let mut zmq_msg = C2SWrapper::new();

let shared_secret = res.0.to_vec();
let (src,decoy) = flow.export_addrs();
let vsp = res.2;

zmq_msg.set_shared_secret(shared_secret);
zmq_msg.set_registration_payload(vsp);
zmq_msg.set_registration_source(RegistrationSource::Detector);
zmq_msg.set_decoy_address(decoy);
zmq_msg.set_registration_address(src);

let repr_str = hex::encode(res.0);
debug!("New registration {}, {}", flow, repr_str);
Expand Down