70e368e235
Deploy Examination / deploy (push) Successful in 7s
New topic group: 网络安全专题 (cybersecurity) Subtopics: - web-security: Web 安全基础 (34 questions) - crypto-basics: 密码学基础 (34 questions) - buffer-overflow: 缓冲区溢出与漏洞利用 (34 questions) - os-security: 操作系统安全 (34 questions) - network-attack: 网络攻防 (34 questions) - secure-coding: 安全编程实践 (34 questions) Question types per subtopic: - single_choice × 20 - true_false × 10 - short_answer × 3 - code_reading × 1 Difficulty range: 1-3 (基础)
63 lines
9.4 KiB
JSON
63 lines
9.4 KiB
JSON
{
|
|
"topic": "network-attack",
|
|
"type": "code_reading",
|
|
"schema_version": "1.0.0",
|
|
"generated": "2026-09-17T00:00:00+08:00",
|
|
"questions": [
|
|
{
|
|
"id": "cr-001",
|
|
"type": "code_reading",
|
|
"difficulty": 3,
|
|
"tags": [
|
|
"网络攻防",
|
|
"arp欺骗",
|
|
"端口扫描"
|
|
],
|
|
"question": "阅读以下 Python 代码,回答后面的问题。该程序包含两个功能模块:ARP 欺骗检测器和多线程端口扫描器。",
|
|
"code": "import socket\nimport struct\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\n# ============ Part A: ARP Spoofing Detector ============\ndef detect_arp_spoofing(iface_eth0_table):\n \"\"\"\n Detect ARP spoofing by checking for duplicate MAC addresses\n in the ARP table (simulated).\n \n Args:\n iface_eth0_table: list of (ip, mac) tuples from ARP cache\n Returns:\n list of suspicious (ip, mac, reason) tuples\n \"\"\"\n seen_macs = {} # mac -> [ip_list]\n alerts = []\n \n for ip, mac in iface_eth0_table:\n mac_lower = mac.lower()\n if mac_lower in seen_macs:\n seen_macs[mac_lower].append(ip)\n else:\n seen_macs[mac_lower] = [ip]\n \n for mac, ips in seen_macs.items():\n if len(ips) > 1:\n reason = (f\"MAC {mac} is mapped to multiple IPs: \"\n f\"{', '.join(ips)}. Possible ARP spoofing.\")\n for ip in ips:\n alerts.append((ip, mac, reason))\n \n return alerts\n\n\n# ============ Part B: Multi-threaded TCP SYN Port Scanner ============\ndef tcp_syn_scan(target_ip, ports, timeout=1.5):\n \"\"\"\n Perform a TCP SYN scan on target_ip for given ports.\n Uses raw sockets to send SYN and interpret responses.\n Falls back to connect scan if raw sockets unavailable.\n \n Returns:\n dict: {port: 'open'|'closed'|'filtered'}\n \"\"\"\n results = {}\n use_raw = False\n \n try:\n # Test if raw sockets are available\n s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)\n s.close()\n use_raw = True\n except (PermissionError, OSError):\n pass\n \n if not use_raw:\n # Fallback: full TCP connect scan\n for port in ports:\n try:\n sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n sock.settimeout(timeout)\n code = sock.connect_ex((target_ip, port))\n sock.close()\n results[port] = 'open' if code == 0 else 'closed'\n except socket.timeout:\n results[port] = 'filtered'\n except OSError:\n results[port] = 'closed'\n return results\n \n # Raw socket SYN scan (simplified)\n for port in ports:\n try:\n sock = socket.socket(socket.AF_INET, socket.SOCK_RAW,\n socket.IPPROTO_TCP)\n sock.settimeout(timeout)\n # Build SYN packet (simplified; real impl needs checksum etc.)\n tcp_header = struct.pack('!HHLLBBHHH',\n 12345, port, 0, 0, # sport, dport, seq, ack\n (5 << 4) | 0, 0x02, 65535, 0) # offset, SYN, window, checksum\n sock.sendto(tcp_header, (target_ip, port))\n # Read response\n resp, _ = sock.recvfrom(1024)\n tcp_resp = resp[20:40] # skip IP header\n flags = struct.unpack('!B', tcp_resp[13:14])[0]\n if flags & 0x12 == 0x12: # SYN+ACK\n results[port] = 'open'\n elif flags & 0x04: # RST\n results[port] = 'closed'\n else:\n results[port] = 'filtered'\n sock.close()\n except socket.timeout:\n results[port] = 'filtered'\n except OSError:\n results[port] = 'closed'\n return results\n\n\ndef parallel_scan(target_ip, port_ranges, max_workers=50):\n \"\"\"\n Scan ports in parallel using a thread pool.\n Args:\n target_ip: target IP address string\n port_ranges: list of (start, end) tuples\n max_workers: maximum number of threads\n Returns:\n dict: {port: 'open'|'closed'|'filtered'}\n \"\"\"\n all_ports = []\n for start, end in port_ranges:\n all_ports.extend(range(start, end + 1))\n \n results = {}\n with ThreadPoolExecutor(max_workers=max_workers) as executor:\n future_map = {}\n for port in all_ports:\n fut = executor.submit(\n lambda p: tcp_syn_scan(target_ip, [p]).get(p, 'closed'),\n port\n )\n future_map[fut] = port\n \n for future in as_completed(future_map):\n port = future_map[future]\n try:\n results[port] = future.result()\n except Exception:\n results[port] = 'closed'\n \n return results\n\n\n# ============ Example Usage (commented out) ============\n# ARP table snapshot from router/switch\n# arp_table = [\n# ('192.168.1.1', 'AA:BB:CC:DD:EE:01'),\n# ('192.168.1.10', 'AA:BB:CC:DD:EE:02'),\n# ('192.168.1.11', 'AA:BB:CC:DD:EE:02'), # duplicate MAC!\n# ('192.168.1.20', 'AA:BB:CC:DD:EE:03'),\n# ]\n# print(detect_arp_spoofing(arp_table))\n# \n# scan_result = parallel_scan('192.168.1.1', [(20, 25), (80, 80), (443, 443)])\n# for port, status in sorted(scan_result.items()):\n# print(f'Port {port}: {status}')",
|
|
"language": "python",
|
|
"explanation": "本题通过一段 Python 代码综合考查 ARP 欺骗检测和端口扫描两个网络攻防核心知识点。Part A 的 detect_arp_spoofing 通过检测同一 MAC 对应多个 IP 来发现可疑 ARP 欺骗;Part B 的 tcp_syn_scan 实现了 SYN 扫描与 Connect 扫描的自动切换,parallel_scan 使用线程池并发扫描。子问题分别考查检测逻辑与局限、connect_ex 返回值语义、以及 Python 闭包变量绑定机制。",
|
|
"sub_questions": [
|
|
{
|
|
"index": 1,
|
|
"type": "short_answer",
|
|
"question": "函数 detect_arp_spoofing 的检测逻辑是什么?它存在什么局限性?",
|
|
"answer": "该函数通过扫描 ARP 表,将所有 (IP, MAC) 映射关系按 MAC 地址聚合,当同一个 MAC 地址对应了多个不同的 IP 地址时,判定为可疑 ARP 欺骗并生成告警。局限性包括:(1) 仅能检测同一 MAC 映射多个 IP 的情况,无法检测攻击者将网关 IP 映射到自身 MAC(一对一替换)的场景;(2) 依赖静态快照而非实时流量监控,可能漏报动态切换的攻击;(3) 无法区分合法的多宿主(multi-homing)主机与攻击行为。",
|
|
"explanation": "该函数的核心思想是'一个MAC地址不应对应多个IP',这是一种启发式检测方法。但高级攻击者可以让不同MAC地址各伪装一个IP,绕过此检测。实际的 ARP 欺骗检测工具(如 arpwatch)还会结合 ARP 请求/应答的频率、主动发送 ARP 探测包等方式增强检测能力。"
|
|
},
|
|
{
|
|
"index": 2,
|
|
"type": "multiple_choice",
|
|
"question": "在 tcp_syn_scan 函数中,当 raw socket 不可用时,fallback 到 connect 扫描。以下关于 connect_ex 方法的返回值 code 的说法,正确的是:",
|
|
"options": {
|
|
"A": "code == 0 表示连接被拒绝,端口关闭",
|
|
"B": "code == 0 表示连接成功建立,端口开放",
|
|
"C": "code == 0 表示目标主机不可达",
|
|
"D": "code 的值与端口状态无关,始终需要额外判断"
|
|
},
|
|
"answer": [
|
|
"B"
|
|
],
|
|
"explanation": "socket.connect_ex() 是 connect() 的非异常版本:连接成功时返回 0(对应 TCP 三次握手完成,端口开放);连接被拒绝时返回 ECONNREFUSED 错误码(非零值,端口关闭);超时则由 settimeout 控制抛出 timeout 异常。因此 code == 0 表示端口开放,选项 B 正确。"
|
|
},
|
|
{
|
|
"index": 3,
|
|
"type": "multiple_choice",
|
|
"question": "parallel_scan 函数中使用了 ThreadPoolExecutor 进行并发扫描。关于其中 lambda 表达式的写法,以下说法正确的是:",
|
|
"options": {
|
|
"A": "lambda p: tcp_syn_scan(target_ip, [p]).get(p, 'closed') 中 p 会正确绑定到 each port",
|
|
"B": "这里存在经典的 Python 闭包陷阱,所有线程可能扫描同一个端口",
|
|
"C": "该写法完全正确,因为 executor.submit 会在提交时立即求值 lambda 的参数",
|
|
"D": "为避免潜在的闭包问题,应将 port 作为参数显式传递而非依赖闭包捕获"
|
|
},
|
|
"answer": [
|
|
"A"
|
|
],
|
|
"explanation": "在这段代码中,lambda p: ... 定义了一个接受参数 p 的函数,而 executor.submit(func, port) 在每次循环中将当前的 port 值作为参数传入。这与经典的闭包陷阱不同——闭包陷阱通常出现在直接在循环内使用 lambda 不带参数、依赖外层循环变量的情况(如 lambda: f(i))。此处 lambda 显式声明了参数 p,并通过 submit 的参数传递获得值,不存在变量共享问题,因此所有线程会正确扫描各自分配的端口。"
|
|
}
|
|
],
|
|
"source": null,
|
|
"related": []
|
|
}
|
|
]
|
|
} |