mirror of
https://github.com/maride/afl-transmit.git
synced 2024-11-24 16:14: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))
|
||||
- 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
|
||||
|
||||
## 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.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()
|
||||
net.RegisterSenderFlags()
|
||||
net.RegisterListenFlags()
|
||||
net.RegisterCryptFlags()
|
||||
RegisterGlobalFlags()
|
||||
flag.Parse()
|
||||
|
||||
@ -31,6 +32,13 @@ func main() {
|
||||
// Read peers file
|
||||
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
|
||||
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
|
||||
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 {
|
||||
// We received the whole content, time to process it
|
||||
unpackErr := logistic.UnpackInto([]byte(cont), outputDirectory)
|
||||
unpackErr := logistic.UnpackInto(cont, outputDirectory)
|
||||
if unpackErr != nil {
|
||||
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
|
||||
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
|
||||
tcpConn, dialErr := net.Dial("tcp", p.Address)
|
||||
if dialErr != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user