diff --git a/scripts/base/protocols/socks/main.bro b/scripts/base/protocols/socks/main.bro index 713161d442..f60c3ce41c 100644 --- a/scripts/base/protocols/socks/main.bro +++ b/scripts/base/protocols/socks/main.bro @@ -16,8 +16,10 @@ export { id: conn_id &log; ## Protocol version of SOCKS. version: count &log; - ## Username for the proxy if extracted from the network. + ## Username used to request a login to the proxy. user: string &log &optional; + ## Password used to request a login to the proxy. + password: string &log &optional; ## Server status for the attempt at using the proxy. status: string &log &optional; ## Client requested SOCKS address. Could be an address, a name @@ -91,3 +93,21 @@ event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Addres if ( "SOCKS" in c$service ) Log::write(SOCKS::LOG, c$socks); } + +event socks_login_userpass(c: connection, user: string, password: string) &priority=5 + { + # Authentication only possible with the version 5. + set_session(c, 5); + + c$socks$user = user; + c$socks$password = password; + } + +event socks_login_reply(c: connection, code: count) &priority=5 + { + # Authentication only possible with the version 5. + set_session(c, 5); + + c$socks$status = v5_status[code]; + } + diff --git a/src/analyzer/protocol/socks/SOCKS.cc b/src/analyzer/protocol/socks/SOCKS.cc index e678528f35..ec1e85653b 100644 --- a/src/analyzer/protocol/socks/SOCKS.cc +++ b/src/analyzer/protocol/socks/SOCKS.cc @@ -57,8 +57,7 @@ void SOCKS_Analyzer::DeliverStream(int len, const u_char* data, bool orig) // with the rest of the conneciton. // // Note that we assume that no payload data arrives before both endpoints - // are done with there part of the SOCKS protocol. - + // are done with their part of the SOCKS protocol. if ( ! pia ) { pia = new pia::PIA_TCP(Conn()); diff --git a/src/analyzer/protocol/socks/events.bif b/src/analyzer/protocol/socks/events.bif index 4f1f8ad1cd..ece69140a1 100644 --- a/src/analyzer/protocol/socks/events.bif +++ b/src/analyzer/protocol/socks/events.bif @@ -27,3 +27,19 @@ event socks_request%(c: connection, version: count, request_type: count, sa: SOC ## p: The destination port for the proxied traffic. event socks_reply%(c: connection, version: count, reply: count, sa: SOCKS::Address, p: port%); +## Generated when a SOCKS client performs username and password based login. +## +## c: The parent connection of the proxy. +## +## user: The given username. +## +## password: The given password. +event socks_login_userpass%(c: connection, user: string, password: string%); + +## Generated when a SOCKS server replies to a login attempt. +## +## c: The parent connection of the proxy. +## +## code: The response code for the attempted login. +event socks_login_reply%(c: connection, code: count%); + diff --git a/src/analyzer/protocol/socks/socks-analyzer.pac b/src/analyzer/protocol/socks/socks-analyzer.pac index db98b3f4b3..7d634e2f46 100644 --- a/src/analyzer/protocol/socks/socks-analyzer.pac +++ b/src/analyzer/protocol/socks/socks-analyzer.pac @@ -148,6 +148,31 @@ refine connection SOCKS_Conn += { return true; %} + function socks5_auth_request_userpass(request: SOCKS5_Auth_Request_UserPass): bool + %{ + StringVal* user = new StringVal(${request.username}.length(), (const char*) ${request.username}.begin()); + StringVal* pass = new StringVal(${request.password}.length(), (const char*) ${request.password}.begin()); + + BifEvent::generate_socks_login_userpass(bro_analyzer(), + bro_analyzer()->Conn(), + user, pass); + return true; + %} + + function socks5_unsupported_authentication(auth_method: uint8): bool + %{ + reporter->Weird(bro_analyzer()->Conn(), fmt("socks5_unsupported_authentication_%d", auth_method)); + return true; + %} + + function socks5_auth_reply(reply: SOCKS5_Auth_Reply): bool + %{ + BifEvent::generate_socks_login_reply(bro_analyzer(), + bro_analyzer()->Conn(), + ${reply.code}); + return true; + %} + function version_error(version: uint8): bool %{ bro_analyzer()->ProtocolViolation(fmt("unsupported/unknown SOCKS version %d", version)); @@ -176,3 +201,18 @@ refine typeattr SOCKS5_Request += &let { refine typeattr SOCKS5_Reply += &let { proc: bool = $context.connection.socks5_reply(this); }; + +refine typeattr SOCKS5_Auth_Negotiation_Reply += &let { +}; + +refine typeattr SOCKS5_Auth_Request_UserPass += &let { + proc: bool = $context.connection.socks5_auth_request_userpass(this); +}; + +refine typeattr SOCKS5_Auth_Reply += &let { + proc: bool = $context.connection.socks5_auth_reply(this); +}; + +refine typeattr SOCKS5_Unsupported_Authentication += &let { + proc: bool = $context.connection.socks5_unsupported_authentication($context.connection.v5_auth_method()); +}; diff --git a/src/analyzer/protocol/socks/socks-protocol.pac b/src/analyzer/protocol/socks/socks-protocol.pac index 05ca4bc861..4e48ea0672 100644 --- a/src/analyzer/protocol/socks/socks-protocol.pac +++ b/src/analyzer/protocol/socks/socks-protocol.pac @@ -2,9 +2,10 @@ type SOCKS_Version(is_orig: bool) = record { version: uint8; msg: case version of { - 4 -> socks4_msg: SOCKS4_Message(is_orig); - 5 -> socks5_msg: SOCKS5_Message(is_orig); - default -> socks_msg_fail: SOCKS_Version_Error(version); + 1 -> socks5_auth_msg: SOCKS5_Auth_Message(is_orig); + 4 -> socks4_msg: SOCKS4_Message(is_orig); + 5 -> socks5_msg: SOCKS5_Message(is_orig); + default -> socks_msg_fail: SOCKS_Version_Error(version); }; }; @@ -14,10 +15,11 @@ type SOCKS_Version_Error(version: uint8) = record { # SOCKS5 Implementation type SOCKS5_Message(is_orig: bool) = case $context.connection.v5_past_authentication() of { - true -> msg: SOCKS5_Real_Message(is_orig); false -> auth: SOCKS5_Auth_Negotiation(is_orig); + true -> msg: SOCKS5_Real_Message(is_orig); }; + type SOCKS5_Auth_Negotiation(is_orig: bool) = case is_orig of { true -> req: SOCKS5_Auth_Negotiation_Request; false -> rep: SOCKS5_Auth_Negotiation_Reply; @@ -32,6 +34,32 @@ type SOCKS5_Auth_Negotiation_Reply = record { selected_auth_method: uint8; } &let { past_auth = $context.connection.set_v5_past_authentication(); + set_auth = $context.connection.set_v5_auth_method(selected_auth_method); +}; + +type SOCKS5_Auth_Message(is_orig: bool) = case is_orig of { + true -> req: SOCKS5_Auth_Request; + false -> rep: SOCKS5_Auth_Reply; +}; + +type SOCKS5_Auth_Request = case $context.connection.v5_auth_method() of { + 0x02 -> userpass : SOCKS5_Auth_Request_UserPass; + default -> unsupported : SOCKS5_Unsupported_Authentication; +}; + +type SOCKS5_Unsupported_Authentication = record { + crap: bytestring &restofdata; +}; + +type SOCKS5_Auth_Request_UserPass = record { + ulen : uint8; + username : bytestring &length=ulen; + plen : uint8; + password : bytestring &length=plen; +}; + +type SOCKS5_Auth_Reply = record { + code : uint8; }; type SOCKS5_Real_Message(is_orig: bool) = case is_orig of { @@ -55,10 +83,10 @@ type SOCKS5_Address = record { } &byteorder = bigendian; type SOCKS5_Request = record { - command: uint8; - reserved: uint8; - remote_name: SOCKS5_Address; - port: uint16; + command : uint8; + reserved : uint8; + remote_name : SOCKS5_Address; + port : uint16; } &byteorder = bigendian; type SOCKS5_Reply = record { @@ -99,10 +127,12 @@ type SOCKS4_Reply = record { refine connection SOCKS_Conn += { %member{ bool v5_authenticated_; + uint8 selected_auth_method_; %} %init{ v5_authenticated_ = false; + selected_auth_method_ = 255; %} function v5_past_authentication(): bool @@ -115,5 +145,16 @@ refine connection SOCKS_Conn += { v5_authenticated_ = true; return true; %} + + function set_v5_auth_method(method: uint8): bool + %{ + selected_auth_method_ = method; + return true; + %} + + function v5_auth_method(): uint8 + %{ + return selected_auth_method_; + %} }; diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log new file mode 100644 index 0000000000..cc5fa80191 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/socks.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path socks +#open 2015-02-05-16-13-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1368517392.724989 CXWv6p3arKYeMETxOg 192.168.0.2 55951 192.168.0.1 1080 5 bob alice succeeded 192.168.0.2 - 22 192.168.0.1 - 55951 +#close 2015-02-05-16-13-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log new file mode 100644 index 0000000000..d53238df93 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.socks.socks-auth/tunnel.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path tunnel +#open 2015-02-05-16-13-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action +#types time string addr port addr port enum enum +1368517392.728523 - 192.168.0.2 0 192.168.0.1 1080 Tunnel::SOCKS Tunnel::DISCOVER +#close 2015-02-05-16-13-12 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log index 148e4adf02..f69df31b66 100644 --- a/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log +++ b/testing/btest/Baseline/scripts.base.protocols.socks.trace1/socks.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path socks -#open 2013-08-26-19-04-20 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user status request.host request.name request_p bound.host bound.name bound_p -#types time string addr port addr port count string string addr string port addr string port -1340213015.276495 CjhGID4nQcgTWjvg4c 10.0.0.55 53994 60.190.189.214 8124 5 - succeeded - www.osnews.com 80 192.168.0.31 - 2688 -#close 2013-08-26-19-04-20 +#open 2015-02-05-17-39-14 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1340213015.276495 CjhGID4nQcgTWjvg4c 10.0.0.55 53994 60.190.189.214 8124 5 - - succeeded - www.osnews.com 80 192.168.0.31 - 2688 +#close 2015-02-05-17-39-14 diff --git a/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log b/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log index d706a11da3..de7b26f875 100644 --- a/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log +++ b/testing/btest/Baseline/scripts.base.protocols.socks.trace2/socks.log @@ -3,8 +3,8 @@ #empty_field (empty) #unset_field - #path socks -#open 2013-08-26-19-04-20 -#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user status request.host request.name request_p bound.host bound.name bound_p -#types time string addr port addr port count string string addr string port addr string port -1340113261.914619 CXWv6p3arKYeMETxOg 10.0.0.50 59580 85.194.84.197 1080 5 - succeeded - www.google.com 443 0.0.0.0 - 443 -#close 2013-08-26-19-04-20 +#open 2015-02-05-17-39-29 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version user password status request.host request.name request_p bound.host bound.name bound_p +#types time string addr port addr port count string string string addr string port addr string port +1340113261.914619 CXWv6p3arKYeMETxOg 10.0.0.50 59580 85.194.84.197 1080 5 - - succeeded - www.google.com 443 0.0.0.0 - 443 +#close 2015-02-05-17-39-29 diff --git a/testing/btest/Traces/socks-auth.pcap b/testing/btest/Traces/socks-auth.pcap new file mode 100644 index 0000000000..1570e22947 Binary files /dev/null and b/testing/btest/Traces/socks-auth.pcap differ diff --git a/testing/btest/scripts/base/protocols/socks/socks-auth.bro b/testing/btest/scripts/base/protocols/socks/socks-auth.bro new file mode 100644 index 0000000000..2123dc1d45 --- /dev/null +++ b/testing/btest/scripts/base/protocols/socks/socks-auth.bro @@ -0,0 +1,5 @@ +# @TEST-EXEC: bro -r $TRACES/socks-auth.pcap %INPUT +# @TEST-EXEC: btest-diff socks.log +# @TEST-EXEC: btest-diff tunnel.log + +@load base/protocols/socks