news 2026/9/26 4:57:08

零基础学Python打CTF:从环境搭建到五大方向实战脚本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零基础学Python打CTF:从环境搭建到五大方向实战脚本

每年都会有人问我同一个问题:想入门CTF,但完全不会编程,到底应该从哪里开始?我的答案一直没变过:先学Python,然后在题目里学Python。CTF(Capture The Flag,夺旗赛)的核心是"在限时内解决一组安全挑战",而Python是这个圈子里使用频率最高的语言。不管是Web、密码学、杂项还是逆向、PWN,动手写脚本解题几乎是每天的日常,Python就像一把万能螺丝刀,什么场景都能拧两下。

这篇文章就是按这个思路写的——从零基础开始,把Python当成CTF的解题工具来学,而不是当成一门"课程"来背。内容覆盖环境搭建、语法速成、五大方向实战脚本、报错排查,最后给出一条能落地执行的进阶路线。希望你看完能做的第一件事,就是打开终端,开始跑第一个脚本。

1. CTF入门与Python的定位:为什么非它不可

1.1 CTF五大方向与Python的用武之地

很多新手第一次接触CTF,都会被五花八门的方向搞晕。其实常规赛事的题目就五大类,每一类都离不开编程能力,而Python几乎在每个方向都能出力:

方向考察重点Python典型用途
WebSQL注入、XSS、命令执行、文件上传、反序列化等构造HTTP请求、枚举参数、自动提取flag
Crypto古典密码、AES/RSA、哈希、随机数快速实现加解密逻辑、大整数运算、破解弱密钥
Misc编码转换、隐写术、流量分析、取证处理文件头、解析图片、分析pcapng流量
Reverse逆向工程、算法还原、注册机编写模拟脚本、还原加密算法、与调试器配合
PWN漏洞利用、栈溢出、堆利用用pwntools编写exploit、收发数据、接管交互

你会发现,即使不做纯开发,CTF解题的每一个环节——从读题到拿flag——都需要写点东西。而Python语法简洁、库极其丰富,是"想法到脚本"距离最短的语言。

1.2 Python为什么是CTF圈子的"通用语"

不是C不好,也不是Go不优秀,但在CTF这种几个小时内要出结果的比赛里,Python有三个不可替代的优势。

第一,语法接近伪代码。你脑子里想的"先尝试所有可能的位移量,再用频率分析判断哪个是明文",几乎可以逐行翻译成Python,不需要被内存管理、指针、模板这些细节打断思路。

第二,生态太全了。requests处理HTTP,PIL处理图片,pwntools直接跟程序交互,pycryptodome和gmpy2覆盖加解密和大整数运算,scapy还能解析流量包。这些库一装,等于赛前就把工具箱备好了。

第三,胶水特性。很多时候解题不是纯Python搞定一切,而是用Python调起系统命令、处理临时文件、把工具链串起来。Python写这种"胶水脚本"是最顺手的。

一个现实判断:如果你准备参加三个月后的比赛,与其花时间研究语言特性,不如直接把Python放到题目里学。刷题碰到一个编码转换,就写一个转换脚本;碰到一道RSA,就写一个数字分解的脚本。很快你就会发现,语法慢慢熟了,解题思路也通了。

1.3 赛前环境准备:从安装到常用库一次配齐

先说Python安装。网上太多教程会让人去官网下载、一路点Next,然后……没有然后,因为环境变量没配,命令行里python直接报"不是内部或外部命令"。

我的建议是:装新版(3.8以上即可,不必追最新),安装时务必勾选"Add Python to PATH";装完打开命令行,敲python --version验证一下。如果你在Windows上遇到,也可以用微软商店直接搜Python安装,那个会自动处理好环境变量,省心很多。

python --version # 配置国内镜像加速,不然pip下载库会慢到怀疑人生 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

编辑器推荐VS Code,安装Python插件后就能直接运行脚本。运行方式很简单:新建一个.py文件,右键"在终端中运行Python文件"。不需要配置复杂的调试器,先把东西跑起来最重要。

