Files
examination/topics/cybersecurity/buffer-overflow/single_choice.json
T
wonder 70e368e235
Deploy Examination / deploy (push) Successful in 7s
feat: add cybersecurity topic with 6 subtopics (204 questions)
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 (基础)
2026-09-17 13:33:04 +08:00

405 lines
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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": []
}
]
}