mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-04-30 13:07:22 +02:00
476 lines
12 KiB
C++
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;
|
|
}
|