CTF常用的库一次装全:

pip install requests pycryptodome gmpy2 pwntools pillow pyzbar

提示:Windows上如果gmpy2安装失败,不要硬扛编译报错,直接到Visual Studio Code或Python官网对应的wheel文件页下载匹配Python版本的whl,再pip install这个whl文件,一分钟搞定。

环境到位之后,接下来的所有脚本都是这个基础上跑的。

2. Python基础语法速成:按CTF需求学,不按教材学

2.1 先搞懂三件事:输入、输出与类型转换

CTF解题脚本大多长成一个样子:从题目给的参数出发,经过一系列处理,最后打印flag。所以输入输出和类型转换是最高频的操作。

# input()拿到的永远是字符串 s = input("> ") print(type(s)) # <class 'str'> # 需要当整数用时,必须显式转换 n = int(s) # 输出时,Python的print会自动换行,且多个参数间默认用空格隔开 print("flag", "is", "here")

有一个点我会反复跟新人强调:eval这个函数能直接执行字符串里的表达式,网上很多代码示例喜欢用它来"简洁地"转数字或解析输入,但在CTF方向里,eval本身就经常是考点——它能让一段字符串变成代码执行,很多Web注入题考的就是你如何利用类似的入口。平时练习千万别图省事在自己的工具脚本里用eval,因为一旦脚本处理了不可信输入,后果很难预料。养成显式转换类型的习惯,后面会少踩很多坑。

另外要理解int(x, base)这个用法,处理十六进制、二进制字符串时非常常见:

print(int("ff", 16)) # 255 print(int("1010", 2)) # 10

2.2 字符串、字节与编码:CTF里的"文字游戏"基础

CTF题目的信息和flag,绝大多数都需要经过编码转换才能读出来。拿到一段看似乱码的字符串,实际上可能是十六进制、Base64、ASCII偏移、URL编码……这些都是Python里几行代码的事。

先搞清楚Python3里最折磨新手的一对概念:str和bytes。str是人能读的文本,bytes是计算机存的原生字节。两者不能直接拼接,中间靠encode/decode转换。

# 字符与ASCII码互转 print(ord('f')) # 102 print(chr(102)) # 'f' # 十六进制字符串转文本 bytes.fromhex("666c6167").decode() # 'flag' "flag".encode().hex() # '666c6167' # Base64解码 import base64 print(base64.b64decode("ZmxhZw==").decode()) # 'flag' # URL解码 from urllib.parse import unquote print(unquote("%66%6c%61%67")) # 'flag'

理解这些转换,Misc方向的入门题就没那么可怕了。练得多了你会发现,"乱码"只是还没找到正确的解码方式而已。

2.3 循环与字典:写第一把"瑞士军刀"

循环解决的是"重复"问题。CTF里最常见的重复动作是枚举:枚举凯撒位移、枚举异或密钥、遍历参数。字典则用来做统计和映射,古典密码分析尤其依赖字符频率。

写一个字符频率统计脚本,是很多人的第一个"解题工具":

from collections import Counter cipher = "iqfcf{vFt1TsXs91_98X_6291jX9f3s9}" freq = Counter(cipher) print(freq.most_common(5)) # [('9', 4), ('f', 4), ('1', 3), ('X', 3), ...]

再配一个循环,把凯撒位移全试一遍:

cipher = "iqfcf{vFt1TsXs91_98X_6291jX9f3s9}" for shift in range(26): result = "" for ch in cipher: if 'a' <= ch <= 'z': result += chr((ord(ch) - ord('a') + shift) % 26 + ord('a')) elif 'A' <= ch <= 'Z': result += chr((ord(ch) - ord('A') + shift) % 26 + ord('A')) else: result += ch if "flag" in result.lower(): print(shift, result)

这个脚本用了两层循环:外层枚举位移,内层逐字符处理。类似这种"枚举+判断"的组合,在后面的RSA小指数攻击、简单异或解密里会反复出现。理解了它,你就理解了多数CTF脚本的基本骨架。

2.4 文件读写:处理二进制数据的基本功

