@@ -3,10 +3,11 @@ use super::helpers::{
3
3
} ;
4
4
use super :: { config:: TEST_CONFIG , Error , TestContext } ;
5
5
use crate :: network_monitor:: { start_packet_monitor, MonitorOptions } ;
6
- use crate :: tests:: helpers:: login_with_retries ;
6
+ use crate :: tests:: helpers:: ConnChecker ;
7
7
8
8
use mullvad_management_interface:: MullvadProxyClient ;
9
9
use mullvad_relay_selector:: query:: builder:: RelayQueryBuilder ;
10
+ use mullvad_types:: states:: TunnelState ;
10
11
use mullvad_types:: {
11
12
constraints:: Constraint ,
12
13
relay_constraints:: {
@@ -802,3 +803,72 @@ pub async fn test_establish_tunnel_without_api(
802
803
// Profit
803
804
Ok ( ( ) )
804
805
}
806
+
807
+ /// Fail to leak traffic to verify that mitigation for MUL-02-002-WP2
808
+ /// ("Firewall allows deanonymization by eavesdropper") works.
809
+ ///
810
+ /// # Vulnerability
811
+ /// 1. Connect to a relay on port 443. Record this relay's IP address (the new gateway of the client)
812
+ /// 2. Start listening for unencrypted traffic on the outbound network interface
813
+ /// (Choose some human-readable, identifiable payload to look for in the outgoing TCP packets)
814
+ /// 3. Start a rogue program which performs a GET request* containing the payload defined in step 2
815
+ /// 4. The network snooper started in step 2 should now be able to observe the network request
816
+ /// containing the identifiable payload being sent unencrypted over the wire
817
+ ///
818
+ /// * or something similiar, as long as it fetches some remote resources
819
+ #[ test_function]
820
+ pub async fn test_mul_02_002 (
821
+ _: TestContext ,
822
+ rpc : ServiceClient ,
823
+ mut mullvad_client : MullvadProxyClient ,
824
+ ) -> Result < ( ) , anyhow:: Error > {
825
+ // Step 0 - Disconnect from any active tunnel connection
826
+ helpers:: disconnect_and_wait ( & mut mullvad_client) . await ?;
827
+ // Step 1 - Choose a relay
828
+ let relay_constraints = RelayQueryBuilder :: new ( )
829
+ . openvpn ( )
830
+ . transport_protocol ( TransportProtocol :: Tcp )
831
+ . port ( 443 )
832
+ . into_constraint ( ) ;
833
+ // Step 1.5 - Connect to the relay
834
+ set_relay_settings (
835
+ & mut mullvad_client,
836
+ RelaySettings :: Normal ( relay_constraints) ,
837
+ )
838
+ . await ?;
839
+
840
+ let tunnel_state = helpers:: connect_and_wait ( & mut mullvad_client) . await ?;
841
+ let TunnelState :: Connected { endpoint, .. } = tunnel_state else {
842
+ panic ! ( "Expected tunnel state to be `Connected` - instead it was {tunnel_state:?}" ) ;
843
+ } ;
844
+ let gateway = endpoint. endpoint . address ;
845
+ // Step 2 - Choose a payload
846
+ // FIXME: This needs to be kept in sync with the magic payload string defined in `connection_cheker::net`.
847
+ // The payload for `ConnChecherk` could also be made configurable.
848
+ let unique_identifier = b"Hello there!" ;
849
+ // Step 2.5 - Start a network monitor snooping the outbound network interface for payload
850
+ let monitor = start_packet_monitor (
851
+ move |packet| {
852
+ packet. destination . ip ( ) == gateway. ip ( )
853
+ && packet. protocol == IpNextHeaderProtocols :: Tcp
854
+ && packet
855
+ . payload
856
+ . windows ( unique_identifier. len ( ) )
857
+ . any ( |window| window == unique_identifier)
858
+ } ,
859
+ MonitorOptions :: default ( ) ,
860
+ )
861
+ . await ;
862
+
863
+ // Step 3 - Start the rogue program using payload + relay info
864
+ let mut checker = ConnChecker :: new ( rpc. clone ( ) , mullvad_client. clone ( ) , gateway) ;
865
+ checker. spawn ( ) . await ?. check_connection ( ) . await ?;
866
+ // Step 4 - Assert that no outgoing traffic contains the payload in plain text.
867
+ let monitor_result = monitor. into_result ( ) . await . unwrap ( ) ;
868
+ assert ! (
869
+ monitor_result. packets. is_empty( ) ,
870
+ "Observed rogue packets! The tunnel seems to be leaking traffic"
871
+ ) ;
872
+ // Step 5 - Profit
873
+ Ok ( ( ) )
874
+ }
0 commit comments