-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fuzz passphrase API of Secure Cell (#583)
* Fuzzers for passphrase API of Secure Cell Currently passphrase API supports only Seal mode so we add tools for that only (just like master key API is right now). In fact, the tools are more or less copypasta of master key tools: - Encryption code path is verified by "roundtrip" tool. It encrypts arbitrary counts of zeros and sees whether it fails to encrypt, or decrypt later, or decrypts incorrectly. Fuzzed values here are the parameter lengths used. - Decryption code path is much more interesting and is verified by "decrypt" tool. Here we accept fuzzable input and try to decrypt it. This is probably the main attack surface of the new API, and boy are we vulnerable! AFL immediately found one crash and two more came after the first one was fixed. Stay tuned... This commit also includes test data for the tools and a meta-tool used to generate that data. Since GoThemis does not support passphrase API yet, we use CGo directly here. * Validate key length on decryption This is the first crash found by AFL. Decryption code fails to validate key length encoded in "algorithm" field of Secure Cell header and just proceeds to use it, assuming that it's one of 128, 192, 256 bits. Maximum value allowed by data format is 4096 bits (512 bytes), but the code allocates only 32 bytes for the derived key. Longer keys will cause a buffer overflow which allows an attacker to overwrite return address stored on the stack and to theoretically execute arbitrary code. Practically, this can be used for denial of service by exploiting the much more likely outcome -- crash due to a segmentation fault. Fix the issue by validating the key length and allowing only values produced by Themis: 128, 192, or 256 bits. * Avoid unsigned overflow in length computations Additions like hdr->iv_length + hdr->auth_tag_length + hdr->kdf_context_length and length += sizeof(hdr->auth_tag_length) + hdr->auth_tag_length can cause unsigned overflow during itermediate computations. This can lead to incorrect buffer lengths being computed, causing further pointer computations to use invalid memory addresses, resuling in arbitrary memory writes controlled by an attacker -- since these lengths come from message data structure that we parse. This is further complicated by the fact that "size_t" is typically 32-bit on 32-bit systems, causing more possible oveflows. Bad news here is that unsigned integer overflow is *not* undefined behavior in C -- it is defined to be two's complement. Therefore most tools like UBSan and static analyzers don't catch issues due to them not being "undefined behavior", so they are not reported. This issue has been caught only due to its 'side effect' in parsing. The only way to avoid issues is to either check for overflows or avoid them. Both options require careful coding of each arithmetic operation, so we opt for avoiding overflows without significant runtime cost. We update the code to use "uint64_t" where the maximum size can exceed uint32_t range (we know that it won't overflow uint64_t because there are only a handful of values added), and similarly use "uint32_t" for cases where the value cannot overflow "uint32_t". Hopefully this is it... By the way, this is *the* reason why Rust does not have implicit casts between integer types. * Configurable PBKDF2 iteration count KDF makes computations slow but fuzzing requires a lot of encryption and decryption. Let's make the default iteration count to be configurable at compile time and use a lower number for fuzzer builds. This drastically increases efficiency of AFL search. This feature is intended to be used mostly for debugging, and maybe to provide custom builds on demand, so it's not documented anywhere except for the code. * Prevent false positive failures on 32-bit systems Some sanitizers are built in 32-bit mode because of AFL's limitations. We should avoid attempts to allocate over 2 GB on 32-bit systems with Address Sanitizer enabled because this usually causes it to abort.
- Loading branch information
Showing
12 changed files
with
543 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
* Copyright (c) 2020 Cossack Labs Limited | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
// Test input generator for "scell_seal_decrypt_pw" fuzzer | ||
// | ||
// Run this tool to generate new test input for fuzzer: | ||
// | ||
// cd tools/afl | ||
// go run generate/scell_seal_decrypt_pw.go "secret" "message" "context" \ | ||
// > input/scell_seal_decrypt_pw/example.dat | ||
// | ||
// "context" may be omitted. Output is binary so you want to redirect it. | ||
// | ||
// Themis must be installed. It is a good idea to build it with a lower PBKDF2 | ||
// iteration count (e.g., THEMIS_DEFAULT_PBKDF2_ITERATIONS=10) to get fuzzing | ||
// results faster. | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"io" | ||
"os" | ||
"unsafe" | ||
) | ||
|
||
/* | ||
#cgo LDFLAGS: -lthemis | ||
#include <stdbool.h> | ||
#include <themis/themis.h> | ||
static bool themis_scell_encrypt_seal_with_passphrase( | ||
const void* passphrase, | ||
size_t passphrase_length, | ||
const void* user_context, | ||
size_t user_context_length, | ||
const void* message, | ||
size_t message_length, | ||
void* encrypted_message, | ||
size_t* encrypted_message_length) | ||
{ | ||
themis_status_t res = themis_secure_cell_encrypt_seal_with_passphrase( | ||
passphrase, | ||
passphrase_length, | ||
user_context, | ||
user_context_length, | ||
message, | ||
message_length, | ||
encrypted_message, | ||
encrypted_message_length | ||
); | ||
return (res == THEMIS_SUCCESS) || (res == THEMIS_BUFFER_TOO_SMALL); | ||
} | ||
*/ | ||
import "C" | ||
|
||
// TODO: use GoThemis once passphrase API is available there | ||
func encrypt(passphrase, context, message []byte) ([]byte, error) { | ||
var encryptedSize C.size_t | ||
if !C.themis_scell_encrypt_seal_with_passphrase(unsafe.Pointer(&passphrase[0]), | ||
C.size_t(len(passphrase)), | ||
unsafe.Pointer(&context[0]), | ||
C.size_t(len(context)), | ||
unsafe.Pointer(&message[0]), | ||
C.size_t(len(message)), | ||
nil, | ||
&encryptedSize, | ||
) { | ||
return nil, fmt.Errorf("failed to compute output size") | ||
} | ||
encrypted := make([]byte, encryptedSize) | ||
if !C.themis_scell_encrypt_seal_with_passphrase(unsafe.Pointer(&passphrase[0]), | ||
C.size_t(len(passphrase)), | ||
unsafe.Pointer(&context[0]), | ||
C.size_t(len(context)), | ||
unsafe.Pointer(&message[0]), | ||
C.size_t(len(message)), | ||
unsafe.Pointer(&encrypted[0]), | ||
&encryptedSize, | ||
) { | ||
return nil, fmt.Errorf("failed to encrypt data") | ||
} | ||
return encrypted, nil | ||
} | ||
|
||
func writeByteString(w io.Writer, str []byte) { | ||
binary.Write(w, binary.BigEndian, uint32(len(str))) | ||
w.Write(str) | ||
} | ||
|
||
func main() { | ||
args := os.Args | ||
|
||
if (len(args) != 3) && (len(args) != 4) { | ||
fmt.Printf("usage:\n\t%s <passphrase> <message> [<context>]\n", args[0]) | ||
os.Exit(1) | ||
} | ||
|
||
passphrase := []byte(args[1]) | ||
message := []byte(args[2]) | ||
|
||
var context []byte | ||
if len(args) == 4 { | ||
context = []byte(args[3]) | ||
} | ||
|
||
encrypted, err := encrypt(passphrase, context, message) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "failed to encrypt: %s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
writeByteString(os.Stdout, passphrase) | ||
writeByteString(os.Stdout, context) | ||
writeByteString(os.Stdout, encrypted) | ||
} |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.