Last active
October 3, 2015 17:08
-
-
Save todb/2492629 to your computer and use it in GitHub Desktop.
CyanidePill -- poison your own DNS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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