def test_should_fail_fast(base_url: str, full_scan_mock: MockSpec, capsys: CaptureFixture): common_args = { 'base_url': base_url, 'session_provider': lambda: full_scan_mock.session() } class FailFastException(Exception): pass class ErrorScanner(Scanner[str]): def __init__(self, **kwargs): super().__init__(result_type=WrapperType(str), **kwargs) def perform(self, **kwargs) -> (str, VulnFlag): raise FailFastException() ms = MasterScanner(scanners=[ RealmScanner(**common_args, realms=['master', 'other']), WellKnownScanner(**common_args), ErrorScanner(**common_args), ClientScanner(**common_args, clients=['client1', 'client2']), LoginScanner(**common_args), SecurityConsoleScanner(**common_args), OpenRedirectScanner(**common_args), FormPostXssScanner(**common_args), NoneSignScanner(**common_args) ], initial_values={ WrapperTypes.USERNAME_TYPE: {Username('admin')}, WrapperTypes.PASSWORD_TYPE: {Password('admin')} }, fail_fast=True) with pytest.raises(FailFastException) as e: status = ms.start() assert status.has_error is True # TODO when vf was fixed # assert status.has_vulns is True captured = capsys.readouterr() print(captured.out) assert captured.err == '' assert 'Find realm master' in captured.out assert 'Public key for realm master : ' in captured.out assert "[+] WellKnownScanner - Find a well known for realm master" in captured.out
def test_perform(base_url: str, master_realm: Realm, other_realm: Realm, capsys: CaptureFixture, well_known_list: List[WellKnown]): def assert0(**kwargs) -> bool: print(kwargs) return kwargs['params']['client_id'] in ['client1', 'client2'] client_scanner = ClientScanner(clients=['client1', 'client2'], base_url=base_url, session_provider=lambda: MockSpec( get={ 'http://localhost:8080/auth/realms/master/client1': RequestSpec(response=MockResponse(status_code=200)), 'http://localhost:8080/auth/realms/master/client2': RequestSpec(response=MockResponse(status_code=404)), 'http://localhost:8080/auth/realms/master/protocol/openid-connect/auth': RequestSpec(response=MockResponse(302), assertion=assert0), 'http://localhost:8080/realms/master/clients-registrations/default/client1': RequestSpec(response=MockResponse(200, response={'data': 'coucou'})), 'http://localhost:8080/realms/master/clients-registrations/default/client2': RequestSpec(response=MockResponse(200, response={'data': 'coucou'})) } ).session()) result, vf = client_scanner.perform(realm=master_realm, well_known=well_known_list[0]) capture = capsys.readouterr() print(capture.out) print(capture.err) assert result == {Client('client1', 'http://localhost:8080/auth/realms/master/client1', client_registration=ClientConfig(name='client1', url='http://localhost:8080/realms/master/clients-registrations/default/client1', json={'data': 'coucou'} ) ), Client('client2', None, client_registration=ClientConfig(name='client2', url='http://localhost:8080/realms/master/clients-registrations/default/client2', json={'data': 'coucou'} ) )} assert not vf.has_vuln assert 'Find a client for realm master: client1' in capture.out
def test_client_registration_scanner_should_not_register(master_realm: Realm, well_known_master: WellKnown, credential_set: Set[Credential], well_known_json_master: dict): class TestRandomStr(RandomStr): def random_str(self) -> str: return '456789' class TestClientRegistrationScanner(ClientRegistrationScanner, TestRandomStr): pass session_provider = lambda: MockSpec( get={ 'http://localhost:8080/auth/realms/master/.well-known/openid-configuration': RequestSpec( MockResponse(status_code=200, response=well_known_json_master) ), }, post={ 'http://localhost:8080/auth/realms/master/clients-registrations/openid-connect': RequestSpec(response=MockResponse(status_code=403), assertion=check_request, assertion_value={'json': { "application_type": "web", "redirect_uris": [ "http://callback/callback"], "client_name": "keycloak-client-456789", "logo_uri": "http://callback/logo.png", "jwks_uri": "http://callback/public_keys.jwks" }}), 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token': RequestSpec( response=MockResponse(status_code=403)) }, ).session() mediator = Mediator([ TestClientRegistrationScanner(['http://callback'], base_url='http://localhost:8080', session_provider=session_provider) ]) mediator.send(WrapperTypes.REALM_TYPE, {master_realm}) mediator.send(WrapperTypes.WELL_KNOWN_TYPE, {well_known_master}) mediator.send(WrapperTypes.CREDENTIAL_TYPE, credential_set) assert mediator.scan_results.get(WrapperTypes.CLIENT_REGISTRATION) == set()
def test_client_registration_scanner_should_register(master_realm: Realm, well_known_master: WellKnown, credential_set: Set[Credential], well_known_json_master: dict): class TestRandomStr(RandomStr): def random_str(self) -> str: return '456789' class TestClientRegistrationScanner(ClientRegistrationScanner, TestRandomStr): pass response = { "redirect_uris": ["http://localhost:8080/callback"], "token_endpoint_auth_method": "client_secret_basic", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code", "none"], "client_id": "539ce782-5d15-4256-a5fa-1a46609d056b", "client_secret": "c94f5fc0-0a04-4e2f-aec6-b1f5edad1d44", "client_name": "keycloak-client-456789", "scope": "address phone offline_access microprofile-jwt", "jwks_uri": "http://localhost:8080/public_keys.jwks", "subject_type": "pairwise", "request_uris": ["http://localhost:8080/rf.txt"], "tls_client_certificate_bound_access_tokens": False, "client_id_issued_at": 1622306364, "client_secret_expires_at": 0, "registration_client_uri": "http://localhost:8080/auth/realms/master/clients-registrations/openid-connect/539ce782-5d15-4256-a5fa-1a46609d056b", "backchannel_logout_session_required": False } session_provider = lambda: MockSpec( get={ 'http://localhost:8080/auth/realms/master/.well-known/openid-configuration': RequestSpec( MockResponse(status_code=200, response=well_known_json_master) ), }, post={ 'http://localhost:8080/auth/realms/master/clients-registrations/openid-connect': RequestSpec(response=MockResponse(status_code=201, response=response), assertion=check_request, assertion_value={'json': { "application_type": "web", "redirect_uris": [ "http://callback/callback"], "client_name": "keycloak-client-456789", "logo_uri": "http://callback/logo.png", "jwks_uri": "http://callback/public_keys.jwks" }})}).session() mediator = Mediator([ TestClientRegistrationScanner(['http://callback'], base_url='http://localhost:8080', session_provider=session_provider) ]) mediator.send(WrapperTypes.REALM_TYPE, {master_realm}) mediator.send(WrapperTypes.WELL_KNOWN_TYPE, {well_known_master}) mediator.send(WrapperTypes.CREDENTIAL_TYPE, credential_set) assert mediator.scan_results.get(WrapperTypes.CLIENT_REGISTRATION) == {ClientRegistration( 'http://callback', name='keycloak-client-456789', url='http://localhost:8080/auth/realms/master/clients-registrations/openid-connect/539ce782-5d15-4256-a5fa-1a46609d056b', json=response )}
def full_scan_mock_session(full_scan_mock: MockSpec) -> Session: return full_scan_mock.session()
def full_scan_mock(master_realm_json, other_realm_json, well_known_json_master: dict, well_known_json_other: dict, login_html_page: str) -> MockSpec: token_response = { 'access_token': 'eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI3ODM4MGM2ZS1iODhmLTQ5NDQtOGRkZS03NTQyMDNkMjFhODEifQ.eyJleHAiOjE2MjE2NzU5NzIsImlhdCI6MTYyMTYzOTk3MiwianRpIjoiMGU2NDcxOTItMzU5ZS00NmU4LWFkYWQtNTQzNmQyNjMyZjA1IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsInN1YiI6IjJjMTZhY2Y1LWMwOTYtNDg5ZC1iYjFjLTU4ZTc0ZTJiZjAzMiIsInR5cCI6IlNlcmlhbGl6ZWQtSUQiLCJzZXNzaW9uX3N0YXRlIjoiZWY3ZjNjZmItMDAzZS00YzViLWEzMWQtYmI0OGFhZjAzNzk3Iiwic3RhdGVfY2hlY2tlciI6ImtKNy05MURtNVEwVXktT1JfVlJnT1d5eF91Wkh3M0ZfczktTVdlUjZRTlEifQ.6yZvyGKEH0NXmLY8nKRQMLsMQYPXq5dYCsIF3LRiOxI', 'refresh_token': 'eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI3ODM4MGM2ZS1iODhmLTQ5NDQtOGRkZS03NTQyMDNkMjFhODEifQ.eyJleHAiOjE2MjE2NzU5NzIsImlhdCI6MTYyMTYzOTk3MiwianRpIjoiMGU2NDcxOTItMzU5ZS00NmU4LWFkYWQtNTQzNmQyNjMyZjA1IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsInN1YiI6IjJjMTZhY2Y1LWMwOTYtNDg5ZC1iYjFjLTU4ZTc0ZTJiZjAzMiIsInR5cCI6IlNlcmlhbGl6ZWQtSUQiLCJzZXNzaW9uX3N0YXRlIjoiZWY3ZjNjZmItMDAzZS00YzViLWEzMWQtYmI0OGFhZjAzNzk3Iiwic3RhdGVfY2hlY2tlciI6ImtKNy05MURtNVEwVXktT1JfVlJnT1d5eF91Wkh3M0ZfczktTVdlUjZRTlEifQ.6yZvyGKEH0NXmLY8nKRQMLsMQYPXq5dYCsIF3LRiOxI' } return MockSpec(get={ 'http://localhost:8080/auth/realms/master/.well-known/openid-configuration': RequestSpec( MockResponse(status_code=200, response=well_known_json_master) ), 'http://localhost:8080/auth/realms/master': RequestSpec( MockResponse(status_code=200, response=master_realm_json)), 'http://localhost:8080/auth/realms/other': RequestSpec( MockResponse(status_code=200, response=other_realm_json)), 'http://localhost:8080/auth/realms/other/.well-known/openid-configuration': RequestSpec( MockResponse(status_code=200, response=well_known_json_other)), 'http://localhost:8080/auth/realms/master/client1': RequestSpec( MockResponse(status_code=200, response='coucou')), 'http://localhost:8080/auth/realms/master/client2': RequestSpec( MockResponse(status_code=200, response='coucou')), 'http://localhost:8080/auth/realms/other/client1': RequestSpec( MockResponse(status_code=200, response='coucou')), 'http://localhost:8080/auth/realms/other/client2': RequestSpec( MockResponse(status_code=200, response='coucou')), 'http://localhost:8080/auth/realms/master/clients-registrations/default/security-admin-console': RequestSpec(MockResponse(status_code=401, response={"error": "invalid_token", "error_description": "Not authorized to view client. Not valid token or client credentials provided."})), 'http://localhost:8080/auth/realms/other/clients-registrations/default/security-admin-console': RequestSpec( MockResponse( status_code=401, response={"error": "invalid_token", "error_description": "Not authorized to view client. Not valid token or client credentials provided."})), 'http://localhost:8080/auth': RequestSpec(MockResponse(status_code=400)), 'http://localhost:8080/auth/realms/master/protocol/openid-connect/auth': RequestSpec(MockResponse(200, response=login_html_page)), 'http://localhost:8080/auth/realms/other/protocol/openid-connect/auth': RequestSpec(MockResponse(200, response=login_html_page)), 'http://localhost:8080/auth/realms/master/protocol/openid-connect/auth?client_id=account-console&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Frealms%2Fmaster%2Faccount%2F%23%2F&state=310f298c-f3d8-4c42-8ebc-44484febf84c&response_mode=fragment&response_type=code&scope=openid&nonce=a6be5274-15e4-4ffe-9905-ffb038b20a8e&code_challenge=Nd1svU3YNT0r6eWHkSmNeX_cxgUPQUVzPfZFXRWaJmY&code_challenge_method=S256': RequestSpec(MockResponse( 200, login_html_page)), 'http://localhost:8080/realms/master/clients-registrations/default/client1': RequestSpec( MockResponse(200, response={"id": "899e2dc1-5fc0-4eaf-bedb-f81a3f9e9313", "clientId": "admin-cli", "name": "${client_admin-cli}", "surrogateAuthRequired": False, "enabled": True, "alwaysDisplayInConsole": False, "clientAuthenticatorType": "client-secret", "redirectUris": [], "webOrigins": [], "notBefore": 0, "bearerOnly": False, "consentRequired": False, "standardFlowEnabled": False, "implicitFlowEnabled": False, "directAccessGrantsEnabled": False, "serviceAccountsEnabled": False, "publicClient": False, "frontchannelLogout": False, "protocol": "openid-connect", "attributes": {}, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": False, "nodeReRegistrationTimeout": 0, "defaultClientScopes": ["web-origins", "roles", "profile", "email"], "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]}) ), 'http://localhost:8080/realms/other/clients-registrations/default/client1': RequestSpec( MockResponse(200, response={"id": "899e2dc1-5fc0-4eaf-bedb-f81a3f9e9313", "clientId": "admin-cli", "name": "${client_admin-cli}", "surrogateAuthRequired": False, "enabled": True, "alwaysDisplayInConsole": False, "clientAuthenticatorType": "client-secret", "redirectUris": [], "webOrigins": [], "notBefore": 0, "bearerOnly": False, "consentRequired": False, "standardFlowEnabled": False, "implicitFlowEnabled": False, "directAccessGrantsEnabled": False, "serviceAccountsEnabled": False, "publicClient": False, "frontchannelLogout": False, "protocol": "openid-connect", "attributes": {}, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": False, "nodeReRegistrationTimeout": 0, "defaultClientScopes": ["web-origins", "roles", "profile", "email"], "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]}) ), 'http://localhost:8080/realms/master/clients-registrations/default/client2': RequestSpec( MockResponse(400) ), 'http://localhost:8080/realms/other/clients-registrations/default/client2': RequestSpec( MockResponse(400) ), }, post={ 'http://localhost:8080/master/token': RequestSpec(MockResponse(status_code=200, response=token_response)), 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token': RequestSpec( MockResponse(status_code=200, response=token_response)), 'http://localhost:8080/other/token': RequestSpec(MockResponse(status_code=200, response=token_response)), 'http://localhost:8080/auth/realms/other/protocol/openid-connect/token': RequestSpec( MockResponse(status_code=200, response=token_response)), 'http://localhost:8080/auth/realms/master/login-actions/authenticate?session_code' '=bR4rBd0QNGsd_kGuqiyLEuYuY6FK3Lx9HCYJEltUQBk&execution=de13838a-ee3d-404e-b16d-b0d7aa320844&client_id' '=account-console&tab_id=GXMjAPR3DsQ': RequestSpec(MockResponse( 302, response=None, headers={'Location': '<openid location>'})), 'http://localhost:8080/auth/realms/master/clients-registrations/openid-connect': RequestSpec(response=MockResponse(status_code=201, response={ "redirect_uris": ["http://localhost:8080/callback"], "token_endpoint_auth_method": "client_secret_basic", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code", "none"], "client_id": "539ce782-5d15-4256-a5fa-1a46609d056b", "client_secret": "c94f5fc0-0a04-4e2f-aec6-b1f5edad1d44", "client_name": "keycloak-client-456789", "scope": "address phone offline_access microprofile-jwt", "jwks_uri": "http://localhost:8080/public_keys.jwks", "subject_type": "pairwise", "request_uris": ["http://localhost:8080/rf.txt"], "tls_client_certificate_bound_access_tokens": False, "client_id_issued_at": 1622306364, "client_secret_expires_at": 0, "registration_client_uri": "http://localhost:8080/auth/realms/master/clients-registrations/openid-connect/539ce782-5d15-4256-a5fa-1a46609d056b", "backchannel_logout_session_required": False })), 'http://localhost:8080/auth/realms/other/clients-registrations/openid-connect': RequestSpec(response=MockResponse(status_code=201, response={ "redirect_uris": ["http://localhost:8080/callback"], "token_endpoint_auth_method": "client_secret_basic", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code", "none"], "client_id": "539ce782-5d15-4256-a5fa-1a46609d056b", "client_secret": "c94f5fc0-0a04-4e2f-aec6-b1f5edad1d44", "client_name": "keycloak-client-456789", "scope": "address phone offline_access microprofile-jwt", "jwks_uri": "http://localhost:8080/public_keys.jwks", "subject_type": "pairwise", "request_uris": ["http://localhost:8080/rf.txt"], "tls_client_certificate_bound_access_tokens": False, "client_id_issued_at": 1622306364, "client_secret_expires_at": 0, "registration_client_uri": "http://localhost:8080/auth/realms/other/clients-registrations/openid-connect/539ce782-5d15-4256-a5fa-1a46609d056b", "backchannel_logout_session_required": False })) })