70e368e235
Deploy Examination / deploy (push) Successful in 7s
New topic group: 网络安全专题 (cybersecurity) Subtopics: - web-security: Web 安全基础 (34 questions) - crypto-basics: 密码学基础 (34 questions) - buffer-overflow: 缓冲区溢出与漏洞利用 (34 questions) - os-security: 操作系统安全 (34 questions) - network-attack: 网络攻防 (34 questions) - secure-coding: 安全编程实践 (34 questions) Question types per subtopic: - single_choice × 20 - true_false × 10 - short_answer × 3 - code_reading × 1 Difficulty range: 1-3 (基础)
405 lines
20 KiB
JSON
405 lines
20 KiB
JSON
{
|
||
"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": []
|
||
}
|
||
]
|
||
} |