Skip to content

Commit d0d6050

Browse files
miguelxpnnikic
authored andcommitted
Fixes #79265: Improper injection of Host header when using fopen for http requests
Check all occurrences of the string "host:" (and other headers), not just the first one.
1 parent 9e6358a commit d0d6050

File tree

3 files changed

+104
-28
lines changed

3 files changed

+104
-28
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ PHP NEWS
2828

2929
- Standard:
3030
. Fixed bug #79254 (getenv() w/o arguments not showing changes). (cmb)
31+
. Fixed bug #79265 (Improper injection of Host header when using fopen for
32+
http requests). (Miguel Xavier Penha Neto)
3133

3234
20 Feb 2020, PHP 7.3.15
3335

ext/standard/http_fopen_wrapper.c

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -460,41 +460,76 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
460460
strip_header(user_headers, t, "content-type:");
461461
}
462462

463-
if ((s = strstr(t, "user-agent:")) &&
464-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
465-
*(s-1) == '\t' || *(s-1) == ' ')) {
466-
have_header |= HTTP_HEADER_USER_AGENT;
463+
s = t;
464+
while ((s = strstr(s, "user-agent:"))) {
465+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
466+
*(s-1) == '\t' || *(s-1) == ' ') {
467+
have_header |= HTTP_HEADER_USER_AGENT;
468+
break;
469+
}
470+
s++;
467471
}
468-
if ((s = strstr(t, "host:")) &&
469-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
470-
*(s-1) == '\t' || *(s-1) == ' ')) {
471-
have_header |= HTTP_HEADER_HOST;
472+
473+
s = t;
474+
while ((s = strstr(s, "host:"))) {
475+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
476+
*(s-1) == '\t' || *(s-1) == ' ') {
477+
have_header |= HTTP_HEADER_HOST;
478+
break;
479+
}
480+
s++;
472481
}
473-
if ((s = strstr(t, "from:")) &&
474-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
475-
*(s-1) == '\t' || *(s-1) == ' ')) {
476-
have_header |= HTTP_HEADER_FROM;
482+
483+
s = t;
484+
while ((s = strstr(s, "from:"))) {
485+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
486+
*(s-1) == '\t' || *(s-1) == ' ') {
487+
have_header |= HTTP_HEADER_FROM;
488+
break;
477489
}
478-
if ((s = strstr(t, "authorization:")) &&
479-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
480-
*(s-1) == '\t' || *(s-1) == ' ')) {
481-
have_header |= HTTP_HEADER_AUTH;
490+
s++;
482491
}
483-
if ((s = strstr(t, "content-length:")) &&
484-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
485-
*(s-1) == '\t' || *(s-1) == ' ')) {
486-
have_header |= HTTP_HEADER_CONTENT_LENGTH;
492+
493+
s = t;
494+
while ((s = strstr(s, "authorization:"))) {
495+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
496+
*(s-1) == '\t' || *(s-1) == ' ') {
497+
have_header |= HTTP_HEADER_AUTH;
498+
break;
499+
}
500+
s++;
487501
}
488-
if ((s = strstr(t, "content-type:")) &&
489-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
490-
*(s-1) == '\t' || *(s-1) == ' ')) {
491-
have_header |= HTTP_HEADER_TYPE;
502+
503+
s = t;
504+
while ((s = strstr(s, "content-length:"))) {
505+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
506+
*(s-1) == '\t' || *(s-1) == ' ') {
507+
have_header |= HTTP_HEADER_CONTENT_LENGTH;
508+
break;
509+
}
510+
s++;
492511
}
493-
if ((s = strstr(t, "connection:")) &&
494-
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
495-
*(s-1) == '\t' || *(s-1) == ' ')) {
496-
have_header |= HTTP_HEADER_CONNECTION;
512+
513+
s = t;
514+
while ((s = strstr(s, "content-type:"))) {
515+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
516+
*(s-1) == '\t' || *(s-1) == ' ') {
517+
have_header |= HTTP_HEADER_TYPE;
518+
break;
519+
}
520+
s++;
497521
}
522+
523+
s = t;
524+
while ((s = strstr(s, "connection:"))) {
525+
if (s == t || *(s-1) == '\r' || *(s-1) == '\n' ||
526+
*(s-1) == '\t' || *(s-1) == ' ') {
527+
have_header |= HTTP_HEADER_CONNECTION;
528+
break;
529+
}
530+
s++;
531+
}
532+
498533
/* remove Proxy-Authorization header */
499534
if (use_proxy && use_ssl && (s = strstr(t, "proxy-authorization:")) &&
500535
(s == t || *(s-1) == '\r' || *(s-1) == '\n' ||

ext/standard/tests/http/bug79265.phpt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Bug #79265 (Improper injection of Host header when using fopen for http requests)
3+
--INI--
4+
allow_url_fopen=1
5+
--SKIPIF--
6+
<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:12342'); ?>
7+
--FILE--
8+
<?php
9+
require 'server.inc';
10+
11+
$responses = array(
12+
"data://text/plain,HTTP/1.0 200 OK\r\n\r\n",
13+
);
14+
15+
$pid = http_server("tcp://127.0.0.1:12342", $responses, $output);
16+
17+
$opts = array(
18+
'http'=>array(
19+
'method'=>"GET",
20+
'header'=>"RandomHeader: localhost:8080\r\n" .
21+
"Cookie: foo=bar\r\n" .
22+
"Host: userspecifiedvalue\r\n"
23+
)
24+
);
25+
$context = stream_context_create($opts);
26+
$fd = fopen('http://127.0.0.1:12342/', 'rb', false, $context);
27+
fseek($output, 0, SEEK_SET);
28+
echo stream_get_contents($output);
29+
fclose($fd);
30+
31+
http_server_kill($pid);
32+
33+
?>
34+
--EXPECT--
35+
GET / HTTP/1.0
36+
Connection: close
37+
RandomHeader: localhost:8080
38+
Cookie: foo=bar
39+
Host: userspecifiedvalue

0 commit comments

Comments
 (0)