Permalink
Browse files

Spoofing DNS proxy server

  • Loading branch information...
0 parents commit d9feeed76eb6b30f870d94d6d0baec6815ec390c @msantos committed Jun 24, 2010
Showing with 484 additions and 0 deletions.
  1. +6 −0 Emakefile
  2. +16 −0 Makefile
  3. +54 −0 README
  4. +101 −0 src/dns.erl
  5. +75 −0 src/sniff.erl
  6. +51 −0 src/spood.erl
  7. +178 −0 src/spoof.erl
  8. +3 −0 start.sh
6 Emakefile
@@ -0,0 +1,6 @@
+{["src/*"],
+ [{i, "include"},
+ {outdir, "ebin"},
+ debug_info]
+}.
+
16 Makefile
@@ -0,0 +1,16 @@
+
+ERL=erl
+APP=spood
+
+all: dir erl
+
+dir:
+ -@mkdir -p ebin deps
+
+erl:
+ @$(ERL) -noinput +B \
+ -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.'
+
+clean:
+ @rm -fv ebin/*.beam
+
54 README
@@ -0,0 +1,54 @@
+
+spood is a spoofing DNS proxy with a vaguely obscene name. All in Erlang.
+
+
+WHAT IS IT
+
+spood is a DNS proxy that listens for requests on localhost and proxies
+the requests by spoofing the packets from the IP addresses of other
+clients. spood might be useful if you're using a DNS tunnel like sods:
+
+http://github.com/msantos/sods
+
+
+REQUIREMENTS
+
+* procket: http://github.com/msantos/procket
+* epcap: http://github.com/msantos/epcap
+
+
+SETUP
+
+1. Adjust src/spood.erl:
+
+Change the IP addresses/MAC addresses to your network.
+
+2. Build it and run:
+
+mkdir deps
+cd deps
+ln -s /path/to/procket .
+ln -s /path/to/epcap .
+make
+./start.sh
+
+
+TODO
+
+* add a ping function to spood
+ * call ICMP ping for {192,168,213,1}, 24
+ * add each host that responds to a list
+ * call list strategy
+
+* add ability to get mac addresses programmatically
+
+* allow user to specify name server address
+
+* support multiple name servers
+
+* add ability to turn on/off debug output
+
+* add sanity checks on sniffed DNS packets, like checking domain
+
+* convert packet manufacturing to use epcap_net
+
101 src/dns.erl
@@ -0,0 +1,101 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% 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 author 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 HOLDER 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.
+-module(dns).
+-behaviour(gen_server).
+
+-include_lib("kernel/src/inet_dns.hrl").
+
+-define(SERVER, ?MODULE).
+-define(DNS_PORT, 53).
+
+-export([start_link/0, start_link/1]).
+-export([send/2]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {
+ s % socket
+ }).
+
+
+send(Port, Data) when is_integer(Port), is_binary(Data) ->
+ gen_server:call(?MODULE, {sniff, Port, Data}).
+
+start_link() ->
+ start_link(?DNS_PORT).
+start_link(Port) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
+
+init([Port]) ->
+ {ok, FD} = procket:listen(53, [
+ {ip, {127,0,0,1}},
+ {protocol, udp},
+ {family, inet},
+ {type, dgram}
+ ]),
+ {ok, Socket} = gen_udp:open(Port, [
+ binary,
+ {fd, FD}
+ ]),
+ {ok, #state{
+ s = Socket
+ }}.
+
+
+% Sniffed reply
+handle_call({sniff, Port, Data}, _From, #state{s = Socket} = State) ->
+ ok = gen_udp:send(Socket, {127,0,0,1}, Port, Data),
+ error_logger:error_report([
+ {port, Port},
+ {decode, inet_dns:decode(Data)}
+ ]),
+ {reply, ok, State};
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+% DNS request from client
+handle_info({udp, Socket, {127,0,0,1}, Port, Data}, #state{s = Socket} = State) ->
+ spoof:send(Port, Data),
+ {noreply, State};
+% WTF?
+handle_info(Info, State) ->
+ error_logger:error_report([{wtf, Info}]),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
75 src/sniff.erl
@@ -0,0 +1,75 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% 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 author 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 HOLDER 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.
+
+-module(sniff).
+-export([service/1]).
+
+-include("epcap_net.hrl").
+
+
+service(Dev) ->
+ {ok, Socket} = packet:socket(),
+ ok = packet:promiscuous(Socket, packet:ifindex(Socket, Dev)),
+ {ok, PL} = inet_parse:resolv(
+ proplists:get_value(resolv_conf, inet_db:get_rc(), "/etc/resolv.conf")),
+ NS = proplists:get_value(nameserver, PL),
+ error_logger:info_report({ns,NS}),
+ loop(Socket, NS).
+
+loop(Socket, NS) ->
+ case procket:recvfrom(Socket, 65535) of
+ nodata ->
+ timer:sleep(10),
+ loop(Socket, NS);
+ {ok, Data} ->
+ P = epcap_net:decapsulate(Data),
+ filter(NS, P),
+ loop(Socket, NS);
+ Error ->
+ error_logger:error_report(Error)
+ end.
+
+filter(NS, [
+ #ether{},
+ #ipv4{
+ saddr = NS
+ },
+ #udp{
+ sport = 53,
+ dport = Port,
+ ulen = Len
+ },
+ Payload
+ ]) when Len > 0, Len < 512 ->
+ dns:send(Port, Payload);
+filter(_,_) ->
+ ok.
+
51 src/spood.erl
@@ -0,0 +1,51 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% 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 author 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 HOLDER 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.
+
+-module(spood).
+-export([start/0,start/3]).
+
+-define(DNS_PORT, 53).
+
+
+start() ->
+ start("eth0",
+ % Client
+ {{16#00,16#15,16#af,16#59,16#08,16#26}, {list, [{192,168,213,110}, {192,168,213,94}]}},
+
+ % Nameserver
+ {{16#00,16#16,16#b6,16#b5,16#3e,16#c6}, {192,168,213,1}}
+ ).
+start(Dev, Client, Nameserver) ->
+ dns:start_link(),
+ spoof:start_link(Dev, Client, Nameserver),
+ spawn(sniff, service, [Dev]).
+
+
178 src/spoof.erl
@@ -0,0 +1,178 @@
+%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
+%% 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 author 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 HOLDER 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.
+-module(spoof).
+-behaviour(gen_server).
+
+-include("epcap_net.hrl").
+-define(SERVER, ?MODULE).
+
+-export([start_link/3, send/2]).
+-export([dns_query/3]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {
+ s, % socket
+ i, % interface index
+ shost, % Client MAC Address
+ dhost, % NS MAC Address
+ daddr, % NS IP Address
+ saddr % Strategy for selecting client IP address
+ }).
+
+send(Port, Data) when is_integer(Port), is_binary(Data) ->
+ gen_server:call(?MODULE, {dns_query, Port, Data}).
+
+
+start_link(Dev, Client, NS) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Dev, Client, NS], []).
+
+init([Dev, {ClientMAC, Strategy}, {NSMAC, NSIP}]) ->
+ crypto:start(),
+ {ok, Socket} = packet:socket(),
+ Ifindex = packet:ifindex(Socket, Dev),
+ {ok, #state{
+ s = Socket,
+ i = Ifindex,
+ shost = ClientMAC,
+ saddr = Strategy,
+
+ dhost = NSMAC,
+ daddr = NSIP
+ }}.
+
+% DNS request from dns server
+handle_call({dns_query, Port, Data}, _From, #state{s = Socket, i = Ifindex} = State) ->
+ Packet = dns_query(Port, Data, State),
+ packet:send(Socket, Ifindex, Packet),
+ error_logger:info_report([
+ {spoofing, Port},
+ {packet, inet_dns:decode(Data)}
+ ]),
+ {reply, ok, State};
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+% WTF?
+handle_info(Info, State) ->
+ error_logger:error_report([wtf, Info]),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+dns_query(SourcePort, Data, #state{
+ shost = {SM1,SM2,SM3,SM4,SM5,SM6},
+ dhost = {DM1,DM2,DM3,DM4,DM5,DM6},
+ saddr = Strategy,
+ daddr = {DA1,DA2,DA3,DA4}
+ }) ->
+
+ {SA1,SA2,SA3,SA4} = strategy(Strategy),
+
+ Id = 1,
+ TTL = 64,
+
+ UDPlen = 8 + byte_size(Data),
+
+ IPlen = 20 + UDPlen,
+
+ IPsum = epcap_net:makesum(
+ <<
+ % IPv4 header
+ 4:4, 5:4, 0:8, IPlen:16,
+ Id:16, 0:1, 1:1, 0:1,
+ 0:13, TTL:8, 17:8, 0:16,
+ SA1:8, SA2:8, SA3:8, SA4:8,
+ DA1:8, DA2:8, DA3:8, DA4:8
+ >>
+ ),
+
+ UDPpad = case UDPlen rem 2 of
+ 0 -> 0;
+ 1 -> 8
+ end,
+
+ UDPsum = epcap_net:makesum(
+ <<
+ SA1:8,SA2:8,SA3:8,SA4:8,
+ DA1:8,DA2:8,DA3:8,DA4:8,
+ 0:8,
+ 17:8,
+ UDPlen:16,
+
+ SourcePort:16,
+ 53:16,
+ UDPlen:16,
+ 0:16,
+ Data/binary,
+ 0:UDPpad
+ >>),
+
+ <<
+ % Ethernet header
+ DM1:8,DM2:8,DM3:8,DM4:8,DM5:8,DM6:8,
+ SM1:8,SM2:8,SM3:8,SM4:8,SM5:8,SM6:8,
+ 16#08, 16#00,
+
+ % IPv4 header
+ 4:4, 5:4, 0:8, IPlen:16,
+ Id:16, 0:1, 1:1, 0:1,
+ 0:13, TTL:8, 17:8, IPsum:16,
+ SA1:8, SA2:8, SA3:8, SA4:8,
+ DA1:8, DA2:8, DA3:8, DA4:8,
+
+ % UDP header
+ SourcePort:16,
+ 53:16,
+ UDPlen:16,
+ UDPsum:16,
+ Data/binary
+ >>.
+
+strategy(Address) when is_list(Address) ->
+ {ok, SA} = inet_parse:address(Address),
+ SA;
+strategy({_,_,_,_} = SA) ->
+ SA;
+strategy({list, [IPList]}) when is_tuple(IPList) ->
+ error_logger:info_report([{iplist, IPList}]),
+ IPList;
+strategy({list, IPList}) when is_list(IPList) ->
+ error_logger:info_report([{shuffle, IPList}]),
+ lists:nth(crypto:rand_uniform(1, length(IPList)), IPList).
+
3 start.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec erl -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -s spood start

0 comments on commit d9feeed

Please sign in to comment.