Misc、Reverse方向经常要跟文件打交道。题目给一个图片、一个压缩包或者一个原始数据文件,脚本里第一步通常是打开、看文件头、按字节处理。

# 以二进制方式读取,才能看到文件的真实内容 with open("secret.png", "rb") as f: data = f.read() print(data[:8]) # b'\x89PNG\r\n\x1a\n',这是PNG的标准文件头

判断文件真实类型时,文件头很关键。很多新手直接改后缀名打不开文件,先用十六进制看头部,再用Python判定真实类型:

def detect_type(data): if data[:8] == b'\x89PNG\r\n\x1a\n': return 'PNG' if data[:4] == b'\x89JF': return 'JPEG' if data[:2] == b'PK': return 'ZIP' return 'unknown'

处理十六进制转储文件时,经常要把十六进制字符串还原为二进制文件:

with open("dump.hex", "r") as f: hex_str = f.read().strip().replace(" ", "").replace("\n", "") with open("out.bin", "wb") as g: g.write(bytes.fromhex(hex_str))

文件操作的基本功,其实就是"以什么模式打开、读完是bytes还是str、写到哪里"。这三步想清楚,大多数跟文件有关的题就不会卡住。

3. 五大方向解题中的Python实战:从读题到拿flag

3.1 Crypto题实战:10行代码解一道入门RSA

密码学方向的题目是最适合练Python的,因为思路清晰、逻辑严密。拿一道入门级RSA题举例:题目给了大整数n、公钥指数e和密文c。解题思路是分解n得到两个质数p和q,算出欧拉函数,求出私钥指数d,再用pow(c, d, n)还原明文。

from sympy import factorint from gmpy2 import invert n = 90581 # 题目给的模数 e = 65537 # 公钥指数 c = 39167 # 密文 factors = list(factorint(n).keys()) p, q = factors[0], factors[1] print(f"p = {p}, q = {q}") phi = (p - 1) * (q - 1) d = int(invert(e, phi)) # 求e关于phi的逆元 m = pow(c, d, n) # 解密 # 把整数明文转成可见文本 flag = m.to_bytes((m.bit_length() + 7) // 8, 'big') print(flag)

每一步的作用讲清楚:分解模数是RSA破解的关键环节;欧拉函数(p-1)(q-1)是计算私钥的基础;invert是求乘法逆元,相当于计算d;最后用Python内置的pow做模幂运算,这是大整数运算效率最高的方法,别用**运算符自己算模。

如果遇到更大的n,硬分解就跑不动了,思路会转向低加密指数攻击、共模攻击、padding预测等,但脚本骨架几乎不变:读参数、算密钥、解明文。这就是为什么我说,把一类题的脚本写熟,后面遇到变体能很快迁移思路。

3.2 Web题实战:用requests和正则自动提取flag

Web方向的核心是考察对漏洞原理的理解,但很多步骤需要用脚本完成。最典型的动作是:给一个URL,POST一个参数,循环试探,从返回内容里找flag。

import requests import re # 注意:这段脚本只在比赛平台/授权的靶场环境中使用 url = "http://target.example.com/query" for i in range(1, 50): data = {"id": f"{i}"} r = requests.post(url, data=data) if "flag" in r.text.lower(): print(f"第{i}条记录返回了关键内容") flag = re.search(r"flag\{[^}]+\}", r.text, re.I) if flag: print(f"Flag: {flag.group(0)}") break

这里用到三个知识点。requests.post发送表单数据;r.text拿到响应文本;re.search用正则从大段HTML里提取flag格式的内容。CTF里flag一般有固定格式,比如常见的flag{...},正则写对就能精准捕获。

实战中经常遇到响应内容很大、直接肉眼找不过来,或者返回数据被编码的情况。这时候建议把每次响应的完整内容保存到本地文件,再慢慢分析,而不是每次都在脚本里打断点看输出。脚本里加一行:

with open(f"response_{i}.html", "w", encoding="utf-8") as f: f.write(r.text)

这样既方便复盘,也避免响应太长把控制台刷爆。

