Commit 84a5d2d7 authored by dmknght's avatar dmknght
Browse files

Upload source code

parents
Pipeline #1456 canceled with stages
Copyright (c) 2013-2015, Ron Bowes
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Makefile
# By Ron
#
# See LICENSE.md
# Can't use a '#' in the shell command
VERSION=$(shell egrep '^.define VERSION' client/dnscat.c | head -n1 | cut -d\" -f2)
OS=$(shell uname -s)
ARCH=$(shell uname -p | sed 's/x86_64/x64/i' | sed 's/i.86/x86/i')
ifeq ($(OS), Linux)
RELEASE_FILENAME="dnscat2-$(VERSION)-client-$(ARCH)"
else
RELEASE_FILENAME="dnscat2-$(VERSION)-client-$(OS)-$(ARCH)"
endif
all:
@cd client && make
@echo "Compile complete!"
@echo "* Client: client/dnscat"
@echo "* Server: server/dnscat_*.rb"
clean:
@cd client && make clean
debug:
@cd client && make debug
@echo "Debug compile complete!"
release:
@make clean
-mkdir dist/
@cd client && make release
@mv client/dnscat .
@strip dnscat
@tar -cvvjf dist/${RELEASE_FILENAME}.tar.bz2 dnscat
@echo "*** Release compiled: `pwd`/${RELEASE_FILENAME}"
@echo "*** By the way, did you update the version number in the server?"
@echo "Release compile complete!"
source_release:
@make clean
-mkdir dist/
@cp -r client dnscat2_client
@tar -cvvjf dist/dnscat2-${VERSION}-client-source.tar.bz2 dnscat2_client
@zip -r dist/dnscat2-${VERSION}-client-source.zip dnscat2_client
@rm -rf dnscat2_client
@cp -r server dnscat2_server
@tar -cvvjf dist/dnscat2-${VERSION}-server.tar.bz2 dnscat2_server
@zip -r dist/dnscat2-${VERSION}-server.zip dnscat2_server
@rm -rf dnscat2_server
dnscat:
@cd client && make dnscat
This diff is collapsed.
# Object files
*.o
# Libraries
*.lib
*.a
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
# Named executables
dnscat
tcpcat
test
# Crash dumps
core
core.*
*.stackdump
# Makefile
# By Ron Bowes
# Created January, 2013
#
# (See LICENSE.md)
#
# Should work for Linux and BSD make.
CC?=gcc
DEBUG_CFLAGS?=-DTESTMEMORY -Werror -O0
RELEASE_CFLAGS?=-Os
CFLAGS+=--std=c89 -I. -Wall -D_DEFAULT_SOURCE -fstack-protector-all -Wformat -Wformat-security -g
LIBS=-pie -Wl,-z,relro,-z,now
OBJS=controller/packet.o \
controller/session.o \
controller/controller.o \
drivers/driver.o \
drivers/command/driver_command.o \
drivers/command/command_packet.o \
drivers/driver_console.o \
drivers/driver_exec.o \
drivers/driver_ping.o \
libs/buffer.o \
libs/crypto/encryptor.o \
libs/crypto/micro-ecc/uECC.o \
libs/crypto/salsa20.o \
libs/crypto/sha3.o \
libs/dns.o \
libs/ll.o \
libs/log.o \
libs/memory.o \
libs/select_group.o \
libs/tcp.o \
libs/types.o \
libs/udp.o \
tunnel_drivers/driver_dns.o \
DNSCAT_DNS_OBJS=${OBJS} dnscat.o
all: dnscat
@echo "*** Build complete! Run 'make debug' to build a debug version!"
debug: CFLAGS += $(DEBUG_CFLAGS)
debug: dnscat
@echo "*** Debug build complete"
release: CFLAGS += ${RELEASE_CFLAGS}
release: dnscat
nocrypto: CFLAGS += -DNO_ENCRYPTION
nocrypto: all
remove:
rm -f /usr/local/bin/dnscat
uninstall: remove
clean:
-rm -f *.o */*.o */*/*.o */*/*/*.o *.exe *.stackdump dnscat tcpcat test driver_tcp driver_dns
-rm -rf win32/Debug/
-rm -rf win32/Release/
-rm -rf win32/*.ncb
-rm -rf win32/*.sln
-rm -rf win32/*.suo
-rm -rf win32/*.vcproj.*
dnscat: ${DNSCAT_DNS_OBJS}
${CC} ${CFLAGS} ${LDFLAGS} -o dnscat ${DNSCAT_DNS_OBJS}
@echo "*** dnscat successfully compiled"
COMMANDS=drivers/command/commands_standard.h \
drivers/command/commands_tunnel.h
drivers/command/driver_command.o: drivers/command/driver_command.c ${COMMANDS}
${CC} -c ${CFLAGS} -o drivers/command/driver_command.o drivers/command/driver_command.c
/**
* controller.c
* Created by Ron Bowes
* On April, 2015
*
* See LICENSE.md
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#include "libs/pstdint.h"
#else
#include <stdint.h>
#endif
#include "libs/dns.h"
#include "libs/log.h"
#include "tunnel_drivers/driver_dns.h"
#include "packet.h"
#include "session.h"
#include "controller.h"
static int max_retransmits = 20;
typedef struct _session_entry_t
{
session_t *session;
struct _session_entry_t *next;
} session_entry_t;
static session_entry_t *first_session;
void controller_add_session(session_t *session)
{
session_entry_t *entry = NULL;
/* Add it to the linked list. */
entry = safe_malloc(sizeof(session_entry_t));
entry->session = session;
entry->next = first_session;
first_session = entry;
}
size_t controller_open_session_count()
{
size_t count = 0;
session_entry_t *entry = first_session;
while(entry)
{
if(!session_is_shutdown(entry->session))
count++;
entry = entry->next;
}
return count;
}
static session_t *sessions_get_by_id(uint16_t session_id)
{
session_entry_t *entry;
for(entry = first_session; entry; entry = entry->next)
if(entry->session->id == session_id)
return entry->session;
return NULL;
}
/* Version beta 0.01 suffered from a bug that I didn't understand: if one
* session had a ton of data to send, all the other sessions were ignored till
* it was done (if it was a new session). This function will get the "next"
* session using a global variable.
*/
static session_entry_t *current_session = NULL;
#if 0
static session_t *sessions_get_next()
{
/* If there's no session, use the first one. */
if(!current_session)
current_session = first_session;
/* If there's still no session, give up. */
if(!current_session)
return NULL;
/* Get the next session. */
current_session = current_session->next;
/* If we're at the end, go to the beginning. Also attempting a record for the
* number of NULL checks one a single pointer. */
if(!current_session)
current_session = first_session;
/* If this is NULL, something crazy is happening (we already had a
* first_session at some point in the past, so it means the linked list has
* had a member removed, which we can't currently do. */
assert(current_session);
/* All done! */
return current_session->session;
}
#endif
/* Get the next session in line that isn't shutdown.
* TODO: can/should we give higher priority to sessions that have data
* waiting?
*/
static session_t *sessions_get_next_active()
{
/* Keep track of where we start. */
session_entry_t *start = NULL;
/* If there's no session, use the first one. */
if(!current_session)
current_session = first_session;
/* If there's still no session, give up. */
if(!current_session)
return NULL;
/* Record our starting point. */
start = current_session;
/* Get the next session. */
do
{
current_session = current_session->next;
/* If we're at the end, go to the beginning. Also attempting a record for the
* number of NULL checks one a single pointer. */
if(!current_session)
current_session = first_session;
if(!session_is_shutdown(current_session->session))
return current_session->session;
}
while(current_session != start);
/* If we reached the starting point, there are no active sessions. */
return NULL;
}
NBBOOL controller_data_incoming(uint8_t *data, size_t length)
{
uint16_t session_id = packet_peek_session_id(data, length);
session_t *session = sessions_get_by_id(session_id);
/* If we weren't able to find a session, print an error and return. */
if(!session)
{
LOG_ERROR("Tried to access a non-existent session (%s): %d", __FUNCTION__, session_id);
return FALSE;
}
/* Pass the data onto the session. */
return session_data_incoming(session, data, length);
}
uint8_t *controller_get_outgoing(size_t *length, size_t max_length)
{
/* This needs to somehow be balanced. */
session_t *session = sessions_get_next_active();
if(!session)
{
/* TODO: Drop to a "probe for sessions" mode instead. */
LOG_FATAL("There are no active sessions left! Goodbye!");
exit(0);
return NULL;
}
return session_get_outgoing(session, length, max_length);
}
static void kill_ignored_sessions()
{
session_entry_t *entry = first_session;
if(max_retransmits < 0)
return;
while(entry)
{
if(!entry->session->is_shutdown && entry->session->missed_transmissions > max_retransmits)
{
LOG_ERROR("The server hasn't returned a valid response in the last %d attempts.. closing session.", entry->session->missed_transmissions - 1);
session_kill(entry->session);
}
entry = entry->next;
}
}
void controller_kill_all_sessions()
{
session_entry_t *entry = first_session;
while(entry)
{
if(!entry->session->is_shutdown)
session_kill(entry->session);
entry = entry->next;
}
}
/* This will be called something like every 50ms. */
void controller_heartbeat()
{
kill_ignored_sessions();
}
void controller_set_max_retransmits(int retransmits)
{
max_retransmits = retransmits;
}
void controller_destroy()
{
session_entry_t *prev_session = NULL;
session_entry_t *entry = first_session;
while(entry)
{
if(!entry->session->is_shutdown)
session_kill(entry->session);
session_destroy(entry->session);
prev_session = entry;
entry = entry->next;
safe_free(prev_session);
}
}
/**
* controller.h
* Created by Ron Bowes
* On April, 2015
*
* See LICENSE.md
*
* The controller basically keeps track of active sessions and passes data
* back and forth between the tunnel driver and the session. There is only
* ever a single instance of this.
*/
#ifndef __CONTROLLER_H__
#define __CONTROLLER_H__
#include "libs/types.h"
#include "session.h"
size_t controller_open_session_count();
void controller_add_session(session_t *session);
NBBOOL controller_data_incoming(uint8_t *data, size_t length);
uint8_t *controller_get_outgoing(size_t *length, size_t max_length);
void controller_kill_all_sessions();
void controller_destroy();
void controller_heartbeat();
void controller_set_max_retransmits(int retransmits);
#endif
/* packet.c
* By Ron Bowes
*
* See LICENSE.md
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include "libs/pstdint.h"
#else
#include <stdint.h>
#endif
#include "libs/buffer.h"
#include "libs/log.h"
#include "libs/memory.h"
#include "packet.h"
packet_t *packet_parse(uint8_t *data, size_t length, options_t options)
{
packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t));
buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length);
/* Validate the size */
if(buffer_get_length(buffer) > MAX_PACKET_SIZE)
{
LOG_FATAL("Packet is too long: %zu bytes\n", buffer_get_length(buffer));
exit(1);
}
packet->packet_id = buffer_read_next_int16(buffer);
packet->packet_type = (packet_type_t) buffer_read_next_int8(buffer);
packet->session_id = buffer_read_next_int16(buffer);
switch(packet->packet_type)
{
case PACKET_TYPE_SYN:
packet->body.syn.seq = buffer_read_next_int16(buffer);
packet->body.syn.options = buffer_read_next_int16(buffer);
if(packet->body.syn.options & OPT_NAME)
packet->body.syn.name = buffer_alloc_next_ntstring(buffer);
break;
case PACKET_TYPE_MSG:
packet->body.msg.seq = buffer_read_next_int16(buffer);
packet->body.msg.ack = buffer_read_next_int16(buffer);
packet->body.msg.data = buffer_read_remaining_bytes(buffer, &packet->body.msg.data_length, -1, FALSE);
break;
case PACKET_TYPE_FIN:
packet->body.fin.reason = buffer_alloc_next_ntstring(buffer);
break;
case PACKET_TYPE_PING:
packet->body.ping.data = buffer_alloc_next_ntstring(buffer);
break;
#ifndef NO_ENCRYPTION
case PACKET_TYPE_ENC:
packet->body.enc.subtype = buffer_read_next_int16(buffer);
packet->body.enc.flags = buffer_read_next_int16(buffer);
switch(packet->body.enc.subtype)
{
case PACKET_ENC_SUBTYPE_INIT:
buffer_read_next_bytes(buffer, packet->body.enc.public_key, 64);
break;
case PACKET_ENC_SUBTYPE_AUTH:
buffer_read_next_bytes(buffer, packet->body.enc.authenticator, 32);
break;
}
break;
#endif
default:
LOG_FATAL("Error: unknown message type (0x%02x)\n", packet->packet_type);
exit(0);
}
buffer_destroy(buffer);
return packet;
}
uint16_t packet_peek_session_id(uint8_t *data, size_t length)
{
buffer_t *buffer = NULL;
uint16_t session_id = -1;
/* Create a buffer of the first 5 bytes. */
if(length < 5)
{
LOG_FATAL("Packet is too short!\n");
return -1;
}
/* Create a buffer with the first 5 bytes of data. */
buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, 5);
/* Discard packet_id. */
buffer_consume(buffer, 2);
/* Discard packet_type. */
buffer_consume(buffer, 1);
/* Finally, get the session_id. */
session_id = buffer_read_next_int16(buffer);
/* Kill the buffer. */
buffer_destroy(buffer);
/* Done! */
return session_id;
}
packet_t *packet_create_syn(uint16_t session_id, uint16_t seq, options_t options)
{
packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t));
packet->packet_type = PACKET_TYPE_SYN;
packet->packet_id = rand() % 0xFFFF;
packet->session_id = session_id;
packet->body.syn.seq = seq;
packet->body.syn.options = options;
return packet;
}
void packet_syn_set_name(packet_t *packet, char *name)
{
if(packet->packet_type != PACKET_TYPE_SYN)
{
LOG_FATAL("Attempted to set the 'name' field of a non-SYN message\n");
exit(1);
}
/* Free the name if it's already set */
if(packet->body.syn.name)
safe_free(packet->body.syn.name);
packet->body.syn.options |= OPT_NAME;
packet->body.syn.name = safe_strdup(name);
}
void packet_syn_set_is_command(packet_t *packet)
{
if(packet->packet_type != PACKET_TYPE_SYN)
{
LOG_FATAL("Attempted to set the 'is_command' field of a non-SYN message\n");
exit(1);
}
/* Just set the field, we don't need anything else. */
packet->body.syn.options |= OPT_COMMAND;
}
packet_t *packet_create_msg(uint16_t session_id, uint16_t seq, uint16_t ack, uint8_t *data, size_t data_length)
{
packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t));
packet->packet_type = PACKET_TYPE_MSG;
packet->packet_id = rand() % 0xFFFF;
packet->session_id = session_id;
packet->body.msg.seq = seq;
packet->body.msg.ack = ack;
packet->body.msg.data = safe_memcpy(data, data_length);
packet->body.msg.data_length = data_length;
return packet;