LCG Disk Pool Manager SQL Injection

2013-03-10T00:00:00
ID PACKETSTORM:120780
Type packetstorm
Reporter Adam Zabrocki
Modified 2013-03-10T00:00:00

Description

                                        
                                            `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  
  
`