注意:Web题一定要在题目指定靶场和授权范围内操作。CTF比赛是全新的、隔离的演练环境,和真实系统完全是两码事。

3.3 Misc题实战:从图片、二维码和流量包里挖信息

Misc在很多人眼里是"杂货铺",但高频套路其实就那么几类:编码转换、图片隐写、二维码处理、流量分析。Python在这些地方很顺手。

图片方向常常用到PIL。比如题给了一张图片,怀疑低比特位隐写了信息(LSB隐写),那就需要逐像素读取RGB值的最低位:

from PIL import Image im = Image.open("secret.png") width, height = im.size bits = "" text = "" for y in range(height): for x in range(width): r, g, b = im.getpixel((x, y))[:3] bits += str(r & 1) # 只取红色通道的最低位 if len(bits) == 8: ch = chr(int(bits, 2)) if 32 < ord(ch) < 127: # 只打印可见字符 text += ch bits = "" print(text)

二维码处理更简单,直接调用库解码:

from pyzbar.pyzbar import decode from PIL import Image data = decode(Image.open("qr.png")) for item in data: print(item.data.decode())

如果二维码上贴了遮挡物或背景不够干净,可以先用图像处理把二维码区域提出来再做二值化,再用这个库识别。这个技巧在做Misc题时很实用。

流量分析是另一个高频考点,尤其是热词里经常出现的pcapng和USB流量题。主流工具是Wireshark或者tshark,但Python里用scapy可以快速做批量过滤:

from scapy.all import rdpcap packets = rdpcap("capture.pcapng") for pkt in packets: if pkt.haslayer("Raw"): print(pkt.summary(), pkt.load)

USB流量题实际上是在解析纯数据流,把USB HID键盘事件还原成按键。这类题用Python处理比手工翻Wireshark高效很多。核心思路是用tshark把usb相关包过滤出来,提取HID Data段的键值字节,再对照USB键盘映射表还原成字母。映射表网上能查到,把它存成一个字典,脚本跑完就是完整键盘输入记录。

这里有个小教训:拿到流量包先别急着写脚本,先抓几个关键包看字段结构,确认协议层次正确,再写解析逻辑。上来就套模板,很容易因为字段名不同白跑一遍。

3.4 逆向与PWN题里的"脚手架":pwntools入门

PWN方向是CTF里偏底层的方向,核心是写exp(exploit,利用脚本)和远程程序交互。pwntools是最常用的库,它的设计哲学就是"让脚本跟程序对话"。

from pwn import * # 远程连接题目给的地址和端口 io = remote("1.2.3.4", 10001) # 接收程序输出,直到某个固定字符串出现 io.recvuntil(b"Please input your name:") # 发送payload io.sendline(b"flag") # 持续交互,方便手动查看程序其他输出 io.interactive()

这段代码虽然短,但完成了整个"连接、接收提示、发送数据、保持交互"的闭环。Reversing方向的题如果需要跟程序多次交互,或者需要根据反馈调整参数,pwntools这套交互框架也是首选。

不要一上来就啃堆利用、shellcode这些超难内容。先把pwntools的收发数据、交互流程跑通,能把题目跑起来,再逐步深入。工具的使用门槛,往往比想象中低很多。

4. 常见报错与解题脚本中的避坑经验

4.1 新手最容易翻车的五个Python报错

写解题脚本的过程,本质上是和报错打交道的过程。下面这些报错,基本是每个新人都会中招的,我把原因和解决办法整理成一个速查表:

报错信息常见原因解决方法
ModuleNotFoundError库没安装pip install 对应库
TypeError: can't concat str to bytes混用了str和bytes先统一调用encode/decode
UnicodeDecodeError以文本方式读二进制文件改用"rb"模式,或指定encoding
ValueError: invalid literal for int()字符串里有空格或非数字字符先strip()清理,再int()转换
RecursionError递归太深改用循环,或提高递归限制

处理UnicodeDecodeError是我个人踩过最多次的坑。很多流量导出或文件转储用文本方式打开后会报错,解决办法是明确按二进制读取,或者指定编码。判断标准很简单:文件内容里如果有大量非ASCII字符,就用"rb"。

