feat: add test script for socks5auth

This commit is contained in:
Jinjie Zhang 2021-08-23 16:55:57 +08:00
parent 5876e431ce
commit 0c366f6436
2 changed files with 200 additions and 135 deletions

View File

@ -97,11 +97,12 @@ class Socks5Proxy(DestinationKnown):
self, self,
message: str, message: str,
reply_code: Optional[int] = None, reply_code: Optional[int] = None,
ver_code: int = SOCKS5_VERSION
) -> layer.CommandGenerator[None]: ) -> layer.CommandGenerator[None]:
if reply_code is not None: if reply_code is not None:
yield commands.SendData( yield commands.SendData(
self.context.client, self.context.client,
bytes([SOCKS5_VERSION, reply_code]) + b"\x00\x01\x00\x00\x00\x00\x00\x00" bytes([ver_code, reply_code]) + b"\x00\x01\x00\x00\x00\x00\x00\x00"
) )
yield commands.CloseConnection(self.context.client) yield commands.CloseConnection(self.context.client)
yield commands.Log(message) yield commands.Log(message)
@ -176,7 +177,7 @@ class Socks5Proxy(DestinationKnown):
if self.check_auth(test_id, test_pw): if self.check_auth(test_id, test_pw):
yield commands.SendData(self.context.client, b"\x01\x00") yield commands.SendData(self.context.client, b"\x01\x00")
else: else:
yield from self.socks_err("authentication failed", 0x01) yield from self.socks_err("authentication failed", 0x01, ver_code=0x01)
return return
self.buf = self.buf[3 + id_len + pw_len:] self.buf = self.buf[3 + id_len + pw_len:]
self.need_auth = False self.need_auth = False

View File

