Import upstream version 0.07+git20200409.8664c0e
Kali Janitor
3 years ago
15 | 15 | endif |
16 | 16 | |
17 | 17 | all: |
18 | @cd client && make | |
18 | @cd client && $(MAKE) | |
19 | 19 | @echo "Compile complete!" |
20 | 20 | @echo "* Client: client/dnscat" |
21 | 21 | @echo "* Server: server/dnscat_*.rb" |
22 | 22 | |
23 | 23 | clean: |
24 | @cd client && make clean | |
24 | @cd client && $(MAKE) clean | |
25 | 25 | |
26 | 26 | debug: |
27 | @cd client && make debug | |
27 | @cd client && $(MAKE) debug | |
28 | 28 | @echo "Debug compile complete!" |
29 | 29 | |
30 | 30 | release: |
31 | @make clean | |
31 | @$(MAKE) clean | |
32 | 32 | -mkdir dist/ |
33 | 33 | @cd client && make release |
34 | 34 | @mv client/dnscat . |
39 | 39 | @echo "Release compile complete!" |
40 | 40 | |
41 | 41 | source_release: |
42 | @make clean | |
42 | @$(MAKE) clean | |
43 | 43 | -mkdir dist/ |
44 | 44 | @cp -r client dnscat2_client |
45 | 45 | @tar -cvvjf dist/dnscat2-${VERSION}-client-source.tar.bz2 dnscat2_client |
51 | 51 | @rm -rf dnscat2_server |
52 | 52 | |
53 | 53 | dnscat: |
54 | @cd client && make dnscat | |
54 | @cd client && $(MAKE) dnscat | |
55 | 55 |
0 | ***** NOTE: The password for the .zip downloads are all "password"! ***** | |
1 | ||
0 | 2 | # Introduction |
1 | 3 | |
2 | 4 | Welcome to dnscat2, a DNS tunnel that WON'T make you sick and kill you! |
8 | 8 | CC?=gcc |
9 | 9 | DEBUG_CFLAGS?=-DTESTMEMORY -Werror -O0 |
10 | 10 | RELEASE_CFLAGS?=-Os |
11 | CFLAGS?=--std=c89 -I. -Wall -D_DEFAULT_SOURCE -fstack-protector-all -Wformat -Wformat-security -g | |
12 | LIBS=-pie -Wl,-z,relro,-z,now | |
11 | CFLAGS?=--std=c89 -I. -Wall -D_DEFAULT_SOURCE -Wformat -Wformat-security -g | |
12 | LIBS=-pie -Wl,-z,relro,-z,now | |
13 | LDFLAGS= | |
14 | ||
15 | OS=$(shell uname -s) | |
16 | ifeq ($(OS),SunOS) | |
17 | # Solaris CC | |
18 | LDFLAGS = -lsocket -lnsl | |
19 | endif | |
13 | 20 | |
14 | 21 | OBJS=controller/packet.o \ |
15 | 22 | controller/session.o \ |
65 | 72 | -rm -rf win32/*.vcproj.* |
66 | 73 | |
67 | 74 | dnscat: ${DNSCAT_DNS_OBJS} |
68 | ${CC} ${CFLAGS} -o dnscat ${DNSCAT_DNS_OBJS} | |
75 | ${CC} ${CFLAGS} -o dnscat ${DNSCAT_DNS_OBJS} ${LDFLAGS} | |
69 | 76 | @echo "*** dnscat successfully compiled" |
70 | 77 | |
71 | 78 | COMMANDS=drivers/command/commands_standard.h \ |
0 | # Makefile | |
1 | # By Ron Bowes | |
2 | # Created January, 2013 | |
3 | # | |
4 | # (See LICENSE.md) | |
5 | # | |
6 | # Should work for Linux and BSD make. | |
7 | ||
8 | CC=i686-w64-mingw32-gcc | |
9 | DEBUG_CFLAGS?=-DTESTMEMORY -Werror -O0 | |
10 | RELEASE_CFLAGS?=-Os | |
11 | # CFLAGS?=--std=c89 -I. -Wall -D_DEFAULT_SOURCE -Wformat -Wformat-security -g -DWIN32=1 -DWINDOWS=1 | |
12 | CFLAGS?=-I. -g | |
13 | LIBS=-pie -Wl,-z,relro,-z,now | |
14 | LDFLAGS= -lws2_32 -ldnsapi | |
15 | ||
16 | OS=$(shell uname -s) | |
17 | ifeq ($(OS),SunOS) | |
18 | # Solaris CC | |
19 | LDFLAGS = -lsocket -lnsl | |
20 | endif | |
21 | ||
22 | OBJS=controller/packet.o \ | |
23 | controller/session.o \ | |
24 | controller/controller.o \ | |
25 | drivers/driver.o \ | |
26 | drivers/command/driver_command.o \ | |
27 | drivers/command/command_packet.o \ | |
28 | drivers/driver_console.o \ | |
29 | drivers/driver_exec.o \ | |
30 | drivers/driver_ping.o \ | |
31 | libs/buffer.o \ | |
32 | libs/crypto/encryptor.o \ | |
33 | libs/crypto/micro-ecc/uECC.o \ | |
34 | libs/crypto/salsa20.o \ | |
35 | libs/crypto/sha3.o \ | |
36 | libs/dns.o \ | |
37 | libs/ll.o \ | |
38 | libs/log.o \ | |
39 | libs/memory.o \ | |
40 | libs/select_group.o \ | |
41 | libs/tcp.o \ | |
42 | libs/types.o \ | |
43 | libs/udp.o \ | |
44 | tunnel_drivers/driver_dns.o \ | |
45 | ||
46 | DNSCAT_DNS_OBJS=${OBJS} dnscat.o | |
47 | ||
48 | all: dnscat.exe | |
49 | @echo "*** Build complete! Run 'make debug' to build a debug version!" | |
50 | ||
51 | debug: CFLAGS += $(DEBUG_CFLAGS) | |
52 | debug: dnscat | |
53 | @echo "*** Debug build complete" | |
54 | ||
55 | release: CFLAGS += ${RELEASE_CFLAGS} | |
56 | release: dnscat | |
57 | ||
58 | nocrypto: CFLAGS += -DNO_ENCRYPTION | |
59 | nocrypto: all | |
60 | ||
61 | remove: | |
62 | rm -f /usr/local/bin/dnscat | |
63 | ||
64 | uninstall: remove | |
65 | ||
66 | clean: | |
67 | -rm -f *.o */*.o */*/*.o */*/*/*.o *.exe *.stackdump dnscat tcpcat test driver_tcp driver_dns | |
68 | -rm -rf win32/Debug/ | |
69 | -rm -rf win32/Release/ | |
70 | -rm -rf win32/*.ncb | |
71 | -rm -rf win32/*.sln | |
72 | -rm -rf win32/*.suo | |
73 | -rm -rf win32/*.vcproj.* | |
74 | ||
75 | dnscat.exe: ${DNSCAT_DNS_OBJS} | |
76 | ${CC} ${CFLAGS} -o dnscat.exe ${DNSCAT_DNS_OBJS} ${LDFLAGS} | |
77 | @echo "*** dnscat successfully compiled" | |
78 | ||
79 | COMMANDS=drivers/command/commands_standard.h \ | |
80 | drivers/command/commands_tunnel.h | |
81 | ||
82 | drivers/command/driver_command.o: drivers/command/driver_command.c ${COMMANDS} | |
83 | ${CC} -c ${CFLAGS} -o drivers/command/driver_command.o drivers/command/driver_command.c |
16 | 16 | #else |
17 | 17 | #include <getopt.h> |
18 | 18 | #include <sys/socket.h> |
19 | #include <sys/wait.h> | |
20 | #include <signal.h> | |
19 | 21 | #endif |
20 | 22 | |
21 | 23 | #include "controller/controller.h" |
416 | 418 | /* This is required for win32 support. */ |
417 | 419 | winsock_initialize(); |
418 | 420 | |
421 | #ifndef WIN32 | |
422 | /* set the SIGCHLD handler to SIG_IGN causing zombie child processes to be reaped automatically */ | |
423 | if(signal(SIGCHLD, SIG_IGN) == SIG_ERR) | |
424 | { | |
425 | perror("Couldn't set SIGCHLD handler to SIG_IGN"); | |
426 | exit(1); | |
427 | } | |
428 | #endif | |
429 | ||
419 | 430 | /* Set the default log level */ |
420 | 431 | log_set_min_console_level(min_log_level); |
421 | 432 | |
543 | 554 | usage(argv[0], "Unrecognized argument"); |
544 | 555 | break; |
545 | 556 | } |
557 | } | |
558 | ||
559 | if(getenv("DNSCAT_DOMAIN")!=NULL) { | |
560 | if(getenv("DNSCAT_SECRET")!=NULL) { | |
561 | session_set_preshared_secret(getenv("DNSCAT_SECRET")); | |
562 | } | |
563 | tunnel_driver = create_dns_driver_internal(group, getenv("DNSCAT_DOMAIN"), "0.0.0.0", 53, DEFAULT_TYPES, NULL); | |
564 | tunnel_driver_created = TRUE; | |
546 | 565 | } |
547 | 566 | |
548 | 567 | create_drivers(drivers_to_create); |
133 | 133 | #elif defined(__APPLE__) |
134 | 134 | # include <libkern/OSByteOrder.h> |
135 | 135 | # define bswap_64 OSSwapInt64 |
136 | #elif defined(__sun) | |
137 | # include <sys/byteorder.h> | |
138 | # define bswap_64 BSWAP64 | |
136 | 139 | #else |
137 | 140 | # error "bswap_64 unsupported" |
138 | 141 | #endif |
72 | 72 | { |
73 | 73 | if(piece_length & 0x80) |
74 | 74 | { |
75 | if(piece_length == 0xc0) | |
75 | if(piece_length & 0xc0) | |
76 | 76 | { |
77 | 77 | uint8_t relative_pos = buffer_read_int8_at(buffer, offset + pos); |
78 | 78 | char *new_data; |
79 | ||
80 | relative_pos |= ((piece_length & 0x03) << 8); | |
79 | 81 | pos++; |
80 | 82 | |
81 | 83 | new_data = buffer_read_dns_name_at(buffer, relative_pos, NULL); |
135 | 137 | static char *buffer_read_ipv4_address_at(buffer_t *buffer, uint32_t offset, char result[16]) |
136 | 138 | { |
137 | 139 | #ifdef WIN32 |
138 | printf("NOT IMPLEMENTED!\n"); | |
140 | printf("NOT IMPLEMENTED IPv4!\n"); | |
139 | 141 | exit(1); |
140 | 142 | #else |
141 | 143 | uint8_t addr[4]; |
179 | 181 | static char *buffer_read_ipv6_address_at(buffer_t *buffer, uint32_t offset, char result[40]) |
180 | 182 | { |
181 | 183 | #ifdef WIN32 |
182 | printf("NOT IMPLEMENTED!\n"); | |
184 | printf("NOT IMPLEMENTED IPv6!\n"); | |
183 | 185 | exit(1); |
184 | 186 | #else |
185 | 187 | uint8_t addr[16]; |
189 | 189 | * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. |
190 | 190 | */ |
191 | 191 | |
192 | #if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) | |
192 | #if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || defined(__MINGW32__) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) | |
193 | 193 | #include <stdint.h> |
194 | 194 | #define _PSTDINT_H_INCLUDED |
195 | 195 | # ifndef PRINTF_INT64_MODIFIER |
22 | 22 | |
23 | 23 | # License |
24 | 24 | |
25 | See LICENSE.md. | |
25 | See [LICENSE.md](../LICENSE.md). | |
26 | 26 | |
27 | 27 | # Challenges |
28 | 28 |
195 | 195 | TunnelDrivers.start({ |
196 | 196 | :controller => self, |
197 | 197 | :driver => DriverDNS, |
198 | :args => [dns[:host], dns[:port], dns[:domains]] | |
198 | :args => [dns[:host], dns[:port], dns[:domains], opts[:cache]] | |
199 | 199 | }) |
200 | 200 | end |
201 | 201 | ) |
9 | 9 | |
10 | 10 | class CryptoHelper |
11 | 11 | def CryptoHelper.bignum_to_binary(bn, size=32) |
12 | if(!bn.is_a?(Bignum)) | |
13 | raise(ArgumentError, "Expected: Bignum; received: #{bn.class}") | |
12 | if(!bn.is_a?(Integer)) | |
13 | raise(ArgumentError, "Expected: Integer; received: #{bn.class}") | |
14 | 14 | end |
15 | 15 | |
16 | 16 | return [bn.to_s(16).rjust(size*2, "\0")].pack("H*") |
17 | 17 | end |
18 | 18 | |
19 | 19 | def CryptoHelper.bignum_to_text(bn, size=32) |
20 | if(!bn.is_a?(Bignum)) | |
21 | raise(ArgumentError, "Expected: Bignum; received: #{bn.class}") | |
20 | if(!bn.is_a?(Integer)) | |
21 | raise(ArgumentError, "Expected: Integer; received: #{bn.class}") | |
22 | 22 | end |
23 | 23 | |
24 | 24 | return CryptoHelper.bignum_to_binary(bn, size).unpack("H*").pop() |
224 | 224 | @public_key_x = params[:public_key_x] || raise(DnscatException, "params[:public_key_x] is required!") |
225 | 225 | @public_key_y = params[:public_key_y] || raise(DnscatException, "params[:public_key_y] is required!") |
226 | 226 | |
227 | if(!@public_key_x.is_a?(Bignum) || !@public_key_y.is_a?(Bignum)) | |
228 | raise(DnscatException, "Public keys have to be Bignums! (Seen: #{@public_key_x.class} #{@public_key_y.class})") | |
227 | if(!@public_key_x.is_a?(Integer) || !@public_key_y.is_a?(Integer)) | |
228 | raise(DnscatException, "Public keys have to be Integers! (Seen: #{@public_key_x.class} #{@public_key_y.class})") | |
229 | 229 | end |
230 | 230 | elsif(@subtype == SUBTYPE_AUTH) |
231 | 231 | @authenticator = params[:authenticator] || raise(DnscatException, "params[:authenticator] is required!") |
148 | 148 | options = 0 |
149 | 149 | |
150 | 150 | # Ignore errant SYNs - they are, at worst, retransmissions that we don't care about |
151 | if(@state != STATE_NEW) | |
152 | raise(DnscatException, "Duplicate SYN received!") | |
153 | end | |
151 | #if(@state != STATE_NEW) | |
152 | # raise(DnscatException, "Duplicate SYN received!") | |
153 | #end | |
154 | 154 | |
155 | 155 | _do_display_crypto_values() |
156 | 156 |
9 | 9 | ## |
10 | 10 | |
11 | 11 | $LOAD_PATH << File.dirname(__FILE__) # A hack to make this work on 1.8/1.9 |
12 | ||
12 | trap "SIGINT" do | |
13 | puts "Ctrl-C is disabled, use exit" | |
14 | end | |
13 | 15 | # Create the window right away so other includes can create their own windows if they want |
14 | 16 | require 'libs/swindow' |
15 | 17 | WINDOW = SWindow.new(nil, true, { :prompt => "dnscat2> ", :name => "main" }) |
87 | 89 | |
88 | 90 | opt :firehose, "If set, all output goes to stdout instead of being put in windows.", |
89 | 91 | :type => :boolean, :default => false |
92 | ||
93 | opt :cache, "If set, caching is enabled on the server.", | |
94 | :type => :boolean, :default => true | |
90 | 95 | end |
91 | 96 | |
92 | 97 | SWindow.set_firehose(opts[:firehose]) |
204 | 209 | TunnelDrivers.start({ |
205 | 210 | :controller => controller, |
206 | 211 | :driver => DriverDNS, |
207 | :args => [dns_settings[:host], dns_settings[:port], dns_settings[:domains]], | |
212 | :args => [dns_settings[:host], dns_settings[:port], dns_settings[:domains], opts[:cache]], | |
208 | 213 | }) |
209 | 214 | |
210 | 215 | # Wait for the input window to finish its thing |
104 | 104 | |
105 | 105 | if(command_packet.get(:is_request)) |
106 | 106 | tunnel_data_incoming(command_packet) |
107 | else | |
108 | handler = @handlers.delete(command_packet.get(:request_id)) | |
109 | if(handler.nil?) | |
110 | @window.puts("Received a response that we have no record of sending:") | |
111 | @window.puts("#{command_packet}") | |
112 | @window.puts() | |
113 | @window.puts("Here are the responses we're waiting for:") | |
114 | @handlers.each_pair do |request_id, the_handler| | |
115 | @window.puts("#{request_id}: #{the_handler[:request]}") | |
116 | end | |
117 | ||
118 | if(handler.get(:command_id) != response.get(:command_id) && command_packet.get(:command_id) != CommandPacket::ERROR) | |
119 | @window.puts("Received a response of a different packet type (that's really weird, please report if you can reproduce!") | |
120 | @window.puts("#{command_packet}") | |
121 | @window.puts() | |
122 | @window.puts("The original packet was:") | |
123 | @window.puts("#{handler}") | |
124 | end | |
125 | ||
126 | return | |
127 | end | |
128 | ||
129 | handler[:proc].call(handler[:request], command_packet) | |
107 | return | |
130 | 108 | end |
131 | 109 | |
110 | handler = @handlers.delete(command_packet.get(:request_id)) | |
111 | if(handler.nil?) | |
112 | @window.puts("Received a response that we have no record of sending:") | |
113 | @window.puts("#{command_packet}") | |
114 | @window.puts() | |
115 | @window.puts("Here are the responses we're waiting for:") | |
116 | @handlers.each_pair do |request_id, the_handler| | |
117 | @window.puts("#{request_id}: #{the_handler[:request]}") | |
118 | end | |
119 | return | |
120 | end | |
121 | ||
122 | if(command_packet.get(:command_id) == CommandPacket::COMMAND_ERROR) | |
123 | @window.puts("Client returned an error: #{command_packet.get(:status)} :: #{command_packet.get(:reason)}") | |
124 | return | |
125 | end | |
126 | ||
127 | if(handler[:request].get(:command_id) != command_packet.get(:command_id)) | |
128 | @window.puts("Received a response of a different packet type (that's really weird, please report if you can reproduce!") | |
129 | @window.puts("#{command_packet}") | |
130 | @window.puts() | |
131 | @window.puts("The original packet was:") | |
132 | @window.puts("#{handler}") | |
133 | return | |
134 | end | |
135 | ||
136 | handler[:proc].call(handler[:request], command_packet) | |
132 | 137 | end |
133 | 138 | |
134 | 139 | def feed(data) |
375 | 375 | attr_accessor :preference, :name |
376 | 376 | |
377 | 377 | def initialize(name, preference = 10) |
378 | if(!name.is_a?(String) || !preference.is_a?(Fixnum)) | |
378 | if(!name.is_a?(String) || !preference.is_a?(Integer)) | |
379 | 379 | raise ArgumentError("Creating an MX record wrong! Please file a bug!") |
380 | 380 | end |
381 | 381 | @name = name |
803 | 803 | @sent = true |
804 | 804 | end |
805 | 805 | |
806 | def reply!() | |
806 | def reply!(_="") | |
807 | 807 | raise ArgumentError("Already sent!") if(@sent) |
808 | 808 | |
809 | 809 | # Cache it if we have a cache |
841 | 841 | begin |
842 | 842 | loop do |
843 | 843 | data = @s.recvfrom(65536) |
844 | ||
845 | # Data is an array where the first element is the actual data, and the second is the host/port | |
846 | request = DNSer::Packet.parse(data[0]) | |
847 | ||
848 | # Create a transaction object, which we can use to respond | |
849 | transaction = Transaction.new(@s, request, data[1][3], data[1][1], @cache) | |
850 | ||
851 | # If caching is enabled, deal with it | |
852 | if(@cache) | |
853 | # This is somewhat expensive, but we aren't using the cache for performance | |
854 | @cache.cleanup!() | |
855 | ||
856 | # See if the transaction is cached | |
857 | cached = @cache[request.trn_id] | |
858 | ||
859 | # Verify it deeper (for security reasons) | |
860 | if(!cached.nil?) | |
861 | puts("POTENTIAL CACHE HIT") | |
862 | if(request == cached[:request]) | |
863 | puts("CACHE HIT") | |
864 | transaction.reply!(cached[:response]) | |
844 | ||
845 | begin | |
846 | # Data is an array where the first element is the actual data, and the second is the host/port | |
847 | request = DNSer::Packet.parse(data[0]) | |
848 | ||
849 | # Create a transaction object, which we can use to respond | |
850 | transaction = Transaction.new(@s, request, data[1][3], data[1][1], @cache) | |
851 | ||
852 | # If caching is enabled, deal with it | |
853 | if(@cache) | |
854 | # This is somewhat expensive, but we aren't using the cache for performance | |
855 | @cache.cleanup!() | |
856 | ||
857 | # See if the transaction is cached | |
858 | cached = @cache[request.trn_id] | |
859 | ||
860 | # Verify it deeper (for security reasons) | |
861 | if(!cached.nil?) | |
862 | if(request == cached[:request]) | |
863 | puts("CACHE HIT") | |
864 | transaction.reply!(cached[:response]) | |
865 | end | |
865 | 866 | end |
866 | 867 | end |
867 | end | |
868 | ||
869 | if(!transaction.sent) | |
870 | begin | |
871 | proc.call(transaction) | |
872 | rescue StandardError => e | |
873 | puts("Caught an error: #{e}") | |
874 | puts(e.backtrace()) | |
875 | transaction.reply!(transaction.response_template({:rcode => DNSer::Packet::RCODE_SERVER_FAILURE})) | |
868 | ||
869 | if(!transaction.sent) | |
870 | begin | |
871 | proc.call(transaction) | |
872 | rescue StandardError => e | |
873 | puts("Caught an error: #{e}") | |
874 | puts(e.backtrace()) | |
875 | transaction.reply!(transaction.response_template({:rcode => DNSer::Packet::RCODE_SERVER_FAILURE})) | |
876 | end | |
876 | 877 | end |
878 | rescue StandardError => e | |
879 | puts("Caught an error: #{e}") | |
880 | puts(e.backtrace()) | |
877 | 881 | end |
878 | 882 | end |
879 | 883 | ensure |
241 | 241 | return response |
242 | 242 | end |
243 | 243 | |
244 | def initialize(parent_window, host, port, domains) | |
244 | def initialize(parent_window, host, port, domains, cache) | |
245 | 245 | if(domains.nil?) |
246 | 246 | domains = [] |
247 | 247 | end |
248 | 248 | |
249 | 249 | # Do this as early as we can, so we can fail early |
250 | @dnser = DNSer.new(host, port, true) | |
250 | @dnser = DNSer.new(host, port, cache) | |
251 | 251 | |
252 | 252 | @id = 'dns%d' % (@@id += 1) |
253 | 253 | @window = SWindow.new(parent_window, false, { |