4.2 一道Web题的启发:Python脚本为什么会"感觉好奇怪"

热词里有个很有意思的题目描述:"小小查询系统,输入id即可查询到信息,但是报错感觉好奇怪"。这道题反映的是Web方向里很经典的一类现象:输入正常参数返回正常内容,输入特殊内容却返回异常的报错信息,而这个报错本身就是解题线索。

很多新手第一次在Python脚本里复现这个过程时,会感觉"脚本怎么跟浏览器表现不一样",原因通常有三个。

第一个是编码问题。requests拿到响应后,自动猜测解码方式,但猜错时页面里的中文会乱码。解决方案是手动指定编码:r.encoding = "utf-8"。第二个是响应内容里有特殊字符,导致正则匹配失败。不要看到一段显示正常就直接用正则,先打印r.text的repr版本,看看是否有隐藏的换行、标签或不规则空格。第三个是会话状态问题。有些查询接口依赖Cookie或上一步的Token,直接用requests.post发一次请求会失败,需要用requests.session()保持状态。

这类"奇怪感觉"其实是很好的训练信号——每当脚本行为和预期不符,把现象拆解成"输入、输出、环境"三类,再逐项排查,比反复试错高效得多。

4.3 写解题脚本的六个好习惯

刷题多了以后,我总结了一套自己写脚本的习惯,分享出来:

  • 每个脚本开头打印接收到的关键参数,确认和解说一致,再往下走。
  • 把请求、解密、文件处理等动作拆成函数,复用时不用复制粘贴。
  • 网络请求一律加超时和重试,题目服务器本来就容易卡顿。
  • 响应和中间结果及时存文件,不只在控制台里看。
  • 用tqdm给循环加进度条,大量枚举时心理压力小很多。
  • 赛后把脚本精简、加注释,存进自己的exp_lib目录,按方向分类。

这六个习惯看起来简单,但能在比赛高压环境下省出大量时间。脚本不是写给自己爽的,是写给"几小时后的自己"看的——那时候你很可能已经忘了当时为什么这么写。

5. 持续进阶:从入门到常态化参赛的一条可行路径

5.1 一条清晰的学习路线

给零基础新人排一条不绕弯的学习路线,大概四个阶段:

阶段目标具体做法
第一阶段能用Python处理文本和编码拿一段Base64、hex、URL编码的字符串反复转换,直到看见种类编码不慌
第二阶段能照葫芦画瓢写解题脚本到CTF题库平台找Crypto和Misc方向的入门题,看题解,理解后自己复现
第三阶段能独立分析题目并写完整exp做Web方向的模拟靶场,练习requests、正则、会话管理
第四阶段按兴趣专精方向喜欢Web就深入框架漏洞和绕过技巧,喜欢二进制就啃pwntools和底层利用

核心做法是,永远不要"先把Python学完再打CTF",两件事要并行。语法学到能写循环和函数,就可以开始找题目练手了。碰到不会的语法和函数,直接查官方文档,查完马上用到题里,记忆会非常牢固。

5.2 刷题平台、工具沉淀与合规实践

刷题平台我推荐几个自己常用的:BUUCTF题库量大,适合系统练;CTFshow有大量针对新人的低成本入门题;攻防世界适合按方向分类刷;polar CTF这类赛事官网也常有赛题归档,赛后可以拿来复盘。多平台交叉刷的好处是能接触不同出题风格,不会只熟悉某一家的偏好。

工具沉淀方面,很多选手会整理自己的"编码工具箱",比如圈子里常说的随波逐流CTF编码工具,就是把Base64、URL、进制转换、古典密码等常用编码解码能力集中到一起,比赛时当双保险用。但我始终建议,工具可以辅助,核心的转换逻辑一定要自己用Python写过一遍。因为比赛时网络环境、工具兼容性都不确定,自己的脚本永远不会"带不动"。

