1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-30 13:07:22 +02:00
2017-05-14 02:11:57 -05:00

476 lines
12 KiB
C++

#include <winsock2.h>
#pragma comment(lib,"wininet.lib")
#pragma comment(lib,"Ws2_32.lib")
#include <string>
#include <set>
#include <sstream>
using namespace std;
#include "dhcpserv.h"
typedef int socklen_t;
extern "C" {
int startDHCPServer(void * server){
return ((DHCPserv*)server)->start();
}
int stopDHCPServer(void * server){
return ((DHCPserv*)server)->stop();
}
// Creates DHCP server
void* createDHCPServer(){
return (void*) new DHCPserv();
}
// Deletes the server
void destroyDHCPServer(void * server){
delete (DHCPserv*)server;
}
// Sets an option in the server
void setDHCPOption(void * server, char* name, unsigned int namelen, char* opt, unsigned int optlen){
string namestr(name,namelen);
string optstr(opt,optlen);
((DHCPserv*)server)->setOption(namestr,optstr);
}
// Gets the DHCP log
unsigned char * getDHCPLog(void * server, unsigned long * size){
string * log = ((DHCPserv*)server)->getLog();
*size = (unsigned long)log->size();
unsigned char* res = (unsigned char*)malloc(*size);
memcpy(res, log->data(), *size);
log->clear();
return res;
}
}
//Gets IP of default interface, or at least default interface to 8.8.8.8
unsigned int getLocalIp(){
//get socket
SOCKET smellySock = socket(AF_INET, SOCK_DGRAM, 0);
if (smellySock == INVALID_SOCKET)
{
return 0;
}
//Se up server socket address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = 0x08080808; //8.8.8.8
connect(smellySock, (const sockaddr*) &server, sizeof(server));
struct sockaddr_in myaddr;
socklen_t size = sizeof(myaddr);
getsockname(smellySock, (struct sockaddr*)&myaddr, &size);
closesocket(smellySock);
return ntohl(*((unsigned int*) & myaddr.sin_addr));
}
// Creates a DHCP option of format type length value
string dhcpoption(unsigned char type, string val){
string ret(1,type);
ret.append(1, (char) val.length()).append(val);
return ret;
}
// Creates a DHCP option with no value
string dhcpoption(unsigned char type){
string ret(1,type);
ret.append(1, '\x00');
return ret;
}
//convert long to IP binary string
string iton(unsigned int ip){
unsigned int source = htonl(ip);
string res((char *)&source,4);
return res;
}
//constructor
DHCPserv::DHCPserv(): options(), log(), thread(NULL), smellySock(0), shuttingDown(false){
}
// Sets an option
void DHCPserv::setOption(string option, string value){
options[option] = value;
}
// Asks server to stop
int DHCPserv::stop(){
shuttingDown = true;
DWORD res = 0xffffffff;
if(thread != NULL)
res = WaitForSingleObject(thread, 5000);
thread = NULL;
return res;
}
// Method to pass to CreateThread
DWORD WINAPI runDHCPServer(void* server)
{
return ((DHCPserv*)server)->run();
}
// Starts server
int DHCPserv::start()
{
//reset log
log.clear();
//get socket
smellySock = socket(AF_INET, SOCK_DGRAM, 0);
if (smellySock == INVALID_SOCKET)
{
return invalidSocket;
}
//Se up server socket address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(dhcpServPort);
server.sin_addr.s_addr = 0; //a.k.a. INADDR_ANY
// Get local IP
string hoststr("SRVHOST");
if (options.count(hoststr) > 0)
{
myIp = ntohl(inet_addr(options[string(hoststr)].c_str()));
server.sin_addr.s_addr = htonl(myIp);
}
else
{
myIp = getLocalIp();
}
if (myIp == 0 || myIp == 0xffffffff)
{
return ipError;
}
// Bind address to socket
if (bind(smellySock, (struct sockaddr *)&server, sizeof(server)) != 0)
{
return bindError;
}
// Now run it!
thread = CreateThread(NULL, 0, &runDHCPServer, this, 0, NULL);
if (thread != NULL)
{
return ERROR_SUCCESS;
}
return GetLastError();
}
// Determines whether ip option should be updated based on option name
void DHCPserv::ipOptionCheck(unsigned int * defaultOption, char * option)
{
string s(option);
if (options.count(s) > 0)
{
(*defaultOption) = ntohl(inet_addr(options[s].c_str()));
}
}
// Determines whether string option should be updated based on option name
void DHCPserv::stringOptionCheck(string * defaultOption, char * option)
{
string s(option);
if (options.count(s) > 0)
{
(*defaultOption) = options[s];
}
}
//Internal run method that does all the hard work
int DHCPserv::run()
{
//All IP-related options are in host byte order
//get SRVHOST
if (myIp == 0)
{
return localIpError; //No default route?
}
string ipString = iton(myIp);
//get DHCPIPSTART
unsigned int startIp = myIp + 1; //default start is just above our IP
ipOptionCheck(&startIp, "DHCPIPSTART");
unsigned int currentIp = startIp;
//get DHCPIPEND
unsigned int endIp = (myIp | 0xFFFFFF00) + 0xFE; //last octet is .254
ipOptionCheck(&endIp, "DHCPIPEND");
//get NETMASK
unsigned int netmask = 0xFFFFFF00; //default class C
ipOptionCheck(&netmask, "NETMASK");
//get ROUTER
unsigned int router = myIp; //we are default ROUTER
ipOptionCheck(&router,"ROUTER");
//get DNSSERVER
unsigned int dnsServer = myIp; //we are default DNSSERVER
ipOptionCheck(&dnsServer, "DNSSERVER");
//get BROADCAST
unsigned int broadcast = INADDR_BROADCAST; //Mandatory for some clients
ipOptionCheck(&broadcast, "BROADCAST");
//get SERVEONCE
bool serveOnce = true;
string soncestr("SERVEONCE");
if (options.count(soncestr) > 0)
{
serveOnce = atoi(options[soncestr].c_str()) != 0 || (options[soncestr].at(0) | 0x20) == 't';
}
//get PXE
bool servePXE = true;
string pxestring("PXE");
if (options.count(pxestring) > 0)
{
servePXE = atoi(options[pxestring].c_str()) != 0 || (options[pxestring].at(0) | 0x20) == 't';
}
//get HOSTNAME
string hostname; //hostname to give out
stringOptionCheck(&hostname, "HOSTNAME");
//get HOSTSTART
unsigned int servedOver = 0;
string hoststr("HOSTSTART");
if(options.count(hoststr) > 0)
servedOver = atoi(options[hoststr].c_str());
//get DHCP filename
string fileName("update1");
stringOptionCheck(&fileName, "FILENAME");
fileName.append(128 - fileName.length(), '\x00');
//get pxelinux conf filename
string pxeConfigFile("update2");
stringOptionCheck(&pxeConfigFile, "PXECONF");
string pxeAltConfigFile("update0");
stringOptionCheck(&pxeAltConfigFile, "PXEALTCONF");
string pxePathPrefix("");
//get DHCP parameters
unsigned int leaseTime = 600;
unsigned int relayIp = 0; // relay ip - not currently suported
unsigned int pxeRebootTime = 2000;
//Se up broadcast socket address
struct sockaddr_in broadcastAddr;
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_port = htons(68);
broadcastAddr.sin_addr.s_addr = htonl(broadcast); //a.k.a. inet_addr("255.255.255.255")
int value = 1;
if (setsockopt(smellySock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof(value)) != 0)
{
return setsockoptError;
}
// Setup timeout
fd_set sockSet;
int n;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100;
// Setup tracker for served yet
set<string> served;
//Main packet-handling loop
while (true)
{
//Wait for request
FD_ZERO(&sockSet);
FD_SET(smellySock, &sockSet);
n = select(1, &sockSet, NULL, NULL, &tv);
if (n == 0) //Timeout
{
if (!shuttingDown)
{
continue;
}
else //we're done!
{
break;
}
}
else if (n == -1)
{
break; //Error
}
//Get request
char receiveBuf[bufferSize];
struct sockaddr client;
socklen_t clientLength = sizeof(client);
int receiveSize = recvfrom(smellySock, receiveBuf, bufferSize, 0, &client, &clientLength);
if (receiveSize <= 240) break; //Error would be -1, DHCP packet must be at least 240
//String-ize it!
string receivedPacket(receiveBuf,receiveSize);
char type = receivedPacket.at(0);
//char hwtype = receivedPacket.at(1);
char hwlen = receivedPacket.at(2);
//char hops = receivedPacket.at(3);
string txid = receivedPacket.substr(4,4); //like buf[4..7]
string elapsed = receivedPacket.substr(8,2);
string flags = receivedPacket.substr(10,2);
string clientip = receivedPacket.substr(12,4);
string givenip = receivedPacket.substr(16,4);
string nextip = receivedPacket.substr(20,4);
string receivedRelayip = receivedPacket.substr(24,4);
string clienthwaddr = receivedPacket.substr(28,hwlen);
string servhostname = receivedPacket.substr(44,64);
string filename = receivedPacket.substr(108,128);
string magic = receivedPacket.substr(236,4);
if (type != Request || magic.compare(DHCPMagic) != 0)
{
continue; //Verify DHCP request
}
unsigned char messageType = 0;
bool pxeclient = false;
// options parsing loop
unsigned int spot = 240;
while (spot < receivedPacket.length() - 3)
{
unsigned char optionType = receivedPacket.at(spot);
if (optionType == 0xff)
{
break;
}
unsigned char optionLen = receivedPacket.at(spot + 1);
string optionValue = receivedPacket.substr(spot + 2, optionLen);
spot = spot + optionLen + 2;
if (optionType == 53)
{
messageType = optionValue.at(0);
}
else if (optionType == 150)
{
pxeclient = true;
}
}
if (pxeclient == false && servePXE == true)
{
continue;//No tftp server request; ignoring (probably not PXE client)
}
// prepare response
ostringstream pkt;
pkt << Response;
pkt << receivedPacket.substr(1,7); //hwtype, hwlen, hops, txid
string elaspedFlags("\x00\x00\x00\x00",4); //elapsed, flags
pkt << elaspedFlags;
pkt << clientip;
if (messageType == DHCPDiscover)
{
// give next ip address (not super reliable high volume but it should work for a basic server)
currentIp += 1;
if (currentIp > endIp)
{
currentIp = startIp;
}
}
pkt << iton(currentIp);
pkt << ipString; //next server ip
pkt << iton(relayIp);
pkt << receivedPacket.substr(28,16); //client hw address
pkt << servhostname;
pkt << fileName;
pkt << DHCPMagic;
pkt << "\x35\x01"; //Option
if (messageType == DHCPDiscover)
{ //DHCP Discover - send DHCP Offer
pkt << DHCPOffer;
}
else if (messageType == DHCPRequest)
{ //DHCP Request - send DHCP ACK
pkt << DHCPAck;
if (servedOver != 0) // NOTE: this is sufficient for low-traffic net
{
servedOver += 1;
}
}
else
{
continue; //ignore unknown DHCP request
}
// Options!
pkt << dhcpoption(OpDHCPServer, ipString);
pkt << dhcpoption(OpLeaseTime, iton(leaseTime));
pkt << dhcpoption(OpSubnetMask, iton(netmask));
pkt << dhcpoption(OpRouter, iton(router));
pkt << dhcpoption(OpDns, iton(dnsServer));
string pxemagic(PXEMagic,4);
pkt << dhcpoption(OpPXEMagic, pxemagic);
// check if already served based on hw addr (MAC address)
if (serveOnce == true && served.count(clienthwaddr) > 0)
{
pkt << dhcpoption(OpPXEConfigFile, pxeAltConfigFile); //Already served; allowing normal boot
}
else
{
pkt << dhcpoption(OpPXEConfigFile, pxeConfigFile);
if (messageType == DHCPRequest)
{
log.append(clienthwaddr);
log.append(iton(currentIp));
}
}
pkt << dhcpoption(OpPXEPathPrefix, pxePathPrefix);
pkt << dhcpoption(OpPXERebootTime, iton(pxeRebootTime));
if ( hostname.length() > 0 )
{
ostringstream sendHostname;
sendHostname << hostname;
if (servedOver != 0)
{
sendHostname << servedOver;
}
pkt << dhcpoption(OpHostname, sendHostname.str());
}
pkt << dhcpoption(OpEnd);
string sendPacket = pkt.str();
// now mark as served. We will then ignore their discovers
//(but we'll respond to requests in case a packet was lost)
if (messageType == DHCPRequest)
{
served.insert(clienthwaddr);
}
//Send response
int sent = sendto(smellySock, sendPacket.c_str(), (int)sendPacket.length(), 0, (struct sockaddr*)&broadcastAddr, (int)sizeof(broadcastAddr));
if (sent != (int)sendPacket.length())
{
break; //Error
}
}
closesocket(smellySock);
return 0;
}