Skip to content

Commit ac4254a

Browse files
nielsdosTimWolla
authored andcommitted
Fix missing randomness check and insufficient random bytes for SOAP HTTP Digest
If php_random_bytes_throw fails, the nonce will be uninitialized, but still sent to the server. The client nonce is intended to protect against a malicious server. See section 5.10 and 5.12 of RFC 7616 [1], and bullet point 2 below. Tim pointed out that even though it's the MD5 of the nonce that gets sent, enumerating 31 bits is trivial. So we have still a stack information leak of 31 bits. Furthermore, Tim found the following issues: * The small size of cnonce might cause the server to erroneously reject a request due to a repeated (cnonce, nc) pair. As per the birthday problem 31 bits of randomness will return a duplication with 50% chance after less than 55000 requests and nc always starts counting at 1. * The cnonce is intended to protect the client and password against a malicious server that returns a constant server nonce where the server precomputed a rainbow table between passwords and correct client response. As storage is fairly cheap, a server could precompute the client responses for (a subset of) client nonces and still have a chance of reversing the client response with the same probability as the cnonce duplication. Precomputing the rainbow table for all 2^31 cnonces increases the rainbow table size by factor 2 billion, which is infeasible. But precomputing it for 2^14 cnonces only increases the table size by factor 16k and the server would still have a 10% chance of successfully reversing a password with a single client request. This patch fixes the issues by increasing the nonce size, and checking the return value of php_random_bytes_throw(). In the process we also get rid of the MD5 hashing of the nonce. [1] RFC 7616: https://www.rfc-editor.org/rfc/rfc7616 Co-authored-by: Tim Düsterhus <[email protected]>
1 parent 0e45ed7 commit ac4254a

File tree

1 file changed

+13
-8
lines changed

1 file changed

+13
-8
lines changed

ext/soap/php_http.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -664,18 +664,23 @@ int make_http_soap_request(zval *this_ptr,
664664
if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
665665
if (Z_TYPE_P(digest) == IS_ARRAY) {
666666
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
667-
zend_long nonce;
667+
unsigned char nonce[16];
668668
PHP_MD5_CTX md5ctx;
669669
unsigned char hash[16];
670670

671-
php_random_bytes_throw(&nonce, sizeof(nonce));
672-
nonce &= 0x7fffffff;
671+
if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) {
672+
ZEND_ASSERT(EG(exception));
673+
php_stream_close(stream);
674+
convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
675+
convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
676+
convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
677+
smart_str_free(&soap_headers_z);
678+
smart_str_free(&soap_headers);
679+
return FALSE;
680+
}
673681

674-
PHP_MD5Init(&md5ctx);
675-
snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, nonce);
676-
PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
677-
PHP_MD5Final(hash, &md5ctx);
678-
make_digest(cnonce, hash);
682+
php_hash_bin2hex(cnonce, nonce, sizeof(nonce));
683+
cnonce[32] = 0;
679684

680685
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
681686
Z_TYPE_P(tmp) == IS_LONG) {

0 commit comments

Comments
 (0)