@ -58,49 +58,49 @@ def test_upstream_https(tctx):
response = Placeholder(bytes) response = Placeholder(bytes)
assert ( assert (
proxy1 proxy1
>> DataReceived(tctx1.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(tctx1.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.upstream)) >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.upstream))
<< OpenConnection(upstream) << OpenConnection(upstream)
>> reply(None) >> reply(None)
<< TlsStartServerHook(Placeholder()) << TlsStartServerHook(Placeholder())
>> reply_tls_start_server(alpn=b"http/1.1") >> reply_tls_start_server(alpn=b"http/1.1")
<< SendData(upstream, clienthello) << SendData(upstream, clienthello)
) )
assert upstream().address == ("example.mitmproxy.org", 8081) assert upstream().address == ("example.mitmproxy.org", 8081)
assert ( assert (
proxy2 proxy2
>> DataReceived(tctx2.client, clienthello()) >> DataReceived(tctx2.client, clienthello())
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(ClientTLSLayer) >> reply_next_layer(ClientTLSLayer)
<< TlsStartClientHook(Placeholder()) << TlsStartClientHook(Placeholder())
>> reply_tls_start_client(alpn=b"http/1.1") >> reply_tls_start_client(alpn=b"http/1.1")
<< SendData(tctx2.client, serverhello) << SendData(tctx2.client, serverhello)
) )
assert ( assert (
proxy1 proxy1
>> DataReceived(upstream, serverhello()) >> DataReceived(upstream, serverhello())
<< SendData(upstream, request) << SendData(upstream, request)
) )
assert ( assert (
proxy2 proxy2
>> DataReceived(tctx2.client, request()) >> DataReceived(tctx2.client, request())
<< SendData(tctx2.client, tls_finished) << SendData(tctx2.client, tls_finished)
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.regular)) >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.regular))
<< OpenConnection(server) << OpenConnection(server)
>> reply(None) >> reply(None)
<< SendData(server, b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n') << SendData(server, b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
>> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") >> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
<< SendData(tctx2.client, response) << SendData(tctx2.client, response)
) )
assert server().address == ("example.com", 80) assert server().address == ("example.com", 80)
assert ( assert (
proxy1 proxy1
>> DataReceived(upstream, tls_finished() + response()) >> DataReceived(upstream, tls_finished() + response())
<< SendData(tctx1.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << SendData(tctx1.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
) )
@ -117,17 +117,17 @@ def test_reverse_proxy(tctx, keep_host_header):
tctx.options.connection_strategy = "lazy" tctx.options.connection_strategy = "lazy"
tctx.options.keep_host_header = keep_host_header tctx.options.keep_host_header = keep_host_header
assert ( assert (
Playbook(modes.ReverseProxy(tctx), hooks=False) Playbook(modes.ReverseProxy(tctx), hooks=False)
>> DataReceived(tctx.client, b"GET /foo HTTP/1.1\r\n" >> DataReceived(tctx.client, b"GET /foo HTTP/1.1\r\n"
b"Host: example.com\r\n\r\n") b"Host: example.com\r\n\r\n")
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent)) >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
<< OpenConnection(server) << OpenConnection(server)
>> reply(None) >> reply(None)
<< SendData(server, b"GET /foo HTTP/1.1\r\n" << SendData(server, b"GET /foo HTTP/1.1\r\n"
b"Host: " + (b"example.com" if keep_host_header else b"localhost:8000") + b"\r\n\r\n") b"Host: " + (b"example.com" if keep_host_header else b"localhost:8000") + b"\r\n\r\n")
>> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") >> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
<< SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
) )
assert server().address == ("localhost", 8000) assert server().address == ("localhost", 8000)
@ -151,53 +151,53 @@ def test_reverse_proxy_tcp_over_tls(tctx: Context, monkeypatch, patch, connectio
playbook = Playbook(modes.ReverseProxy(tctx)) playbook = Playbook(modes.ReverseProxy(tctx))
if connection_strategy == "eager": if connection_strategy == "eager":
( (
playbook playbook
<< OpenConnection(tctx.server) << OpenConnection(tctx.server)
>> DataReceived(tctx.client, b"\x01\x02\x03") >> DataReceived(tctx.client, b"\x01\x02\x03")
>> reply(None, to=OpenConnection(tctx.server)) >> reply(None, to=OpenConnection(tctx.server))
) )
else: else:
( (
playbook playbook
>> DataReceived(tctx.client, b"\x01\x02\x03") >> DataReceived(tctx.client, b"\x01\x02\x03")
) )
if patch: if patch:
( (
playbook
<< NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(tcp.TCPLayer)
<< TcpStartHook(flow)
>> reply()
)
if connection_strategy == "lazy":
(
playbook
<< OpenConnection(tctx.server)
>> reply(None)
)
assert (
playbook
<< TcpMessageHook(flow)
>> reply()
<< SendData(tctx.server, data)
)
assert data() == b"\x01\x02\x03"
else:
if connection_strategy == "lazy":
(
playbook playbook
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(tcp.TCPLayer) >> reply_next_layer(tcp.TCPLayer)
<< TcpStartHook(flow) << TcpStartHook(flow)
>> reply() >> reply()
<< OpenConnection(tctx.server) )
>> reply(None) if connection_strategy == "lazy":
(
playbook
<< OpenConnection(tctx.server)
>> reply(None)
) )
assert ( assert (
playbook playbook
<< TlsStartServerHook(Placeholder()) << TcpMessageHook(flow)
>> reply_tls_start_server() >> reply()
<< SendData(tctx.server, data) << SendData(tctx.server, data)
)
assert data() == b"\x01\x02\x03"
else:
if connection_strategy == "lazy":
(
playbook
<< NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(tcp.TCPLayer)
<< TcpStartHook(flow)
>> reply()
<< OpenConnection(tctx.server)
>> reply(None)
)
assert (
playbook
<< TlsStartServerHook(Placeholder())
>> reply_tls_start_server()
<< SendData(tctx.server, data)
) )
assert tls.parse_client_hello(data()).sni == "localhost" assert tls.parse_client_hello(data()).sni == "localhost"
@ -212,25 +212,25 @@ def test_transparent_tcp(tctx: Context, monkeypatch, connection_strategy):
sock = object() sock = object()
playbook = Playbook(modes.TransparentProxy(tctx)) playbook = Playbook(modes.TransparentProxy(tctx))
( (
playbook playbook
<< GetSocket(tctx.client) << GetSocket(tctx.client)
>> reply(sock) >> reply(sock)
) )
if connection_strategy == "lazy": if connection_strategy == "lazy":
assert playbook assert playbook
else: else:
assert ( assert (
playbook playbook
<< OpenConnection(tctx.server) << OpenConnection(tctx.server)
>> reply(None) >> reply(None)
>> DataReceived(tctx.server, b"hello") >> DataReceived(tctx.server, b"hello")
<< NextLayerHook(Placeholder(NextLayer)) << NextLayerHook(Placeholder(NextLayer))
>> reply_next_layer(tcp.TCPLayer) >> reply_next_layer(tcp.TCPLayer)
<< TcpStartHook(flow) << TcpStartHook(flow)
>> reply() >> reply()
<< TcpMessageHook(flow) << TcpMessageHook(flow)
>> reply() >> reply()
<< SendData(tctx.client, b"hello") << SendData(tctx.client, b"hello")
) )
assert flow().messages[0].content == b"hello" assert flow().messages[0].content == b"hello"
assert not flow().messages[0].from_client assert not flow().messages[0].from_client
@ -246,10 +246,10 @@ def test_transparent_failure(tctx: Context, monkeypatch):
monkeypatch.setattr(platform, "original_addr", raise_err) monkeypatch.setattr(platform, "original_addr", raise_err)
assert ( assert (
Playbook(modes.TransparentProxy(tctx), logs=True) Playbook(modes.TransparentProxy(tctx), logs=True)
<< GetSocket(tctx.client) << GetSocket(tctx.client)
>> reply(object()) >> reply(object())
<< Log("Transparent mode failure: RuntimeError('platform-specific error')", "info") << Log("Transparent mode failure: RuntimeError('platform-specific error')", "info")
) )
@ -264,11 +264,11 @@ def test_reverse_eager_connect_failure(tctx: Context):
tctx.options.connection_strategy = "eager" tctx.options.connection_strategy = "eager"
playbook = Playbook(modes.ReverseProxy(tctx)) playbook = Playbook(modes.ReverseProxy(tctx))
assert ( assert (
playbook playbook
<< OpenConnection(tctx.server) << OpenConnection(tctx.server)
>> reply("IPoAC unstable") >> reply("IPoAC unstable")
<< CloseConnection(tctx.client) << CloseConnection(tctx.client)
>> ConnectionClosed(tctx.client) >> ConnectionClosed(tctx.client)
) )
@ -278,13 +278,13 @@ def test_transparent_eager_connect_failure(tctx: Context, monkeypatch):
monkeypatch.setattr(platform, "original_addr", lambda sock: ("address", 22)) monkeypatch.setattr(platform, "original_addr", lambda sock: ("address", 22))
assert ( assert (
Playbook(modes.TransparentProxy(tctx), logs=True) Playbook(modes.TransparentProxy(tctx), logs=True)
<< GetSocket(tctx.client) << GetSocket(tctx.client)
>> reply(object()) >> reply(object())
<< OpenConnection(tctx.server) << OpenConnection(tctx.server)
>> reply("something something") >> reply("something something")
<< CloseConnection(tctx.client) << CloseConnection(tctx.client)
>> ConnectionClosed(tctx.client) >> ConnectionClosed(tctx.client)
) )
@ -303,14 +303,14 @@ def test_socks5_success(address: str, packed: bytes, tctx: Context):
server = Placeholder(Server) server = Placeholder(Server)
nextlayer = Placeholder(NextLayer) nextlayer = Placeholder(NextLayer)
assert ( assert (
playbook playbook
>> DataReceived(tctx.client, CLIENT_HELLO) >> DataReceived(tctx.client, CLIENT_HELLO)
<< SendData(tctx.client, SERVER_HELLO) << SendData(tctx.client, SERVER_HELLO)
>> DataReceived(tctx.client, b"\x05\x01\x00" + packed + b"\x12\x34applicationdata") >> DataReceived(tctx.client, b"\x05\x01\x00" + packed + b"\x12\x34applicationdata")
<< OpenConnection(server) << OpenConnection(server)
>> reply(None) >> reply(None)
<< SendData(tctx.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00") << SendData(tctx.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
<< NextLayerHook(nextlayer) << NextLayerHook(nextlayer)
) )
assert server().address == (address, 0x1234) assert server().address == (address, 0x1234)
assert nextlayer().data_client() == b"applicationdata" assert nextlayer().data_client() == b"applicationdata"
@ -334,9 +334,6 @@ def test_socks5_trickle(tctx: Context):
(b"abcd", (b"abcd",
None, None,
"Invalid SOCKS version. Expected 0x05, got 0x61"), "Invalid SOCKS version. Expected 0x05, got 0x61"),
(b"\x05\x01\x02",
b"\x05\xFF\x00\x01\x00\x00\x00\x00\x00\x00",
"mitmproxy only supports SOCKS without authentication"),
(CLIENT_HELLO + b"\x05\x02\x00\x01\x7f\x00\x00\x01\x12\x34", (CLIENT_HELLO + b"\x05\x02\x00\x01\x7f\x00\x00\x01\x12\x34",
SERVER_HELLO + b"\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00", SERVER_HELLO + b"\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00",
r"Unsupported SOCKS5 request: b'\x05\x02\x00\x01\x7f\x00\x00\x01\x124'"), r"Unsupported SOCKS5 request: b'\x05\x02\x00\x01\x7f\x00\x00\x01\x124'"),
@ -346,8 +343,8 @@ def test_socks5_trickle(tctx: Context):
]) ])
def test_socks5_err(data: bytes, err: bytes, msg: str, tctx: Context): def test_socks5_err(data: bytes, err: bytes, msg: str, tctx: Context):
playbook = ( playbook = (
Playbook(modes.Socks5Proxy(tctx), logs=True) Playbook(modes.Socks5Proxy(tctx), logs=True)
>> DataReceived(tctx.client, data) >> DataReceived(tctx.client, data)
) )
if err: if err:
playbook << SendData(tctx.client, err) playbook << SendData(tctx.client, err)
@ -356,26 +353,93 @@ def test_socks5_err(data: bytes, err: bytes, msg: str, tctx: Context):
assert playbook assert playbook
@pytest.mark.parametrize("client_greeting,server_choice,client_auth,server_resp,address,packed", [
(b"\x05\x01\x02",
b"\x05\x02",
b"\x01\x04" + b"user" + b"\x08" + b"password",
b"\x01\x00",
"127.0.0.1",
b"\x01\x7f\x00\x00\x01"),
(b"\x05\x02\x01\x02",
b"\x05\x02",
b"\x01\x04" + b"user" + b"\x08" + b"password",
b"\x01\x00",
"127.0.0.1",
b"\x01\x7f\x00\x00\x01"),
])
def test_socks5_auth_success(client_greeting: bytes, server_choice: bytes, client_auth: bytes, server_resp: bytes,
address: bytes, packed: bytes, tctx: Context):
tctx.options.socks5auth = "user:password"
server = Placeholder(Server)
nextlayer = Placeholder(NextLayer)
playbook = (
Playbook(modes.Socks5Proxy(tctx), logs=True)
>> DataReceived(tctx.client, client_greeting)
<< SendData(tctx.client, server_choice)
>> DataReceived(tctx.client, client_auth)
<< SendData(tctx.client, server_resp)
>> DataReceived(tctx.client, b"\x05\x01\x00" + packed + b"\x12\x34applicationdata")
<< OpenConnection(server)
>> reply(None)
<< SendData(tctx.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
<< NextLayerHook(nextlayer)
)
assert playbook
assert server().address == (address, 0x1234)
assert nextlayer().data_client() == b"applicationdata"
@pytest.mark.parametrize("client_greeting,server_choice,client_auth,err,msg", [
(b"\x05\x01\x00",
None,
None,
b"\x05\xFF\x00\x01\x00\x00\x00\x00\x00\x00",
"mitmproxy only support user password authentication"),
(b"\x05\x02\x00\x02",
b"\x05\x02",
b"\x01\x04" + b"user" + b"\x07" + b"errcode",
b"\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00",
"authentication failed"),
])
def test_socks5_auth_fail(client_greeting: bytes, server_choice: bytes, client_auth: bytes, err: bytes, msg: str,
tctx: Context):
tctx.options.socks5auth = "user:password"
playbook = (
Playbook(modes.Socks5Proxy(tctx), logs=True)
>> DataReceived(tctx.client, client_greeting)
)
if server_choice is None:
playbook << SendData(tctx.client, err)
else:
playbook << SendData(tctx.client, server_choice)
playbook >> DataReceived(tctx.client, client_auth)
playbook << SendData(tctx.client, err)
playbook << CloseConnection(tctx.client)
playbook << Log(msg)
assert playbook
def test_socks5_eager_err(tctx: Context): def test_socks5_eager_err(tctx: Context):
tctx.options.connection_strategy = "eager" tctx.options.connection_strategy = "eager"
server = Placeholder(Server) server = Placeholder(Server)
assert ( assert (
Playbook(modes.Socks5Proxy(tctx)) Playbook(modes.Socks5Proxy(tctx))
>> DataReceived(tctx.client, CLIENT_HELLO) >> DataReceived(tctx.client, CLIENT_HELLO)
<< SendData(tctx.client, SERVER_HELLO) << SendData(tctx.client, SERVER_HELLO)
>> DataReceived(tctx.client, b"\x05\x01\x00\x01\x7f\x00\x00\x01\x12\x34") >> DataReceived(tctx.client, b"\x05\x01\x00\x01\x7f\x00\x00\x01\x12\x34")
<< OpenConnection(server) << OpenConnection(server)
>> reply("out of socks") >> reply("out of socks")
<< SendData(tctx.client, b"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00") << SendData(tctx.client, b"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00")
<< CloseConnection(tctx.client) << CloseConnection(tctx.client)
) )
def test_socks5_premature_close(tctx: Context): def test_socks5_premature_close(tctx: Context):
assert ( assert (
Playbook(modes.Socks5Proxy(tctx), logs=True) Playbook(modes.Socks5Proxy(tctx), logs=True)
>> DataReceived(tctx.client, b"\x05") >> DataReceived(tctx.client, b"\x05")
>> ConnectionClosed(tctx.client) >> ConnectionClosed(tctx.client)
<< Log(r"Client closed connection before completing SOCKS5 handshake: b'\x05'") << Log(r"Client closed connection before completing SOCKS5 handshake: b'\x05'")
<< CloseConnection(tctx.client) << CloseConnection(tctx.client)
) )