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