mirror of
				https://github.com/maride/afl-transmit.git
				synced 2025-10-11 01:56:50 +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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user