// AesOracle prepends and appends 5-10 random bytes to a plaintext, // encrypts the plaintext under a predetermined BlockMode, then // returns the detected BlockMode func AesOracle(plaintext []byte, encrypter cipher.BlockMode) string { // Generate random bytes to prepend/append to plaintext prependBytes, _ := bytes.Random(r.Intn(5) + 5) appendBytes, _ := bytes.Random(r.Intn(5) + 5) plaintext = append(prependBytes, plaintext...) plaintext = append(plaintext, appendBytes...) plaintext, _ = blocks.Pkcs7(plaintext, aes.BlockSize) ciphertext := make([]byte, len(plaintext)) modifiedCiphertext := make([]byte, len(plaintext)) // Modify the first block of the plaintext modified := plaintext modified[0] = byte(255) encrypter.CryptBlocks(ciphertext, plaintext) encrypter.CryptBlocks(modifiedCiphertext, modified) // If the second block in the modified ciphertext is affected by a // change in the first block of the plaintext, return CBC mode if ciphertext[16] != modifiedCiphertext[16] { return "CBC" } return "ECB" }
func AppendSecretEncryptEcb(plaintext, key []byte, prefix bool) []byte { if prefix == true { if len(prefixBytes) == 0 { prefixBytes, _ = bytes.Random(r.Intn(99) + 1) } plaintext = append(prefixBytes, plaintext...) } secret, _ := base64.StdEncoding.DecodeString("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK") plaintext = append(plaintext, secret...) ciphertext, _ := EcbEncrypt(plaintext, key) return ciphertext }
func RandomAesMode() (cipher.BlockMode, string, error) { block, err := aes.NewCipher(NewAesKey()) if err != nil { return nil, "", err } mode := r.Intn(2) if mode == 1 { iv, _ := bytes.Random(aes.BlockSize) encrypter := cipher.NewCBCEncrypter(block, iv) return encrypter, "CBC", nil } encrypter := NewECBEncrypter(block) return encrypter, "ECB", nil }
/* CBC bitflipping attack * Create a function, that given an input, prepends: * "comment1=cooking%20MCs;userdata=" * and appends: * ";comment2=%20like%20a%20pound%20of%20bacon" * , quotes out the ; and =, and pads and encrypts it under AES CBC. * Another function should decrypt the string and return true if * ";admin=true;" exists in the string. Modify the ciphertext to * make the second funcion return true. */ func c16() (actual, expected Result) { key := crypto.NewAesKey() input := "XadminXtrue" inputBytes := []byte(input) str := profile.ProcessComment(input) iv, _ := bytes.Random(aes.BlockSize) encrypted, err := crypto.CbcEncrypt([]byte(str), key, iv) if err != nil { panic(err) } // Flip the targeted bytes ("X"s in the input string) encrypted[16] = encrypted[16] ^ 59 ^ inputBytes[0] encrypted[22] = encrypted[22] ^ 61 ^ inputBytes[6] hasAdmin := profile.HasAdmin(encrypted, key, iv) return hasAdmin, true }
/* ECB cut and paste * Make a profile_for function that takes an email address and returns * a user object encoded cookie-style ([email protected]&uid=10). * Encrypt the encoded profile with AES ECB, and *supply* this to the * "attacker". Find a way to create a valid admin profile. */ func c13() (actual, expected Result) { expected = "email=XXXXXXXXXXXXXX&uid=1&role=admin" key, _ := bytes.Random(aes.BlockSize) // A 14 byte-long email address will put role= at the end of the second block attackEmail := string(stdBytes.Repeat([]byte("X"), 14)) // admin padded w/ 10 bytes will put admin at the beginning of the second block attackEmail2 := string(stdBytes.Repeat([]byte("X"), 10)) + "admin" profile1 := profile.New(attackEmail) profile2 := profile.New(attackEmail2) encrypted1, err := profile.Encrypt([]byte(profile1), key) if err != nil { log.Println(err) } encrypted2, err := profile.Encrypt([]byte(profile2), key) if err != nil { log.Println(err) } // Use first two blocks of encrypted1 and thirdBlock of encrypted2 to // create the attack ciphertext adminProfile := encrypted1[0:32] thirdBlock := encrypted2[16:32] adminProfile = append(adminProfile, thirdBlock...) decrypted, err := profile.Decrypt(adminProfile, key) if err != nil { log.Fatal(err) } // Parse the modified ciphertext and encode the admin profile parsed := profile.Parse(string(decrypted)) encoded := profile.Encode(parsed) return encoded, expected }
/* CBC padding oracle * Write a CBC padding oracle that decrypts a ciphertext and detects * if the plaintext is padded properly with PKCS#7. Choose a random line * from 17.txt, encrypt it, then decrypt it using the oracle. */ func c17() (actual, expected Result) { input, _ := ioutil.ReadFile("input/17.txt") strs := strings.Split(string(input), "\n") str := strs[r.Intn(10)] decodedStr, _ := base64.StdEncoding.DecodeString(str) if crypto.GlobalAesKey == nil { crypto.GlobalAesKey = crypto.NewAesKey() } key := crypto.GlobalAesKey iv, _ := bytes.Random(aes.BlockSize) ciphertext, err := crypto.CbcEncrypt([]byte(decodedStr), key, iv) if err != nil { log.Fatal(err) } blocks, err := bytes.SplitIntoBlocks(ciphertext, aes.BlockSize) if err != nil { log.Fatal(err) } var plaintext []byte for n := 0; n < len(blocks); n++ { block := blocks[n] controlled := make([]byte, aes.BlockSize) plaintextBlock := make([]byte, aes.BlockSize) intermediate := make([]byte, aes.BlockSize) prevBlock := make([]byte, aes.BlockSize) if n == 0 { prevBlock = iv } else { prevBlock = blocks[n-1] } for i := aes.BlockSize - 1; i >= 0; i-- { paddingLen := aes.BlockSize - i paddingByte := byte(paddingLen) // Set the last paddingLen bytes of controlled to so that when decrypted, // each will be a valid padding byte. for j := 0; j < paddingLen; j++ { controlled[i+j] = paddingByte ^ intermediate[i+j] } for b := 0; b <= 256; b++ { controlled[i] = byte(b) controlled := append(controlled, block...) valid, _ := crypto.CbcPaddingOracle(controlled, iv) if valid { // The padding is valid and we control the ith byte of the // block XORed with the intermediate state. XOR is an inverse // operation so finding the ith byte of the intermediate state // is as simple as: intermediate[i] = paddingByte ^ controlled[i] break } } plaintextBlock[i] = prevBlock[i] ^ intermediate[i] } plaintext = append(plaintext, plaintextBlock...) } decrypted, _ := crypto.CbcDecrypt(ciphertext, key, iv) return string(plaintext), string(decrypted) }
// NewAesKey generates a random AES key func NewAesKey() []byte { key, _ := bytes.Random(aes.BlockSize) return key }