Skip to content

Instantly share code, notes, and snippets.

@todb
Last active October 3, 2015 17:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save todb/2492629 to your computer and use it in GitHub Desktop.
Save todb/2492629 to your computer and use it in GitHub Desktop.
CyanidePill -- poison your own DNS
#!/usr/bin/env ruby
# Note, this must be run as root, and is super dangerous.
# You should not use it. It was written in about 75 minutes total.
# Copyright (c) 2012 Tod Beardsley
# Licensed under the Ruby license.
require 'packetfu'
require 'net/dns'
include PacketFu
def get_packets
@pkts = PacketFu::PcapFile.read "sample.pcap"
@pkts.size
end
class CyanidePill
attr_reader :dns_packet, :name, :addr, :hosts_file, :req
def initalize
@dns_packet = nil
@name = nil
@addr = nil
@req = nil
end
def read(pkt)
clear
pkt = packetize_packet(pkt)
parse_packet(pkt) if validate_packet(pkt)
self.to_s
end
def to_s
if @addr && @name
"%-16s%s" % [@addr, @name]
else
""
end
end
def clear
@dns_packet = @name = @addr = @req = nil
end
def parse_packet(pkt)
@dns_packet = Net::DNS::Packet.parse(pkt.payload) rescue nil
return unless @dns_packet
if @dns_packet.question.first.qType.to_s == "A"
@name = @dns_packet.question.first.qName
if @name =~ /\.$/
@name = @name[0,@name.size-1]
end
end
if @name
resp = @dns_packet.answer.select {|a| a.type == "A"}.first
@addr = resp.address.to_s if resp
end
if @addr
@req = pkt.ip_daddr
end
@name = @addr = @req = nil unless (@name && @addr)
[@name, @addr, @req]
end
def packetize_packet(pkt)
return pkt if pkt.kind_of? Packet
return Packet.parse(pkt)
end
def validate_packet(pkt)
return false unless pkt.is_udp?
return false unless pkt.udp_sport == 53
return true
end
def prep_hosts
@can_poison = false
old_data = ""
fh = File.open("/etc/hosts", "rb")
fh.each_line do |line|
old_data << line
if line =~ /# CyanidePill/
@can_poison = true
break
end
end
fh.close
if poisonable?
@hosts_file = File.open("/etc/hosts", "wb")
@hosts_file.write old_data
@hosts_file.flush
end
end
def poisonable?
!!@can_poison
end
end
def poison_myself
puts "Poisoning myself..."
c = CyanidePill.new
c.prep_hosts
unless c.poisonable?
puts "Sorry, can't poison yourself."
return nil
end
@resolved_names = []
@cap = Capture.new(
:iface => "#{ARGV[0] || 'wlan0'}",
:start => true,
:filter => 'udp',
:promisc => true
)
loop do
@cap.stream.each do |pkt|
begin
c.read(pkt)
next if c.to_s.empty?
if @resolved_names.include? c.name
puts "%-16s%-16s%s" % [c.req, "<Skipping>", c.name]
next
else
@resolved_names << c.name
puts c.to_s
end
c.hosts_file.puts c.to_s
c.hosts_file.flush
rescue Interrupt
puts "Have fun with your poisoned cache!"
c.hosts_file.flush
c.hosts_file.close
raise $!
end
end
end
end
def test_cp
get_packets
c = CyanidePill.new
@pkts.each do |pkt|
c.read pkt.data
puts c.to_s.inspect
sleep 0.1;
end
@pkts.size
end
# Uncomment to run automatically. And again, you shouldn't.
# poison_myself
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment