feat: add cybersecurity topic with 6 subtopics (204 questions)
Deploy Examination / deploy (push) Successful in 7s
Deploy Examination / deploy (push) Successful in 7s
New topic group: 网络安全专题 (cybersecurity) Subtopics: - web-security: Web 安全基础 (34 questions) - crypto-basics: 密码学基础 (34 questions) - buffer-overflow: 缓冲区溢出与漏洞利用 (34 questions) - os-security: 操作系统安全 (34 questions) - network-attack: 网络攻防 (34 questions) - secure-coding: 安全编程实践 (34 questions) Question types per subtopic: - single_choice × 20 - true_false × 10 - short_answer × 3 - code_reading × 1 Difficulty range: 1-3 (基础)
This commit is contained in:
@@ -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 <stdio.h>\n#include <string.h>\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 <string>\\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": "请阅读以下代码,分析其中的安全问题并回答相关问题。"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user