diff --git a/topics/cybersecurity/buffer-overflow/code_reading.json b/topics/cybersecurity/buffer-overflow/code_reading.json new file mode 100644 index 0000000..7646cf1 --- /dev/null +++ b/topics/cybersecurity/buffer-overflow/code_reading.json @@ -0,0 +1,71 @@ +{ + "topic": "buffer-overflow", + "type": "code_reading", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "cr-001", + "type": "code_reading", + "difficulty": 2, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "code": "#include \n#include \n\nvoid vulnerable_function(char *input) {\n char buffer[64];\n strcpy(buffer, input);\n printf(\"Input received: %s\\n\", buffer);\n}\n\nint main(int argc, char *argv[]) {\n if (argc != 2) {\n printf(\"Usage: %s \\n\", argv[0]);\n return 1;\n }\n vulnerable_function(argv[1]);\n return 0;\n}", + "language": "c", + "sub_questions": [ + { + "index": 1, + "type": "single_choice", + "question": "这段代码存在什么安全漏洞?", + "options": { + "A": "SQL 注入漏洞", + "B": "缓冲区溢出漏洞", + "C": "跨站脚本(XSS)漏洞", + "D": "整数溢出漏洞" + }, + "answer": "B", + "explanation": "vulnerable_function 中使用 strcpy() 将用户输入复制到大小为 64 字节的 buffer 中。由于 strcpy() 不检查输入长度,当 argv[1] 的长度超过 63 字节(含 '\\0' 终止符)时,会发生缓冲区溢出,覆盖栈上 buffer 之后的数据。这是典型的栈缓冲区溢出漏洞。" + }, + { + "index": 2, + "type": "single_choice", + "question": "如果 argv[1] 的长度为 200 字节,在 32 位 x86 系统上,溢出数据可能覆盖以下哪些内容?(不考虑编译器保护)", + "options": { + "A": "仅 buffer 数组的相邻变量", + "B": "buffer 数组、保存的 EBP 和函数返回地址", + "C": "仅函数返回地址", + "D": "全局变量区的数据" + }, + "answer": "B", + "explanation": "buffer[64] 位于栈上,溢出 200 字节的数据会依次覆盖:buffer 之后的栈空间、保存的 EBP(帧指针,通常 4 字节)、函数返回地址(4 字节),以及 main 函数的栈帧内容。200 字节远超 buffer 大小,足以覆盖返回地址及更多栈内容。注意这里假设没有 Stack Canary 等保护。" + }, + { + "index": 3, + "type": "single_choice", + "question": "如何修复这段代码中的安全漏洞?", + "options": { + "A": "将 buffer 大小改为 256", + "B": "使用 strncpy(buffer, input, sizeof(buffer)) 并确保字符串以 '\\0' 结尾", + "C": "在 strcpy 之前检查 strlen(input) < 64 即可,无需更换函数", + "D": "将 buffer 声明为 static 变量" + }, + "answer": "B", + "explanation": "最正确的修复是使用有长度限制的安全函数。strncpy(buffer, input, sizeof(buffer)) 限制复制的字节数不超过 buffer 大小,并且需要确保 buffer[sizeof(buffer)-1] = '\\0' 以保证字符串终止。选项 A 只是增大了缓冲区但未消除漏洞。选项 C 的检查存在 TOCTOU(检查与使用之间的时间窗口)问题,且仍有竞态风险。选项 D 改变存储位置但不解决溢出问题。" + }, + { + "index": 4, + "type": "short_answer", + "question": "假设该程序使用 gcc -fstack-protector-strong 编译,当发生栈溢出时程序的行为会有什么变化?请简要解释。", + "answer": "使用 -fstack-protector-strong 编译后,编译器会在 buffer 和保存的 EBP 之间插入一个 Stack Canary 随机值。当 buffer 发生溢出时,溢出数据会先覆盖 canary 值。在函数返回之前,程序会检查 canary 是否被修改。如果 canary 被篡改,程序会调用 __stack_chk_fail() 函数,输出错误信息(如 '*** stack smashing detected ***')并终止程序(abort),从而阻止攻击者利用溢出劫持执行流。", + "explanation": "Stack Canary 是一种编译器级别的栈溢出检测机制。编译器在函数的局部变量和保存的寄存器/返回地址之间插入一个随机的 canary 值。函数返回前检查该值的完整性,若被修改则立即终止程序。这能有效检测并阻止栈溢出攻击,但攻击者仍可能通过信息泄露获取 canary 值来绕过此保护。" + } + ], + "explanation": "", + "source": null, + "related": [], + "question": "请阅读以下代码,分析其中的安全问题并回答相关问题。" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/buffer-overflow/meta.json b/topics/cybersecurity/buffer-overflow/meta.json new file mode 100644 index 0000000..747b029 --- /dev/null +++ b/topics/cybersecurity/buffer-overflow/meta.json @@ -0,0 +1,29 @@ +{ + "slug": "buffer-overflow", + "name": "缓冲区溢出与漏洞利用", + "description": "栈溢出、ROP、ASLR/DEP 绕过", + "tags": [ + "缓冲区溢出", + "栈溢出", + "rop", + "aslr", + "dep", + "漏洞利用" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/buffer-overflow/short_answer.json b/topics/cybersecurity/buffer-overflow/short_answer.json new file mode 100644 index 0000000..d88baa3 --- /dev/null +++ b/topics/cybersecurity/buffer-overflow/short_answer.json @@ -0,0 +1,81 @@ +{ + "topic": "buffer-overflow", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "栈溢出", + "漏洞利用" + ], + "question": "请简述经典的栈溢出攻击的基本步骤,包括攻击者如何控制程序执行流。", + "answer": "经典的栈溢出攻击步骤如下:(1) 攻击者向程序输入超长数据,使数据超出目标缓冲区的大小;(2) 溢出的数据依次覆盖保存的 EBP(帧指针)和函数返回地址;(3) 攻击者将返回地址修改为 shellcode 的地址或其他目标地址(如 system() 函数);(4) 当被溢出的函数执行 return 指令时,CPU 从栈上弹出被篡改的返回地址并跳转到该地址执行;(5) 程序执行流被劫持到攻击者指定的恶意代码。", + "keywords": [ + "返回地址", + "溢出", + "覆盖", + "shellcode", + "执行流", + "EBP", + "return" + ], + "scoring_rubric": "满分需描述:(1) 输入超长数据导致溢出(2分);(2) 覆盖返回地址(2分);(3) 将返回地址修改为恶意地址(2分);(4) 函数返回时跳转到恶意代码执行(2分);(5) 整体逻辑清晰连贯(2分)。部分正确按点给分。", + "source": null, + "related": [], + "explanation": "" + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "aslr", + "dep", + "漏洞利用" + ], + "question": "分别解释 DEP 和 ASLR 的防御原理,并说明它们各自可以防御哪类攻击。", + "answer": "DEP(数据执行保护):通过硬件 NX/XD 位将栈、堆等数据内存区域标记为不可执行。当 CPU 尝试在这些区域执行指令时会触发异常。DEP 主要防御直接在栈/堆上注入并执行 shellcode 的攻击方式。ASLR(地址空间布局随机化):在每次程序运行时随机化栈、堆、共享库、PIE 可执行文件等关键内存区域的基地址。ASLR 主要防御需要预先知道内存地址的攻击,如精确跳转到 shellcode 或已知函数地址的攻击。两者互补:DEP 阻止代码注入,ASLR 阻止地址预测,但都不能单独防御所有攻击(如信息泄露+ROP 可以同时绕过两者)。", + "keywords": [ + "NX", + "不可执行", + "随机化", + "基地址", + "shellcode", + "地址预测", + "互补" + ], + "scoring_rubric": "满分需包含:(1) DEP 原理——NX 位/数据区域不可执行(2分);(2) DEP 防御——阻止 shellcode 执行(1分);(3) ASLR 原理——随机化内存布局/基地址(2分);(4) ASLR 防御——阻止地址预测(1分);(5) 说明两者互补但可被绕过(2分);(6) 表述准确完整(2分)。", + "source": null, + "related": [], + "explanation": "" + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 3, + "tags": [ + "rop", + "漏洞利用" + ], + "question": "什么是 ROP(Return-Oriented Programming)?请解释其基本原理、与传统 shellcode 注入的区别,以及为什么 ROP 能绕过 DEP 保护。", + "answer": "ROP(面向返回的编程)是一种代码复用攻击技术。基本原理:攻击者在程序或共享库的已执行代码段中搜索以 ret 指令结尾的短小指令序列(称为 gadget),然后在栈上精心布局这些 gadget 的地址。每次函数返回(ret)时,程序跳转到栈上下一个 gadget 的地址执行,执行完该 gadget 的指令后又遇到 ret,继续跳转到下一个 gadget,如此串联形成攻击链。与传统 shellcode 注入的区别:(1) 不需要在数据区域注入新代码;(2) 利用的是已有的、合法的、可执行的代码片段。能绕过 DEP 的原因:DEP 将数据区域标记为不可执行,但 ROP 使用的 gadget 位于代码段(.text),代码段本身就是可执行的,因此不触发 DEP 保护。", + "keywords": [ + "gadget", + "ret", + "代码复用", + "代码段", + "可执行", + "不注入新代码", + "串联" + ], + "scoring_rubric": "满分需包含:(1) ROP 基本定义——代码复用攻击(2分);(2) gadget 概念——以 ret 结尾的代码片段(2分);(3) 执行方式——栈上布局 gadget 地址,ret 串联执行(2分);(4) 与 shellcode 注入的区别——不注入新代码(2分);(5) 为何绕过 DEP——gadget 位于可执行代码段(2分)。", + "source": null, + "related": [], + "explanation": "" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/buffer-overflow/single_choice.json b/topics/cybersecurity/buffer-overflow/single_choice.json new file mode 100644 index 0000000..85bb5ab --- /dev/null +++ b/topics/cybersecurity/buffer-overflow/single_choice.json @@ -0,0 +1,405 @@ +{ + "topic": "buffer-overflow", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "缓冲区溢出的根本原因是什么?", + "options": { + "A": "编译器优化错误", + "B": "程序向缓冲区写入的数据超过了其分配的大小", + "C": "操作系统内存管理缺陷", + "D": "CPU 指令集设计缺陷" + }, + "answer": "B", + "explanation": "缓冲区溢出的根本原因是程序在向缓冲区写入数据时,没有对写入长度进行有效检查,导致写入的数据量超过了缓冲区预先分配的大小。超出部分会覆盖相邻内存区域的内容。编译器优化、操作系统和 CPU 设计本身并不是导致缓冲区溢出的直接原因。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "在 x86 架构中,栈溢出攻击通常会覆盖哪个关键数据来控制程序执行流?", + "options": { + "A": "全局变量", + "B": "堆元数据", + "C": "函数返回地址", + "D": "段选择子" + }, + "answer": "C", + "explanation": "在 x86 架构的函数调用约定中,函数返回地址保存在栈上,紧邻局部变量之上。当局部缓冲区发生栈溢出时,溢出的数据会覆盖返回地址。攻击者通过精心构造的输入将返回地址修改为恶意代码(如 shellcode)的地址,从而劫持程序执行流。全局变量位于数据段而非栈上,堆元数据在堆上,段选择子是段描述符的索引,这些都不是栈溢出的直接目标。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "以下哪个 C 语言函数最常被用于演示缓冲区溢出漏洞?", + "options": { + "A": "strncpy()", + "B": "strcpy()", + "C": "snprintf()", + "D": "memcpy_s()" + }, + "answer": "B", + "explanation": "strcpy() 函数不会检查源字符串的长度,直接将其复制到目标缓冲区,直到遇到源字符串的终止符 '\\0' 为止。如果源字符串长度超过目标缓冲区大小,就会发生缓冲区溢出。strncpy() 有长度限制参数,snprintf() 会限制输出长度,memcpy_s() 是 C11 引入的安全版本,它们都在一定程度上防止了溢出。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "栈溢出" + ], + "question": "在 x86(32位)函数调用中,以下哪种栈帧布局描述是正确的?", + "options": { + "A": "从低地址到高地址依次为:返回地址、保存的 EBP、局部变量、函数参数", + "B": "从低地址到高地址依次为:局部变量、保存的 EBP、返回地址、函数参数", + "C": "从低地址到高地址依次为:函数参数、返回地址、保存的 EBP、局部变量", + "D": "从低地址到高地址依次为:局部变量、返回地址、保存的 EBP、函数参数" + }, + "answer": "B", + "explanation": "在 x86 栈中,栈从高地址向低地址增长。函数调用时,参数先入栈(高地址),然后是返回地址,再是保存的旧 EBP,最后是局部变量(低地址)。因此从低地址到高地址的顺序是:局部变量 → 保存的 EBP → 返回地址 → 函数参数。这就是为什么溢出局部变量缓冲区时,可以依次覆盖保存的 EBP 和返回地址。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "栈溢出" + ], + "question": "NOP sled(NOP 滑板)在缓冲区溢出攻击中的作用是什么?", + "options": { + "A": "加密 shellcode 以绕过检测", + "B": "增大跳转到 shellcode 的命中范围", + "C": "防止程序崩溃", + "D": "清空寄存器中的残留数据" + }, + "answer": "B", + "explanation": "NOP sled 是一连串 NOP(No Operation,x86 中为 0x90)指令,放在 shellcode 前面。攻击者不需要精确跳转到 shellcode 的起始地址,只要跳转到 NOP sled 中的任意位置,CPU 就会依次执行 NOP 指令「滑」到 shellcode。这大大提高了攻击的成功率,因为跳转地址只需落在 NOP sled 范围内即可,而非精确命中 shellcode 的第一个字节。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "dep", + "漏洞利用" + ], + "question": "DEP(Data Execution Prevention,数据执行保护)的基本原理是什么?", + "options": { + "A": "对栈内存进行加密", + "B": "将栈和堆等数据区域标记为不可执行", + "C": "随机化进程地址空间布局", + "D": "检查所有函数调用的参数合法性" + }, + "answer": "B", + "explanation": "DEP(数据执行保护)通过将栈、堆等数据内存区域标记为不可执行(NX 位 / XD 位),防止攻击者直接在这些区域注入并执行 shellcode。当程序试图执行数据区域的指令时,会触发硬件异常。这迫使攻击者寻找其他技术(如 ROP)来绕过 DEP。选项 A 描述的是栈加密,选项 C 描述的是 ASLR,选项 D 描述的是参数校验,都不是 DEP 的原理。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "aslr", + "漏洞利用" + ], + "question": "ASLR(Address Space Layout Randomization)通过什么方式提高系统安全性?", + "options": { + "A": "对代码段进行加密", + "B": "每次程序运行时随机化关键内存区域的基地址", + "C": "禁用栈上的可执行权限", + "D": "使用 canary 值检测栈溢出" + }, + "answer": "B", + "explanation": "ASLR(地址空间布局随机化)在每次程序运行时,随机化栈、堆、共享库和可执行文件基址等关键内存区域的加载地址。这使得攻击者无法预先确定 shellcode 或 ROP gadget 的内存地址,大大增加了利用的难度。选项 A 描述代码加密,选项 C 描述 DEP/NX,选项 D 描述的是 Stack Canary,都不是 ASLR 的机制。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "rop", + "漏洞利用" + ], + "question": "ROP(Return-Oriented Programming,面向返回的编程)的核心思想是什么?", + "options": { + "A": "直接在栈上注入并执行 shellcode", + "B": "利用程序或共享库中已有的代码片段(gadget),通过连续的 ret 指令串联执行", + "C": "修改堆元数据来控制程序行为", + "D": "利用格式化字符串漏洞读写内存" + }, + "answer": "B", + "explanation": "ROP 的核心思想是不注入新代码(从而绕过 DEP),而是利用程序自身或共享库中已存在的代码片段(称为 gadget)。每个 gadget 以 ret 指令结尾,攻击者在栈上精心布局多个 gadget 的地址,使得每次 ret 跳转到下一个 gadget,从而实现任意计算。这种方式利用已有可执行代码,因此可以绕过将数据区域标记为不可执行的 DEP 保护。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "rop", + "漏洞利用" + ], + "question": "在 ROP 攻击中,「gadget」是指什么?", + "options": { + "A": "攻击者编写的一段完整恶意程序", + "B": "以 ret 指令结尾的短小代码片段", + "C": "操作系统提供的系统调用接口", + "D": "编译器插入的安全检查代码" + }, + "answer": "B", + "explanation": "在 ROP 攻击中,gadget 是指程序或共享库中已存在的、以 ret(或等价的间接跳转)指令结尾的短小代码片段。这些片段本身可能是编译后的合法指令序列的尾部部分,但由于 x86 变长指令编码的特性,从不同偏移开始解码可以得到不同的指令序列。攻击者将这些 gadget 的地址排列在栈上,通过 ret 指令逐个跳转执行,完成恶意逻辑。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "以下哪个函数调用存在缓冲区溢出风险?", + "options": { + "A": "fgets(buf, sizeof(buf), stdin)", + "B": "gets(buf)", + "C": "snprintf(buf, sizeof(buf), \"%s\", input)", + "D": "strncpy(buf, input, sizeof(buf))" + }, + "answer": "B", + "explanation": "gets() 函数没有长度限制参数,它会持续读取输入直到遇到换行符或 EOF,完全不检查目标缓冲区的大小,是缓冲区溢出的经典元凶。POSIX 和 C11 标准已将其废弃。fgets() 有长度限制,snprintf() 会按格式化长度截断,strncpy() 有长度参数,它们都提供了长度控制机制(虽然使用不当仍可能有问题,但 gets() 是天生不安全的)。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "栈溢出", + "漏洞利用" + ], + "question": "Stack Canary(栈金丝雀)的工作原理是什么?", + "options": { + "A": "在栈帧底部插入一个随机值,函数返回前检查该值是否被修改", + "B": "对栈上的所有数据进行加密", + "C": "在每次函数调用时随机化栈的起始地址", + "D": "限制程序只能执行白名单中的系统调用" + }, + "answer": "A", + "explanation": "Stack Canary 是一种栈溢出检测机制。编译器在函数的局部变量和保存的 EBP/返回地址之间插入一个随机值(canary)。函数返回前,程序检查该值是否仍然完整。如果发生栈溢出,溢出数据必然先覆盖 canary,程序在检查时就会发现 canary 被篡改并终止执行。这个概念来源于煤矿中用金丝雀检测有毒气体的做法。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "aslr", + "漏洞利用" + ], + "question": "ASLR 在什么情况下可能被绕过?", + "options": { + "A": "当程序使用了 -fstack-protector 编译选项时", + "B": "当攻击者能够通过信息泄露获取某个内存地址时", + "C": "当系统启用了 DEP 保护时", + "D": "当程序使用动态链接时" + }, + "answer": "B", + "explanation": "ASLR 的安全性依赖于地址的不可预测性。如果攻击者能够通过某种漏洞(如格式化字符串漏洞、未初始化内存读取等)泄露一个已知模块的内存地址,就可以计算出该模块的基地址偏移,进而推导出其他区域的地址。这使得 ASLR 保护被绕过。编译选项、DEP 和动态链接本身不会导致 ASLR 被绕过。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "栈溢出" + ], + "question": "在 64 位系统上,函数参数的传递方式与 32 位有什么关键区别?", + "options": { + "A": "64 位系统所有参数仍然通过栈传递", + "B": "64 位系统(System V AMD64 ABI)前 6 个整数参数通过寄存器传递", + "C": "64 位系统不使用栈来保存返回地址", + "D": "64 位系统不支持函数调用" + }, + "answer": "B", + "explanation": "在 System V AMD64 ABI(Linux/macOS 64 位)中,前 6 个整数/指针参数通过寄存器 RDI、RSI、RDX、RCX、R8、R9 传递,浮点参数通过 XMM 寄存器传递,超出部分才通过栈传递。这意味着在 64 位系统上进行 ROP 攻击时,需要先使用 pop gadget 控制寄存器,增加了利用复杂度。Windows x64 的约定略有不同(前 4 个参数使用 RCX、RDX、R8、R9)。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "以下哪个编译选项可以启用 Stack Canary 保护?", + "options": { + "A": "-fno-stack-protector", + "B": "-fstack-protector-strong", + "C": "-O2", + "D": "-fPIC" + }, + "answer": "B", + "explanation": "-fstack-protector-strong 是 GCC 提供的启用 Stack Canary 保护的编译选项,它会对所有可能存在溢出风险的函数(包括使用了局部数组的函数)启用 canary 检查。-fno-stack-protector 是禁用该保护。-O2 是优化级别选项,-fPIC 是生成位置无关代码的选项,它们都不直接涉及 canary 保护。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "dep", + "aslr", + "漏洞利用" + ], + "question": "以下哪种技术组合可以同时绕过 DEP 和 ASLR?", + "options": { + "A": "单纯的栈溢出 + shellcode 注入", + "B": "信息泄露获取基地址 + ROP 链调用 mprotect 或 VirtualProtect", + "C": "使用 gets() 替代 fgets()", + "D": "关闭编译器优化选项" + }, + "answer": "B", + "explanation": "要同时绕过 DEP 和 ASLR,攻击者通常需要两步:(1) 通过信息泄露漏洞获取某个已知地址(如 libc 基址),从而绕过 ASLR;(2) 利用 ROP 技术调用 mprotect()(Linux)或 VirtualProtect()(Windows)将某块内存区域标记为可执行,然后在该区域执行 shellcode,从而绕过 DEP。单纯的 shellcode 注入会被 DEP 阻止,gets() 的使用与保护绕过无关,关闭优化也不会绕过这些保护。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "aslr", + "漏洞利用" + ], + "question": "在 Linux 系统中,以下哪个操作可以查看当前进程是否启用了 ASLR?", + "options": { + "A": "cat /proc/sys/kernel/randomize_va_space", + "B": "cat /proc/sys/vm/overcommit_memory", + "C": "sysctl -w kernel.exec-shield=1", + "D": "cat /etc/security/limits.conf" + }, + "answer": "A", + "explanation": "/proc/sys/kernel/randomize_va_space 文件控制 ASLR 的状态:0 表示禁用,1 表示部分随机化(仅随机化栈和共享库),2 表示完全随机化(还包括堆和 PIE)。overcommit_memory 控制内存过量提交策略,exec-shield 是旧的内核安全特性,limits.conf 配置资源限制,它们都不是直接查看或控制 ASLR 的接口。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "格式化字符串漏洞与缓冲区溢出有什么关系?", + "options": { + "A": "它们是完全不同的漏洞类型,没有关联", + "B": "格式化字符串漏洞可以用于信息泄露(如泄露栈上数据),辅助绕过 ASLR,为缓冲区溢出攻击铺路", + "C": "格式化字符串漏洞就是缓冲区溢出的一种形式", + "D": "格式化字符串漏洞只能读取数据,不能写入" + }, + "answer": "B", + "explanation": "格式化字符串漏洞和缓冲区溢出是不同的漏洞类型,但可以配合使用。格式化字符串漏洞允许攻击者通过 %x/%p 等格式符泄露栈上数据(包括 canary 值、返回地址、libc 地址等),也可以通过 %n 写入任意内存。这些能力可以辅助缓冲区溢出攻击:泄露地址绕过 ASLR,泄露 canary 值绕过栈保护。选项 C 不准确,格式化字符串漏洞的核心是格式化函数的参数控制。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "rop", + "aslr", + "漏洞利用" + ], + "question": "什么是 ret2libc 攻击?", + "options": { + "A": "一种将 shellcode 注入 libc 函数的方法", + "B": "一种利用 libc 库中已有函数(如 system())而非注入代码的攻击技术", + "C": "一种修改 libc 源代码的后门技术", + "D": "一种使程序静态链接 libc 的编译方法" + }, + "answer": "B", + "explanation": "ret2libc 是 ROP 的一种早期形式。攻击者不注入新的可执行代码(绕过 DEP),而是将栈上的返回地址修改为 libc 中已有的函数地址(如 system()、execve()),并在栈上布置该函数的参数。例如,跳转到 system(\"/bin/sh\") 来获取 shell。它是 ROP 技术的简化版——只使用一个 gadget(函数),而非构造完整的 gadget 链。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "以下哪个工具最常用于发现二进制程序中的 ROP gadget?", + "options": { + "A": "nmap", + "B": "ROPgadget / Ropper", + "C": "GDB", + "D": "strace" + }, + "answer": "B", + "explanation": "ROPgadget 和 Ropper 是专门用于在二进制文件中搜索可用 ROP gadget 的工具。它们会扫描可执行段,寻找以 ret、jmp 或 call 结尾的有用指令序列,并列出所有可用于构造 ROP 链的 gadget。nmap 是网络扫描工具,GDB 是调试器(虽然可以辅助分析但不是专门的 gadget 搜索工具),strace 是系统调用追踪工具。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "缓冲区溢出", + "栈溢出" + ], + "question": "以下哪种堆溢出利用技术描述是正确的?", + "options": { + "A": "堆溢出只能覆盖相邻堆块的数据,无法控制执行流", + "B": "堆溢出可以通过覆盖堆块的元数据(如 chunk header),在后续的堆操作中实现任意地址写入,进而控制执行流", + "C": "堆溢出和栈溢出的利用方式完全相同", + "D": "堆溢出只能导致程序崩溃,不能被利用" + }, + "answer": "B", + "explanation": "堆溢出可以覆盖相邻堆块的 chunk header(包含 size、标志位和前后指针等元数据)。当程序执行 free() 或 malloc() 等堆操作时,glibc 的堆管理器(ptmalloc2)会读取并使用这些被篡改的元数据,可能导致 unlink 操作中写入任意地址(即 write-what-where),进而覆盖 GOT 表项、函数指针等关键数据来劫持执行流。这种技术比栈溢出更复杂,但同样可以实现代码执行。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/buffer-overflow/true_false.json b/topics/cybersecurity/buffer-overflow/true_false.json new file mode 100644 index 0000000..a27c27d --- /dev/null +++ b/topics/cybersecurity/buffer-overflow/true_false.json @@ -0,0 +1,142 @@ +{ + "topic": "buffer-overflow", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "C 语言中的 strcpy() 函数会自动检查目标缓冲区的大小,防止溢出。", + "answer": false, + "explanation": "strcpy() 不会检查目标缓冲区的大小,它只是简单地将源字符串复制到目标地址直到遇到 '\\0' 终止符。这是它成为缓冲区溢出经典案例的根本原因。要安全地复制字符串,应使用 strncpy()、strlcpy() 或 strcpy_s() 等带长度限制的替代函数。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 2, + "tags": [ + "dep", + "漏洞利用" + ], + "question": "DEP(数据执行保护)可以完全阻止所有类型的缓冲区溢出攻击。", + "answer": false, + "explanation": "DEP 只能阻止在数据区域(如栈和堆)直接执行注入代码的攻击方式。但它无法阻止 ROP(Return-Oriented Programming)等不注入新代码的攻击技术,因为 ROP 利用的是已有可执行代码段中的 gadget。此外,JIT-Spray 等技术也可以绕过 DEP。因此 DEP 是重要的安全层,但并非万能。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "aslr", + "漏洞利用" + ], + "question": "当系统启用了 ASLR 后,攻击者就无法预测任何内存地址,因此缓冲区溢出攻击不可能成功。", + "answer": false, + "explanation": "ASLR 虽然随机化了内存布局,但可以通过信息泄露漏洞(如格式化字符串漏洞、越界读取等)获取已知模块的地址,从而计算偏移量绕过 ASLR。此外,部分系统中 ASLR 的熵可能不足(如 32 位系统的随机化空间有限),暴力破解也是可行的。还存在部分覆盖(partial overwrite)等技术。因此 ASLR 大大提高了攻击难度,但并非不可绕过。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 1, + "tags": [ + "栈溢出" + ], + "question": "栈的增长方向是从高地址向低地址,而栈上缓冲区的写入方向通常是从低地址向高地址。", + "answer": true, + "explanation": "在 x86/x64 架构中,栈指针(ESP/RSP)在 push 操作时递减(向低地址增长),这是栈的生长方向。而数组/缓冲区的元素按索引递增方向存储(从低地址到高地址)。正是这两个方向的不一致,使得缓冲区溢出可以向高地址方向覆盖保存的 EBP 和返回地址,从而实现栈溢出攻击。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 2, + "tags": [ + "rop", + "漏洞利用" + ], + "question": "ROP 攻击需要注入新的可执行代码到目标进程中。", + "answer": false, + "explanation": "ROP(Return-Oriented Programming)的核心特点恰恰是不注入任何新代码。它完全利用目标程序或其加载的共享库中已存在的代码片段(gadget),通过在栈上精心布局 gadget 地址,利用 ret 指令串联执行。正是因为不注入新代码,ROP 可以绕过将数据区域标记为不可执行的 DEP 保护。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "gets() 函数已被 C11 标准正式废弃,不推荐在任何场景中使用。", + "answer": true, + "explanation": "gets() 函数由于天生不安全(没有任何方式限制读取长度),已被 C11 标准正式移除。POSIX 标准也已将其标记为废弃。如果程序中仍在使用 gets(),应替换为 fgets() 或其他带长度限制的输入函数。几乎所有现代编译器在检测到 gets() 使用时都会发出警告。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 2, + "tags": [ + "栈溢出" + ], + "question": "Stack Canary 的值在每次进程运行时都是相同的,但每次函数调用时会变化。", + "answer": false, + "explanation": "在现代实现中,Stack Canary 的值在进程启动时随机生成(通过 /dev/urandom 等),每次进程运行时值都不同。在进程生命周期内,同一函数的 canary 值通常保持不变(进程级随机化)。有些实现可能在 fork 时刷新 canary,但典型的用户态程序中 canary 在进程级别是固定的,而非每次函数调用都变化。关键在于每次进程运行时的值不同,使得攻击者无法预先知道 canary。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "缓冲区溢出漏洞只存在于 C/C++ 等不提供自动内存管理的语言中。", + "answer": true, + "explanation": "缓冲区溢出的根源是缺乏自动边界检查和内存安全保护。C/C++ 直接操作内存,不提供数组边界检查,是缓冲区溢出的高发语言。Java、Python 等语言提供自动内存管理和边界检查,不会产生经典的缓冲区溢出。但需注意:这些语言的底层解释器/虚拟机如果是用 C/C++ 实现的,其自身可能存在缓冲区溢出漏洞。从语言特性层面来说,该说法基本正确。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 2, + "tags": [ + "aslr", + "漏洞利用" + ], + "question": "在 Linux 系统中,非 PIE(Position Independent Executable)的可执行文件加载地址是固定的,其代码段地址不受 ASLR 影响。", + "answer": true, + "explanation": "在 Linux 中,只有位置无关可执行文件(PIE,使用 -fPIE -pie 编译)的加载地址才会被 ASLR 随机化。非 PIE 可执行文件的代码段(.text)加载到固定的基地址(如 0x400000),不受 ASLR 影响。但其栈、堆、共享库(libc 等)地址仍然会被随机化。因此开启 PIE(编译选项 -pie)是提高 ASLR 有效性的重要措施。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 1, + "tags": [ + "缓冲区溢出" + ], + "question": "地址无关代码(PIC / PIE)是一种缓解缓冲区溢出利用的防御机制。", + "answer": true, + "explanation": "PIE(Position Independent Executable)使可执行文件的代码段在每次加载时地址随机化,配合 ASLR 使得攻击者无法预测代码段地址,从而更难构造 ROP 链或预测 gadget 位置。虽然 PIE 本身不阻止缓冲区溢出的发生,但它是重要的利用缓解措施,提高了攻击者利用溢出漏洞的难度。这是纵深防御策略的一部分。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/crypto-basics/code_reading.json b/topics/cybersecurity/crypto-basics/code_reading.json new file mode 100644 index 0000000..064bc39 --- /dev/null +++ b/topics/cybersecurity/crypto-basics/code_reading.json @@ -0,0 +1,54 @@ +{ + "topic": "crypto-basics", + "type": "code_reading", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "cr-001", + "type": "code_reading", + "difficulty": 2, + "tags": [ + "密码学", + "哈希", + "加密", + "数字签名" + ], + "question": "阅读以下 Python 代码,回答问题。", + "code": "import hashlib\nimport hmac\nimport secrets\n\n# ========== 第一部分:哈希计算 ==========\ndef compute_sha256(message: str) -> str:\n \"\"\"计算字符串的 SHA-256 哈希值\"\"\"\n return hashlib.sha256(message.encode('utf-8')).hexdigest()\n\n# ========== 第二部分:HMAC 认证 ==========\ndef generate_hmac(key: bytes, message: str) -> str:\n \"\"\"使用 HMAC-SHA256 生成消息认证码\"\"\"\n return hmac.new(key, message.encode('utf-8'), hashlib.sha256).hexdigest()\n\ndef verify_hmac(key: bytes, message: str, expected_mac: str) -> bool:\n \"\"\"验证 HMAC,使用 compare_digest 防止时序攻击\"\"\"\n computed_mac = generate_hmac(key, message)\n return hmac.compare_digest(computed_mac, expected_mac)\n\n# ========== 第三部分:简单密码哈希 ==========\ndef hash_password(password: str) -> str:\n salt = secrets.token_hex(16) # 32 字符的随机盐\n salted = salt + password\n hash_val = compute_sha256(salted)\n return f\"{salt}${hash_val}\"\n\ndef check_password(stored: str, password: str) -> bool:\n salt, hash_val = stored.split('$', 1)\n return compute_sha256(salt + password) == hash_val\n\n# ========== 主程序 ==========\nif __name__ == '__main__':\n msg = \"Hello, Cryptography!\"\n print(f\"SHA-256: {compute_sha256(msg)}\")\n\n key = secrets.token_bytes(32)\n mac = generate_hmac(key, msg)\n print(f\"HMAC: {mac}\")\n print(f\"Verify: {verify_hmac(key, msg, mac)}\")\n\n stored = hash_password(\"my_secret_123\")\n print(f\"Stored: {stored}\")\n print(f\"Check: {check_password(stored, 'my_secret_123')}\")", + "language": "python", + "sub_questions": [ + { + "index": 1, + "type": "short_answer", + "question": "函数 generate_hmac 与直接对消息做 SHA-256 哈希相比,核心区别是什么?为什么 verify_hmac 中使用 hmac.compare_digest 而不是普通的 == 运算符?", + "answer": "核心区别:generate_hmac 使用 HMAC(基于密钥的哈希消息认证码),除了保证消息完整性外,还提供了消息认证(即只有持有共享密钥的人才能生成合法的 MAC);而普通 SHA-256 哈希不涉及密钥,任何人都可以计算,仅能用于完整性校验,无法验证消息来源。使用 hmac.compare_digest 是因为它执行恒定时间的字符串比较,可以防止时序攻击(timing attack)——如果使用 ==,Python 的字符串比较会在发现第一个不匹配字符时立即返回,攻击者可以通过测量响应时间逐字节猜测正确的 MAC 值。", + "explanation": "HMAC 是密码学中最常用的消息认证码构造方式,由 Bellare 等人于 1996 年提出。它将密钥与消息结合进行哈希,安全性可归约到底层哈希函数的性质。时序攻击是侧信道攻击的一种,compare_digest 是 Python 标准库提供的安全比较函数。" + }, + { + "index": 2, + "type": "single_choice", + "question": "函数 hash_password 中使用 secrets.token_hex(16) 生成盐值。以下关于盐(salt)在密码哈希中作用的描述,哪一项最准确?", + "options": { + "A": "盐的主要作用是增加密码的熵,使暴力破解单个密码所需时间大幅增加", + "B": "盐的主要作用是确保即使两个用户使用相同密码,其存储的哈希值也不同,从而防止彩虹表攻击", + "C": "盐的主要作用是对密码进行加密,使攻击者无法直接读取原始密码", + "D": "盐的主要作用是缩短哈希值长度,节省存储空间" + }, + "answer": "B", + "explanation": "盐是一个随机值,与密码拼接后再哈希。其核心目的是打消预计算攻击(彩虹表攻击):因为每个用户的盐不同,攻击者无法为常见密码预先计算哈希表。即使两个用户使用相同密码,由于盐不同,最终哈希值也完全不同。选项 A 描述不准确——盐不增加密码本身的熵;选项 C 混淆了哈希与加密的概念;选项 D 与盐的功能无关。" + }, + { + "index": 3, + "type": "short_answer", + "question": "当前 hash_password 的实现使用了单次 SHA-256 哈希加盐。请指出这种实现的一个主要安全缺陷,并提出具体的改进建议。", + "answer": "主要安全缺陷:SHA-256 计算速度极快(现代 GPU 可达每秒数十亿次哈希),这使得暴力破解和字典攻击非常高效。即使有盐,攻击者获得哈希值后仍可对单个目标进行高速离线破解。改进建议:应使用专用的慢哈希算法替代单次 SHA-256,如 bcrypt、scrypt 或 Argon2。这些算法内置了盐处理、可调节的工作因子(迭代次数/内存消耗/并行度),能显著增加离线破解的成本。例如使用 Python 的 bcrypt 库:import bcrypt; hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))。", + "explanation": "这是密码存储领域的经典考点。NIST 等标准机构不推荐使用普通哈希函数(即使是加盐的)存储密码。bcrypt 是最广泛采用的方案,Argon2 是 2015 年密码哈希竞赛(PHC)的获胜算法,被认为是目前最佳选择。" + } + ], + "explanation": "本题通过一段真实的 Python 密码学代码,考察学生对哈希、HMAC 和密码存储三个核心概念的理解。第一部分是基础哈希计算;第二部分引入密钥和消息认证;第三部分模拟密码存储场景,故意使用不安全的实现来考察学生的安全分析能力。三个子题从概念理解、原理辨析到实际安全评估,难度递进。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/crypto-basics/meta.json b/topics/cybersecurity/crypto-basics/meta.json new file mode 100644 index 0000000..0877031 --- /dev/null +++ b/topics/cybersecurity/crypto-basics/meta.json @@ -0,0 +1,29 @@ +{ + "slug": "crypto-basics", + "name": "密码学基础", + "description": "对称/非对称加密、哈希、数字签名、PKI", + "tags": [ + "密码学", + "加密", + "哈希", + "数字签名", + "pki", + "证书" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/crypto-basics/short_answer.json b/topics/cybersecurity/crypto-basics/short_answer.json new file mode 100644 index 0000000..01e6616 --- /dev/null +++ b/topics/cybersecurity/crypto-basics/short_answer.json @@ -0,0 +1,89 @@ +{ + "topic": "crypto-basics", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 1, + "tags": [ + "密码学", + "哈希" + ], + "question": "什么是密码学哈希函数?请列举密码学哈希函数的三个核心安全性质,并简要说明每个性质的含义。", + "answer": "密码学哈希函数是一种将任意长度的输入映射为固定长度输出的单向函数,广泛用于数据完整性校验、数字签名和密码存储等场景。其三个核心安全性质为:(1)抗原像性(Pre-image Resistance):给定哈希值 h,在计算上难以找到任意消息 m 使得 H(m) = h;(2)抗第二原像性(Second Pre-image Resistance):给定消息 m1,在计算上难以找到另一个不同的消息 m2 使得 H(m1) = H(m2);(3)抗碰撞性(Collision Resistance):在计算上难以找到任意两个不同的消息 m1 和 m2 使得 H(m1) = H(m2)。", + "keywords": [ + "哈希函数", + "单向函数", + "抗原像性", + "抗碰撞性", + "抗第二原像性", + "固定长度输出", + "SHA-256" + ], + "scoring_rubric": "定义正确得 3 分;三个性质各 2 分(名称正确 1 分,含义准确 1 分);满分 9 分。若仅给出定义但未列出性质,最高 3 分;性质名称正确但含义不准确扣 1 分。", + "explanation": "密码学哈希函数是密码学的基础构件之一。与普通哈希函数不同,密码学哈希函数需要满足严格的安全性质。抗原像性保证了从哈希值无法反推出原始消息(单向性);抗第二原像性保证了给定一个消息,无法找到另一个产生相同哈希值的消息;抗碰撞性则是最强的保证,即无法找到任意一对碰撞消息。常见算法包括 SHA-256、SHA-3 等。MD5 和 SHA-1 因已被发现碰撞而不再被视为安全。", + "source": null, + "related": [] + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "密码学", + "加密", + "数字签名", + "pki" + ], + "question": "请比较对称加密与非对称加密的工作原理、典型应用场景及各自的优缺点。为什么在实际系统中(如 TLS)通常将两者结合使用?", + "answer": "对称加密使用相同的密钥进行加密和解密(如 AES、ChaCha20),优点是加解密速度快、适合大数据量处理,缺点是密钥分发困难——通信双方必须事先安全共享密钥。非对称加密使用一对密钥(公钥加密、私钥解密,如 RSA、ECC),优点是无需安全信道分发密钥、支持数字签名,缺点是计算开销大、速度慢。在实际系统如 TLS 中,两者结合使用:利用非对称加密(或密钥交换协议如 ECDHE)在握手阶段安全协商出一个对称会话密钥,之后的数据传输使用该对称密钥进行高效加密。这种方式兼顾了安全性(安全密钥协商)和性能(高效数据传输)。", + "keywords": [ + "对称加密", + "非对称加密", + "AES", + "RSA", + "ECC", + "密钥分发", + "TLS", + "会话密钥", + "混合加密" + ], + "scoring_rubric": "对称加密原理和优缺点 3 分;非对称加密原理和优缺点 3 分;结合使用的原因和机制 4 分;满分 10 分。缺少具体算法名称不扣分,但原理描述错误扣相应分数。", + "explanation": "对称加密和非对称加密是现代密码学的两大支柱。对称加密速度快但面临密钥分发难题;非对称加密解决了密钥分发问题但性能受限。实际协议(TLS、SSH、PGP 等)普遍采用混合加密方案:用非对称方法做身份认证和密钥协商,用对称方法做批量数据加密,取两者之长。这是信息安全课程中的核心对比知识点。", + "source": null, + "related": [] + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 3, + "tags": [ + "密码学", + "数字签名", + "pki", + "证书" + ], + "question": "在 PKI(公钥基础设施)体系中,数字证书起什么作用?请描述从证书申请到证书验证的完整流程,包括涉及的主要角色和 X.509 证书中的关键字段。", + "answer": "数字证书的核心作用是将公钥与实体身份进行可信绑定,解决公钥的真实性问题。完整流程如下:(1)申请:用户(Subject)生成密钥对,将公钥连同身份信息提交给 CA(证书授权机构);(2)审核与签发:CA 验证申请者身份后,用自己的私钥对证书信息进行签名,生成 X.509 格式的数字证书;(3)使用:用户将证书发送给通信对方;(4)验证:接收方使用 CA 的公钥验证证书签名,检查证书是否过期、是否被吊销(通过 CRL 或 OCSP),以及域名等信息是否匹配。X.509 证书的关键字段包括:Version(版本)、Serial Number(序列号)、Issuer(签发者 DN)、Subject(持有者 DN)、Subject Public Key Info(持有者公钥)、Validity(有效期:Not Before / Not After)、Signature Algorithm(签名算法)、Extensions(扩展,如 Subject Alternative Name)。", + "keywords": [ + "PKI", + "CA", + "数字证书", + "X.509", + "公钥绑定", + "证书签名", + "证书验证", + "CRL", + "OCSP", + "证书吊销" + ], + "scoring_rubric": "证书作用说明 2 分;申请流程 2 分;签发过程 2 分;验证过程 2 分;关键字段列举(至少 5 个)2 分;满分 10 分。流程顺序错误或遗漏主要角色各扣 1 分。", + "explanation": "PKI 是支撑互联网信任的基础架构。数字证书是其核心产物,本质上是 CA 对「公钥属于某个实体」这一声明的背书。理解证书生命周期(申请→审核→签发→分发→使用→验证→续期/吊销)以及信任链(Root CA → Intermediate CA → End-entity)对于理解 HTTPS、代码签名、电子邮件加密等实际应用至关重要。X.509 是最广泛使用的证书格式标准。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/crypto-basics/single_choice.json b/topics/cybersecurity/crypto-basics/single_choice.json new file mode 100644 index 0000000..1b8ae04 --- /dev/null +++ b/topics/cybersecurity/crypto-basics/single_choice.json @@ -0,0 +1,412 @@ +{ + "topic": "crypto-basics", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "加密" + ], + "question": "以下哪种加密算法属于对称加密?", + "options": { + "A": "AES", + "B": "RSA", + "C": "ECC", + "D": "Diffie-Hellman" + }, + "answer": "A", + "explanation": "AES(Advanced Encryption Standard)是对称加密算法,加密和解密使用相同的密钥。RSA 和 ECC 都是非对称加密算法,使用公钥/私钥对。Diffie-Hellman 是密钥交换协议,本身不是加密算法,但属于非对称密码学范畴。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "加密" + ], + "question": "AES 支持的密钥长度不包括以下哪一项?", + "options": { + "A": "128 位", + "B": "192 位", + "C": "256 位", + "D": "512 位" + }, + "answer": "D", + "explanation": "AES 标准支持三种密钥长度:128 位、192 位和 256 位,分别对应 AES-128、AES-192 和 AES-256。512 位不是 AES 标准定义的密钥长度。更长的密钥通常意味着更高的安全性,但 AES 最高只定义到 256 位。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "加密" + ], + "question": "关于 DES 和 3DES,以下说法正确的是?", + "options": { + "A": "DES 使用 128 位密钥,安全性很高", + "B": "3DES 通过对数据执行三次 DES 加密来提升安全性", + "C": "DES 目前仍被认为是最安全的对称加密标准", + "D": "3DES 的密钥有效长度为 168 位,不存在任何已知弱点" + }, + "answer": "B", + "explanation": "3DES(Triple DES)通过对数据执行三次 DES 加密(通常使用加密-解密-加密 EDE 模式)来提升安全性。DES 实际使用 56 位密钥(加 8 位校验),已被证明不够安全。3DES 的有效密钥长度为 112 位(两密钥模式)或 168 位(三密钥模式),但存在中间相遇攻击等弱点,NIST 已在 2023 年正式废弃 3DES。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "加密" + ], + "question": "RSA 算法的安全性基于以下哪个数学难题?", + "options": { + "A": "离散对数问题", + "B": "大整数质因数分解问题", + "C": "背包问题", + "D": "椭圆曲线离散对数问题" + }, + "answer": "B", + "explanation": "RSA 的安全性基于大整数质因数分解的困难性:将两个大素数相乘很容易,但将乘积分解回原来的两个素数在计算上极其困难。离散对数问题对应的是 Diffie-Hellman 和 DSA 等算法的基础。椭圆曲线离散对数问题(ECDLP)是 ECC 算法的安全性基础。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "加密" + ], + "question": "与 RSA 相比,ECC(椭圆曲线密码学)的主要优势是什么?", + "options": { + "A": "加解密速度更快,但需要更长的密钥", + "B": "在相同安全强度下,所需密钥长度更短", + "C": "ECC 不依赖任何数学难题,因此绝对安全", + "D": "ECC 只能用于签名,不能用于加密" + }, + "answer": "B", + "explanation": "ECC 最大的优势是在提供相同安全强度的情况下,所需的密钥长度远小于 RSA。例如 256 位 ECC 密钥的安全强度约等于 3072 位 RSA 密钥,这意味着更小的存储空间、更快的计算和更低的带宽消耗。ECC 的安全性基于椭圆曲线离散对数问题,并非绝对安全,且 ECC 既可用于密钥交换和加密,也可用于数字签名。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "哈希" + ], + "question": "以下关于哈希函数的描述,正确的是?", + "options": { + "A": "哈希函数是可逆的,可以从哈希值恢复原始数据", + "B": "哈希函数将任意长度的输入映射为固定长度的输出", + "C": "哈希函数的输出长度随输入长度线性增长", + "D": "不同的输入一定产生不同的哈希值" + }, + "answer": "B", + "explanation": "哈希函数的核心特性是将任意长度的输入映射为固定长度的输出,例如 SHA-256 始终输出 256 位。哈希函数是单向函数,不可从哈希值逆向恢复原始数据。由于输出空间有限而输入空间无限,理论上不同的输入可能产生相同的哈希值(碰撞),所以\"不同输入一定产生不同哈希值\"是错误的。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "哈希" + ], + "question": "MD5 算法目前被认为不安全的主要原因是什么?", + "options": { + "A": "MD5 的输出长度只有 32 位,太短", + "B": "MD5 已被发现存在实际可行的碰撞攻击", + "C": "MD5 是对称加密算法,密钥太短", + "D": "MD5 的计算速度太慢,不适合实际应用" + }, + "answer": "B", + "explanation": "MD5 的输出长度实际是 128 位(通常以 32 位十六进制字符串表示),不是 32 位。MD5 不是加密算法而是哈希函数,不存在密钥的概念。MD5 的计算速度其实很快,但这反而有利于碰撞攻击。MD5 不安全的根本原因是 2004 年王小云教授等人展示了实际可行的碰撞攻击,此后碰撞实例不断被构造出来,因此不应再用于安全场景。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "哈希" + ], + "question": "SHA-256 产生的哈希值长度是多少位?", + "options": { + "A": "128 位", + "B": "160 位", + "C": "256 位", + "D": "512 位" + }, + "answer": "C", + "explanation": "SHA-256 是 SHA-2 家族的成员,顾名思义,它产生 256 位(32 字节)的哈希值,通常用 64 个十六进制字符表示。SHA-1 产生 160 位输出,SHA-512 产生 512 位输出。128 位是 MD5 的输出长度。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "数字签名" + ], + "question": "数字签名的主要功能不包括以下哪一项?", + "options": { + "A": "身份认证", + "B": "数据完整性保护", + "C": "数据加密", + "D": "不可否认性" + }, + "answer": "C", + "explanation": "数字签名的三大核心功能是:身份认证(确认签名者身份)、数据完整性(确保数据未被篡改)和不可否认性(签名者不能否认签名行为)。数字签名本身不提供数据加密功能,虽然签名过程中会用到非对称加密技术,但签名结果并不对原始数据进行保密处理。若需同时加密,通常需要将数字签名与加密机制结合使用。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "数字签名" + ], + "question": "在数字签名过程中,发送方用什么密钥对消息摘要进行签名?", + "options": { + "A": "接收方的公钥", + "B": "接收方的私钥", + "C": "发送方的公钥", + "D": "发送方的私钥" + }, + "answer": "D", + "explanation": "数字签名的基本流程是:发送方先对消息计算哈希得到消息摘要,然后用自己的私钥对摘要进行加密(签名)。接收方收到后用发送方的公钥解密(验签),再与自己计算的摘要比对。使用发送方的私钥签名可以确保只有持有私钥的人才能生成该签名,从而实现身份认证和不可否认性。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "PKI", + "证书" + ], + "question": "PKI(公钥基础设施)中,CA 的主要职责是什么?", + "options": { + "A": "生成对称加密密钥", + "B": "颁发和管理数字证书", + "C": "对所有网络流量进行加密", + "D": "存储用户的私钥" + }, + "answer": "B", + "explanation": "CA(Certificate Authority,证书颁发机构)是 PKI 的核心组件,主要负责颁发、续期、吊销和管理数字证书。CA 通过验证实体(个人、组织、服务器等)的身份后,将其公钥与身份信息绑定在数字证书中。CA 不负责生成对称密钥或加密所有网络流量,而用户的私钥应由用户自己安全保管,不应存储在 CA 端。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "PKI", + "证书" + ], + "question": "X.509 数字证书中通常不包含以下哪项信息?", + "options": { + "A": "证书持有者的公钥", + "B": "证书持有者的私钥", + "C": "颁发该证书的 CA 信息", + "D": "证书的有效期" + }, + "answer": "B", + "explanation": "X.509 证书包含持有者的公钥、持有者的身份信息(如域名、组织名)、颁发机构(CA)的信息、有效期、序列号以及 CA 的数字签名等内容。证书中绝对不包含持有者的私钥,私钥必须由持有者自行秘密保管。证书的本质是将公钥与身份信息绑定的可信声明,如果包含私钥就会导致严重的安全问题。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "PKI", + "证书" + ], + "question": "当浏览器访问 HTTPS 网站时发现证书已过期,这违反了以下哪项证书检查?", + "options": { + "A": "证书链验证", + "B": "有效期检查", + "C": "证书吊销检查", + "D": "域名匹配检查" + }, + "answer": "B", + "explanation": "证书包含一个明确的有效期(notBefore 和 notAfter 字段),浏览器在验证证书时会检查当前时间是否在有效期内。如果证书已过期,说明有效期检查失败。证书链验证是检查证书是否由受信任的 CA 签发;吊销检查是通过 CRL 或 OCSP 确认证书是否被提前撤销;域名匹配检查是确认证书中的域名与实际访问的域名一致。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "密码学", + "PKI" + ], + "question": "以下哪种机制用于实时查询证书是否已被吊销?", + "options": { + "A": "CRL(证书吊销列表)", + "B": "OCSP(在线证书状态协议)", + "C": "TLS 握手", + "D": "DNS 解析" + }, + "answer": "B", + "explanation": "OCSP(Online Certificate Status Protocol,在线证书状态协议)允许客户端实时向 OCSP 响应器查询单个证书的吊销状态,相比 CRL 更加即时和高效。CRL 虽然也用于检查证书吊销状态,但它是周期性发布的完整列表,实时性较差。TLS 握手是建立加密连接的过程,DNS 解析是域名到 IP 的映射,两者都不是证书吊销检查机制。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "密码学", + "加密" + ], + "question": "关于分组密码的工作模式,以下哪项说法正确?", + "options": { + "A": "ECB 模式对相同明文块总是产生相同的密文块,因此模式安全性较差", + "B": "所有工作模式都能完全防止重放攻击", + "C": "CBC 模式不需要初始化向量(IV)", + "D": "CTR 模式将分组密码转换为流密码,因此不需要密钥" + }, + "answer": "A", + "explanation": "ECB(Electronic Codebook)模式对每个明文块独立加密,相同明文块总是产生相同密文块,这会泄露数据模式,因此安全性较差,不适合加密大量数据。CBC 模式需要一个随机的初始化向量(IV)来确保相同明文产生不同密文。CTR 模式虽然将分组密码转换为类似流密码的模式,但仍需要密钥来驱动分组加密运算。工作模式本身并不能防止重放攻击。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "密码学", + "加密" + ], + "question": "Diffie-Hellman 密钥交换协议容易受到以下哪种攻击?", + "options": { + "A": "已知明文攻击", + "B": "中间人攻击", + "C": "差分密码分析", + "D": "线性密码分析" + }, + "answer": "B", + "explanation": "原始的 Diffie-Hellman 密钥交换协议不包含身份认证机制,因此容易受到中间人攻击(MITM)。攻击者可以分别与通信双方建立独立的密钥交换,从而拦截和篡改所有通信内容。已知明文攻击、差分密码分析和线性密码分析主要针对分组密码,不是 DH 密钥交换的主要威胁。实际应用中通常结合数字证书或数字签名来抵御中间人攻击。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "密码学", + "哈希" + ], + "question": "HMAC(基于哈希的消息认证码)相比普通哈希函数多了什么安全特性?", + "options": { + "A": "更快的计算速度", + "B": "更长的输出长度", + "C": "消息认证,能验证消息来源和完整性", + "D": "数据加密能力" + }, + "answer": "C", + "explanation": "HMAC 将密钥与哈希函数结合使用,能够同时验证消息的完整性和来源真实性(消息认证)。只有持有正确密钥的人才能生成有效的 HMAC 值,而普通哈希函数不涉及密钥,任何人计算同一消息的哈希值都相同,因此无法验证消息来源。HMAC 并不提供加密能力,其输出长度与所使用的哈希函数相同,计算速度也与底层哈希函数相当。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "密码学", + "加密" + ], + "question": "以下哪种攻击方式针对的是密钥交换过程而非加密算法本身?", + "options": { + "A": "暴力破解", + "B": "生日攻击", + "C": "中间人攻击", + "D": "彩虹表攻击" + }, + "answer": "C", + "explanation": "中间人攻击针对的是密钥交换或通信建立过程,攻击者在通信双方之间插入自己,分别与双方建立独立连接。暴力破解是尝试所有可能的密钥来破解加密算法;生日攻击是针对哈希函数碰撞的攻击方法;彩虹表攻击是针对密码哈希的预计算表攻击。这三种攻击都不是直接针对密钥交换过程的。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "密码学", + "加密", + "密钥管理" + ], + "question": "在密钥管理的最佳实践中,以下哪项做法是正确的?", + "options": { + "A": "将加密密钥硬编码在源代码中以便于使用", + "B": "所有系统共享同一个密钥以简化管理", + "C": "定期轮换密钥,并安全销毁不再使用的旧密钥", + "D": "将密钥与加密数据存储在同一个数据库中" + }, + "answer": "C", + "explanation": "定期轮换密钥是密钥管理的核心最佳实践,可以限制单个密钥泄露造成的影响范围。旧密钥不再使用时应安全销毁,防止被攻击者获取。将密钥硬编码在源代码中会导致密钥随代码泄露;所有系统共享同一密钥违反最小权限原则,且一旦泄露影响范围巨大;密钥与加密数据存储在同一位置违反了密钥分离原则,攻击者获取数据的同时也会获取解密密钥。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "密码学", + "加密" + ], + "question": "对称加密和非对称加密的根本区别是什么?", + "options": { + "A": "对称加密速度慢,非对称加密速度快", + "B": "对称加密使用同一密钥加密和解密,非对称加密使用不同的密钥", + "C": "对称加密不安全,非对称加密绝对安全", + "D": "对称加密只能加密文本,非对称加密可以加密任何数据" + }, + "answer": "B", + "explanation": "对称加密和非对称加密的根本区别在于密钥的使用方式:对称加密使用同一个密钥进行加密和解密,通信双方必须安全地共享该密钥;非对称加密使用一对密钥(公钥和私钥),公钥加密的数据只有对应私钥才能解密。实际上对称加密通常比非对称加密速度更快,两者都能加密各种类型的数据,且两者都不是绝对安全的。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/crypto-basics/true_false.json b/topics/cybersecurity/crypto-basics/true_false.json new file mode 100644 index 0000000..1ac540f --- /dev/null +++ b/topics/cybersecurity/crypto-basics/true_false.json @@ -0,0 +1,150 @@ +{ + "topic": "crypto-basics", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "密码学", + "加密" + ], + "question": "对称加密算法中,加密和解密使用相同的密钥。", + "answer": true, + "explanation": "对称加密的核心特征就是加密和解密共享同一个密钥。发送方用该密钥对明文进行加密,接收方用同一个密钥对密文进行解密。常见算法如 AES、DES、3DES 都属于对称加密。由于双方必须持有同一密钥,密钥的分发和管理是对称加密面临的主要挑战。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 1, + "tags": [ + "密码学", + "加密" + ], + "question": "非对称加密算法(如 RSA)中,用公钥加密的数据只能用对应的私钥解密。", + "answer": true, + "explanation": "非对称加密使用一对数学相关的密钥:公钥和私钥。公钥可以公开分发,用于加密数据;只有持有对应私钥的人才能解密这些数据。这一机制解决了对称加密中密钥分发的难题,是 HTTPS、数字签名等安全协议的基础。RSA、ECC、ElGamal 都是典型的非对称加密算法。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "密码学", + "加密" + ], + "question": "非对称加密的计算速度通常比对称加密快,因此适合加密大量数据。", + "answer": false, + "explanation": "事实恰恰相反。非对称加密(如 RSA)涉及大数幂运算等复杂数学操作,计算开销远大于对称加密(如 AES 的位运算和查表操作),速度通常慢几个数量级。因此在实际应用中,常见做法是用非对称加密来安全交换一个对称会话密钥,再用该对称密钥加密后续的大量数据,这就是混合加密体制的由来。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 2, + "tags": [ + "哈希", + "密码学" + ], + "question": "密码学哈希函数(如 SHA-256)是可逆的,即可以从哈希值推导出原始输入。", + "answer": false, + "explanation": "密码学哈希函数被设计为单向函数(one-way function),从输入计算哈希值很容易,但从哈希值反推输入在计算上是不可行的。这是哈希函数的核心安全属性之一。如果哈希可逆,那么密码存储、数字签名、完整性校验等依赖哈希的机制都会彻底崩溃。SHA-256、SHA-3、BLAKE2 等都满足这一单向性要求。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 2, + "tags": [ + "哈希", + "密码学" + ], + "question": "不同的输入经过密码学哈希函数计算后,一定会产生不同的输出(哈希值)。", + "answer": false, + "explanation": "由于哈希函数的输出长度固定(如 SHA-256 输出 256 位),而输入空间是无限的,根据鸽巢原理,必然存在不同输入产生相同输出的情况,这称为哈希碰撞(collision)。密码学哈希函数的安全性要求是使找到碰撞在计算上不可行,而非完全不存在碰撞。MD5 和 SHA-1 就是因为被发现存在实际可利用的碰撞而被弃用。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 1, + "tags": [ + "哈希", + "密码学" + ], + "question": "MD5 算法目前仍被广泛推荐用于密码存储和安全证书签名。", + "answer": false, + "explanation": "MD5 已被证明存在严重的安全漏洞,研究人员可以在普通计算机上快速构造碰撞。早在 2004 年,王小云教授团队就展示了 MD5 碰撞攻击的实际可行性。目前 MD5 不应再用于任何安全敏感的场景,包括密码存储和证书签名。现代应用应使用 SHA-256、SHA-3 或 BLAKE2 等更安全的哈希算法,密码存储则推荐 bcrypt、scrypt 或 Argon2。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 2, + "tags": [ + "数字签名", + "密码学" + ], + "question": "数字签名过程中,发送方使用自己的私钥对消息的哈希值进行签名,接收方使用发送方的公钥进行验证。", + "answer": true, + "explanation": "数字签名的工作流程是:发送方先对消息计算哈希值,然后用自己的私钥对哈希值进行加密(签名),接收方用发送方的公钥解密签名得到哈希值,再与自己计算的消息哈希值比对。这一过程同时实现了身份认证(只有私钥持有者能生成该签名)和完整性验证(哈希值匹配说明消息未被篡改)。RSA 签名、ECDSA 等都是遵循这一原理的算法。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 3, + "tags": [ + "数字签名", + "密码学" + ], + "question": "数字签名能同时提供消息完整性、身份认证和不可否认性。", + "answer": true, + "explanation": "数字签名提供了三大安全属性:第一,完整性——签名是对消息哈希值的加密,任何篡改都会导致验证失败;第二,身份认证——只有持有私钥的人才能生成有效签名,因此可以确认消息来源;第三,不可否认性——由于私钥只有签名者持有,签名者事后无法否认自己曾签署过该消息,这在法律和商业场景中尤为重要。这三大属性使数字签名成为电子合同、软件发布签名等场景的核心技术。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 2, + "tags": [ + "PKI", + "证书", + "密码学" + ], + "question": "在 PKI(公钥基础设施)体系中,数字证书是由证书颁发机构(CA)签发的,用于将公钥与实体身份进行绑定。", + "answer": true, + "explanation": "PKI 的核心组件就是数字证书和 CA。证书颁发机构(CA)在验证申请者身份后,用自己的私钥对包含申请者公钥和身份信息的证书进行签名,生成数字证书。依赖方(如浏览器)通过验证 CA 的签名来确认证书的真实性,从而信任证书中的公钥确实属于声称的实体。X.509 是最常用的证书标准,TLS/SSL、S/MIME 等协议都依赖此机制。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 3, + "tags": [ + "PKI", + "证书", + "密码学" + ], + "question": "如果一个网站的数字证书已过期,浏览器会自动信任该证书并建立安全连接,不会有任何警告。", + "answer": false, + "explanation": "浏览器在建立 TLS 连接时会严格检查证书的有效期。如果证书已过期,浏览器会立即中断连接并显示明显的安全警告页面,提示用户该连接不安全。有效期是证书的重要约束字段,CA 在签发证书时设定合理期限(通常 1-2 年),过期后证书失效。这是 PKI 体系中限制私钥泄露影响范围的重要机制——即使私钥泄露,过期后证书也会自动失效,降低了持续风险。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/network-attack/code_reading.json b/topics/cybersecurity/network-attack/code_reading.json new file mode 100644 index 0000000..6a4705a --- /dev/null +++ b/topics/cybersecurity/network-attack/code_reading.json @@ -0,0 +1,63 @@ +{ + "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": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/network-attack/meta.json b/topics/cybersecurity/network-attack/meta.json new file mode 100644 index 0000000..7ea0981 --- /dev/null +++ b/topics/cybersecurity/network-attack/meta.json @@ -0,0 +1,28 @@ +{ + "slug": "network-attack", + "name": "网络攻防", + "description": "中间人攻击、ARP 欺骗、DNS 劫持、端口扫描", + "tags": [ + "网络攻防", + "中间人攻击", + "arp欺骗", + "dns劫持", + "端口扫描" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/network-attack/short_answer.json b/topics/cybersecurity/network-attack/short_answer.json new file mode 100644 index 0000000..25b55d5 --- /dev/null +++ b/topics/cybersecurity/network-attack/short_answer.json @@ -0,0 +1,86 @@ +{ + "topic": "network-attack", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击", + "arp欺骗" + ], + "question": "请简要描述 ARP 欺骗(ARP Spoofing)攻击的原理,包括攻击者如何利用 ARP 协议的缺陷来实现中间人攻击。", + "answer": "ARP 协议在设计时缺乏身份验证机制,主机收到 ARP 应答报文后会无条件更新本地 ARP 缓存。攻击者利用这一缺陷,向目标主机发送伪造的 ARP 应答报文,将网关的 IP 地址映射为攻击者的 MAC 地址,同时向网关发送伪造的 ARP 应答,将目标主机的 IP 地址映射为攻击者的 MAC 地址。这样,目标主机与网关之间的通信流量都会经过攻击者的主机,攻击者即可截获、篡改或丢弃数据包,实现中间人攻击。", + "keywords": [ + "ARP协议", + "缺乏身份验证", + "无条件更新", + "伪造ARP应答", + "MAC地址", + "ARP缓存", + "中间人" + ], + "scoring_rubric": "答出ARP协议无身份验证机制(2分);答出伪造ARP应答报文更新ARP缓存的过程(3分);答出双向欺骗使流量经过攻击者实现中间人(3分);表述清晰、逻辑完整(2分)。满分10分。", + "explanation": "ARP 欺骗利用了 ARP 协议设计之初缺乏认证机制的缺陷。攻击者通过发送伪造的 ARP 应答报文,让目标主机和网关都将攻击者的 MAC 地址误认为对方的 MAC 地址,从而将双向流量引向攻击者,实现中间人攻击。回答时应涵盖协议缺陷、伪造报文过程和双向欺骗机制三个层面。", + "source": null, + "related": [] + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "什么是 DNS 劫持?请说明 DNS 劫持的常见实现方式,并列举至少两种防御措施。", + "answer": "DNS 劫持是指攻击者通过篡改 DNS 解析过程,将用户请求的域名解析到错误的 IP 地址,从而将用户引导至恶意网站。常见实现方式包括:(1) 本地 DNS 劫持——修改受害者主机的 hosts 文件或 DNS 配置,使其指向恶意 DNS 服务器;(2) 中间人 DNS 劫持——在网络传输过程中截获 DNS 查询请求并返回伪造的 DNS 响应;(3) 权威 DNS 篡改——入侵 DNS 服务器直接修改域名记录;(4) DNS 缓存投毒——向 DNS 缓存服务器注入伪造的 DNS 记录。防御措施包括:使用 DNS over HTTPS (DoH) 或 DNS over TLS (DoT) 加密 DNS 查询;部署 DNSSEC 对 DNS 响应进行数字签名验证;使用可信的公共 DNS 服务器(如 8.8.8.8、1.1.1.1);定期检查本地 DNS 配置和 hosts 文件。", + "keywords": [ + "DNS解析", + "篡改", + "错误IP地址", + "hosts文件", + "DNS缓存投毒", + "DNSSEC", + "DoH", + "DoT", + "加密" + ], + "scoring_rubric": "准确解释DNS劫持概念(2分);列举至少两种实现方式并简要说明(4分);列举至少两种有效的防御措施(3分);表述准确完整(1分)。满分10分。", + "explanation": "DNS 劫持通过篡改域名解析过程将用户引导至恶意网站。常见方式涵盖本地劫持(hosts文件/配置篡改)、传输层劫持(中间人DNS响应篡改)和服务端劫持(DNS服务器入侵/缓存投毒)。防御关键在于加密DNS查询(DoH/DoT)和签名验证(DNSSEC)。", + "source": null, + "related": [] + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 3, + "tags": [ + "网络攻防", + "端口扫描", + "中间人攻击" + ], + "question": "请比较 TCP SYN 扫描(半开放扫描)和 TCP Connect 扫描(全连接扫描)的区别,包括工作原理、隐蔽性和适用场景。", + "answer": "TCP Connect 扫描(全连接扫描)直接调用操作系统的 connect() 系统函数与目标端口完成完整的 TCP 三次握手。如果连接成功建立,说明端口开放;如果连接被拒绝(收到 RST),说明端口关闭。这种方式简单可靠,但会在目标系统日志中留下完整的连接记录,隐蔽性差。TCP SYN 扫描(半开放扫描)不完成完整的三次握手:扫描器向目标端口发送 SYN 包,如果收到 SYN/ACK 响应,说明端口开放,此时扫描器直接发送 RST 包断开连接,而不发送最终的 ACK;如果收到 RST 响应,说明端口关闭。由于连接从未完全建立,不会被大多数应用程序日志记录,隐蔽性较高。但 SYN 扫描通常需要原始套接字(raw socket)权限或管理员/root 权限。在适用场景方面,Connect 扫描适用于普通用户权限下对可靠性的要求较高的场景;SYN 扫描适用于渗透测试、红队演练等需要降低被发现概率的场景。", + "keywords": [ + "SYN扫描", + "半开放", + "Connect扫描", + "全连接", + "三次握手", + "RST", + "隐蔽性", + "原始套接字", + "日志记录" + ], + "scoring_rubric": "正确描述两种扫描的工作流程差异(4分);准确对比隐蔽性方面的区别及原因(3分);说明适用场景和权限要求(2分);表述条理清晰(1分)。满分10分。", + "explanation": "SYN 扫描与 Connect 扫描的核心差异在于是否完成三次握手。SYN 扫描在收到 SYN/ACK 后发送 RST 断开,连接未完全建立,不会记录到应用日志中,隐蔽性高但需 root 权限;Connect 扫描通过系统调用完成完整握手,简单可靠但留下完整日志。回答时应从工作原理、隐蔽性、权限要求和适用场景四个维度进行对比。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/network-attack/single_choice.json b/topics/cybersecurity/network-attack/single_choice.json new file mode 100644 index 0000000..48c75d5 --- /dev/null +++ b/topics/cybersecurity/network-attack/single_choice.json @@ -0,0 +1,409 @@ +{ + "topic": "network-attack", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "中间人攻击(Man-in-the-Middle Attack)的核心原理是什么?", + "options": { + "A": "直接破解目标主机的密码", + "B": "在通信双方之间秘密拦截并可能篡改数据", + "C": "利用操作系统漏洞获取 root 权限", + "D": "通过大量请求使目标服务器过载" + }, + "answer": "B", + "explanation": "中间人攻击的核心是攻击者将自己置于通信双方之间,能够拦截、读取甚至篡改双方传输的数据,而通信双方对此毫不知情。A 描述的是暴力破解攻击,C 描述的是提权攻击,D 描述的是拒绝服务(DDoS)攻击。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "ARP 协议工作在 OSI 模型的哪一层?", + "options": { + "A": "应用层", + "B": "传输层", + "C": "网络层", + "D": "数据链路层" + }, + "answer": "C", + "explanation": "ARP(Address Resolution Protocol)协议用于将 IP 地址解析为 MAC 地址,工作在 OSI 模型的网络层(第三层)。虽然 ARP 的结果用于数据链路层的帧封装,但 ARP 协议本身在网络层定义和运作。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "ARP 欺骗攻击中,攻击者通常发送什么类型的 ARP 报文来污染目标主机的 ARP 缓存表?", + "options": { + "A": "ARP 请求报文,将自己的 MAC 地址映射为网关的 IP", + "B": "ARP 应答报文,将自己的 MAC 地址映射为网关的 IP", + "C": "RARP 请求报文,请求 IP 地址分配", + "D": "免费 ARP 报文,仅用于检测 IP 冲突" + }, + "answer": "B", + "explanation": "在 ARP 欺骗中,攻击者主动发送伪造的 ARP 应答(Reply)报文,声称某个 IP 地址(如网关)对应的 MAC 地址是攻击者的 MAC 地址。目标主机收到后会更新 ARP 缓存表,将后续发往网关的流量发送给攻击者。A 虽然也可能涉及伪造,但 ARP 请求是广播形式,应答是更直接的欺骗手段;C 的 RARP 是逆向 ARP,与此无关;D 的免费 ARP 仅用于 IP 冲突检测,不能直接实现欺骗。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "DNS 劫持攻击的本质是什么?", + "options": { + "A": "使 DNS 服务器过载导致无法响应", + "B": "篡改 DNS 解析过程,使域名指向错误的 IP 地址", + "C": "加密 DNS 查询内容以防止被监听", + "D": "利用 DNS 协议进行数据外泄" + }, + "answer": "B", + "explanation": "DNS 劫持的本质是攻击者通过篡改 DNS 解析过程(如修改本地 hosts 文件、劫持 DNS 服务器、篡改 DNS 响应报文等),将目标域名解析到攻击者控制的错误 IP 地址上,从而引导用户访问恶意网站。A 描述的是 DNS 洪泛攻击(DDoS 的一种),C 描述的是 DNSSEC 或 DoH 的防御功能,D 描述的是 DNS 隧道技术。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "TCP SYN 扫描又被称为「半开扫描」,原因是?", + "options": { + "A": "扫描只发送一次数据包", + "B": "扫描只完成 TCP 三次握手的前两步,不建立完整连接", + "C": "扫描只对一半的端口进行探测", + "D": "扫描速率只有全连接扫描的一半" + }, + "answer": "B", + "explanation": "TCP SYN 扫描发送 SYN 包后,如果收到 SYN/ACK 响应,说明端口开放,但扫描器发送 RST 包而非最后的 ACK 包来中断连接,因此只完成了三次握手的前两步(SYN → SYN/ACK → RST),没有建立完整的 TCP 连接。这种方式比全连接扫描更隐蔽,且速度更快。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "以下哪种技术可以有效防御 HTTPS 环境下的中间人攻击?", + "options": { + "A": "使用静态 ARP 绑定", + "B": "启用 HTTPS 证书验证和证书固定(Certificate Pinning)", + "C": "关闭 ICMP 协议", + "D": "使用 UDP 协议替代 TCP" + }, + "answer": "B", + "explanation": "HTTPS 通过 TLS/SSL 加密传输数据,结合证书验证可以确保通信对方的身份合法性。证书固定(Certificate Pinning)进一步限制只有特定证书才被信任,防止攻击者使用伪造证书进行中间人攻击。A 仅防御 ARP 欺骗型中间人攻击,不防 HTTPS 层面的攻击;C 关闭 ICMP 对防御中间人攻击无直接帮助;D 使用 UDP 不仅不能防御中间人攻击,还丢失了 TCP 的可靠性保障。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "在局域网中实施 ARP 欺骗后,攻击者通常还需要开启什么功能才能让目标主机正常上网而不察觉攻击?", + "options": { + "A": "IP 转发(IP Forwarding)", + "B": "NAT 地址转换", + "C": "DHCP 服务", + "D": "DNS 缓存服务" + }, + "answer": "A", + "explanation": "攻击者通过 ARP 欺骗截获目标主机的流量后,需要开启 IP 转发功能(IP Forwarding),将截获的数据包正常转发给网关,再将网关的响应回传给目标主机。这样目标主机的网络通信不会中断,攻击者可以在中间透明地监听或篡改数据。如果不开 IP 转发,目标主机将无法上网,攻击很容易被发现。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "以下哪一项属于 DNS 劫持的常见实施方式?", + "options": { + "A": "修改目标主机的 hosts 文件", + "B": "向目标主机发送大量 ICMP 包", + "C": "利用 SQL 注入获取数据库信息", + "D": "通过钓鱼邮件获取用户密码" + }, + "answer": "A", + "explanation": "修改 hosts 文件是一种典型的 DNS 劫持方式。hosts 文件的优先级高于 DNS 服务器查询,攻击者在其中写入错误的域名-IP 映射后,目标主机访问该域名时会直接访问错误的 IP 地址。B 是 ICMP 洪泛攻击,C 是 Web 应用攻击,D 是社会工程学攻击,均不属于 DNS 劫持。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "Nmap 工具中,-sS 和 -sT 两种扫描方式的主要区别是什么?", + "options": { + "A": "-sS 扫描 UDP 端口,-sT 扫描 TCP 端口", + "B": "-sS 是半开扫描不建立完整连接,-sT 是全连接扫描", + "C": "-sS 扫描速度更慢,-sT 速度更快", + "D": "-sS 只能扫描本地端口,-sT 可以扫描远程端口" + }, + "answer": "B", + "explanation": "Nmap 中 -sS 指定 SYN 扫描(半开扫描),发送 SYN 后根据响应判断端口状态,不完成三次握手,需要 root 权限但更隐蔽快速;-sT 指定 TCP Connect 扫描(全连接扫描),通过系统调用完成完整的三次握手,不需要 root 权限但更容易被日志记录。A 错误,两者都是 TCP 扫描;C 错误,-sS 通常更快;D 错误,两者都可以扫描远程端口。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "SSL Stripping 攻击的原理是?", + "options": { + "A": "暴力破解 SSL 证书的私钥", + "B": "降级 HTTPS 连接为 HTTP,使加密保护失效", + "C": "伪造 CA 机构签发虚假证书", + "D": "利用 SSL 协议漏洞直接解密通信内容" + }, + "answer": "B", + "explanation": "SSL Stripping 是一种中间人攻击技术,攻击者在客户端和服务器之间拦截通信。当客户端请求 HTTPS 页面时,攻击者与服务器建立 HTTPS 连接,但以 HTTP 方式与客户端通信,从而「剥离」了 SSL/TLS 加密层。客户端的地址栏显示 HTTP 而非 HTTPS,数据以明文传输。该攻击由 Moxie Marlinspike 在 2009 年提出。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "要防御 ARP 欺骗攻击,以下哪种方案最为全面有效?", + "options": { + "A": "仅在客户端绑定静态 ARP 条目", + "B": "在网络设备上启用动态 ARP 检测(DAI),并结合 DHCP Snooping", + "C": "关闭所有主机的 ARP 协议", + "D": "将所有主机的 ARP 缓存超时时间设为 0" + }, + "answer": "B", + "explanation": "动态 ARP 检测(Dynamic ARP Inspection, DAI)配合 DHCP Snooping 可以在网络交换机层面验证 ARP 报文的合法性,丢弃非法 ARP 报文,是目前最为全面有效的 ARP 欺骗防御方案。A 的静态绑定在大规模网络中难以维护,且无法防御网关侧的 ARP 欺骗;C 关闭 ARP 协议会导致局域网内无法正常通信;D 设超时为 0 会导致 ARP 请求泛滥且不能阻止攻击。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "DNSSEC(DNS Security Extensions)主要解决哪类 DNS 安全问题?", + "options": { + "A": "DNS 服务器遭受 DDoS 攻击", + "B": "DNS 响应被篡改或伪造", + "C": "DNS 查询内容被第三方窃听", + "D": "DNS 服务器配置错误" + }, + "answer": "B", + "explanation": "DNSSEC 通过数字签名机制为 DNS 数据提供来源验证和数据完整性保护,确保 DNS 响应来自权威 DNS 服务器且未被篡改。它可以有效防止 DNS 缓存投毒等 DNS 欺骗攻击。A 需要 DDoS 防护方案,C 需要 DNS over HTTPS(DoH)或 DNS over TLS(DoT),D 需要配置审计工具。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "以下哪种端口扫描方式能够同时检测端口状态和操作系统类型?", + "options": { + "A": "PING 扫描", + "B": "TCP FIN 扫描", + "C": "Nmap 的操作系统指纹识别扫描(-O 参数)", + "D": "UDP 扫描" + }, + "answer": "C", + "explanation": "Nmap 的操作系统指纹识别(-O)通过分析目标主机对各种探测包的响应特征(如 TCP 窗口大小、TTL 值、TCP 选项等)来推断操作系统类型,结合端口扫描功能可以同时获得端口状态和操作系统信息。A 的 PING 扫描仅检测主机是否在线;B 的 FIN 扫描仅判断端口状态;D 的 UDP 扫描仅探测 UDP 端口。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "ARP 欺骗与 DNS 劫持结合使用时,攻击者可以实现什么效果?", + "options": { + "A": "仅能窃听局域网内的通信内容", + "B": "将目标用户对特定域名的访问重定向到钓鱼网站,且难以被发现", + "C": "直接获取目标主机的管理员权限", + "D": "使目标主机完全断网" + }, + "answer": "B", + "explanation": "ARP 欺骗将攻击者置于通信中间人位置,DNS 劫持则在 DNS 解析阶段将域名指向攻击者控制的 IP。两者结合后,攻击者可以先通过 ARP 欺骗截获流量,再篡改 DNS 响应,将用户对合法网站的访问重定向到精心制作的钓鱼网站。由于用户输入的域名无误,且钓鱼网站可以模仿真实界面,极难被发现。A 不够全面,C 与此类网络层攻击无关,D 是攻击失效而非攻击目的。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "ARP 协议的主要功能是?", + "options": { + "A": "将域名解析为 IP 地址", + "B": "将 IP 地址解析为 MAC 地址", + "C": "将 MAC 地址解析为 IP 地址", + "D": "分配 IP 地址给网络设备" + }, + "answer": "B", + "explanation": "ARP(Address Resolution Protocol,地址解析协议)的核心功能是在已知目标 IP 地址的情况下,通过广播请求获取对应设备的 MAC 地址。A 是 DNS 的功能,C 是 RARP(逆向 ARP)的功能,D 是 DHCP 的功能。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "以下关于 UDP 端口扫描的描述,哪项是正确的?", + "options": { + "A": "UDP 扫描速度比 TCP SYN 扫描更快", + "B": "UDP 扫描通过发送 UDP 数据包,根据是否收到 ICMP 端口不可达消息判断端口状态", + "C": "UDP 扫描不需要特殊权限即可执行", + "D": "UDP 扫描的准确性比 TCP 扫描更高" + }, + "answer": "B", + "explanation": "UDP 端口扫描的原理是向目标 UDP 端口发送 UDP 数据包,如果收到 ICMP Port Unreachable 响应则说明端口关闭,如果没有响应(或收到 UDP 应答)则端口可能开放。由于 UDP 是无连接协议,扫描效率较低且准确性不如 TCP 扫描(因为无响应可能是端口开放也可能是包被丢弃)。A 错误,UDP 扫描通常更慢;C 不完全准确,需要原始套接字权限;D 错误,UDP 扫描准确性通常更低。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "DNS 缓存投毒(DNS Cache Poisoning)攻击中,攻击者利用的是 DNS 协议的哪个缺陷?", + "options": { + "A": "DNS 查询使用明文传输,缺乏加密", + "B": "DNS 协议缺乏对响应报文的来源认证机制", + "C": "DNS 服务器没有访问控制功能", + "D": "DNS 协议不支持 IPv6 地址" + }, + "answer": "B", + "explanation": "DNS 缓存投毒攻击利用的是传统 DNS 协议缺乏对响应报文来源的认证机制。DNS 使用 UDP 协议,且查询报文中的 Transaction ID 仅 16 位,攻击者可以通过预测或暴力猜测 Transaction ID,在合法响应到达之前向 DNS 缓存服务器注入伪造的 DNS 响应记录。DNSSEC 的引入正是为了解决这一问题,通过数字签名验证 DNS 响应的来源和完整性。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "以下哪种场景最容易遭受中间人攻击?", + "options": { + "A": "使用 VPN 连接的远程办公", + "B": "连接公共场合未加密的开放 Wi-Fi", + "C": "通过 4G/5G 蜂窝网络上网", + "D": "使用有线网络连接企业内网" + }, + "answer": "B", + "explanation": "公共场合的开放 Wi-Fi 通常没有加密保护,攻击者可以在同一网络中轻松实施 ARP 欺骗、DNS 劫持等中间人攻击。此外,还可以搭建恶意 Wi-Fi 热点(Evil Twin)诱骗用户连接。A 的 VPN 加密了所有通信,大幅降低中间人攻击风险;C 的蜂窝网络有较强的链路层加密;D 的有线企业内网通常有网络隔离和监控措施。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "端口扫描的主要目的是什么?", + "options": { + "A": "破解目标主机的登录密码", + "B": "发现目标主机开放的服务和潜在攻击入口", + "C": "加密目标主机的网络通信", + "D": "提升目标主机的网络带宽" + }, + "answer": "B", + "explanation": "端口扫描是网络安全评估的基础步骤,通过探测目标主机各端口的状态(开放、关闭、过滤),可以了解目标主机上运行了哪些网络服务(如 Web 服务、SSH、FTP 等),进而发现潜在的安全漏洞和攻击入口。A 是暴力破解的工作,C 和 D 与端口扫描无关。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "网络攻防", + "arp欺骗", + "中间人攻击" + ], + "question": "在 ARP 欺骗实现的中间人攻击中,攻击者同时欺骗网关和目标主机的技术术语叫什么?", + "options": { + "A": "单向 ARP 欺骗", + "B": "双向 ARP 欺骗(ARP 双毒化)", + "C": "ARP 泛洪攻击", + "D": "ARP 重放攻击" + }, + "answer": "B", + "explanation": "双向 ARP 欺骗(Bidirectional ARP Spoofing / ARP Double Poisoning)是指攻击者同时向网关和目标主机发送伪造的 ARP 应答:告诉目标主机'网关的 MAC 是攻击者的 MAC',同时告诉网关'目标主机的 MAC 是攻击者的 MAC'。这样双向流量都经过攻击者,实现完整的中间人攻击。A 的单向欺骗只能截获一个方向的流量;C 的 ARP 泛洪是用大量 ARP 报文淹没交换机 MAC 表;D 不是标准术语。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/network-attack/true_false.json b/topics/cybersecurity/network-attack/true_false.json new file mode 100644 index 0000000..283ac32 --- /dev/null +++ b/topics/cybersecurity/network-attack/true_false.json @@ -0,0 +1,149 @@ +{ + "topic": "network-attack", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "ARP 协议在设计时没有内置任何认证机制,因此 ARP 欺骗攻击才容易实施。", + "answer": true, + "explanation": "正确。ARP 协议在设计之初处于可信网络环境的假设下,没有任何身份认证和验证机制。任何主机都可以发送 ARP 应答报文,即使它并没有收到对应的 ARP 请求,接收方也不会验证报文的真实性。这种缺乏认证的设计是 ARP 欺骗(ARP Spoofing)得以实施的根本原因。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "使用 HTTPS 协议访问网站可以完全防止 DNS 劫持攻击。", + "answer": false, + "explanation": "错误。HTTPS 能保护数据传输的机密性和完整性,但 DNS 解析发生在 HTTPS 连接建立之前。如果 DNS 解析阶段被劫持,用户会被引导到攻击者控制的 IP 地址。虽然 HTTPS 的证书验证机制可能在后续阶段发现异常(如证书不匹配),但 DNS 劫持本身仍然发生。要防御 DNS 劫持,应使用 DNSSEC、DoH(DNS over HTTPS)或 DoT(DNS over TLS)。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "TCP FIN 扫描在所有操作系统上的行为都是一致的。", + "answer": false, + "explanation": "错误。TCP FIN 扫描的行为依赖于目标操作系统对 RFC 793 的实现。按照 RFC 793 规范,关闭的端口收到 FIN 包应返回 RST,开放的端口应忽略。但 Microsoft Windows 系统不遵循此规范,无论端口是否开放都返回 RST。因此 TCP FIN 扫描在 Linux/Unix 系统上有效,但在 Windows 系统上无法区分端口状态。这也是 Nmap 要进行操作系统指纹识别的原因之一。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 1, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "中间人攻击只能发生在局域网内部,无法在互联网上实施。", + "answer": false, + "explanation": "错误。虽然 ARP 欺骗等常见的中间人攻击主要在局域网内实施,但中间人攻击可以在多种网络环境中发生。例如,BGP 劫持可以在互联网骨干网层面实施中间人攻击;恶意 Wi-Fi 热点可以在公共场合对用户实施中间人攻击;DNS 劫持也可以在互联网层面将用户引导到恶意服务器。此外,ISP 层面的流量劫持也是互联网上的中间人攻击形式。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "arp欺骗" + ], + "question": "ARP 欺骗攻击可以使交换机退化为类似集线器的工作模式,导致所有流量被广播。", + "answer": false, + "explanation": "错误。ARP 欺骗和 MAC 泛洪(MAC Flooding)是两种不同的攻击。ARP 欺骗是通过伪造 ARP 应答污染目标主机的 ARP 缓存,使特定目标的流量被发送到攻击者,但交换机的 MAC 地址表仍然正常工作。使交换机退化为集线器模式的是 MAC 泛洪攻击,它通过发送大量伪造源 MAC 地址的帧使交换机 MAC 表溢出,交换机被迫将所有帧广播到所有端口。两者虽然都与局域网安全相关,但原理不同。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 1, + "tags": [ + "网络攻防", + "DNS劫持" + ], + "question": "修改本地 hosts 文件可以使特定域名的 DNS 解析结果被劫持。", + "answer": true, + "explanation": "正确。hosts 文件的解析优先级高于 DNS 服务器查询。操作系统在进行域名解析时,会首先检查 hosts 文件中是否存在对应的映射记录。如果攻击者在 hosts 文件中为某个域名添加了错误的 IP 映射,系统就会直接使用该错误地址,而不会向 DNS 服务器发起查询,从而实现 DNS 劫持。这也是很多恶意软件常用的劫持手段。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "防火墙和入侵检测系统(IDS)无法检测到 TCP SYN 扫描。", + "answer": false, + "explanation": "错误。虽然 TCP SYN 扫描(半开扫描)比全连接扫描更隐蔽,但现代防火墙和 IDS 仍然可以通过多种方式检测到它。例如:检测短时间内对大量端口的 SYN 请求模式;检测同一源 IP 向多个端口发送 SYN 后收到 RST 的异常行为;利用状态检测(Stateful Inspection)发现未完成的 TCP 握手。入侵防御系统(IPS)还可以主动阻断可疑的扫描行为。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "中间人攻击" + ], + "question": "在中间人攻击中,攻击者可以同时修改 HTTP 请求和 HTTP 响应的内容。", + "answer": true, + "explanation": "正确。在 HTTP 明文通信的中间人攻击中,攻击者位于通信双方之间,拥有对数据流的完全控制权。攻击者可以修改客户端发送的 HTTP 请求(如篡改表单数据、添加恶意头部),也可以修改服务器返回的 HTTP 响应(如注入恶意脚本、替换页面内容、插入广告)。这也是为什么 HTTPS 的加密和完整性保护如此重要——它可以防止中间人篡改通信内容。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 1, + "tags": [ + "网络攻防", + "端口扫描" + ], + "question": "端口 80 通常用于 HTTP 服务,端口 443 通常用于 HTTPS 服务。", + "answer": true, + "explanation": "正确。根据 IANA(互联网号码分配机构)的服务端口分配标准,端口 80 是 HTTP(超文本传输协议)的默认端口,端口 443 是 HTTPS(HTTP over TLS/SSL)的默认端口。当用户访问 http://example.com 时默认使用 80 端口,访问 https://example.com 时默认使用 443 端口。这些是端口扫描中最常见的发现。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 2, + "tags": [ + "网络攻防", + "DNS劫持", + "arp欺骗" + ], + "question": "DNS 劫持和 ARP 欺骗都属于网络层攻击,都可以用于将用户重定向到恶意网站。", + "answer": true, + "explanation": "正确。DNS 劫持通过篡改 DNS 解析结果将域名指向恶意 IP,ARP 欺骗则通过伪造 ARP 映射截获流量后可进一步篡改 DNS 响应。两者都可以将用户对正常网站的访问重定向到攻击者控制的恶意网站。DNS 劫持工作在网络层(影响域名到 IP 的解析),ARP 欺骗工作在数据链路层到网络层的边界(影响 IP 到 MAC 的映射),但两者都属于广义的网络层攻击范畴。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/os-security/code_reading.json b/topics/cybersecurity/os-security/code_reading.json new file mode 100644 index 0000000..dc84e2f --- /dev/null +++ b/topics/cybersecurity/os-security/code_reading.json @@ -0,0 +1,68 @@ +{ + "topic": "os-security", + "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": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "阅读以下 Windows C/C++ 代码,分析其安全行为并回答子问题。", + "language": "c", + "code": "#include \n#include \n\nint main() {\n HANDLE hToken = NULL;\n HANDLE hRestrictedToken = NULL;\n SID_IDENTIFIER_AUTHORITY sia = SECURITY_MANDATORY_LABEL_AUTHORITY;\n PSID pIntegritySid = NULL;\n TOKEN_MANDATORY_LABEL tml;\n\n // Step 1: Open current process token\n if (!OpenProcessToken(GetCurrentProcess(),\n TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT,\n &hToken)) {\n printf(\"OpenProcessToken failed: %lu\\n\", GetLastError());\n return 1;\n }\n\n // Step 2: Create a restricted token\n SID_AND_ATTRIBUTES sidToDisable;\n SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;\n AllocateAndInitializeSid(&ntAuth, 2,\n SECURITY_BUILTIN_DOMAIN_RID,\n DOMAIN_ALIAS_RID_ADMINS,\n 0, 0, 0, 0, 0, 0,\n &sidToDisable.Sid);\n sidToDisable.Attributes = 0;\n\n if (!CreateRestrictedToken(hToken,\n DISABLE_MAX_PRIVILEGE,\n 1, &sidToDisable,\n 0, NULL,\n 0, NULL,\n &hRestrictedToken)) {\n printf(\"CreateRestrictedToken failed: %lu\\n\", GetLastError());\n FreeSid(sidToDisable.Sid);\n CloseHandle(hToken);\n return 1;\n }\n\n // Step 3: Set low integrity level\n AllocateAndInitializeSid(&sia, 1,\n SECURITY_MANDATORY_LOW_RID,\n 0, 0, 0, 0, 0, 0, 0,\n &pIntegritySid);\n tml.Label.Attributes = SE_GROUP_INTEGRITY;\n tml.Label.Sid = pIntegritySid;\n\n SetTokenInformation(hRestrictedToken,\n TokenIntegrityLevel,\n &tml,\n sizeof(tml));\n\n // Step 4: Create process with restricted token\n STARTUPINFO si = { sizeof(si) };\n PROCESS_INFORMATION pi;\n if (CreateProcessAsUser(\n hRestrictedToken,\n \"C:\\\\Windows\\\\notepad.exe\",\n NULL, NULL, NULL,\n FALSE,\n CREATE_NEW_CONSOLE,\n NULL, NULL, &si, &pi)) {\n printf(\"Process created with restricted token (PID: %lu)\\n\",\n pi.dwProcessId);\n CloseHandle(pi.hProcess);\n CloseHandle(pi.hThread);\n } else {\n printf(\"CreateProcessAsUser failed: %lu\\n\", GetLastError());\n }\n\n // Cleanup\n FreeSid(pIntegritySid);\n FreeSid(sidToDisable.Sid);\n CloseHandle(hRestrictedToken);\n CloseHandle(hToken);\n return 0;\n}", + "source": null, + "related": [], + "sub_questions": [ + { + "index": 1, + "type": "single_choice", + "question": "这段代码中 CreateRestrictedToken() 的 DISABLE_MAX_PRIVILEGE 标志的作用是什么?", + "options": { + "A": "禁用所有用户的登录权限", + "B": "移除令牌中的所有特权,使新令牌几乎没有任何特权", + "C": "仅禁用网络相关的特权", + "D": "将所有特权降低为只读权限" + }, + "answer": "B", + "explanation": "DISABLE_MAX_PRIVILEGE 标志告诉 CreateRestrictedToken() 移除源令牌中的所有特权(SeDebugPrivilege、SeBackupPrivilege 等),使得受限令牌几乎没有可用特权。这是实现最低权限运行的核心手段。结合代码中将 Administrators 组 SID 标记为 deny-only(sidToDisable.Attributes = 0),新建的进程既没有管理特权,也无法通过 Administrators 组获得资源访问权限。" + }, + { + "index": 2, + "type": "single_choice", + "question": "代码第 3 步将 TokenIntegrityLevel 设置为 SECURITY_MANDATORY_LOW_RID 的效果是:", + "options": { + "A": "使进程可以访问所有低权限用户的文件", + "B": "将进程的完整性级别设为'低',使其无法写入中/高等级的资源(如注册表、文件系统敏感区域)", + "C": "降低进程的 CPU 优先级", + "D": "限制进程只能使用 64MB 内存" + }, + "answer": "B", + "explanation": "Windows 的完整性级别机制(Mandatory Integrity Control)为安全对象分配低(Low)、中(Medium)、高(High)、系统(System)等级别。进程默认以中等级别运行。将进程的完整性级别设为 LOW 后,该进程只能写入同样标记为 LOW 完整性的对象,无法修改中等级别的对象(如大多数用户文件和注册表键)。这是 Windows 保护敏感资源不被低信任度进程篡改的重要机制,常见于 IE 的保护模式和 Chrome 的沙箱。" + }, + { + "index": 3, + "type": "short_answer", + "question": "综合分析这段代码实现了什么样的安全目标?它采用了哪些安全机制的组合?", + "answer": "这段代码实现了一个最小特权进程创建模式,其安全目标是:即使以管理员身份运行本程序,创建的子进程(notepad.exe)也将以极低权限运行,无法执行特权操作或修改敏感资源。它组合了三种安全机制:1)CreateRestrictedToken + DISABLE_MAX_PRIVILEGE:移除所有特权并禁用 Administrators 组,防止提权和管理资源访问;2)TokenIntegrityLevel 设为 Low:利用强制完整性控制(MIC),阻止进程写入中高等级的安全对象;3)CreateProcessAsUser:使用受限令牌创建新进程,确保子进程从启动起就处于受限状态。这种组合防御(Defense in Depth)策略确保了即使单一机制被绕过,其他机制仍提供保护。", + "keywords": [ + "最小特权", + "CreateRestrictedToken", + "完整性级别", + "CreateProcessAsUser", + "Defense in Depth", + "MIC" + ], + "scoring_rubric": "满分标准:正确理解代码意图——创建低权限子进程(2分);说明 CreateRestrictedToken 的作用——移除特权和禁用组(2分);说明完整性级别设置的作用——MIC 阻止写入高完整性对象(2分);说明 CreateProcessAsUser 的作用——使用受限令牌启动新进程(1分);总结为组合防御策略(1分)。", + "explanation": "" + } + ], + "explanation": "" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/os-security/meta.json b/topics/cybersecurity/os-security/meta.json new file mode 100644 index 0000000..446769f --- /dev/null +++ b/topics/cybersecurity/os-security/meta.json @@ -0,0 +1,28 @@ +{ + "slug": "os-security", + "name": "操作系统安全", + "description": "权限模型、沙箱、Windows 安全机制(Win32 API)", + "tags": [ + "操作系统安全", + "权限模型", + "沙箱", + "win32api", + "windows安全" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/os-security/short_answer.json b/topics/cybersecurity/os-security/short_answer.json new file mode 100644 index 0000000..de4d7c3 --- /dev/null +++ b/topics/cybersecurity/os-security/short_answer.json @@ -0,0 +1,85 @@ +{ + "topic": "os-security", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "简述 Linux 中 DAC(自主访问控制)、MAC(强制访问控制)和 Capabilities 三种权限机制的区别和适用场景。", + "answer": "DAC 基于文件所有者自行设置权限(如 rwx 权限位和 ACL),灵活性高但安全性依赖于用户配置,适用于一般桌面和服务器环境。MAC 由系统安全策略统一强制执行,用户无法覆盖(如 SELinux 的安全上下文),适用于高安全需求环境如政府、军事系统。Capabilities 将 root 的超级权限拆分为细粒度的独立特权(如 CAP_NET_BIND_SERVICE),允许进程仅获得所需特权,适用于需要最小特权原则的服务进程和容器环境。三者可组合使用:DAC 作为基础权限控制,MAC 提供强制策略,Capabilities 实现精细特权分配。", + "keywords": [ + "DAC", + "MAC", + "Capabilities", + "自主访问控制", + "强制访问控制", + "SELinux", + "最小特权", + "ACL", + "权限" + ], + "scoring_rubric": "满分标准:正确区分三种机制(3分);说明 DAC 基于所有者自主控制(1分);说明 MAC 由系统策略强制执行(1分);说明 Capabilities 将 root 权限细粒化(1分);提及各自适用场景(2分);说明三者可组合使用(1分)。部分正确酌情给分。", + "source": null, + "related": [], + "explanation": "" + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "解释 Linux 容器技术中 Namespace 和 Cgroup 各自的作用,并说明它们如何协同工作实现容器的安全隔离。", + "answer": "Namespace 负责资源隔离:每种 Namespace(PID、NET、MNT、UTS、IPC、USER、Cgroup)隔离一类系统资源,使容器内的进程只能看到属于该容器命名空间的资源,如同拥有独立的系统视图。Cgroup(Control Groups)负责资源限制:通过配置文件限制进程组可使用的 CPU 时间、内存、磁盘 I/O、网络带宽等资源上限,防止单个容器耗尽宿主机资源。两者协同工作:Namespace 提供'看不见彼此'的隔离,Cgroup 提供'用不了太多'的限制。同时结合 seccomp 限制系统调用、AppArmor/SELinux 提供强制访问控制,构成完整的容器安全体系。", + "keywords": [ + "Namespace", + "Cgroup", + "隔离", + "资源限制", + "PID", + "NET", + "容器", + "seccomp" + ], + "scoring_rubric": "满分标准:正确描述 Namespace 的隔离作用(2分);列举至少 3 种 Namespace 类型(1分);正确描述 Cgroup 的资源限制作用(2分);说明两者的协同关系(2分);提及额外的安全层如 seccomp(1分)。部分正确酌情给分。", + "source": null, + "related": [], + "explanation": "" + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 3, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "描述 Windows 安全子系统在一次文件访问请求中的完整检查流程,包括涉及的关键组件(如 Access Token、Security Descriptor、DACL 等)。", + "answer": "当进程尝试访问一个文件时,Windows 安全子系统的检查流程如下:1)进程调用如 CreateFile() 的 Win32 API;2)内核安全引用监视器(Security Reference Monitor)介入;3)系统获取进程的 Access Token(包含用户 SID、组 SID 列表和特权);4)获取文件的 Security Descriptor(包含 Owner SID、DACL 和 SACL);5)安全引用监视器遍历 DACL 中的 ACE(访问控制条目),将请求的访问掩码与每个 ACE 中的允许/拒绝权限进行匹配;6)如果遇到拒绝 ACE 立即拒绝;如果所有匹配的 ACE 都允许则放行;如果没有匹配的 ACE 且 DACL 不存在则拒绝(NULL DACL 除外,此时允许所有人完全访问);7)若 SACL 中有审计条目,生成安全审计事件;8)检查进程特权(如 SeBackupPrivilege 可绕过部分 DACL 检查);9)返回允许或拒绝的结果给调用者。", + "keywords": [ + "Access Token", + "Security Descriptor", + "DACL", + "SACL", + "ACE", + "Security Reference Monitor", + "SID", + "访问掩码" + ], + "scoring_rubric": "满分标准:提及安全引用监视器(1分);正确描述 Access Token 的内容和作用(2分);正确描述 Security Descriptor 及其组成(2分);说明 DACL/ACE 的匹配逻辑(拒绝优先)(2分);提及 SACL 审计功能(1分);提及特权检查(1分);流程描述完整连贯(1分)。部分正确酌情给分。", + "source": null, + "related": [], + "explanation": "" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/os-security/single_choice.json b/topics/cybersecurity/os-security/single_choice.json new file mode 100644 index 0000000..7a06e95 --- /dev/null +++ b/topics/cybersecurity/os-security/single_choice.json @@ -0,0 +1,414 @@ +{ + "topic": "os-security", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "在 UNIX/Linux 权限模型中,文件权限 rwxr-xr-- 对应的八进制数值是:", + "options": { + "A": "744", + "B": "754", + "C": "644", + "D": "755" + }, + "answer": "B", + "explanation": "权限分为三组:所有者(rwx=4+2+1=7)、同组(r-x=4+0+1=5)、其他(r--=4+0+0=4),因此八进制为 754。A 选项 744 表示 rwxr-----,C 选项 644 表示 rw-r--r--,D 选项 755 表示 rwxr-xr-x,均不符合题意。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "以下哪个 Linux 命令可以将文件 /etc/passwd 的所有者修改为用户 alice?", + "options": { + "A": "chmod alice /etc/passwd", + "B": "chown alice /etc/passwd", + "C": "chgrp alice /etc/passwd", + "D": "passwd alice /etc/passwd" + }, + "answer": "B", + "explanation": "chown (change owner) 命令用于修改文件所有者。chmod 用于修改文件权限位,chgrp 用于修改文件所属组,passwd 用于修改用户密码。注意修改文件所有者通常需要 root 权限。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "Linux 中 SUID 位的作用是:", + "options": { + "A": "使文件只能被 root 用户读取", + "B": "使程序在执行时以文件所有者的身份运行", + "C": "使文件不可被删除", + "D": "使目录下的文件自动继承目录的组" + }, + "answer": "B", + "explanation": "SUID (Set User ID) 位使得任何用户执行该程序时,进程的有效用户 ID 变为文件所有者的 UID,而不是执行者的 UID。典型例子是 /usr/bin/passwd,它需要以 root 身份运行来修改 /etc/shadow。A 描述的是文件读权限限制,C 描述的是 sticky bit 在某些场景下的效果(实际 sticky bit 防止非所有者删除目录中的文件),D 描述的是 SGID 对目录的效果。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "在访问控制模型中,DAC(自主访问控制)与 MAC(强制访问控制)的核心区别是:", + "options": { + "A": "DAC 由系统管理员统一管理,MAC 由资源所有者决定", + "B": "DAC 允许资源所有者自行设定访问权限,MAC 由系统安全策略统一强制执行", + "C": "DAC 只用于文件系统,MAC 只用于网络", + "D": "DAC 比 MAC 安全性更高" + }, + "answer": "B", + "explanation": "DAC(Discretionary Access Control)允许资源的所有者自行决定谁可以访问该资源,灵活性高但可能因配置不当导致安全问题。MAC(Mandatory Access Control)由系统管理员预定义的安全策略强制执行,普通用户无法覆盖,安全性更高。Linux 的 SELinux 和 AppArmor 就是 MAC 实现。A 恰好说反了;C 不正确,两者都可用于多种资源;D 不正确,MAC 通常比 DAC 更安全。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "Linux 的 Capabilities 机制相比传统的 root/普通用户二分模型,其主要优势是:", + "options": { + "A": "完全取代了传统的 UID/GID 机制", + "B": "可以将 root 的超级权限细分为多个独立的特权能力,按需授予", + "C": "使普通用户自动获得所有系统特权", + "D": "只适用于容器环境,不能用于普通进程" + }, + "answer": "B", + "explanation": "Linux Capabilities 将传统的 root 全能权限拆分为多个独立的细粒度能力,如 CAP_NET_BIND_SERVICE(绑定低端口)、CAP_SYS_PTRACE(跟踪进程)等。这样非 root 进程也可以获得所需的特定特权,而无需拥有全部 root 权限,符合最小特权原则。A 错误,Capabilities 补充但未取代 UID/GID;C 错误,Capabilities 需显式授予;D 错误,Capabilities 普遍适用于 Linux 进程。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "沙箱(Sandbox)在操作系统安全中的主要作用是:", + "options": { + "A": "加速程序的运行速度", + "B": "隔离程序的运行环境,限制其对系统资源的访问", + "C": "为程序提供更大的内存空间", + "D": "自动修复程序中的安全漏洞" + }, + "answer": "B", + "explanation": "沙箱是一种安全机制,通过创建受限的隔离环境来运行不可信的程序,限制其对文件系统、网络、系统调用等资源的访问,从而保护宿主系统。典型应用包括浏览器沙箱、移动端应用沙箱、容器化等。A、C 均不是沙箱的功能,D 也不正确,沙箱不负责修复漏洞。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "seccomp(Secure Computing Mode)是 Linux 内核提供的一种安全机制,其工作原理是:", + "options": { + "A": "加密进程的内存空间", + "B": "限制进程可以使用的系统调用,禁止不在白名单中的调用", + "C": "将进程迁移到独立的网络命名空间", + "D": "对进程的所有文件操作进行审计日志记录" + }, + "answer": "B", + "explanation": "seccomp 是 Linux 内核提供的系统调用过滤机制。在 seccomp 模式下,进程只能使用有限的系统调用(如 read、write、exit、sigreturn)。seccomp-bpf 扩展允许通过 BPF 程序定义更灵活的过滤规则(白名单/黑名单)。Docker、Chrome 等都使用了 seccomp 来限制容器或子进程的系统调用。A 是加密概念,C 是 namespace 功能,D 是 auditd 的功能。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "Linux Namespace 技术可以隔离以下哪些系统资源?", + "options": { + "A": "仅进程 ID(PID)", + "B": "进程 ID、网络、挂载点、用户 ID、主机名等多种资源", + "C": "仅文件系统", + "D": "仅网络和内存" + }, + "answer": "B", + "explanation": "Linux Namespace 是容器技术的基础之一,提供了多种资源隔离类型:PID(进程号)、NET(网络栈)、MNT(挂载点)、UTS(主机名)、IPC(进程间通信)、USER(用户和组 ID)、Cgroup(cgroup 根目录)等。每种 namespace 负责隔离特定的系统资源,组合使用可以实现完整的环境隔离。因此 A、C、D 的描述都过于局限。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "以下哪种技术不属于操作系统级沙箱机制?", + "options": { + "A": "Linux seccomp", + "B": "Windows AppContainer", + "C": "Java 虚拟机(JVM)的字节码验证", + "D": "FreeBSD Capsicum" + }, + "answer": "C", + "explanation": "JVM 的字节码验证属于应用程序层面的安全机制(语言运行时沙箱),而非操作系统内核级沙箱。seccomp 是 Linux 内核的系统调用过滤,AppContainer 是 Windows 8+ 的应用隔离容器,Capsicum 是 FreeBSD 的能力(capability)和沙箱框架,三者都是操作系统内核提供的沙箱机制。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "windows安全" + ], + "question": "Windows 操作系统中,用于标识安全主体(用户、组、计算机)的唯一标识符是:", + "options": { + "A": "PID(进程 ID)", + "B": "SID(安全标识符)", + "C": "GUID(全局唯一标识符)", + "D": "MAC 地址" + }, + "answer": "B", + "explanation": "SID (Security Identifier) 是 Windows 中用于唯一标识安全主体的字符串,格式如 S-1-5-21-xxx。每个用户、组和计算机都有一个唯一的 SID,它被广泛用于访问控制列表(ACL)中的权限判定。PID 是进程标识符,GUID 是通用唯一标识符,MAC 地址是网络硬件地址,均非 Windows 安全主体的标识。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "在 Windows 安全模型中,Access Token(访问令牌)包含以下哪些信息?", + "options": { + "A": "仅用户的密码哈希", + "B": "用户的 SID、所属组的 SID 列表、特权列表和默认 DACL", + "C": "仅用户的登录时间和 IP 地址", + "D": "用户的浏览器 Cookie 和浏览历史" + }, + "answer": "B", + "explanation": "Access Token 是 Windows 安全子系统的核心数据结构,当用户登录时由系统创建。它包含:用户 SID、所属组 SID 列表、分配的特权(如 SeShutdownPrivilege)、默认所有者 SID、默认 DACL(自主访问控制列表)等。系统在每次访问安全对象时都会检查进程的 Access Token 来决定是否允许。A、C、D 的内容均不是 Access Token 的组成部分。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "以下 Win32 API 函数中,哪个用于创建一个受限的令牌(Restricted Token)以实现最小特权运行?", + "options": { + "A": "OpenProcessToken()", + "B": "CreateRestrictedToken()", + "C": "AdjustTokenPrivileges()", + "D": "DuplicateTokenEx()" + }, + "answer": "B", + "explanation": "CreateRestrictedToken() 可以基于现有令牌创建一个新的受限令牌,能删除特权、将 SID 标记为仅拒绝(deny-only)、添加受限 SID 等,常用于实现最低权限运行。OpenProcessToken() 用于打开进程的令牌句柄,AdjustTokenPrivileges() 用于启用/禁用令牌中的特权,DuplicateTokenEx() 用于复制令牌(可改变模拟级别),但只有 CreateRestrictedToken() 专门用于创建受限令牌。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "Windows 中的 UAC(User Account Control)机制的主要目的是:", + "options": { + "A": "加密用户的文件", + "B": "即使用户是管理员组成员,默认也以标准用户权限运行程序,需显式提升才能获得管理员权限", + "C": "自动更新操作系统补丁", + "D": "防止用户安装任何软件" + }, + "answer": "B", + "explanation": "UAC 是 Windows Vista 引入的安全特性。在 UAC 开启时,即使用户属于 Administrators 组,其进程默认也只获得标准用户令牌(filtered token),当操作需要管理员权限时,系统会弹出确认对话框(consent prompt)或要求输入管理员凭据(credential prompt)。这有效限制了恶意软件自动获取完全管理权限。A 是加密功能,C 是 Windows Update 功能,D 说法过于绝对。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "在 Win32 API 中,使用以下哪个函数可以检查当前进程是否有权限访问一个安全对象?", + "options": { + "A": "CreateFile()", + "B": "AccessCheck()", + "C": "VirtualAlloc()", + "D": "GetCurrentProcess()" + }, + "answer": "B", + "explanation": "AccessCheck() 函数用于检查指定的访问令牌是否拥有对安全对象的特定访问权限。它接受安全描述符(SECURITY_DESCRIPTOR)、客户端令牌、请求的访问掩码等参数,返回是否允许访问以及授予的访问权限。CreateFile() 虽然在打开文件时会进行访问检查,但它不是专门的权限检查函数;VirtualAlloc() 用于内存分配;GetCurrentProcess() 获取当前进程句柄。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "Windows 中安全描述符(SECURITY_DESCRIPTOR)的四个主要组成部分是:", + "options": { + "A": "SID、Token、Handle、Session", + "B": "Owner SID、Group SID、DACL、SACL", + "C": "用户名、密码、域、权限等级", + "D": "进程 ID、线程 ID、句柄表、访问掩码" + }, + "answer": "B", + "explanation": "SECURITY_DESCRIPTOR 是 Windows 中描述安全对象访问控制信息的结构体,包含四个核心组件:Owner SID(所有者安全标识符)、Group SID(主组安全标识符,主要用于 POSIX 子系统)、DACL(Discretionary ACL,自主访问控制列表,决定谁可以访问)、SACL(System ACL,系统访问控制列表,用于审计)。A、C、D 均不是安全描述符的组成部分。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "Linux 中 umask 的作用是:", + "options": { + "A": "设置用户的登录密码", + "B": "设置新创建文件和目录的默认权限掩码,屏蔽不需要的权限位", + "C": "查看当前系统的安全补丁状态", + "D": "加密文件系统" + }, + "answer": "B", + "explanation": "umask 是一个权限屏蔽码,用于确定新创建文件和目录的默认权限。例如 umask 为 022 时,新建文件权限为 644(rw-r--r--),新建目录权限为 755(rwxr-xr-x)。umask 屏蔽掉的权限位不会被赋予新文件。它不涉及密码设置、补丁查看或文件系统加密。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "Docker 容器在安全隔离方面与传统虚拟机的主要区别是:", + "options": { + "A": "Docker 容器比虚拟机隔离性更强", + "B": "Docker 容器共享宿主机内核,通过 Namespace 和 Cgroup 实现隔离;虚拟机有独立的内核", + "C": "虚拟机不支持网络隔离,Docker 支持", + "D": "Docker 容器不需要任何安全配置" + }, + "answer": "B", + "explanation": "Docker 容器与虚拟机的核心区别在于:容器直接运行在宿主机内核上,利用 Namespace 隔离系统资源、Cgroup 限制资源使用,因此共享内核;而虚拟机通过 Hypervisor 运行完整的操作系统,拥有独立的内核。由于共享内核,容器的隔离性理论上弱于虚拟机,容器逃逸可能直接影响宿主机。A 说反了;C 错误,虚拟机也支持网络隔离;D 错误,容器仍需安全配置(如 seccomp、AppArmor 等)。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "操作系统安全", + "windows安全" + ], + "question": "Windows 的 NTFS 文件系统支持的安全特性是:", + "options": { + "A": "文件级权限控制(ACL)", + "B": "仅支持文件加密,不支持权限控制", + "C": "仅支持目录级别的权限控制", + "D": "与 FAT32 的安全特性完全相同" + }, + "answer": "A", + "explanation": "NTFS(New Technology File System)支持为每个文件和目录设置独立的 ACL(Access Control List),可以精确控制不同用户和组对该文件/目录的访问权限(读、写、执行等)。NTFS 还支持 EFS(Encrypting File System)加密和审计。FAT32 不支持文件级权限控制,因此 D 错误。B 和 C 的描述不准确。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "调用 Win32 API CreateProcessAsUser() 时,第一个参数 hToken 必须具有哪些访问权限才能成功创建进程?", + "options": { + "A": "TOKEN_QUERY", + "B": "TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY", + "C": "TOKEN_ALL_ACCESS", + "D": "TOKEN_IMPERSONATE" + }, + "answer": "B", + "explanation": "CreateProcessAsUser() 要求传入的令牌句柄至少具有 TOKEN_QUERY(查询令牌信息)、TOKEN_DUPLICATE(复制令牌)和 TOKEN_ASSIGN_PRIMARY(将令牌分配给主令牌)三种访问权限。其中 TOKEN_ASSIGN_PRIMARY 是关键权限,允许将令牌用作新进程的主令牌。仅 TOKEN_QUERY(A)不够,TOKEN_ALL_ACCESS(C)包含所需权限但不是最低要求,TOKEN_IMPERSONATE(D)用于模拟而非进程创建。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "RBAC(基于角色的访问控制)相比直接为用户分配权限的主要优势是:", + "options": { + "A": "完全消除了安全漏洞", + "B": "通过角色将用户和权限解耦,简化权限管理并支持职责分离", + "C": "使每个用户都成为管理员", + "D": "只适用于小型系统" + }, + "answer": "B", + "explanation": "RBAC(Role-Based Access Control)引入角色作为用户和权限之间的中间层:管理员将权限分配给角色,再将角色分配给用户。这带来多个优势:1)用户变动时只需调整角色关联,无需逐一修改权限;2)支持职责分离(SoD),可约束某些角色不能同时被同一用户拥有;3)便于审计和合规。A 过于绝对,RBAC 不消除漏洞;C 与 RBAC 相悖;D 错误,RBAC 广泛用于大型企业系统。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/os-security/true_false.json b/topics/cybersecurity/os-security/true_false.json new file mode 100644 index 0000000..c0580cd --- /dev/null +++ b/topics/cybersecurity/os-security/true_false.json @@ -0,0 +1,150 @@ +{ + "topic": "os-security", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "在 Linux 系统中,root 用户(UID=0)可以绕过文件的所有权限检查。", + "answer": false, + "explanation": "这个说法不完全正确。root 用户虽然拥有极大的特权,但并不能绕过所有权限检查。具体来说:1)root 可以读写大多数文件,但在执行文件时仍需文件具有执行权限;2)如果文件系统被挂载为只读,root 也不能写入;3)对于使用了 POSIX ACL 或强制访问控制(如 SELinux)的系统,root 也受限于安全策略;4)带有不可变属性(immutable flag, chattr +i)的文件,root 也不能修改,除非先移除该标志。因此 root 并非完全绕过所有权限检查。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "Linux 中的 Sticky Bit 可以防止目录中的文件被非所有者删除。", + "answer": true, + "explanation": "当一个目录设置了 Sticky Bit(权限位末尾的 t,如 /tmp 的 drwxrwxrwt),只有文件的所有者、目录的所有者或 root 用户才能删除或重命名该目录中的文件。即使其他用户对该目录有写权限,也不能删除别人的文件。这是防止共享目录中文件被任意删除的重要安全机制,最典型的例子是 /tmp 目录。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "chroot 可以提供完整的安全沙箱隔离,足以防止所有类型的逃逸攻击。", + "answer": false, + "explanation": "chroot 只改变了进程看到的文件系统根目录,是一种非常基础的隔离手段,并非真正的安全沙箱。存在多种 chroot 逃逸方法:1)具有 root 权限的进程可以创建设备节点访问宿主磁盘;2)通过 mount 系统调用可能访问宿主文件系统;3)chroot 不隔离网络、进程、IPC 等资源。因此 chroot 不足以提供完整的安全隔离,现代方案通常使用 namespace、seccomp、SELinux 等组合来实现更完善的沙箱。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 1, + "tags": [ + "操作系统安全", + "windows安全" + ], + "question": "Windows 的 Windows Defender 是操作系统内置的防病毒和安全防护软件。", + "answer": true, + "explanation": "Windows Defender(现称 Microsoft Defender Antivirus)是 Windows 操作系统内置的安全防护软件,从 Windows 8 起默认启用。它提供实时保护、云安全智能、防火墙等功能,是 Windows 安全中心的核心组件之一。在 Windows 10/11 中,Defender 已成为功能完善的防病毒解决方案,无需额外安装第三方杀毒软件。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "在 Windows 中,普通用户进程可以通过 DuplicateTokenEx() 将自己的令牌提升为管理员权限。", + "answer": false, + "explanation": "DuplicateTokenEx() 只能复制一个已有的令牌,并可能改变其模拟级别(如从 SecurityIdentification 提升到 SecurityImpersonation 或 SecurityDelegation),但它无法凭空为令牌添加管理员权限。要获得管理员权限的令牌,必须通过 UAC 提升(如 ShellExecute 以 runas 动词运行)或由已具有管理员权限的进程显式创建和分配。安全子系统不会允许低权限进程自我提权。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 2, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "容器技术(如 Docker)的隔离强度与虚拟机相同,可以完全替代虚拟机用于安全敏感场景。", + "answer": false, + "explanation": "容器与虚拟机的隔离机制本质不同。容器共享宿主机内核,通过 namespace 和 cgroup 实现隔离,攻击者若利用内核漏洞可能实现容器逃逸,直接威胁宿主机。而虚拟机拥有独立的内核和虚拟硬件,通过 Hypervisor 实现硬件级别的隔离,攻击面更小。在多租户、高安全需求场景下,虚拟机或 Kata Containers 等轻量级 VM 方案更合适。容器更适合可信代码的快速部署和资源高效利用。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 1, + "tags": [ + "操作系统安全", + "权限模型" + ], + "question": "在 Linux 中,文件的 SUID 位对 shell 脚本会像对二进制程序一样生效。", + "answer": false, + "explanation": "Linux 内核会为设置了 SUID 位的二进制可执行文件切换有效用户 ID,但对于 shell 脚本(以 #!/bin/bash 等开头的解释型脚本),出于安全原因,大多数现代 Linux 系统会忽略其 SUID 位。这是因为脚本执行依赖于解释器,且脚本内容可被修改,存在严重的安全风险。如果需要以其他用户身份运行脚本,应使用 sudo 等机制。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全" + ], + "question": "Windows 的 Credential Guard 功能可以防止 Pass-the-Hash 攻击。", + "answer": true, + "explanation": "Windows Credential Guard 利用基于虚拟化的安全(VBS)技术,将 NTLM 密码哈希和 Kerberos 票据等凭据存储在一个独立的、受保护的虚拟机中(称为安全隔离环境),即使攻击者获得了操作系统内核权限也无法直接访问这些凭据。Pass-the-Hash 攻击依赖于获取 NTLM 哈希来伪造认证,Credential Guard 使攻击者无法读取内存中的哈希值,从而有效防御此类攻击。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 1, + "tags": [ + "操作系统安全", + "沙箱" + ], + "question": "浏览器的同源策略(Same-Origin Policy)是一种操作系统级别的沙箱机制。", + "answer": false, + "explanation": "同源策略是浏览器实现的一种 Web 应用安全策略,属于应用程序层面的安全机制,而非操作系统级别。它限制来自一个源(协议+域名+端口)的脚本访问另一个源的资源,防止恶意网站通过 JavaScript 窃取用户数据。操作系统级沙箱指的是如 seccomp、namespace、AppContainer 等由内核提供的隔离机制。虽然浏览器也会使用操作系统提供的进程沙箱(如 Chrome 的 sandbox),但同源策略本身不是操作系统机制。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 2, + "tags": [ + "操作系统安全", + "windows安全", + "win32api" + ], + "question": "Windows 的 ASLR(地址空间布局随机化)可以完全阻止缓冲区溢出攻击。", + "answer": false, + "explanation": "ASLR 通过随机化可执行文件、DLL、堆、栈等在内存中的加载地址,增加了攻击者预测目标地址的难度,是重要的漏洞利用缓解措施。但它不能完全阻止缓冲区溢出攻击:1)信息泄露漏洞可能暴露内存布局,绕过 ASLR;2)32 位系统的地址空间有限,随机化熵较低,暴力猜测成功率可观;3)攻击者可使用 ROP(Return-Oriented Programming)结合信息泄露绕过 ASLR;4)攻击者可能利用未启用 ASLR 的模块。ASLR 应与 DEP、CFG 等其他缓解措施配合使用。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/secure-coding/code_reading.json b/topics/cybersecurity/secure-coding/code_reading.json new file mode 100644 index 0000000..d65ecf7 --- /dev/null +++ b/topics/cybersecurity/secure-coding/code_reading.json @@ -0,0 +1,59 @@ +{ + "topic": "secure-coding", + "type": "code_reading", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "cr-001", + "type": "code_reading", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出", + "内存安全" + ], + "question": "阅读以下C代码,回答问题。", + "code": "#include \n#include \n#include \n\nvoid process_input(const char *user_input) {\n unsigned int len = strlen(user_input);\n // 检查长度是否合理\n if (len > 1024) {\n printf(\"Input too long!\\n\");\n return;\n }\n // 分配缓冲区:len + 1 用于存放结尾的 '\\0'\n char *buffer = (char *)malloc(len + 1);\n if (buffer == NULL) {\n printf(\"Memory allocation failed!\\n\");\n return;\n }\n strcpy(buffer, user_input);\n printf(\"Processed: %s\\n\", buffer);\n free(buffer);\n}\n\nint main(int argc, char *argv[]) {\n if (argc < 2) {\n printf(\"Usage: %s \\n\", argv[0]);\n return 1;\n }\n process_input(argv[1]);\n return 0;\n}", + "language": "c", + "sub_questions": [ + { + "index": 1, + "type": "single_choice", + "question": "当 user_input 的 strlen 返回值恰好为 SIZE_MAX(即 (size_t)-1)时,len + 1 的结果是什么?", + "options": { + "A": "0,因为无符号整数溢出后回绕到 0", + "B": "SIZE_MAX + 1 = 一个更大的数", + "C": "程序会崩溃", + "D": "编译器会报错" + }, + "answer": "A", + "explanation": "unsigned int 在 C 语言中发生溢出时会进行模 2^N 回绕(wrap around)。当 len 为 SIZE_MAX 时,len + 1 回绕为 0。此时 malloc(0) 的行为是实现定义的——可能返回 NULL 或一个可 free 的指针,但分配的大小为 0 字节。后续 strcpy 会写入远超分配空间的数据,导致堆缓冲区溢出。" + }, + { + "index": 2, + "type": "single_choice", + "question": "这段代码在上述溢出场景下,strcpy(buffer, user_input) 会导致什么后果?", + "options": { + "A": "正常复制,程序正常运行", + "B": "堆缓冲区溢出(heap buffer overflow),可能执行任意代码", + "C": "栈溢出", + "D": "只会影响 buffer 变量,不会影响其他数据" + }, + "answer": "B", + "explanation": "当 len + 1 回绕为 0 时,malloc(0) 可能分配一个极小的内存块(或返回一个有效指针但大小为 0),而 user_input 的实际长度远超分配空间。strcpy 会将整个 user_input 复制到这个过小的缓冲区中,造成堆缓冲区溢出,可能覆盖堆上的其他数据结构,攻击者可利用此漏洞执行任意代码。" + }, + { + "index": 3, + "type": "short_answer", + "question": "请修改 process_input 函数,修复其中存在的所有安全问题(至少指出并修复 2 个问题)。", + "answer": "修复方案应包括:\n1. 将 strlen 返回值类型改为 size_t(而非 unsigned int),避免类型截截断;\n2. 在 len + 1 运算前检查 len 是否等于 SIZE_MAX(或使用安全的加法函数如 __builtin_add_overflow / SafeInt),防止整数溢出回绕;\n3. 使用 strncpy 或 memcpy 替代 strcpy,限制拷贝长度;\n4. 检查 malloc 返回值后,对 buffer 大小为 0 的情况进行防御;\n5. 考虑使用 calloc 或更安全的内存分配函数。\n\n示例修复代码:\nvoid process_input(const char *user_input) {\n size_t len = strlen(user_input);\n if (len > 1024) { printf(\"Input too long!\\n\"); return; }\n if (len == SIZE_MAX) { printf(\"Invalid input length!\\n\"); return; }\n char *buffer = (char *)malloc(len + 1);\n if (buffer == NULL || len + 1 == 0) { printf(\"Allocation failed!\\n\"); return; }\n memcpy(buffer, user_input, len);\n buffer[len] = '\\0';\n printf(\"Processed: %s\\n\", buffer);\n free(buffer);\n}", + "explanation": "修复涉及多个层面:类型安全(size_t)、整数溢出检查(SIZE_MAX 边界)、安全的内存复制(memcpy 替代 strcpy)。这些都是安全编码的基本实践。" + } + ], + "explanation": "此代码阅读题考察学生对整数溢出导致缓冲区溢出的安全漏洞理解,以及修复安全漏洞的能力。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/secure-coding/meta.json b/topics/cybersecurity/secure-coding/meta.json new file mode 100644 index 0000000..db8a37f --- /dev/null +++ b/topics/cybersecurity/secure-coding/meta.json @@ -0,0 +1,28 @@ +{ + "slug": "secure-coding", + "name": "安全编程实践", + "description": "输入校验、整数溢出、格式化字符串、内存安全", + "tags": [ + "安全编程", + "输入校验", + "整数溢出", + "格式化字符串", + "内存安全" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/secure-coding/short_answer.json b/topics/cybersecurity/secure-coding/short_answer.json new file mode 100644 index 0000000..16c15a3 --- /dev/null +++ b/topics/cybersecurity/secure-coding/short_answer.json @@ -0,0 +1,83 @@ +{ + "topic": "secure-coding", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "简述Web应用中输入校验的「双重校验」原则(客户端+服务端),以及各自的作用和局限性。", + "answer": "双重校验原则要求在客户端和服务端都对用户输入进行校验,两者缺一不可。\n\n1. 客户端校验:在浏览器/前端通过JavaScript等脚本对用户输入进行即时检查(如格式、长度、范围)。其作用是提升用户体验,减少无效请求,提供即时反馈。局限性在于客户端代码完全受用户控制,攻击者可以绕过(禁用JS、篡改请求、使用代理工具直接发送恶意请求),因此不能作为安全保障。\n\n2. 服务端校验:在后端对所有接收到的输入进行严格校验,是安全的最后一道防线。其作用是防止恶意数据进入系统,防御SQL注入、XSS、命令注入等攻击。局限性在于反馈延迟、增加服务端负担,且校验规则需要与业务逻辑紧密配合。\n\n核心原则:永远不要信任来自客户端的任何数据。客户端校验是便利性措施,服务端校验是安全性措施。", + "keywords": [ + "客户端校验", + "服务端校验", + "输入验证", + "不可信输入", + "XSS", + "SQL注入", + "白名单" + ], + "scoring_rubric": "满分标准:(1) 说明双重校验的概念,即客户端和服务端都需要校验(2分);(2) 正确描述客户端校验的作用:提升用户体验、即时反馈(2分);(3) 正确描述客户端校验的局限性:可被绕过、不可信赖(2分);(4) 正确描述服务端校验的作用:安全最后防线、防御注入攻击(2分);(5) 提出核心原则——不信任客户端数据(2分)。", + "explanation": "输入校验是Web安全的基础。OWASP Top 10中多次将注入类漏洞列为第一大安全风险。双重校验体现了纵深防御思想:客户端校验解决易用性问题,服务端校验解决安全性问题。很多初级开发者误以为客户端校验就够了,导致直接通过API发送未经校验的恶意数据,引发SQL注入、存储型XSS等严重漏洞。理解这一原则是安全编程的入门基础。", + "source": null, + "related": [] + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "解释整数溢出的原理,并给出至少两种防御方法。", + "answer": "整数溢出原理:计算机中整数以固定位数的二进制存储(如32位有符号整数范围为 -2^31 ~ 2^31-1)。当算术运算的结果超出该数据类型能表示的范围时,高位被截断,导致结果「回绕」(wrap around)。例如,32位无符号整数 0xFFFFFFFF + 1 = 0x00000000(回绕为0)。有符号整数溢出还可能导致正数变负数,这在C/C++中属于未定义行为(UB)。\n\n整数溢出在安全上的危害包括:(1) 缓冲区溢出——分配内存时计算出的大小因溢出变小,导致后续写入越界;(2) 绕过边界检查——索引或长度校验因溢出被绕过;(3) 逻辑错误——资金计算、权限判断等场景产生错误结果。\n\n防御方法(至少两种):\n1. 使用安全的整数运算库/内置函数:如 GCC 的 __builtin_add_overflow()、Clang 的 __builtin_*_overflow 系列函数,或 C23 标准的 中的 ckd_add/ckd_mul 等检查溢出宏。这些工具在发生溢出时返回溢出标志,程序可据此进行错误处理。\n\n2. 运算前进行边界检查(前置校验):在执行加减乘除之前,检查操作数是否会导致溢出。例如,乘法前检查 a > SIZE_MAX / b;加法前检查 a > MAX - b。若会溢出则拒绝操作。\n\n3. 使用更大位宽的数据类型:将计算临时提升到更大的类型(如用64位计算后再检查是否超出32位范围),但这只是权宜之计,不能根本解决问题。\n\n4. 使用支持安全算术的语言或编译选项:如 Rust 默认在 debug 模式下检测溢出,Java 的 Math.addExact() 会在溢出时抛出 ArithmeticException,或使用 -ftrapv 编译选项让C程序在有符号溢出时中止。", + "keywords": [ + "整数溢出", + "回绕", + "缓冲区溢出", + "未定义行为", + "边界检查", + "安全算术", + "__builtin_add_overflow", + "前置校验" + ], + "scoring_rubric": "满分标准:(1) 正确解释整数溢出原理——固定位数存储、结果超出范围导致回绕或截断(3分);(2) 说明溢出的安全危害,如缓冲区溢出、边界检查绕过等(2分);(3) 给出至少两种有效的防御方法并说明原理(各2分,共4分);(4) 表述准确,术语使用正确(1分)。若仅给出一种防御方法扣2分;若原理描述不完整扣1-2分。", + "explanation": "整数溢出是一种常见但容易被忽视的安全漏洞,尤其在C/C++等系统编程语言中。2014年OpenSSL的Heartbleed漏洞的根因之一就涉及整数溢出。在安全编程中,任何涉及内存分配大小、数组索引、循环计数的整数运算都必须考虑溢出可能。掌握溢出原理和防御方法是编写安全系统软件的基本功。", + "source": null, + "related": [] + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "安全编程", + "格式化字符串" + ], + "question": "对比 sprintf/snprintf 和 printf/fprintf 在安全性上的差异,说明为什么格式化字符串漏洞是危险的。", + "answer": "函数分类与安全性差异:\n\n1. printf/fprintf 是输出到标准输出/文件流的格式化输出函数。它们的安全风险在于:如果格式字符串(format string)来自用户输入,攻击者可以在格式串中插入 %x(读取栈内容)、%n(向任意地址写入)、%s(读取指针指向的内容)等格式说明符,导致信息泄露或任意内存写入。\n\n2. sprintf/snprintf 是输出到缓冲区的格式化写入函数。除了与 printf 相同的格式化字符串漏洞外,sprintf 还有一个额外风险:没有长度限制,如果格式化后的字符串长度超过目标缓冲区大小,就会发生缓冲区溢出。snprintf 通过限制最大写入字节数(包括 NUL 终止符)来缓解这个问题,是 sprintf 的安全替代品。\n\n安全性对比:\n- sprintf vs snprintf:snprintf 通过 size 参数防止缓冲区溢出,应始终优先使用 snprintf 而非 sprintf。\n- printf 类 vs sprintf 类:printf 类将结果输出到标准流,不会直接造成缓冲区溢出;sprintf 类可能同时面临缓冲区溢出和格式化字符串漏洞的双重风险。\n\n格式化字符串漏洞为什么危险:\n1. 信息泄露:%x、%p 等可以读取栈上的敏感数据(密钥、地址、canary 值)。\n2. 任意地址写入:%n 可以将已输出的字符数写入任意内存地址,攻击者可以精确控制写入的值和地址,从而覆写函数返回地址、GOT 表项等,实现代码执行。\n3. 进程崩溃:%s 对栈上随机值的解引用大概率导致段错误,可造成拒绝服务。\n4. 组合利用:信息泄露 + 任意写入的组合使得这类漏洞几乎总能转化为完整的代码执行利用。\n\n防御方法:始终将格式字符串硬编码为字面量(如 printf(\"%s\", user_input) 而非 printf(user_input)),对 sprintf 使用 snprintf 并正确计算缓冲区大小。", + "keywords": [ + "格式化字符串", + "sprintf", + "snprintf", + "printf", + "缓冲区溢出", + "%n", + "任意写入", + "信息泄露", + "GOT覆写" + ], + "scoring_rubric": "满分标准:(1) 正确区分 printf/fprintf 和 sprintf/snprintf 的功能差异(2分);(2) 说明 snprintf 相比 sprintf 的安全优势——防止缓冲区溢出(2分);(3) 解释格式化字符串漏洞的原理——用户可控的格式串中的 %x、%n 等(2分);(4) 说明危害性:信息泄露、任意地址写入、可能实现代码执行(3分);(5) 提出正确的防御方法——硬编码格式字符串、使用 snprintf(1分)。若未说明 %n 的写入能力扣2分;若未区分 sprintf/snprintf 扣1分。", + "explanation": "格式化字符串漏洞是经典的C/C++安全问题,常见于将用户输入直接作为 printf 系列函数的第一个参数。虽然现代编译器(如 GCC 的 -Wformat-security)会对此发出警告,但该漏洞在遗留代码中仍然广泛存在。snprintf 是 sprintf 的安全替代,但 snprintf 本身不能防御格式化字符串攻击——它只防止缓冲区溢出。理解这些函数族的安全特性差异对于编写安全的C程序至关重要。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/secure-coding/single_choice.json b/topics/cybersecurity/secure-coding/single_choice.json new file mode 100644 index 0000000..bd568fc --- /dev/null +++ b/topics/cybersecurity/secure-coding/single_choice.json @@ -0,0 +1,407 @@ +{ + "topic": "secure-coding", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在输入校验策略中,白名单(白名单验证)相对于黑名单(黑名单过滤)的主要优势是什么?", + "options": { + "A": "白名单只允许已知合法的输入格式,能有效防御未知攻击向量", + "B": "白名单实现更简单,不需要维护规则列表", + "C": "白名单处理速度更快,因为它匹配的规则更少", + "D": "白名单不需要考虑编码绕过问题" + }, + "answer": "A", + "explanation": "白名单验证的核心思想是「默认拒绝,仅放行已知合法的输入」。这意味着只有明确符合预定义规则的输入才能通过,因此即使攻击者使用全新的、前所未见的攻击载荷,只要它不符合白名单规则就会被拒绝。相比之下,黑名单试图列举所有已知的恶意模式,但攻击者总能通过编码变换、大小写混淆、零宽字符等手段绕过过滤。B 错误:白名单通常需要更精确地定义合法输入的规则,实现并不比黑名单简单。C 错误:速度差异不是核心优势,两者在规则数量上没有必然的大小关系。D 错误:白名单同样需要考虑输入编码问题,否则仍可能被绕过。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "关于输入校验的执行位置,以下哪种做法是最安全的?", + "options": { + "A": "仅在客户端(前端)使用 JavaScript 进行输入校验", + "B": "仅在服务端(后端)进行输入校验", + "C": "客户端和服务端都进行输入校验,但以服务端校验为最终安全保障", + "D": "在数据库层面使用存储过程进行所有输入校验" + }, + "answer": "C", + "explanation": "安全的最佳实践是采用「纵深防御」策略:客户端校验用于提供即时反馈、改善用户体验(如提示格式错误),但客户端校验可以被完全绕过(攻击者可以直接发送 HTTP 请求,不经过前端),因此不能作为安全屏障。服务端校验是真正的安全防线,因为所有请求最终都要经过服务端处理。两者结合既保证了用户体验,又确保了安全性。A 错误:客户端校验可以被绕过,不能单独依赖。B 错误:虽然服务端校验是必须的,但缺少客户端校验会降低用户体验。D 错误:将所有校验放在数据库层会导致架构耦合,且不适用于所有输入类型。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "以下代码存在 SQL 注入风险,最有效的修复方式是什么?\n\n```python\nquery = \"SELECT * FROM users WHERE name = '\" + user_input + \"'\"\n```", + "options": { + "A": "对 user_input 中的单引号进行转义,将 `'` 替换为 `''`", + "B": "使用参数化查询(Prepared Statement)", + "C": "将 user_input 限制为最多 50 个字符", + "D": "在 user_input 外层再加一层单引号包裹" + }, + "answer": "B", + "explanation": "参数化查询(Prepared Statement)是防御 SQL 注入的行业标准做法。它通过将 SQL 语句结构与数据完全分离,确保用户输入始终被当作数据而非代码执行,从根本上消除了 SQL 注入的可能性。A 错误:手动转义引号是一种不完整且容易出错的防御方式,在某些字符集(如 GBK)下可能被绕过(宽字节注入),且无法覆盖所有注入场景。C 错误:限制输入长度不能防止 SQL 注入,即使很短的输入也可以构造恶意 SQL。D 错误:这不仅不能解决问题,反而可能导致 SQL 语法错误。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "以下哪段代码存在命令注入(Command Injection)漏洞?", + "options": { + "A": "subprocess.run(['ping', '-c', '4', user_input], shell=False)", + "B": "os.system('ping -c 4 ' + user_input)", + "C": "subprocess.run(['ping', '-c', '4', sanitize(user_input)], shell=False)", + "D": "result = subprocess.check_output(['ping', '-c', '4', user_input], shell=False, stderr=subprocess.DEVNULL)" + }, + "answer": "B", + "explanation": "`os.system()` 通过 shell 执行命令,当 user_input 中包含 shell 元字符(如 `; rm -rf /` 或 `$(malicious_command)`)时,这些字符会被 shell 解释执行,从而导致命令注入。A、D 使用 `subprocess.run`/`check_output` 并设置 `shell=False`,此时命令参数以列表形式传递,不经过 shell 解析,用户输入被视为单个参数而非 shell 表达式,因此是安全的。C 在 A 的基础上额外使用了 sanitize 函数进行输入净化,更加安全。防御命令注入的核心原则是:永远不要将用户输入直接拼接到 shell 命令中;如必须调用系统命令,应使用参数列表形式(list)且关闭 shell 解释。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "在 C 语言中,需要对用户输入的两个 `int` 值进行相加并分配内存。以下哪种做法最安全?", + "options": { + "A": "int *buf = (int*)malloc((a + b) * sizeof(int));", + "B": "if (a > 0 && b > 0 && a <= INT_MAX - b) { int *buf = (int*)malloc((a + b) * sizeof(int)); }", + "C": "int *buf = (int*)malloc((long)(a + b) * sizeof(int));", + "D": "unsigned int total = (unsigned int)a + (unsigned int)b; int *buf = (int*)malloc(total * sizeof(int));" + }, + "answer": "B", + "explanation": "B 是唯一正确的做法——在执行加法之前先检查是否会溢出。`a <= INT_MAX - b` 确保 `a + b` 的结果不会超过 `INT_MAX`,从而避免整数溢出。如果溢出发生,加法结果会回绕(wrap around)为一个很小的数,导致 `malloc` 分配远小于预期的内存,后续写入会造成堆缓冲区溢出。A 错误:直接相加不检查溢出,当 a + b 超过 INT_MAX 时结果为负数或很小的正数。C 错误:虽然将结果转为 long,但 `(a + b)` 在转换之前已经溢出了,溢出发生在 int 层面,cast 无法修复。D 错误:转为 unsigned int 不能防止溢出——unsigned 溢出虽然不会触发未定义行为(会回绕),但仍会导致 malloc 分配过小的内存,造成相同的安全问题。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "以下 C 代码检查数组索引是否越界,但存在漏洞。根本原因是什么?\n\n```c\nvoid access_array(int index, int offset) {\n if (index + offset < ARRAY_SIZE) {\n array[index + offset] = value;\n }\n}\n```", + "options": { + "A": "数组 array 没有被正确初始化", + "B": "index + offset 可能发生整数溢出,导致边界检查被绕过", + "C": "缺少对 index 和 offset 的负数检查", + "D": "ARRAY_SIZE 应该使用 sizeof 运算符计算" + }, + "answer": "B", + "explanation": "当 `index` 和 `offset` 都是较大的正整数时,`index + offset` 可能发生整数溢出(例如 index = 0x7FFFFFF0, offset = 0x20,结果为 0x80000010 即一个负数),这个负数在与 `ARRAY_SIZE`(通常被解读为 unsigned 比较)比较时可能变为一个非常大的正数,从而绕过 `< ARRAY_SIZE` 的检查,导致数组越界访问。修复方式应改为:`if (offset < ARRAY_SIZE && index < ARRAY_SIZE - offset)`,先检查减法不会下溢,再检查加法结果。C 也是相关问题(负数可以绕过单边检查),但根本原因是算术溢出导致检查条件本身被绕过。A 和 D 与漏洞无关。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "格式化字符串" + ], + "question": "以下哪段代码存在格式化字符串漏洞?", + "options": { + "A": "printf(\"%s\", user_input);", + "B": "printf(user_input);", + "C": "fprintf(stderr, \"%.*s\", MAX_LEN, user_input);", + "D": "snprintf(buf, sizeof(buf), \"%s\", user_input);" + }, + "answer": "B", + "explanation": "`printf(user_input)` 将用户输入直接作为格式化字符串使用。如果攻击者输入 `%x%x%x%x%n`,`printf` 会将栈上的数据作为参数读取(`%x` 泄露栈数据),`%n` 会将已输出的字符数写入指定地址,从而实现任意内存读取和写入。A 安全:`user_input` 作为 `%s` 的参数,仅被当作普通字符串输出,其中的 `%` 字符不会被解释为格式化说明符。C 安全:同样将 user_input 作为 `%s` 的参数,并限制了输出长度。D 安全:`snprintf` 中 `%s` 是格式字符串,user_input 是参数,缓冲区大小受 `sizeof(buf)` 限制。防御原则:永远不要将用户输入直接作为 `printf` 系列函数的第一个参数。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "安全编程", + "格式化字符串" + ], + "question": "在一次渗透测试中,测试人员发现某个 C 程序使用 `printf(user_input)` 输出日志。攻击者输入 `%7$n` 成功向某个内存地址写入了值。这个攻击能够成功的根本原因是什么?", + "options": { + "A": "printf 的内部缓冲区发生了溢出", + "B": "格式化说明符 %n 会将已输出的字符数写入对应参数指向的地址,而 %7$ 通过栈偏移定位到了攻击者可控的地址", + "C": "printf 在处理 %n 时自动调用了 system()", + "D": "编译器没有开启栈保护(Stack Canary)导致 %n 可以覆盖返回地址" + }, + "answer": "B", + "explanation": "格式化字符串漏洞的核心机制是:`%n` 说明符将「到目前为止已输出的字符数」写入一个由参数指定的地址。当用户输入作为格式化字符串时,攻击者可以通过构造特定格式串来控制 printf 从栈上读取的「参数」。`%7$n` 表示取第 7 个参数(从格式化字符串参数之后开始计数)的值作为写入目标地址,同时通过控制 `%n` 之前的输出字符数来控制写入的值。这实现了「任意地址写任意值」。A 错误:这与缓冲区溢出无关,是格式化字符串的参数解释机制被滥用。C 错误:`%n` 的行为是内存写入,不是调用 system()。D 错误:栈保护(Stack Canary)保护的是返回地址被覆盖的场景,格式化字符串漏洞利用的是 printf 的参数解释机制,不直接触发 canary 违例。当然,现代编译器和 libc 提供了格式化字符串编译期警告和运行时保护,但问题问的是漏洞根因。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "以下代码存在缓冲区溢出风险。最有效的修复方式是什么?\n\n```c\nvoid greet(char *name) {\n char buf[64];\n strcpy(buf, name);\n printf(\"Hello, %s!\\n\", buf);\n}\n```", + "options": { + "A": "将 strcpy 替换为 strncpy(buf, name, sizeof(buf) - 1); 并确保 buf[sizeof(buf)-1] = '\\0';", + "B": "将 buf 的大小改为 256 字节以容纳更多输入", + "C": "使用 sprintf(buf, \"%s\", name) 替代 strcpy", + "D": "在函数开头添加 if (strlen(name) < 64) 检查后再 strcpy" + }, + "answer": "A", + "explanation": "`strncpy` 限制了最多复制 `sizeof(buf) - 1` 个字符,防止溢出,之后手动添加 null 终止符确保字符串正确终止(`strncpy` 在源字符串长度超过 n 时不会自动添加 `\\0`)。这是防御缓冲区溢出的标准做法。B 错误:增大缓冲区只是推迟了问题——只要输入足够长,仍会溢出,这是一种「鸵鸟策略」。C 错误:`sprintf` 同样不检查目标缓冲区大小,存在相同的溢出风险,只是将 `strcpy` 换了一个等价的函数。D 错误:`strlen(name)` 本身需要遍历整个字符串,如果 name 不是 null 终止的(已经被破坏的情况),`strlen` 可能读取越界内存导致未定义行为;此外,`strlen` 检查与 `strcpy` 之间存在 TOCTOU(检查时间与使用时间)竞态问题。更优的做法是使用 `strlcpy`(如果平台支持)或 `snprintf(buf, sizeof(buf), \"%s\", name)`。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "现代编译器的栈保护机制(Stack Canary)能够有效防御以下哪种攻击?", + "options": { + "A": "格式化字符串漏洞导致的任意内存写入", + "B": "栈缓冲区溢出覆盖返回地址(Return Address)", + "C": "堆(Heap)上的 Use-After-Free 漏洞", + "D": "整数溢出导致的内存分配不足" + }, + "answer": "B", + "explanation": "Stack Canary(栈金丝雀)是一种编译器插入的运行时保护机制。编译器在函数栈帧中、局部变量与返回地址之间插入一个随机值(canary),函数返回前检查该值是否被修改。如果发生栈缓冲区溢出并试图覆盖返回地址,必然会先破坏 canary 值,程序在函数返回前检测到 canary 被篡改就会终止进程并报告栈溢出。A 错误:格式化字符串漏洞通过 printf 的 `%n` 直接写入指定地址,不经过栈变量覆盖,不会触发 canary 检查。C 错误:Use-After-Free 是堆内存管理问题,与栈保护无关。D 错误:整数溢出导致的内存分配不足是一个逻辑层面的安全问题,栈保护机制对此无能为力。需要注意的是,Stack Canary 不能防御所有栈溢出——如果攻击者能泄露 canary 值(如通过格式化字符串或信息泄露漏洞),仍然可以绕过此保护。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在 Web 应用中防御 XSS(跨站脚本攻击)时,以下哪种做法是最基本且有效的输出编码策略?", + "options": { + "A": "对所有用户输入进行 Base64 编码后再存入数据库", + "B": "在输出到 HTML 页面时,对特殊字符进行 HTML 实体编码(如 < 转义为 <)", + "C": "在前端使用 JavaScript 的 eval() 函数解析用户输入", + "D": "限制用户输入长度不超过 100 个字符即可防止 XSS" + }, + "answer": "B", + "explanation": "HTML 实体编码是防御 XSS 最基本的输出编码策略:将 <、>、&、双引号、单引号等特殊字符转义为对应的 HTML 实体,使浏览器将其视为纯文本而非可执行代码。A 选项的 Base64 编码仅是编码格式转换,解码后仍然是恶意脚本,无法防御 XSS。C 选项使用 eval() 反而会直接执行任意代码,极大增加风险。D 选项限制输入长度既不能可靠阻止攻击(短 payload 也可构成 XSS),也不是正确的防御思路——安全应依赖输出编码而非输入限制。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "关于内容安全策略(Content Security Policy, CSP),以下说法正确的是?", + "options": { + "A": "CSP 通过 HTTP 响应头 Content-Security-Policy 配置,可以限制页面能加载和执行的资源来源", + "B": "只要设置了 CSP,就完全不需要对用户输入做输出编码了", + "C": "CSP 的 default-src 指令只能设置为 'self',不能配置外部域名", + "D": "CSP 是浏览器端的技术,只在客户端生效,服务端无法控制" + }, + "answer": "A", + "explanation": "CSP 是一种通过 HTTP 响应头声明的安全策略,它告诉浏览器只允许从指定来源加载和执行脚本、样式、图片等资源,从而有效限制 XSS 攻击面。B 选项错误:CSP 是纵深防御的一层,不能替代输出编码。CSP 可能被绕过(如 CSP 配置不当、存在 JSONP endpoint 等),输出编码仍是基础防线。C 选项错误:default-src 可以配置任意来源,包括 'self'、特定域名、https: 协议等。D 选项错误:CSP 虽由浏览器执行,但策略本身由服务端通过 HTTP 响应头下发,服务端完全控制策略内容。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "以下 C 语言代码片段存在整数安全问题,其根本原因是什么?\n\nint buf_size = user_input * 4; // user_input 为 unsigned int\nchar *buf = malloc(buf_size);\nmemcpy(buf, src, user_input * 4);", + "options": { + "A": "malloc 可能返回 NULL,代码未检查分配是否成功", + "B": "有符号整数 buf_size 与无符号整数相乘可能导致有符号整数溢出,产生未定义行为", + "C": "memcpy 的第三个参数类型不匹配", + "D": "应该使用 calloc 代替 malloc 来避免安全问题" + }, + "answer": "B", + "explanation": "这段代码的核心问题是:user_input 是 unsigned int,乘以 4 后赋值给有符号的 int 类型 buf_size。当 user_input 足够大(如 0x40000001),user_input * 4 在无符号算术下会回绕到一个较小的值,赋值给 int 时可能产生负数或一个远小于预期的正数。malloc 分配了过小的缓冲区,但 memcpy 仍按原始乘积拷贝数据,导致堆缓冲区溢出。虽然 A 选项也是合理的编程习惯问题,但题目问的是整数安全问题的根本原因。C 选项错误:size_t 和 int 在此场景不影响类型安全。D 选项错误:calloc 并不能解决整数溢出问题。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "在安全编码中,使用安全的整数运算库(如 GCC 的 __builtin_add_overflow 或 Microsoft 的 SafeInt)的主要目的是什么?", + "options": { + "A": "提升整数运算的执行速度,减少 CPU 开销", + "B": "将所有整数运算自动转换为浮点运算以避免溢出", + "C": "在运算发生溢出时立即检测并返回错误状态,而非依赖未定义行为或静默回绕", + "D": "将有符号整数统一转换为无符号整数进行运算" + }, + "answer": "C", + "explanation": "安全整数运算库的核心价值是在算术运算发生溢出时提供可靠的检测机制。例如 __builtin_add_overflow(a, b, &result) 在加法溢出时返回 true 并可通过返回值判断是否溢出,而非让程序继续使用一个错误的回绕值。C 语言中,有符号整数溢出是未定义行为(UB),无符号整数溢出会静默回绕——两者都可能导致安全漏洞(如缓冲区溢出)。A 选项错误:安全库通常有少量额外开销,不是为了性能。B 选项错误:浮点运算有精度损失,不是解决整数溢出的正确方案。D 选项错误:简单转为无符号不解决溢出问题,无符号整数同样会回绕。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "关于 Use-After-Free(UAF)漏洞,以下描述正确的是?", + "options": { + "A": "UAF 是指程序试图释放一个从未被分配的内存地址", + "B": "UAF 发生在程序释放某块内存后,仍然通过旧指针(悬挂指针)访问该内存,此时内存可能已被重新分配给其他对象", + "C": "使用 malloc 分配内存就不会产生 UAF 问题,只有手动管理内存的语言才会出现", + "D": "UAF 只会导致程序崩溃,不会被利用来执行任意代码" + }, + "answer": "B", + "explanation": "Use-After-Free 的本质是:程序调用 free() 释放了某块堆内存后,没有将指针置为 NULL,后续代码继续通过该悬挂指针(dangling pointer)访问已释放的内存。此时该内存区域可能已被分配给其他对象,读取会获得不可预期的数据,写入则可能篡改其他对象的内容——攻击者可以精心构造堆布局,通过 UAF 实现类型混淆或控制流劫持,最终执行任意代码。A 选项描述的是 double free 或 free of unallocated memory,不是 UAF。C 选项错误:任何有手动内存管理的语言(C/C++)都可能出现 UAF。D 选项错误:UAF 是浏览器、内核等高价值目标中被频繁利用的漏洞类型,可以实现远程代码执行。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "以下 C 代码存在 Double Free 漏洞,其最可能造成的后果是什么?\n\nchar *buf = malloc(100);\n// ... 使用 buf ...\nfree(buf);\n// 某些错误处理路径中再次调用\nfree(buf);", + "options": { + "A": "程序会忽略第二次 free 调用,不会产生任何影响", + "B": "Double Free 会破坏堆管理器的内部数据结构,可能导致程序崩溃或被利用来实现任意代码执行", + "C": "Double Free 只会造成内存泄漏,不会产生安全风险", + "D": "编译器会自动检测 Double Free 并在编译时报错" + }, + "answer": "B", + "explanation": "Double Free 是指对同一块已释放的内存再次调用 free()。堆管理器(如 glibc 的 ptmalloc)使用元数据链表管理空闲块,double free 会破坏这些元数据结构(如在 fastbin 中形成环形链表),导致后续 malloc/free 操作出现不可预期行为。攻击者可以利用被破坏的堆结构,通过精心构造的分配序列实现堆内存的任意写入,最终劫持控制流。A 选项错误:标准 malloc 实现不会忽略 double free,glibc 会检测到并 abort,但这已经是防御性行为。C 选项错误:double free 不是内存泄漏,而是堆损坏。D 选项错误:编译器通常无法静态检测 double free,需要运行时工具(如 AddressSanitizer)来检测。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在 Web 应用中实现文件下载功能时,以下哪种做法最容易导致路径遍历(Path Traversal)漏洞?", + "options": { + "A": "使用用户提供的文件名直接拼接到文件系统路径,如 path = /uploads/ + user_filename", + "B": "将文件映射到数据库中的 ID,通过 ID 查找对应的真实文件路径", + "C": "对用户输入进行白名单校验,只允许字母和数字字符", + "D": "使用 chroot 或容器化技术隔离文件存储目录" + }, + "answer": "A", + "explanation": "直接将用户输入拼接到文件路径是路径遍历漏洞的典型成因。攻击者可以提供 ../../../etc/passwd 作为文件名,读取服务器上的任意文件。B 选项是安全的做法:通过间接映射(ID 到文件路径)将用户输入与真实文件路径完全隔离,用户无法控制实际路径。C 选项通过白名单过滤也有效防止了路径遍历(../ 等特殊字符被拒绝),属于输入校验的正确做法。D 选项通过隔离限制了漏洞影响范围,即使存在路径遍历也无法逃出沙箱。只有 A 选项完全没有防护,是路径遍历的标准攻击场景。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在防御路径遍历攻击时,以下哪组措施组合最为全面和可靠?", + "options": { + "A": "仅对用户输入中的 ../ 进行字符串替换删除", + "B": "先对输入进行 URL 解码,再用 realpath() 规范化路径,最后验证规范化后的路径是否以允许的基准目录开头", + "C": "限制文件名长度不超过 255 个字符", + "D": "在 Web 服务器配置中禁止目录列表功能即可" + }, + "answer": "B", + "explanation": "B 选项是防御路径遍历的最全面方案:(1) URL 解码防止 %2e%2e%2f 等编码绕过;(2) realpath() 将路径解析为绝对路径并消除所有符号链接、./ 和 ../ 等,得到规范化路径;(3) 验证规范化路径是否位于允许的基准目录下(startsWith 检查),确保最终访问的文件在预期范围内。这三个步骤缺一不可。A 选项的简单字符串替换可以被多种方式绕过,如 ....//(删除一个 ../ 后仍剩一个 ../)、%2e%2e/ 编码、反斜杠路径分隔符等。C 选项与路径遍历无关。D 选项仅影响目录浏览,不阻止直接的路径遍历访问。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "安全编程" + ], + "question": "在系统安全设计中,最小权限原则(Principle of Least Privilege)的正确实践是?", + "options": { + "A": "给所有开发人员相同的生产环境访问权限,便于协作", + "B": "进程和服务应仅拥有完成其功能所必需的最小权限集,需要时临时提升权限,用完后立即撤销", + "C": "使用 root 或管理员账户运行所有服务,避免权限不足导致的功能异常", + "D": "最小权限原则只适用于操作系统层面,应用程序层面不需要考虑" + }, + "answer": "B", + "explanation": "最小权限原则要求任何主体(用户、进程、服务)仅被授予完成其任务所必需的最小权限。这意味着:(1) 日常运行使用低权限账户;(2) 需要特权操作时通过 sudo 等机制临时提权;(3) 操作完成后立即降回低权限。即使进程被攻破,攻击者也只能获得有限的权限,限制了横向移动和影响范围。A 选项违反了权限分离原则。C 选项以 root 运行服务是严重的安全隐患——一旦被利用,攻击者直接获得最高权限。D 选项错误:最小权限原则适用于所有层面,包括操作系统、应用程序、数据库、API 访问控制等。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "GCC 编译选项 -fstack-protector-strong 的主要作用是什么?", + "options": { + "A": "启用地址空间布局随机化(ASLR),使栈地址不可预测", + "B": "在函数返回前检查栈上的 canary 值是否被篡改,检测栈缓冲区溢出并终止程序", + "C": "将所有局部变量从栈上移到堆上分配,避免栈溢出", + "D": "禁止函数使用递归调用,防止栈空间耗尽" + }, + "answer": "B", + "explanation": "-fstack-protector-strong 在编译时在函数的栈帧中插入一个随机的 canary 值(哨兵值),位于局部变量和返回地址之间。函数返回前检查该 canary 值是否与原始值一致——如果栈缓冲区溢出覆盖了返回地址,必然会先破坏 canary 值,程序检测到后调用 __stack_chk_fail 终止执行并记录日志。-fstack-protector-strong 相比 -fstack-protector 更积极:它保护所有包含数组、地址取用操作或局部变量大小超过一定阈值的函数。A 选项描述的是 ASLR,是操作系统级特性,非编译器选项。C 选项不正确:编译器不会自动将局部变量移到堆上。D 选项不正确:该选项不限制递归,递归栈溢出是运行时问题。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/secure-coding/true_false.json b/topics/cybersecurity/secure-coding/true_false.json new file mode 100644 index 0000000..7a77568 --- /dev/null +++ b/topics/cybersecurity/secure-coding/true_false.json @@ -0,0 +1,148 @@ +{ + "topic": "secure-coding", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在输入校验中,黑名单校验比白名单校验更安全。", + "answer": false, + "explanation": "白名单校验比黑名单校验更安全。黑名单(拒绝已知的恶意输入)容易遗漏未知攻击向量,攻击者只需找到一个绕过方式即可成功。白名单(只允许已知合法的输入格式)默认拒绝一切未明确允许的输入,安全边界更紧。例如,白名单只允许字母数字字符,就自然阻止了所有注入类攻击,而黑名单很难穷举所有可能的恶意字符组合。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 2, + "tags": [ + "安全编程", + "格式化字符串" + ], + "question": "在C语言中,使用 printf(str) 而非 printf(\"%s\", str) 存在格式化字符串漏洞风险。", + "answer": true, + "explanation": "当 str 由用户控制时,printf(str) 存在严重的格式化字符串漏洞。攻击者可以在 str 中嵌入 %x、%n 等格式说明符:%x 可以泄露栈上的敏感数据(栈信息泄露),%n 可以向任意地址写入数据(任意写)。正确做法是使用 printf(\"%s\", str) 将用户输入作为纯数据输出,而非格式化字符串的一部分。类似的危险函数还包括 fprintf、sprintf、snprintf 等——只要格式化参数来自不可信输入,都存在同类风险。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "在C语言中,有符号整数溢出属于未定义行为(Undefined Behavior)。", + "answer": true, + "explanation": "根据C语言标准(C99/C11 §6.5/5),有符号整数溢出是未定义行为(UB),编译器可以做任何假设,包括直接优化掉溢出检查。这与无符号整数不同——无符号整数溢出是定义良好的(模 2^n 回绕)。因此,在安全编程中,对有符号整数做溢出检查时,不能依赖溢出后的值来判断,而应在运算前检测是否会溢出(例如检查 a > INT_MAX - b 再做 a + b)。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 2, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "strncpy 一定比 strcpy 更安全。", + "answer": false, + "explanation": "strncpy 并不总是更安全。strncpy 存在以下陷阱:(1) 如果源字符串长度 >= n,目标缓冲区不会以 null 结尾,后续字符串操作可能越界读取;(2) 如果源字符串远短于 n,strncpy 会用零字节填充剩余空间,造成不必要的性能开销;(3) 开发者仍需手动确保 n 不超过目标缓冲区大小。更安全的替代方案是使用 strlcpy(BSD 扩展)或 snprintf(buf, sizeof(buf), \"%s\", src),它们保证目标始终 null 结尾。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "使用参数化查询(Prepared Statements)可以有效防止SQL注入攻击。", + "answer": true, + "explanation": "参数化查询通过将SQL语句结构与数据严格分离来防止注入。数据库驱动会将参数值作为纯数据传输,不会被解释为SQL代码。例如,SELECT * FROM users WHERE id = ? 中,即使参数值包含 ' OR 1=1 -- 等注入载荷,数据库也只会将其视为普通字符串进行匹配,而非SQL逻辑。这是防御SQL注入的首选方案,比手动转义或存储过程更可靠。注意:如果动态拼接表名、列名或ORDER BY等SQL结构部分,参数化查询无法覆盖,仍需白名单校验。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 1, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "C11标准已将 gets() 函数标记为废弃(deprecated)并推荐使用更安全的替代方案。", + "answer": true, + "explanation": "gets() 在 C11 标准(ISO/IEC 9899:2011 Annex K)中已被正式移除。gets() 不接受长度参数,无法限制读取的字符数,是经典的缓冲区溢出漏洞来源(例如1988年Morris蠕虫就利用了 gets() 的缺陷)。C11 推荐使用 fgets(buf, size, stdin) 替代,fgets 允许指定最大读取长度,但需注意它会保留换行符。微软的 gets_s() 是另一个安全替代,但它是平台特定扩展,不具有跨平台可移植性。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 2, + "tags": [ + "安全编程", + "整数溢出" + ], + "question": "无符号整数相减永远不会出现安全问题,因为无符号数没有负数。", + "answer": false, + "explanation": "无符号整数相减确实不会产生负数——但当被减数小于减数时,会发生无符号下溢(underflow),结果回绕到一个极大的正数。例如,(unsigned)3 - 5 在32位系统下等于 4294967294(即 2^32 - 2),而非 -2。这在安全场景中非常危险:如果用作内存分配大小、数组索引或循环边界,会导致分配过大的内存、越界访问或无限循环。正确做法是在减法前检查操作数关系,确保被减数 >= 减数。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 3, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "部署内容安全策略(CSP)可以完全阻止所有XSS攻击。", + "answer": false, + "explanation": "CSP是防御XSS的重要辅助手段,但无法完全阻止所有XSS。CSP的局限性包括:(1) 如果页面本身存在内联脚本且无法迁移到外部文件,需要使用 unsafe-inline,这削弱了CSP防护;(2) 已知的DOM型XSS(如通过innerHTML注入事件处理器)在某些CSP配置下仍可利用;(3) 如果CDN或第三方库被攻破,允许的脚本源可能成为攻击载体;(4) CSP无法阻止基于CSS或HTML属性的某些信息泄露攻击。XSS防御应采用纵深策略:输出编码 + CSP + 输入校验 + HttpOnly Cookie + 子资源完整性(SRI)。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 2, + "tags": [ + "安全编程", + "内存安全" + ], + "question": "地址空间布局随机化(ASLR)是一种有效的内存安全防御技术,可以增加攻击者利用内存漏洞的难度。", + "answer": true, + "explanation": "ASLR通过在每次程序运行时随机化关键内存区域(栈、堆、共享库、可执行文件基址)的位置,使攻击者无法预测目标地址,从而大幅增加利用难度。例如,ROP攻击需要知道 gadget 的精确地址,ASLR使这些地址在每次运行时都不同。但ASLR并非万能:(1) 信息泄露漏洞可以泄露内存地址,绕过ASLR;(2) 32位系统的熵不足,暴力破解可能成功;(3) 部分实现(如旧版Windows ASLR)不覆盖所有区域。ASLR应与DEP/NX、Stack Canary、CFI等技术配合使用,构成完整的纵深防御体系。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 1, + "tags": [ + "安全编程", + "输入校验" + ], + "question": "在Web应用中,只要服务端进行了充分的输入校验,客户端就不需要做任何输入校验。", + "answer": false, + "explanation": "服务端校验是安全的最后防线,必须执行,但客户端校验同样有价值:(1) 用户体验——即时反馈比提交后再报错体验更好,减少不必要的网络请求;(2) 服务端负载——客户端预过滤可以减少无效请求对服务端的压力;(3) 辅助防御——增加攻击者篡改请求的成本。但客户端校验绝不能替代服务端校验,因为客户端代码(JavaScript、HTML属性)可被用户完全绕过(禁用JS、修改DOM、直接发送HTTP请求)。正确的做法是:客户端校验用于提升体验,服务端校验用于保障安全,两者并行。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/web-security/code_reading.json b/topics/cybersecurity/web-security/code_reading.json new file mode 100644 index 0000000..72f2591 --- /dev/null +++ b/topics/cybersecurity/web-security/code_reading.json @@ -0,0 +1,74 @@ +{ + "topic": "web-security", + "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": [ + "web安全", + "sql注入", + "xss", + "ssrf", + "文件上传" + ], + "question": "阅读以下 PHP 代码,该代码实现了一个简单的文件处理功能。请分析其中存在的安全漏洞。", + "source": null, + "related": [], + "code": "$filename\";\n}\n\n// 用户搜索功能\nif (isset($_GET['keyword'])) {\n $keyword = $_GET['keyword'];\n $conn = new mysqli('localhost', 'root', 'password123', 'mydb');\n $sql = \"SELECT * FROM products WHERE name LIKE '%$keyword%'\";\n $result = $conn->query($sql);\n echo \"

