Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Sun, 10 Mar 2013 08:17:17 +0100
From: Adam Zabrocki <pi3@....com.pl>
To: full-disclosure@...ts.grok.org.uk, oss-security@...ts.openwall.com
Cc: secalert <secalert@...urityreason.pl>
Subject: Multiple SQL Injection vulnerabilities in Disk Pool Manager (DPM)

Name:                 Multiple SQL Injection vulnerabilities in
                      Disk Pool Manager (DPM)
Author:               Adam Zabrocki (<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


Download attachment "signature.asc" of type "application/pgp-signature" (837 bytes)

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.