学有余力时,还可以关注行业内的一些合规实践,比如各类厂商的SRC漏洞报告渠道——在授权范围内,向厂商提交你发现的安全问题。这跟CTF比赛不一样,它面对的是真实系统,必须严格遵守规则和边界。我见过不少CTF选手通过这条路积累了宝贵的实战经验,但前提永远是合规和授权。

5.3 从单打独斗到常态化参赛的个人体会

参加常态化赛事以后,我发现一个规律:真正拉开差距的不是知识量,而是脚本沉淀速度和临场心态。比赛是几个小时的连续作战,临时去查库、查语法会让节奏断掉。所以我在比赛前一定会做两件事:检查常用库是否都装好,把过去整理的exp_lib目录拉到手边。

写题解(writeup)也是特别重要的一环。每次比赛后,把每道题的解题脚本浓缩成注释清晰的单文件,附上自己的踩坑记录,发到社区或个人博客。这个过程倒逼你理解题目本质,同时也让后来的新人少走弯路。我自己很多经验,都是从别人的writeup里学来的——这个圈子之所以高效,正是因为大家都愿意把经验写下来传下去。

我个人在带新人时反复强调的只有一句话:别把Python当一门"课"来学,把它当成一把"螺丝刀"。CTF里每一个需要你重复手工做的事,都值得写脚本;每一次报错,都是你精进工具的契机。从今天的环境搭建开始,到写出第一个能自动提取flag的脚本,这段路我走过,无数人也走过,它没有想象中那么难,但一定需要你亲手敲下第一行代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 4:55:30

JS蛇簧联轴器选型与源头厂家辨别实操指南

JS蛇簧联轴器这些年在国内重工机械领域用得越来越广&#xff0c;但很奇怪&#xff0c;随便搜一下能看到的信息&#xff0c;要么是贸易商挂出来的标准参数页&#xff0c;要么是几家大品牌的产品册内容&#xff0c;真正讲清楚"这东西到底怎么选、怎么验货、怎么跟源头厂打交…

作者头像 李华
网站建设 2026/9/26 4:55:25

AIS 数据采集器

AIS 数据采集器搭建全流程日期&#xff1a;2026-09-25项目路径&#xff1a;C:\ais_projectMQTT 服务器&#xff1a;TDengine 数据库&#xff1a;ais基于TDengine的数据库存储&#xff0c;将从mqttx收到的数据存入超级表中&#xff0c;并根据唯一ID创建子表存储&#xff0c;便于…

作者头像 李华
网站建设 2026/9/26 4:54:47

RESTful API设计灵魂:Roy Fielding六大约束与CRUD思维辨析

你接手过一个号称“RESTful”的老接口吗&#xff1f;点开代码&#xff0c;清一色的POST /api/getXXX、POST /api/updateXXX&#xff0c;问就是“REST 不就是增删改查嘛&#xff0c;用 HTTP 方法对应 CRUD 不就完事了”。这个误读太普遍了&#xff0c;导致很多人把 REST 当成一种…

作者头像 李华
网站建设 2026/9/26 4:54:41

Frida工业级封装:构建安卓逆向作战系统

1. “次元剑”不是新工具&#xff0c;而是逆向工程师的作战系统思维“次元剑”这三个字最近在逆向工程和渗透测试圈子里高频出现&#xff0c;但它压根不是某个开源项目仓库里能git clone下来的独立软件——它没有GitHub star数&#xff0c;没有官方文档站&#xff0c;也没有安装…

作者头像 李华
网站建设 2026/9/26 4:53:24

MES基础业务考核试题解析:ISA-95、BOM与数据模型实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 4:52:54

Unity内置渲染管线屏幕高斯模糊后处理:Shader实现与避坑指南

刚看到这个问题的时候&#xff0c;我第一反应是“这不就是个后处理嘛&#xff0c;网上模板一大把”。但真到自己动手在Unity 内置渲染管线&#xff08;Built-in Render Pipeline&#xff09;里把屏幕模糊 Shader 调顺溜&#xff0c;还是有不少容易踩的细节。尤其这几天群里好几…

作者头像 李华