mirror of
https://github.com/maride/afl-transmit.git
synced 2024-11-21 15:04:25 +00:00
Add AES encryption between nodes
This commit is contained in:
parent
0a4217b5b9
commit
ddaab52527
15
README.md
15
README.md
@ -6,6 +6,7 @@ Transfer AFL files over a mesh to fuzz across multiple servers
|
|||||||
|
|
||||||
- Using DEFLATE compression format (see [RFC 1951](https://www.ietf.org/rfc/rfc1951.html))
|
- Using DEFLATE compression format (see [RFC 1951](https://www.ietf.org/rfc/rfc1951.html))
|
||||||
- Automatically syncs the main fuzzer to secondary nodes, and all secondary fuzzers back to the main node
|
- Automatically syncs the main fuzzer to secondary nodes, and all secondary fuzzers back to the main node
|
||||||
|
- Encrypts traffic between nodes using AES-256, dropping plaintext packets
|
||||||
- Usable on UNIXoid (Linux, OSX) systems and Windows
|
- Usable on UNIXoid (Linux, OSX) systems and Windows
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -22,3 +23,17 @@ As a countermeasure, use the `--restrict-to-peers` flags to only allow connectio
|
|||||||
- On your host 10.0.0.2: `./afl-transmit --fuzzer-directory /ram/output --peers 10.0.0.1`
|
- On your host 10.0.0.2: `./afl-transmit --fuzzer-directory /ram/output --peers 10.0.0.1`
|
||||||
- On your host 10.0.0.3: `./afl-transmit --fuzzer-directory /ram/output --peers 10.0.0.1`
|
- On your host 10.0.0.3: `./afl-transmit --fuzzer-directory /ram/output --peers 10.0.0.1`
|
||||||
|
|
||||||
|
### Crypto
|
||||||
|
|
||||||
|
If you want to encrypt your traffic between the nodes - which is advised, as it increases security and there is nearly no argument against it - you can do so by specifying a random key with `--key`.
|
||||||
|
To keep *afl-transmit* simple, the symmetric encryption algorithm AES256-GCM was chosen over an asymmetric variant. This means you need to specify the same key on all nodes.
|
||||||
|
|
||||||
|
Key generation is fairly simple, you just need to get 32 random bytes from somewhere (buy them, or use `/dev/urandom`), and wrap them with base64.
|
||||||
|
For example like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tee transmit.key
|
||||||
|
./afl-transmit --key $(cat transmit.key) --fuzzer-directory ...
|
||||||
|
```
|
||||||
|
|
||||||
|
As already said, the same key must be used on all nodes.
|
||||||
|
8
main.go
8
main.go
@ -19,6 +19,7 @@ func main() {
|
|||||||
watchdog.RegisterWatchdogFlags()
|
watchdog.RegisterWatchdogFlags()
|
||||||
net.RegisterSenderFlags()
|
net.RegisterSenderFlags()
|
||||||
net.RegisterListenFlags()
|
net.RegisterListenFlags()
|
||||||
|
net.RegisterCryptFlags()
|
||||||
RegisterGlobalFlags()
|
RegisterGlobalFlags()
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -31,6 +32,13 @@ func main() {
|
|||||||
// Read peers file
|
// Read peers file
|
||||||
net.ReadPeers()
|
net.ReadPeers()
|
||||||
|
|
||||||
|
// Initialize crypto if desired
|
||||||
|
cryptErr := net.InitCrypt()
|
||||||
|
if cryptErr != nil {
|
||||||
|
fmt.Printf("Failed to initialize crypt function: %s", cryptErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Start watchdog for local afl instances
|
// Start watchdog for local afl instances
|
||||||
go watchdog.WatchFuzzers(outputDirectory)
|
go watchdog.WatchFuzzers(outputDirectory)
|
||||||
|
|
||||||
|
86
net/crypt.go
Normal file
86
net/crypt.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key string
|
||||||
|
sharedGCM cipher.AEAD
|
||||||
|
nonceSize int
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterCryptFlags Registers the flags required for cryptography
|
||||||
|
func RegisterCryptFlags() {
|
||||||
|
flag.StringVar(&key, "key", "", "32 random bytes, base64-wrapped, to AES-encrypt traffic between nodes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCrypt creates a cipher object out of the key handed over via --key
|
||||||
|
func InitCrypt() error {
|
||||||
|
// Check if a key was handed over
|
||||||
|
if key == "" {
|
||||||
|
// no key, no service
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap base64'ed crypto bytes
|
||||||
|
rawKey, base64Err := base64.StdEncoding.DecodeString(key)
|
||||||
|
if base64Err != nil {
|
||||||
|
return fmt.Errorf("failed to unpack base64'ed key: %s", base64Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create cipher object using that key
|
||||||
|
block, cipherErr := aes.NewCipher(rawKey)
|
||||||
|
if cipherErr != nil {
|
||||||
|
return fmt.Errorf("failed to use your key as AES256 key: %s", cipherErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create GCM with cipher object
|
||||||
|
gcm, gcmErr := cipher.NewGCM(block)
|
||||||
|
if gcmErr != nil {
|
||||||
|
return fmt.Errorf("failed to create GCM for your key: %s", gcmErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set shared GCM instance
|
||||||
|
sharedGCM = gcm
|
||||||
|
nonceSize = gcm.NonceSize()
|
||||||
|
|
||||||
|
// No error to report
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CryptApplicable checks if we are able to encrypt or decrypt things, means if this instance was given a proper key
|
||||||
|
func CryptApplicable() bool {
|
||||||
|
// if the shared GCM object is set, we were able to get the key off user's hands, else we don't crypt
|
||||||
|
return sharedGCM != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts the given bytes using the symmetric key
|
||||||
|
func Encrypt(plain []byte) ([]byte, error) {
|
||||||
|
// create nonce and fill it with random bytes
|
||||||
|
nonce := make([]byte, nonceSize)
|
||||||
|
_, readErr := io.ReadFull(rand.Reader, nonce)
|
||||||
|
if readErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get random bytes: %s", readErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt plaintext
|
||||||
|
return sharedGCM.Seal(nonce, nonce, plain, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the given bytes using the symmetric key
|
||||||
|
func Decrypt(enc []byte) ([]byte, error) {
|
||||||
|
// Sanity check on input
|
||||||
|
if len(enc) < sharedGCM.NonceSize() {
|
||||||
|
return nil, fmt.Errorf("failed to decrypt packet: too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt encrypted bytes
|
||||||
|
return sharedGCM.Open(nil, enc[:nonceSize], enc[nonceSize:], nil)
|
||||||
|
}
|
@ -76,9 +76,20 @@ func handle(conn net.Conn, outputDirectory string) {
|
|||||||
// Read raw content
|
// Read raw content
|
||||||
cont, contErr := ioutil.ReadAll(conn) // bufio.NewReader(conn).ReadString('\x00')
|
cont, contErr := ioutil.ReadAll(conn) // bufio.NewReader(conn).ReadString('\x00')
|
||||||
|
|
||||||
|
// Check if we are able to decrypt
|
||||||
|
if CryptApplicable() {
|
||||||
|
// Decrypt packet
|
||||||
|
var decryptErr error
|
||||||
|
cont, decryptErr = Decrypt(cont)
|
||||||
|
if decryptErr != nil {
|
||||||
|
log.Printf("Failed to decrypt packet from %s: %s", conn.RemoteAddr().String(), decryptErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if contErr == nil || contErr == io.EOF {
|
if contErr == nil || contErr == io.EOF {
|
||||||
// We received the whole content, time to process it
|
// We received the whole content, time to process it
|
||||||
unpackErr := logistic.UnpackInto([]byte(cont), outputDirectory)
|
unpackErr := logistic.UnpackInto(cont, outputDirectory)
|
||||||
if unpackErr != nil {
|
if unpackErr != nil {
|
||||||
log.Printf("Encountered error processing packet from %s: %s", conn.RemoteAddr().String(), unpackErr)
|
log.Printf("Encountered error processing packet from %s: %s", conn.RemoteAddr().String(), unpackErr)
|
||||||
}
|
}
|
||||||
|
11
net/peer.go
11
net/peer.go
@ -37,6 +37,17 @@ func CreatePeer(address string) Peer {
|
|||||||
|
|
||||||
// Sends the given content to the peer
|
// Sends the given content to the peer
|
||||||
func (p *Peer) SendToPeer(content []byte) {
|
func (p *Peer) SendToPeer(content []byte) {
|
||||||
|
// Encrypt content if desired
|
||||||
|
if CryptApplicable() {
|
||||||
|
// Encrypt packet
|
||||||
|
var encryptErr error
|
||||||
|
content, encryptErr = Encrypt(content)
|
||||||
|
if encryptErr != nil {
|
||||||
|
log.Printf("Failed to decrypt packet from %s: %s", p.Address, encryptErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build up a connection
|
// Build up a connection
|
||||||
tcpConn, dialErr := net.Dial("tcp", p.Address)
|
tcpConn, dialErr := net.Dial("tcp", p.Address)
|
||||||
if dialErr != nil {
|
if dialErr != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user