Skip to content

Commit 81510a0

Browse files
dongfangtianyupre-commit-ci[bot]abhinavsingh
authored
FIX proxy authentication bypass with HTTP/1.0 requests #1267 (#1342)
* test: Add test case to reproduce bug #1267 * fix: Bypass proxy authentication with HTTP/1.0 requests #1267 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Abhinav Singh <[email protected]>
1 parent a8a7d7e commit 81510a0

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

proxy/http/proxy/auth.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class AuthPlugin(HttpProxyBasePlugin):
2727
def before_upstream_connection(
2828
self, request: HttpParser,
2929
) -> Optional[HttpParser]:
30-
if self.flags.auth_code and request.headers:
30+
if self.flags.auth_code:
31+
request.headers = request.headers or {}
3132
if httpHeaders.PROXY_AUTHORIZATION not in request.headers:
3233
raise ProxyAuthenticationFailed()
3334
parts = request.headers[httpHeaders.PROXY_AUTHORIZATION][1].split()

tests/plugin/test_http_proxy_plugins.py

+73-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"""
1111
import gzip
1212
import json
13+
import base64
1314
import selectors
1415
from typing import Any, cast
1516
from urllib import parse as urlparse
@@ -29,7 +30,8 @@
2930
from proxy.http.parser import HttpParser, httpParserTypes
3031
from proxy.common.utils import bytes_, build_http_request, build_http_response
3132
from proxy.http.responses import (
32-
NOT_FOUND_RESPONSE_PKT, PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT,
33+
NOT_FOUND_RESPONSE_PKT, PROXY_AUTH_FAILED_RESPONSE_PKT,
34+
PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT,
3335
)
3436
from proxy.common.constants import DEFAULT_HTTP_PORT, PROXY_AGENT_HEADER_VALUE
3537
from .utils import get_plugin_by_test_name
@@ -555,3 +557,73 @@ async def test_shortlink_plugin_external(self) -> None:
555557
),
556558
)
557559
self.assertFalse(self.protocol_handler.work.has_buffer())
560+
561+
@pytest.mark.asyncio # type: ignore[misc]
562+
@pytest.mark.parametrize(
563+
"_setUp",
564+
(
565+
('test_auth_plugin'),
566+
),
567+
indirect=True,
568+
) # type: ignore[misc]
569+
async def test_auth_plugin(self) -> None:
570+
self.flags.auth_code = base64.b64encode(bytes_("admin:123456"))
571+
572+
request = b'\r\n'.join([
573+
b'GET http://www.facebook.com/tr/ HTTP/1.1',
574+
b'Host: www.facebook.com',
575+
b'User-Agent: proxy.py v2.4.4rc5.dev3+g95b646a.d20230811',
576+
b'',
577+
b'',
578+
])
579+
580+
self._conn.recv.return_value = request
581+
self.mock_selector.return_value.select.side_effect = [
582+
[(
583+
selectors.SelectorKey(
584+
fileobj=self._conn.fileno(),
585+
fd=self._conn.fileno(),
586+
events=selectors.EVENT_READ,
587+
data=None,
588+
),
589+
selectors.EVENT_READ,
590+
)],
591+
]
592+
await self.protocol_handler._run_once()
593+
self.assertEqual(
594+
self.protocol_handler.work.buffer[0],
595+
PROXY_AUTH_FAILED_RESPONSE_PKT,
596+
)
597+
598+
@pytest.mark.asyncio # type: ignore[misc]
599+
@pytest.mark.parametrize(
600+
"_setUp",
601+
(
602+
('test_auth_plugin'),
603+
),
604+
indirect=True,
605+
) # type: ignore[misc]
606+
async def test_auth_plugin_bypass(self) -> None:
607+
self.flags.auth_code = base64.b64encode(bytes_("admin:123456"))
608+
609+
# miss requests header when https and HTTP 1.0
610+
request = b'CONNECT www.facebook.com:443 HTTP/1.0\r\n\r\n'
611+
612+
self._conn.recv.return_value = request
613+
self.mock_selector.return_value.select.side_effect = [
614+
[(
615+
selectors.SelectorKey(
616+
fileobj=self._conn.fileno(),
617+
fd=self._conn.fileno(),
618+
events=selectors.EVENT_READ,
619+
data=None,
620+
),
621+
selectors.EVENT_READ,
622+
)],
623+
]
624+
await self.protocol_handler._run_once()
625+
626+
self.assertEqual(
627+
self.protocol_handler.work.buffer[0],
628+
PROXY_AUTH_FAILED_RESPONSE_PKT,
629+
)

tests/plugin/utils.py

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
FilterByUpstreamHostPlugin, RedirectToCustomServerPlugin,
1717
)
1818
from proxy.http.proxy import HttpProxyBasePlugin
19+
from proxy.http.proxy.auth import AuthPlugin
1920

2021

2122
def get_plugin_by_test_name(test_name: str) -> Type[HttpProxyBasePlugin]:
@@ -36,4 +37,6 @@ def get_plugin_by_test_name(test_name: str) -> Type[HttpProxyBasePlugin]:
3637
plugin = FilterByURLRegexPlugin
3738
elif test_name == 'test_shortlink_plugin':
3839
plugin = ShortLinkPlugin
40+
elif test_name == 'test_auth_plugin':
41+
plugin = AuthPlugin
3942
return plugin

0 commit comments

Comments
 (0)