Skip to content

Commit 1692268

Browse files
committed
ext/openssl: openssl_encrypt() zend mm heap overflow on AES-WRAP-PAD mode.
Fix #22186 close GH-22187
1 parent 9056908 commit 1692268

3 files changed

Lines changed: 51 additions & 2 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ PHP NEWS
4848
. Fixed bug GH-20469 (Unsafe inheritance cache replay with reentrant
4949
autoloading). (Levi Morrison)
5050

51+
- OpenSSL:
52+
. Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
53+
openssl_encrypt with AES-WRAP-PAD). (David Carlier)
54+
5155
- Phar:
5256
. Fixed a bypass of the magic ".phar" directory protection in
5357
Phar::addEmptyDir() for paths starting with "/.phar", while allowing

ext/openssl/openssl.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7932,6 +7932,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
79327932
const char *aad, size_t aad_len, int enc) /* {{{ */
79337933
{
79347934
int i = 0;
7935+
size_t outlen = data_len + EVP_CIPHER_block_size(cipher_type);
79357936

79367937
if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
79377938
php_openssl_store_errors();
@@ -7945,7 +7946,19 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
79457946
return FAILURE;
79467947
}
79477948

7948-
*poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
7949+
#ifdef EVP_CIPH_WRAP_MODE
7950+
if ((EVP_CIPHER_mode(cipher_type)) == EVP_CIPH_WRAP_MODE) {
7951+
/*
7952+
* RFC 5649 wrap-with-padding rounds the input up to the block size
7953+
* and prepends an integrity block, we reserve one extra block.
7954+
* See EVP_EncryptUpdate(3): wrap mode may write up to
7955+
* inl + cipher_block_size bytes.
7956+
*/
7957+
outlen += EVP_CIPHER_block_size(cipher_type);
7958+
}
7959+
#endif
7960+
7961+
*poutbuf = zend_string_alloc(outlen, false);
79497962

79507963
if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
79517964
&i, (const unsigned char *)data, (int)data_len)) {
@@ -7957,7 +7970,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
79577970
}
79587971
*/
79597972
php_openssl_store_errors();
7960-
zend_string_release_ex(*poutbuf, 0);
7973+
zend_string_release_ex(*poutbuf, false);
79617974
return FAILURE;
79627975
}
79637976

ext/openssl/tests/gh22186.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD)
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
/* openssl_get_cipher_methods() enumerates provider ciphers, but openssl_encrypt()
8+
* resolves names via the legacy EVP_get_cipherbyname(), so on some builds the
9+
* cipher is listed yet not usable. Probe the actual call path instead. */
10+
if (!@openssl_encrypt("test", "aes-128-wrap-pad", str_repeat("k", 16),
11+
OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, str_repeat("\0", 4))) {
12+
die('skip aes-128-wrap-pad not usable on this OpenSSL build');
13+
}
14+
?>
15+
--FILE--
16+
<?php
17+
$pass = str_repeat("k", 16);
18+
$iv = str_repeat("\0", 4);
19+
20+
for ($i = 1; $i < 258; $i++) {
21+
$data = str_repeat("a", $i);
22+
$enc = openssl_encrypt($data, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
23+
$dec = openssl_decrypt($enc, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
24+
if ($dec !== $data) {
25+
die("mismatch at $i\n");
26+
}
27+
}
28+
29+
echo "done\n";
30+
?>
31+
--EXPECT--
32+
done

0 commit comments

Comments
 (0)