diff --git a/README.md b/README.md index d99acbb5a298350385d3e58efd4479b52ff695df..a1703281926b7f27b4613762d5c2955edc11e764 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,27 @@ where * `ENTITY_ID_HERE` is the entity ID of the SP * `BEARER_TOKEN_HERE` is your authorization token for the conformance IdP +### SP-initiated (workflow) + +Peforming SP-initiated login is more difficult than the IdP-initiated login used in previous templates. + +You can use the included [nuclei workflow](https://docs.projectdiscovery.io/templates/workflows/overview) +to scan a server for available endpoints and perform SP-initiated login if possible: + +```sh +nuclei -w workflow/saml-sp-workflow.yaml -u https://sp.example.com/ -code -sf secret-file.yaml +``` + +where + +* `https://sp.example.com/` is the address of a (potential) SP server (only the domain is relevant) + ## Current limitations * templates are only usable with the conformace IdP * testing cannot be run in parallel for the same entity ID * only `HTTP-POST` binding is currently supported -* nuclei has to be invoked individually for each target (for each entity ID) -* only SPs which accept unsolicited logins (IdP-initiated) can be tested +* IdP-initiated templates have to be invoked individually for each target (for each entity ID) +* only SPs which accept unsolicited logins (IdP-initiated) + or with a [Request Initiation](https://docs.oasis-open.org/security/saml/Post2.0/sstc-request-initiation.html) endpoint can be tested * headless browser test behaves differently than raw HTTP test (nuclei limitation) diff --git a/nuclei-templates/saml-raw-all.yaml b/nuclei-templates/saml-raw-all.yaml index 0fc049f842956c288c38e73cb5d09afd2b8dfdf5..3ad63d9ebcad7e1f2b7f8ccd6a5d45c03b3892d2 100644 --- a/nuclei-templates/saml-raw-all.yaml +++ b/nuclei-templates/saml-raw-all.yaml @@ -3,7 +3,7 @@ info: name: SAML signature validation author: T&I Incubator, GÉANT severity: high - tags: saml,raw + tags: saml,signature,raw variables: CONFORMANCE_IDP_BASE_URL: https://conformance-idp.maiv1.incubator.geant.org/ CONFORMANCE_IDP_HOSTNAME: '{{replace_regex(CONFORMANCE_IDP_BASE_URL, "^https?://|/.*$", "")}}' diff --git a/workflow/saml-detect-endpoints.yaml b/workflow/saml-detect-endpoints.yaml new file mode 100644 index 0000000000000000000000000000000000000000..aeb52c221b68af46d8feba53266563c59002036a --- /dev/null +++ b/workflow/saml-detect-endpoints.yaml @@ -0,0 +1,107 @@ +id: saml-detect-endpoints +info: + name: SAML endpoints detection + author: T&I Incubator, GÉANT + severity: info + tags: saml,detect,tech + +variables: + CONFORMANCE_IDP_ENTITY_ID: "urn:x-simplesamlphp:geant:incubator:conformance-idp" + +http: + - method: GET + path: + - "{{RootURL}}{{spinitendpoint}}?entityID={{url_encode(CONFORMANCE_IDP_ENTITY_ID)}}" + redirects: false + attack: batteringram + payloads: + spinitendpoint: + - "" + - "/" + - "/api/login/sso" + - "/api/saml-client/returnFromSAML" + - "/applications/secure-sp/Shibboleth.sso/Login" + - "/auth/shibboleth.sso/ds" + - "/auth/shibexternal/Shibboleth.sso/DS" + - "/auth/shibexternal/Shibboleth.sso/Login" + - "/edugainlogin/Shibboleth.sso/Login" + - "/edugain/Shibboleth.sso/Login" + - "/eduid/Shibboleth.sso/Login" + - "/federation-manager/Shibboleth.sso/Login" + - "/login/shibboleth/callback" + - "/research-and-scholarship/Shibboleth.sso/DS" + - "/research-and-scholarship/Shibboleth.sso/EDS" + - "/research-and-scholarship/Shibboleth.sso/Login" + - "/research-and-scholarship/Shibboleth.sso/Login1" + - "/research-and-scholarship/Shibboleth.sso/SeamlessAccess" + - "/research-and-scholarship/Shibboleth.sso/Wayfinder" + - "/saml/login" + - "/saml/sp" + - "/saml/start" + - "/Shibboleth.sso/discovery" + - "/Shibboleth.sso/Discovery" + - "/shibboleth.sso/DS" + - "/Shibboleth.sso/DS" + - "/Shibboleth.sso/DS/Login" + - "/Shibboleth.sso/DS/seamless-access" + - "/Shibboleth.sso/DS/thiss.io" + - "/Shibboleth.sso/DS-WayfOA" + - "/Shibboleth.sso/DS1" + - "/Shibboleth.sso/DS2" + - "/Shibboleth.sso/DS3" + - "/Shibboleth.sso/EDS" + - "/Shibboleth.sso/edugain" + - "/Shibboleth.sso/FORCE" + - "/-shibboleth.sso/Login" + - "/shibboleth.sso/Login" + - "/Shibboleth.sso/Login" + - "/Shibboleth.sso/Loginn" + - "/Shibboleth.sso/Login1" + - "/Shibboleth.sso/OpenAthens" + - "/Shibboleth.sso/SeamlessAccess" + - "/Shibboleth.sso/SeamlessLogin" + - "/Shibboleth.sso/Shibboleth.sso/Login" + - "/Shibboleth.sso/shibtest" + - "/Shibboleth.sso/UniboAuth" + - "/Shibboleth.sso/WAYF" + - "/Shibboleth.sso/WAYF/Chooser" + - "/test/edugain/Shibboleth.sso/Login" + - "/wayf/" + - "/wayfless/boa" + stop-at-first-match: true + matchers: + - type: dsl + name: hasspinitendpoint + condition: and + dsl: + - "status_code == 302 || status_code == 303 || status_code == 307" + - "contains(location, 'SAMLRequest=')" + extractors: + - type: dsl + name: spinitendpoint + dsl: + - "spinitendpoint" + +code: + - engine: + - python3 + - python + - py + source: | + import base64 + import zlib + import os + import re + from urllib.parse import unquote + + saml_request = unquote(re.sub(r'^.*SAMLRequest=(.*?)(&|$).*', r'\1', os.getenv("http_location"))) + saml_request = zlib.decompress(base64.b64decode(saml_request), -zlib.MAX_WBITS).decode() + SP_ENTITY_ID = re.sub(r'^.*>(.*?)</\w+:Issuer.*$', r'\1', saml_request) + print(SP_ENTITY_ID) + + extractors: + - type: dsl + name: SP_ENTITY_ID + dsl: + - response +# digest: 4a0a0047304502205ecec927cfdf45f5d2711393ed7e8321517f98a969858659a1aa75db26d0b23b022100a390a5522fef78825412f4b60c38abb0f7f41d6f5ac970b24bb5b4c0308887ad:16ea744faf4d9956cf0107f1f3cb7c4c \ No newline at end of file diff --git a/workflow/saml-signature-request-init-raw.yaml b/workflow/saml-signature-request-init-raw.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6215f0f55e167416a9509e82a3fe6aeb5a07b431 --- /dev/null +++ b/workflow/saml-signature-request-init-raw.yaml @@ -0,0 +1,82 @@ +id: saml-signature-request-init-raw +info: + name: SP initiated SAML authentication endpoint check + author: T&I Incubator, GÉANT + severity: high + tags: saml,signature,raw +variables: + CONFORMANCE_IDP_BASE_URL: https://conformance-idp.maiv1.incubator.geant.org/ + CONFORMANCE_IDP_HOSTNAME: '{{replace_regex(CONFORMANCE_IDP_BASE_URL, "^https?://|/.*$", "")}}' + CONFORMANCE_IDP_ENTITY_ID: "urn:x-simplesamlphp:geant:incubator:conformance-idp" + TEST_CASES: + - noSignature + - invalidSignature + +flow: | + set("TEST_CASE", "standardResponse"); + http(); + for (let testcase of iterate(template["TEST_CASES"])) { + set("TEST_CASE", testcase); + http(); + } + +http: + - raw: + - | + @Host: https://{{CONFORMANCE_IDP_HOSTNAME}} + POST /module.php/conformance/test/setup?testId={{url_encode(TEST_CASE)}}&spEntityId={{url_encode(SP_ENTITY_ID)}} HTTP/1.1 + Host: {{CONFORMANCE_IDP_HOSTNAME}} + + - | + GET {{spinitendpoint}}?entityID={{url_encode(CONFORMANCE_IDP_ENTITY_ID)}} HTTP/1.1 + Host: {{Hostname}} + + # HTTP-POST binding + - | + @Host: {{replace_regex(trim(acs,"[]"), "^https?://|/.*$", "")}} + POST {{trim(acs,"[]")}} HTTP/1.1 + Host: {{replace_regex(trim(acs,"[]"), "^https?://|/.*$", "")}} + Content-Type: application/x-www-form-urlencoded + + SAMLResponse={{url_encode(trim(samlresponse,"[]"))}} + + disable-path-automerge: true + redirects: true + extractors: + - type: xpath + name: acs + internal: true + attribute: action + xpath: + - '/html/body/form' + - type: xpath + name: samlresponse + internal: true + attribute: value + xpath: + - '/html/body/form/input[2]' + # breaks the template :( + #- type: dsl + # dsl: + # - 'TEST_CASE' + matchers-condition: and + matchers: + - type: status + condition: or + status: + - 200 + - 302 + - 303 + - type: word + words: + - Welcome + - Hello + - Logged in + condition: or + part: body + - type: dsl + dsl: + # ignore the happy case + - '!contains(TEST_CASE, "standardResponse")' + condition: and + diff --git a/workflow/saml-sp-workflow.yaml b/workflow/saml-sp-workflow.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8d8d07950ed609bb7c5b0455c4a680bd7793e98e --- /dev/null +++ b/workflow/saml-sp-workflow.yaml @@ -0,0 +1,19 @@ +id: saml-sp-workflow +info: + name: SAML signature checks + author: T&I Incubator + description: Scan SAML SPs for SAML signature validation problems and vulnerabilities + severity: high +workflows: + - template: workflow/saml-detect-endpoints.yaml + subtemplates: + - template: workflow/saml-signature-request-init-raw.yaml + # unfortunatelly it seems that is not possible to combine matchers and shared execution context like this: + #matchers: + #- name: hasspinitendpoint + #subtemplates: + #- template: nuclei-templates/saml-signature-request-init-raw.yaml + #subtemplates: + # - template: nuclei-templates/saml-signature-request-init-invalid-signatures.yaml + #- tags: ssl,tls,dns,tech,detect + - tags: ssl,tls,dns,tech,detect