Results for: $keyword

\";\n while ($row = $result->fetch_assoc()) {\n echo \"

{$row['name']} - {$row['price']}

\";\n }\n}\n\n// URL 抓取功能\nif (isset($_GET['url'])) {\n $content = file_get_contents($_GET['url']);\n echo $content;\n}\n?>", + "language": "php", + "sub_questions": [ + { + "index": 1, + "type": "single_choice", + "question": "代码中的文件上传部分存在哪些安全问题?", + "options": { + "A": "仅缺少文件大小限制", + "B": "未验证文件类型和扩展名、使用原始文件名、存在存储型 XSS", + "C": "仅存在目录遍历风险", + "D": "仅缺少 CSRF Token" + }, + "answer": "B", + "explanation": "文件上传代码存在多个严重问题:①未验证文件类型和扩展名,攻击者可上传 .php 等可执行脚本;②使用原始文件名 $file['name'],可能导致路径遍历(如 ../../etc/cron.d/evil)或文件名解析漏洞;③直接将文件名输出到 HTML 中(`$filename`),未做转义,存在存储型 XSS。选项 A 只提到大小限制,选项 C 只提到目录遍历,选项 D 只提到 CSRF,均不够全面。因此正确答案是 B。" + }, + { + "index": 2, + "type": "single_choice", + "question": "搜索功能中的 SQL 查询存在什么漏洞?以下哪个 payload 可以利用该漏洞获取所有用户数据?", + "options": { + "A": "keyword=test", + "B": "keyword=' UNION SELECT username, password, email FROM users--", + "C": "keyword=", + "D": "keyword=127.0.0.1" + }, + "answer": "B", + "explanation": "搜索功能中 `$keyword` 直接拼接到 SQL 查询中,存在 SQL 注入漏洞。选项 B 使用联合查询注入(UNION SELECT),通过闭合原有的 LIKE 条件,注入 UNION 查询来读取 users 表中的敏感数据(用户名、密码、邮箱)。`--` 用于注释掉后续 SQL。选项 C 是 XSS payload,虽然代码中也存在 XSS 漏洞(`$keyword` 未经转义输出),但不能获取数据库数据。选项 A 是正常搜索,选项 D 是 IP 地址,均不能利用该 SQL 注入。" + }, + { + "index": 3, + "type": "single_choice", + "question": "URL 抓取功能存在什么类型的漏洞?攻击者可以如何利用?", + "options": { + "A": "XSS 漏洞,注入脚本到 URL 参数", + "B": "SQL 注入,通过 URL 参数修改数据库查询", + "C": "SSRF 漏洞,可以读取本地文件或访问内网服务", + "D": "文件上传漏洞,通过 URL 上传文件到服务器" + }, + "answer": "C", + "explanation": "`file_get_contents($_GET['url'])` 直接使用用户输入的 URL 发起请求,且未对协议和目标地址做任何限制,存在严重的 SSRF 漏洞。攻击者可以:①使用 `file:///etc/passwd` 读取服务器本地文件;②使用 `http://169.254.169.254/latest/meta-data/` 获取云实例元数据和临时凭证;③访问内网服务如 `http://192.168.1.1:8080/admin`。此外,`echo $content` 将远程内容直接输出,还可被利用进行 XSS 攻击(存储型或反射型),但该功能的核心漏洞是 SSRF。因此正确答案是 C。" + }, + { + "index": 4, + "type": "short_answer", + "question": "请针对代码中的三个功能模块,分别给出至少一条关键修复建议。", + "answer": "1. 文件上传:①使用白名单验证文件扩展名(仅允许 jpg/png/gif 等);②使用 move_uploaded_file 前检查文件 MIME 类型和 Magic Number;③将文件重命名为 UUID 等随机名称;④对输出的文件名使用 htmlspecialchars() 转义。\n2. 搜索功能:①使用参数化查询替换字符串拼接(如 $stmt = $conn->prepare(\"SELECT * FROM products WHERE name LIKE ?\"); $stmt->bind_param('s', $keyword));②对输出的 $keyword 使用 htmlspecialchars() 转义防止 XSS;③不要在代码中硬编码数据库密码,使用环境变量或配置文件。\n3. URL 抓取:①建立允许访问的域名/IP白名单;②限制协议只能为 http/https;③解析 URL 域名后的 IP 地址,拒绝内网 IP 段;④对返回内容做 htmlspecialchars() 转义后再输出。", + "explanation": "该答案针对每个功能模块给出了具体的修复建议。文件上传需要类型验证、重命名和输出转义;搜索功能需要参数化查询、输出转义和凭证安全;URL 抓取需要白名单、协议限制、内网 IP 过滤和输出转义。这些建议涵盖了代码中识别出的所有主要漏洞。" + } + ], + "explanation": "" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/web-security/meta.json b/topics/cybersecurity/web-security/meta.json new file mode 100644 index 0000000..4317a30 --- /dev/null +++ b/topics/cybersecurity/web-security/meta.json @@ -0,0 +1,29 @@ +{ + "slug": "web-security", + "name": "Web 安全基础", + "description": "XSS / SQL 注入 / CSRF / SSRF / 文件上传", + "tags": [ + "web安全", + "xss", + "sql注入", + "csrf", + "ssrf", + "文件上传" + ], + "created": "2026-09-17", + "question_files": [ + "code_reading.json", + "short_answer.json", + "single_choice.json", + "true_false.json" + ], + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } +} \ No newline at end of file diff --git a/topics/cybersecurity/web-security/short_answer.json b/topics/cybersecurity/web-security/short_answer.json new file mode 100644 index 0000000..3ebcc91 --- /dev/null +++ b/topics/cybersecurity/web-security/short_answer.json @@ -0,0 +1,92 @@ +{ + "topic": "web-security", + "type": "short_answer", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sa-001", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "web安全", + "xss" + ], + "question": "请简述 XSS 攻击的三种主要类型(存储型、反射型、DOM 型),并分别说明其攻击流程和典型场景。", + "answer": "1. 存储型 XSS(持久型):攻击者将恶意脚本提交并存储到服务器端(如论坛帖子、评论区),当其他用户访问包含该内容的页面时,恶意脚本从服务器返回并在浏览器中执行。典型场景:论坛评论、用户资料、站内消息。\n\n2. 反射型 XSS(非持久型):攻击者构造包含恶意脚本的 URL,诱导用户点击后,服务器将恶意脚本作为参数值「反射」回 HTML 响应中执行。典型场景:搜索结果页显示搜索关键词、错误消息显示用户输入。\n\n3. DOM 型 XSS:攻击者构造的 URL 中包含恶意数据,前端 JavaScript 直接从 URL(如 location.hash、document.referrer)读取数据并写入 DOM(如 innerHTML),不经过服务器处理。典型场景:单页应用(SPA)、前端路由处理。", + "source": null, + "related": [], + "keywords": [ + "存储型", + "反射型", + "DOM型", + "恶意脚本", + "服务器存储", + "URL参数", + "DOM操作", + "innerHTML", + "浏览器执行" + ], + "scoring_rubric": "满分标准:正确列出三种类型(各1分),每种类型需说明攻击流程(各2分)和至少一个典型场景(各1分)。总分9分。若仅列出类型名称但无流程和场景,最高得3分。", + "explanation": "" + }, + { + "id": "sa-002", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "web安全", + "sql注入", + "csrf" + ], + "question": "对比 SQL 注入和 CSRF 两种攻击方式,从攻击目标、利用条件和防御策略三个方面进行分析。", + "answer": "攻击目标:\n- SQL 注入:针对后端数据库,通过篡改 SQL 查询语义来窃取、修改或删除数据库数据,甚至执行系统命令。\n- CSRF:针对已认证用户的会话,利用浏览器自动携带凭据的特性,以用户身份执行非预期的操作(如转账、改密码)。\n\n利用条件:\n- SQL 注入:需要应用程序存在将用户输入直接拼接至 SQL 语句的代码缺陷,攻击者构造恶意输入即可触发,不需要用户登录。\n- CSRF:需要用户已登录目标站点且浏览器保存了有效的会话凭证,同时目标站点的状态更改请求缺少有效的来源验证。\n\n防御策略:\n- SQL 注入:使用参数化查询/预编译语句、输入验证与过滤、最小权限原则、使用 ORM 框架。\n- CSRF:使用 CSRF Token 验证、SameSite Cookie 属性、验证 Referer/Origin 头、关键操作要求二次确认。", + "source": null, + "related": [], + "keywords": [ + "SQL注入", + "CSRF", + "数据库", + "会话", + "参数化查询", + "CSRF Token", + "SameSite", + "输入验证", + "拼接", + "自动携带Cookie" + ], + "scoring_rubric": "满分标准:三个维度(攻击目标、利用条件、防御策略)各占3分,每个维度中两种攻击的对比各1.5分。回答需体现关键差异,表述准确完整。总分9分。仅对比一个维度或泛泛而谈,酌情扣分。", + "explanation": "" + }, + { + "id": "sa-003", + "type": "short_answer", + "difficulty": 2, + "tags": [ + "web安全", + "文件上传", + "ssrf" + ], + "question": "请分别描述防御文件上传漏洞和 SSRF 漏洞的最佳实践(至少各列出三点)。", + "answer": "文件上传防御最佳实践:\n1. 服务端验证文件类型:检查文件扩展名白名单、MIME 类型和文件头签名(Magic Number),不依赖前端验证。\n2. 重命名上传文件:使用随机名称(如 UUID)替换原始文件名,避免路径遍历和文件名解析漏洞。\n3. 隔离存储:将上传文件存储在 Web 根目录之外,或使用独立的文件存储服务(如对象存储)。\n4. 禁用执行权限:确保上传目录不可执行脚本。\n5. 限制文件大小:防止 DoS 攻击。\n\nSSRF 防御最佳实践:\n1. URL 白名单:只允许访问预定义的可信域名和 IP 范围。\n2. 协议限制:只允许 http:// 和 https:// 协议,禁止 file://、gopher://、dict:// 等危险协议。\n3. IP 地址校验:解析域名为 IP 后检查是否为内网地址(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、169.254.0.0/16 等),防止 DNS 重绑定。\n4. 响应处理:限制响应大小和类型,避免将完整响应返回给客户端。\n5. 禁用不必要的 URL 重定向跟随。", + "source": null, + "related": [], + "keywords": [ + "文件类型验证", + "白名单", + "重命名", + "UUID", + "隔离存储", + "执行权限", + "Magic Number", + "协议限制", + "内网地址", + "DNS重绑定", + "URL白名单", + "IP校验" + ], + "scoring_rubric": "满分标准:文件上传防御列出至少3点(每点1.5分,共4.5分),SSRF防御列出至少3点(每点1.5分,共4.5分)。每点需表述清楚且正确。总分9分。少于3点或表述错误酌情扣分。", + "explanation": "" + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/web-security/single_choice.json b/topics/cybersecurity/web-security/single_choice.json new file mode 100644 index 0000000..76db56e --- /dev/null +++ b/topics/cybersecurity/web-security/single_choice.json @@ -0,0 +1,408 @@ +{ + "topic": "web-security", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "xss" + ], + "question": "以下哪种攻击方式的核心原理是在网页中注入恶意脚本并在其他用户的浏览器中执行?", + "options": { + "A": "SQL 注入", + "B": "跨站脚本攻击(XSS)", + "C": "跨站请求伪造(CSRF)", + "D": "服务端请求伪造(SSRF)" + }, + "answer": "B", + "explanation": "XSS(Cross-Site Scripting)的核心是将恶意脚本注入到网页中,当其他用户访问该页面时,恶意脚本在受害者浏览器中执行。SQL 注入针对数据库查询,CSRF 利用用户已认证身份伪造请求,SSRF 是让服务端发起非预期的请求。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "xss" + ], + "question": "以下哪种 XSS 攻击类型的恶意脚本不会被存储在服务器端?", + "options": { + "A": "存储型 XSS", + "B": "反射型 XSS", + "C": "DOM 型 XSS", + "D": "B 和 C 都对" + }, + "answer": "D", + "explanation": "存储型 XSS 的恶意脚本被持久化存储在服务器(如数据库),每次用户访问都会触发。反射型 XSS 的恶意脚本通过 URL 参数等方式传递,服务器将其「反射」回响应中,不会存储。DOM 型 XSS 完全在客户端通过修改 DOM 执行,不经过服务器处理。因此 B 和 C 都不会存储在服务器端,正确答案是 D。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "xss" + ], + "question": "以下哪个 HTML 代码片段存在反射型 XSS 漏洞?", + "options": { + "A": "

Welcome, admin

", + "B": "

Welcome,

", + "C": "

Welcome,

", + "D": "

Welcome,

" + }, + "answer": "C", + "explanation": "选项 C 直接将用户输入的 `name` 参数未经任何转义或过滤就输出到 HTML 页面中,攻击者可以构造 `` 等恶意参数值实现注入。选项 B 使用了 `htmlspecialchars()` 对特殊字符进行转义,是安全的做法。选项 A 是硬编码内容,不存在注入点。选项 D 中的脚本是开发者写死的,不涉及用户输入。因此正确答案是 C。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "xss" + ], + "question": "为了防御 XSS 攻击,Content-Security-Policy(CSP)头中最关键的作用是什么?", + "options": { + "A": "加密传输数据", + "B": "限制浏览器只执行来自可信来源的脚本", + "C": "阻止所有表单提交", + "D": "过滤用户输入中的特殊字符" + }, + "answer": "B", + "explanation": "CSP(内容安全策略)通过 HTTP 响应头告诉浏览器只允许加载和执行来自指定可信来源的脚本,从而有效阻止内联脚本和未授权外部脚本的执行。加密传输是 HTTPS 的功能,过滤用户输入是服务端输入验证的工作,阻止表单提交与 CSP 无关。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "sql注入" + ], + "question": "SQL 注入攻击的根本原因是什么?", + "options": { + "A": "数据库软件存在漏洞", + "B": "用户输入未经过充分的验证和参数化处理就拼接到 SQL 语句中", + "C": "服务器操作系统不安全", + "D": "网络传输未加密" + }, + "answer": "B", + "explanation": "SQL 注入的根本原因是应用程序将用户提供的输入直接拼接到 SQL 查询语句中,而没有进行参数化处理或充分的验证与过滤,使得攻击者可以通过构造特殊输入来改变 SQL 语句的语义。数据库本身、操作系统和网络加密都不是 SQL 注入的直接原因。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "sql注入" + ], + "question": "以下哪种技术是防御 SQL 注入最有效的方法?", + "options": { + "A": "使用存储过程", + "B": "使用参数化查询(Prepared Statements)", + "C": "对输入进行长度限制", + "D": "禁用错误信息显示" + }, + "answer": "B", + "explanation": "参数化查询(Prepared Statements)是防御 SQL 注入最有效的方法,它将 SQL 语句结构与数据完全分离,数据库引擎不会将参数值解释为 SQL 命令的一部分。存储过程也可以防御,但如果存储过程内部仍然使用动态拼接 SQL,仍然存在注入风险。长度限制只是降低了攻击面,不能从根本上阻止注入。禁用错误信息显示只是减少了信息泄露,不能阻止注入本身。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "sql注入" + ], + "question": "在 SQL 注入中,以下 payload `1' OR '1'='1` 主要利用的是什么原理?", + "options": { + "A": "利用数据库权限过大", + "B": "利用 SQL 语句中字符串拼接后 OR 条件永远为真", + "C": "利用数据库的联合查询功能", + "D": "利用时间延迟来获取数据" + }, + "answer": "B", + "explanation": "该 payload 的原理是在原本的查询条件后闭合单引号,添加 `OR '1'='1'`,使 WHERE 条件永远为真。例如原 SQL 为 `SELECT * FROM users WHERE name='[输入]' AND password='...'`,拼接后变成 `WHERE name='1' OR '1'='1' AND password='...'`,由于 `'1'='1'` 恒为真,整个 OR 表达式为真,绕过了正常验证。利用权限过大和联合查询是其他类型的注入手法,时间延迟是盲注的一种手段。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "web安全", + "sql注入" + ], + "question": "以下哪种 SQL 注入方式在无法直接看到查询结果时,通过观察页面响应时间来推断数据?", + "options": { + "A": "联合查询注入", + "B": "报错注入", + "C": "基于时间的盲注", + "D": "堆叠查询注入" + }, + "answer": "C", + "explanation": "基于时间的盲注(Time-based Blind SQL Injection)是在无法通过页面回显或报错获取数据时使用的一种技术。攻击者通过在注入 payload 中加入 `SLEEP()`、`WAITFOR DELAY` 等时间延迟函数,根据服务器响应时间的长短来判断注入条件是否成立,从而逐位推断出数据。联合查询注入依赖可见的查询结果回显,报错注入依赖数据库错误信息,堆叠查询注入依赖执行多条 SQL 语句。因此正确答案是 C。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "csrf" + ], + "question": "CSRF(跨站请求伪造)攻击成功的前提条件是什么?", + "options": { + "A": "目标网站存在 XSS 漏洞", + "B": "用户已在目标网站登录且浏览器保存了认证凭据", + "C": "目标网站没有使用 HTTPS", + "D": "目标网站的数据库未加密" + }, + "answer": "B", + "explanation": "CSRF 攻击的本质是利用浏览器自动携带 Cookie 等认证凭据的特性。当用户已登录目标网站并在浏览器中保存了有效的会话凭证后,攻击者诱导用户访问恶意页面,浏览器会自动在请求中附带目标网站的认证信息,从而以用户身份执行非预期操作。XSS 可以辅助 CSRF 但不是必要条件,HTTPS 和数据库加密与 CSRF 无直接关系。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "csrf" + ], + "question": "以下哪种措施能最有效地防御 CSRF 攻击?", + "options": { + "A": "使用验证码", + "B": "在请求中添加并验证 CSRF Token", + "C": "限制密码长度", + "D": "使用 MD5 加密密码" + }, + "answer": "B", + "explanation": "CSRF Token 是防御 CSRF 攻击的标准方案。服务器为每个用户会话生成一个随机且不可预测的令牌,在执行敏感操作时要求客户端随请求一起提交该令牌。由于攻击者无法获知这个令牌,因此无法伪造有效请求。验证码虽然也能在一定程度上防御 CSRF,但会影响用户体验且不适合所有场景。限制密码长度和使用 MD5 加密密码与 CSRF 防御无关。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-011", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "csrf" + ], + "question": "浏览器的 SameSite Cookie 属性设置为 `Lax` 时,以下哪种请求会携带 Cookie?", + "options": { + "A": "第三方网站发起的 POST 表单提交", + "B": "用户从第三方网站点击链接跳转到目标网站(顶层导航 GET)", + "C": "第三方网站通过 JavaScript 发起的 XMLHttpRequest", + "D": "第三方网站通过 img 标签发起的 GET 请求" + }, + "answer": "B", + "explanation": "SameSite=Lax 是一种折中策略。它允许第三方发起的顶层导航(即用户主动点击链接导致的 GET 请求)携带 Cookie,但阻止第三方发起的 POST 表单提交、AJAX 请求以及嵌入在 img/script 等标签中的跨站请求携带 Cookie。这样既保留了用户正常点击链接跳转的功能,又阻止了大部分 CSRF 攻击场景。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-012", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "ssrf" + ], + "question": "SSRF(服务端请求伪造)攻击的攻击面是什么?", + "options": { + "A": "受害者浏览器", + "B": "目标服务器的后端应用", + "C": "DNS 服务器", + "D": "数据库客户端" + }, + "answer": "B", + "explanation": "SSRF(Server-Side Request Forgery)是攻击者利用服务器端应用程序发起请求的功能,让服务器代替攻击者访问内部或外部资源。攻击面是服务器的后端应用——攻击者通过控制应用的请求目标 URL,使服务器向内网服务、云元数据接口等非预期地址发起请求。受害者浏览器是 XSS 的攻击面,DNS 服务器和数据库客户端不是 SSRF 的直接攻击面。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-013", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "ssrf" + ], + "question": "攻击者利用 SSRF 访问 AWS 实例元数据服务时,通常请求的地址是?", + "options": { + "A": "http://127.0.0.1:8080", + "B": "http://169.254.169.254/latest/meta-data/", + "C": "http://10.0.0.1", + "D": "http://localhost:3306" + }, + "answer": "B", + "explanation": "AWS EC2 实例元数据服务监听在特殊的链路本地地址 169.254.169.254 上,通过 HTTP 协议提供实例的临时凭证、安全组、用户数据等敏感信息。攻击者通过 SSRF 漏洞让目标服务器访问该地址,即可窃取云实例的 IAM 角色凭证等敏感信息。127.0.0.1:8080 和 localhost:3306 通常是本地服务端口,10.0.0.1 是常见的内网网关地址,但都不是 AWS 元数据服务的标准地址。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-014", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "ssrf" + ], + "question": "以下哪项是防御 SSRF 攻击的推荐做法?", + "options": { + "A": "仅对用户输入进行前端校验", + "B": "建立 URL 白名单并校验请求目标 IP 地址,拒绝内网地址", + "C": "禁用服务器的 DNS 解析", + "D": "要求所有请求必须使用 POST 方法" + }, + "answer": "B", + "explanation": "防御 SSRF 最有效的方法是在服务端建立 URL 白名单,同时校验解析后的 IP 地址,拒绝向内网地址(如 127.0.0.0/8、10.0.0.0/8、169.254.0.0/16 等)发起请求。前端校验可以被轻易绕过,禁用 DNS 解析会影响正常功能,使用 POST 方法并不能阻止 SSRF(服务端仍可向任意地址发送 POST 请求)。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-015", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "文件上传" + ], + "question": "文件上传漏洞最直接的危害是什么?", + "options": { + "A": "占用服务器存储空间", + "B": "攻击者可上传恶意文件(如 WebShell)从而控制服务器", + "C": "导致其他用户密码泄露", + "D": "造成网站排名下降" + }, + "answer": "B", + "explanation": "文件上传漏洞最直接也最严重的危害是攻击者可以上传恶意脚本文件(如 PHP WebShell),然后通过浏览器访问该文件,使恶意代码在服务器上执行,从而获得对服务器的控制权限。虽然占用存储空间是可能的附带影响,但远不及代码执行的危害。密码泄露和网站排名下降与文件上传漏洞没有直接因果关系。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-016", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "文件上传" + ], + "question": "仅通过前端 JavaScript 验证文件扩展名来限制上传文件类型,为什么不够安全?", + "options": { + "A": "JavaScript 运行太慢会影响用户体验", + "B": "攻击者可以禁用 JavaScript 或使用代理工具修改请求绕过前端验证", + "C": "JavaScript 不支持正则表达式", + "D": "浏览器会自动修改文件扩展名" + }, + "answer": "B", + "explanation": "前端验证完全在客户端执行,攻击者可以通过多种方式绕过:禁用浏览器 JavaScript、使用 Burp Suite 等代理工具拦截并修改上传请求、直接发送 HTTP 请求而不经过浏览器页面等。因此仅靠前端验证是不可靠的,必须在服务端再次验证文件类型、扩展名和内容。JavaScript 的运行速度和正则表达式支持都不是安全问题的原因,浏览器也不会自动修改扩展名。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-017", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "web安全", + "文件上传" + ], + "question": "以下哪种文件上传防御策略最容易被绕过?", + "options": { + "A": "验证文件的 Magic Number(文件头签名)", + "B": "仅通过检查 Content-Type 请求头判断文件类型", + "C": "将上传文件重命名为随机字符串并存储在非 Web 根目录", + "D": "使用独立的文件处理服务并禁用执行权限" + }, + "answer": "B", + "explanation": "Content-Type 请求头是由客户端自行设置的,攻击者可以使用代理工具轻松将其修改为任意值(如 image/png),而实际文件内容仍然可以是恶意脚本。因此仅检查 Content-Type 是最容易被绕过的方式。检查文件头签名需要伪造更深层的文件格式信息,重命名和非 Web 根目录存储使攻击者难以定位和执行恶意文件,独立服务加禁用执行权限从架构层面消除风险。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-018", + "type": "single_choice", + "difficulty": 1, + "tags": [ + "web安全", + "xss" + ], + "question": "以下哪个 HTTP 响应头设置有助于防止浏览器嗅探 MIME 类型,从而降低 XSS 风险?", + "options": { + "A": "X-Frame-Options", + "B": "X-Content-Type-Options: nosniff", + "C": "Strict-Transport-Security", + "D": "Access-Control-Allow-Origin" + }, + "answer": "B", + "explanation": "X-Content-Type-Options: nosniff 告诉浏览器必须遵守服务器声明的 Content-Type,不要进行 MIME 类型嗅探。这可以防止攻击者上传看似图片但实际是 HTML/JavaScript 的文件被浏览器当作脚本执行。X-Frame-Options 防止页面被嵌入 iframe(防点击劫持),Strict-Transport-Security 强制 HTTPS,Access-Control-Allow-Origin 用于 CORS 跨域控制。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-019", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "sql注入" + ], + "question": "以下 Python 代码中,哪一段能有效防止 SQL 注入?", + "options": { + "A": "cursor.execute(\"SELECT * FROM users WHERE name='\" + user_input + \"'\")", + "B": "cursor.execute(\"SELECT * FROM users WHERE name=%s\", (user_input,))", + "C": "cursor.execute(f\"SELECT * FROM users WHERE name='{user_input}'\")", + "D": "cursor.execute(\"SELECT * FROM users WHERE name=\" + user_input.strip())" + }, + "answer": "B", + "explanation": "选项 B 使用了参数化查询(占位符 %s 加元组参数),数据库驱动会自动处理参数转义和类型安全,SQL 结构与数据完全分离,是防止 SQL 注入的标准做法。选项 A 和 C 使用字符串拼接/f-string,用户输入直接嵌入 SQL 语句,存在注入风险。选项 D 仅做了 strip() 去空格,仍是字符串拼接,同样不安全。因此正确答案是 B。", + "source": null, + "related": [] + }, + { + "id": "sc-020", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "web安全", + "ssrf" + ], + "question": "攻击者通过 SSRF 利用 `file:///etc/passwd` 协议读取服务器本地文件,这说明应用程序存在什么问题?", + "options": { + "A": "应用程序未限制请求使用的协议类型", + "B": "应用程序的防火墙配置错误", + "C": "应用程序使用了弱密码", + "D": "应用程序的数据库未授权" + }, + "answer": "A", + "explanation": "如果应用程序允许用户控制 URL 并未限制协议类型,攻击者可以使用 file://、gopher://、dict:// 等非 HTTP 协议来读取本地文件或探测内部服务。正确的做法是严格限制只允许 http:// 和 https:// 协议,并校验请求目标。防火墙配置错误、弱密码和数据库未授权都与 SSRF 中的协议滥用无关。因此正确答案是 A。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/cybersecurity/web-security/true_false.json b/topics/cybersecurity/web-security/true_false.json new file mode 100644 index 0000000..5339b31 --- /dev/null +++ b/topics/cybersecurity/web-security/true_false.json @@ -0,0 +1,148 @@ +{ + "topic": "web-security", + "type": "true_false", + "schema_version": "1.0.0", + "generated": "2026-09-17T00:00:00+08:00", + "questions": [ + { + "id": "tf-001", + "type": "true_false", + "difficulty": 1, + "tags": [ + "web安全", + "xss" + ], + "question": "XSS 攻击的目标是窃取或篡改客户端(浏览器端)的数据,而不是直接攻击服务器。", + "answer": true, + "explanation": "正确。XSS 攻击的主要目标是在受害者的浏览器中执行恶意脚本,从而窃取 Cookie、会话令牌、篡改页面内容或进行钓鱼等操作。它是一种客户端攻击,虽然恶意脚本可能存储在服务器上(存储型),但攻击效果发生在用户浏览器端。", + "source": null, + "related": [] + }, + { + "id": "tf-002", + "type": "true_false", + "difficulty": 1, + "tags": [ + "web安全", + "sql注入" + ], + "question": "使用了 HTTPS 加密传输的 Web 应用不会受到 SQL 注入攻击。", + "answer": false, + "explanation": "错误。HTTPS 只负责加密客户端与服务器之间的通信传输过程,防止数据在传输中被窃听或篡改。但 SQL 注入发生在服务端应用层——恶意输入通过 HTTPS 传输后仍然会被拼接到 SQL 查询中。加密传输与应用层输入验证是两个不同层面的安全问题。", + "source": null, + "related": [] + }, + { + "id": "tf-003", + "type": "true_false", + "difficulty": 2, + "tags": [ + "web安全", + "csrf" + ], + "question": "CSRF 攻击中,攻击者需要窃取用户的 Cookie 才能完成攻击。", + "answer": false, + "explanation": "错误。CSRF 攻击的关键在于浏览器会在请求中自动携带目标站点的 Cookie,攻击者无需窃取或知道 Cookie 的具体内容。攻击者只需诱导用户访问恶意页面或点击恶意链接,浏览器会自动在请求中附带目标站点的有效 Cookie,从而使请求被服务器认为是合法的。", + "source": null, + "related": [] + }, + { + "id": "tf-004", + "type": "true_false", + "difficulty": 2, + "tags": [ + "web安全", + "ssrf" + ], + "question": "SSRF 漏洞只能用于访问内网资源,无法对外网发起请求。", + "answer": false, + "explanation": "错误。SSRF 不仅可以让服务器访问内网资源(如内网数据库、管理后台),也可以对外网发起请求。攻击者可以利用 SSRF 进行端口扫描、访问外部 API、探测云服务元数据等。SSRF 的「伪造请求」特性意味着它可以访问服务器能够触及的任何网络地址。", + "source": null, + "related": [] + }, + { + "id": "tf-005", + "type": "true_false", + "difficulty": 1, + "tags": [ + "web安全", + "文件上传" + ], + "question": "在文件上传功能中,仅将上传目录的文件执行权限禁用,就可以完全防止文件上传漏洞。", + "answer": false, + "explanation": "错误。禁用上传目录的执行权限是一种有效的缓解措施,但不能「完全防止」文件上传漏洞。在某些配置下(如 Nginx 的 misconfiguration、解析漏洞、或配合本地文件包含 LFI 漏洞),攻击者仍可能利用上传的文件。安全防御应采用多层策略:验证文件类型、重命名文件、限制大小、扫描恶意内容、设置正确的目录权限等。", + "source": null, + "related": [] + }, + { + "id": "tf-006", + "type": "true_false", + "difficulty": 2, + "tags": [ + "web安全", + "xss" + ], + "question": "HttpOnly 标志的 Cookie 无法被 JavaScript 的 document.cookie 读取,因此可以完全防止 XSS 攻击。", + "answer": false, + "explanation": "错误。HttpOnly 标志确实可以防止 JavaScript 读取 Cookie,从而保护会话令牌不被 XSS 窃取。但 XSS 的危害远不止窃取 Cookie——攻击者仍可以通过注入脚本来篡改页面内容、进行钓鱼攻击、记录键盘输入、发起 CSRF 请求等。HttpOnly 只是减轻了 XSS 的一种具体危害,而非防止 XSS 攻击本身。", + "source": null, + "related": [] + }, + { + "id": "tf-007", + "type": "true_false", + "difficulty": 1, + "tags": [ + "web安全", + "sql注入" + ], + "question": "参数化查询(Prepared Statements)通过将 SQL 结构与数据分离来防止 SQL 注入。", + "answer": true, + "explanation": "正确。参数化查询的核心机制是先定义 SQL 语句的结构(使用占位符),然后单独传递参数值。数据库引擎会将参数值严格作为数据处理,不会将其解释为 SQL 命令的一部分,从而从根本上杜绝了通过输入改变 SQL 语义的可能性。", + "source": null, + "related": [] + }, + { + "id": "tf-008", + "type": "true_false", + "difficulty": 2, + "tags": [ + "web安全", + "csrf" + ], + "question": "GET 请求不会受到 CSRF 攻击,只有 POST 请求才需要 CSRF 防护。", + "answer": false, + "explanation": "错误。GET 请求同样可能受到 CSRF 攻击。如果应用程序使用 GET 请求执行状态更改操作(如 `GET /delete?id=1`),攻击者可以通过 img 标签、链接等方式诱导浏览器发起 GET 请求。正确的做法是:所有状态更改操作应使用 POST/PUT/DELETE 等方法,并对所有修改数据的请求实施 CSRF 防护。", + "source": null, + "related": [] + }, + { + "id": "tf-009", + "type": "true_false", + "difficulty": 2, + "tags": [ + "web安全", + "ssrf" + ], + "question": "对用户输入的 URL 进行黑名单过滤(如禁止 127.0.0.1)是防御 SSRF 的最可靠方法。", + "answer": false, + "explanation": "错误。黑名单过滤很容易被绕过。攻击者可以使用:①十进制/十六进制/八进制 IP 表示(如 2130706433、0x7f000001);②DNS 重绑定技术;③IPv6 地址(::1);④域名指向内网地址(如 nip.io);⑤URL 编码和重定向等方式绕过。白名单 + 解析后 IP 校验才是更可靠的方案。", + "source": null, + "related": [] + }, + { + "id": "tf-010", + "type": "true_false", + "difficulty": 1, + "tags": [ + "web安全", + "文件上传" + ], + "question": "将上传的文件重命名为随机字符串(如 UUID)可以有效防止攻击者通过猜测文件名来访问上传的恶意文件。", + "answer": true, + "explanation": "正确。将上传文件重命名为随机且不可预测的名称(如 UUID),攻击者无法猜测文件路径来直接访问和执行上传的恶意脚本。这是文件上传安全防御的一个重要措施,但通常需要配合其他策略(如类型验证、存储位置隔离等)一起使用。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/index.json b/topics/index.json index 62c5344..9624a5d 100644 --- a/topics/index.json +++ b/topics/index.json @@ -519,6 +519,103 @@ } } ] + }, + { + "slug": "cybersecurity", + "name": "网络安全专题", + "description": "网络安全核心知识:Web 安全、密码学、缓冲区溢出、操作系统安全、网络攻防、安全编程", + "subtopics": [ + { + "slug": "web-security", + "name": "Web 安全基础", + "description": "XSS / SQL 注入 / CSRF / SSRF / 文件上传", + "path": "topics/cybersecurity/web-security", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + }, + { + "slug": "crypto-basics", + "name": "密码学基础", + "description": "对称/非对称加密、哈希、数字签名、PKI", + "path": "topics/cybersecurity/crypto-basics", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + }, + { + "slug": "buffer-overflow", + "name": "缓冲区溢出与漏洞利用", + "description": "栈溢出、ROP、ASLR/DEP 绕过", + "path": "topics/cybersecurity/buffer-overflow", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + }, + { + "slug": "os-security", + "name": "操作系统安全", + "description": "权限模型、沙箱、Windows 安全机制(Win32 API)", + "path": "topics/cybersecurity/os-security", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + }, + { + "slug": "network-attack", + "name": "网络攻防", + "description": "中间人攻击、ARP 欺骗、DNS 劫持、端口扫描", + "path": "topics/cybersecurity/network-attack", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + }, + { + "slug": "secure-coding", + "name": "安全编程实践", + "description": "输入校验、整数溢出、格式化字符串、内存安全", + "path": "topics/cybersecurity/secure-coding", + "stats": { + "total": 34, + "by_type": { + "code_reading": 1, + "short_answer": 3, + "single_choice": 20, + "true_false": 10 + } + } + } + ] } ] -} +} \ No newline at end of file