1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
From b9e09489f8160eb5e38a11e84ef6c8b74c2ec828 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sun, 16 Apr 2023 15:05:03 +0200
Subject: [PATCH] Fix missing randomness check and insufficient random bytes
for SOAP HTTP Digest
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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 <timwolla@php.net>
(cherry picked from commit 126d517ce240e9f638d9a5eaa509eaca49ef562a)
(cherry picked from commit 0cfca9aa1395271833848daec0bace51d965531d)
---
NEWS | 6 ++++++
ext/soap/php_http.c | 19 ++++++++++++++-----
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/NEWS b/NEWS
index 11f6e7ad5a8..0770d913467 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.0.29
+
+- Soap:
+ . Fixed bug GHSA-76gg-c692-v2mw (Missing error check and insufficient random
+ bytes in HTTP Digest authentication for SOAP). (nielsdos, timwolla)
+
Backported from 8.0.28
- Core:
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 3a890d7c36f..3bfa4f6f54c 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -646,14 +646,23 @@ int make_http_soap_request(zval *this_ptr,
if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
if (Z_TYPE_P(digest) == IS_ARRAY) {
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
+ unsigned char nonce[16];
PHP_MD5_CTX md5ctx;
unsigned char hash[16];
- PHP_MD5Init(&md5ctx);
- snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, php_rand());
- PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
- PHP_MD5Final(hash, &md5ctx);
- make_digest(cnonce, hash);
+ if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) {
+ ZEND_ASSERT(EG(exception));
+ php_stream_close(stream);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
+ smart_str_free(&soap_headers_z);
+ smart_str_free(&soap_headers);
+ return FALSE;
+ }
+
+ php_hash_bin2hex(cnonce, nonce, sizeof(nonce));
+ cnonce[32] = 0;
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
Z_TYPE_P(tmp) == IS_LONG) {
From e4dd20803ac5579deec54dc6b699d359890f96f0 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 6 Jun 2023 18:05:22 +0200
Subject: [PATCH] Fix GH-11382 add missing hash header for bin2hex
(cherry picked from commit 40439039c224bb8cdebd1b7b3d03b8cc11e7cce7)
---
ext/soap/php_http.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 3bfa4f6f54..72b5bdec2b 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -22,7 +22,8 @@
#include "php_soap.h"
#include "ext/standard/base64.h"
#include "ext/standard/md5.h"
-#include "ext/standard/php_rand.h"
+#include "ext/standard/php_random.h"
+#include "ext/hash/php_hash.h"
static char *get_http_header_value(char *headers, char *type);
static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
|