学习笔记 - CTFshow_Pwn知识精炼(长期更新)
前言
本博文为CTFshow中Pwn系列的做题笔记和知识总结,用于记录本人摸索 二进制世界 的历程;
没啥好说的 , 干就完了
前置基础
Pwn5-12——计组基础:
题目附件给出的信息十分重要
1 | section .data |
总结:/image1.png)
1. 寄存器(CPU 的贴身口袋)
EAX (累加器):头号办事员。装返回值、做加减乘除、装系统调用号。
EBX, ECX, EDX:办事材料袋。系统调用时,专门用来递交第 1、2、3 个参数。
ESI, EDI (变址):人形指针。专门指向内存地址,负责数据的搬运。
2. 寻址方式(找东西的套路)
唯一核心看懂 [](中括号等于“开门取物”):
无
[](如*mov ecx, msg*):把 门牌号(内存地址) 揣进口袋。有
[](如*mov eax, [ecx]*):按着门牌号去找,开门把里面的东西 拿出来。复合寻址(如 [
ecx + edx*2 - 6]):小区大门(ecx) + 楼层(edx*2) + 微调退几步(-6) = 最终开门取物。
3. 系统调用(int 0x80 办事大厅)
这是 32 位 Linux 呼叫内核干活的固定套路:
EAX 装指令:想打印填 4,想退出填 1。
其他口袋装材料:比如把屏幕代号、字符串地址、长度分别塞进 EBX、ECX、EDX。
按呼叫铃:执行
int 0x80,系统瞬间接管帮你办妥。
补充:小端序中高字节存高地址,低字节存低地址。若想让程序读入
0xdeadbeef去覆盖返回地址,用 Python 构造 Payload 时必须倒着敲:\xef\xbe\xad\xde。
Pwn13-16 ——gcc编译的初步运用
对于 .c 结尾的 C 语言文件,或者 .s 结尾的汇编文件,GCC 原生支持。只要敲一行命令 gcc 源码 -o 程序名 就能成功编译。
但是对于 .asm 结尾的汇编文件,要先用 nasm 把代码翻译成半成品的 .o 文件,再用 ld 工具把半成品拼装成真正的程序(例如先 nasm -f elf flag.asm -o flag.o,再 ld -m elf_i386 -o flag flag.o)。
补充:
nasm: 常用汇编器(NASM)命令
-f elf: 指定输出格式为 ELF,这是 Linux 常用的目标文件格式。flag.asm: 输入的汇编源代码文件。-o flag.o: 指定输出的目标文件名。
ld: GNU 链接器命令
-m elf_i386: 指定生成 32 位架构的 ELF 格式可执行文件。-o flag: 指定输出的可执行文件名。flag.o: 输入的目标文件。
其他常见输出格式:
-f bin: 生成纯二进制文件。-f win32: 生成 Windows 平台目标文件。-f macho: 生成 Mac OS X 目标文件。
踩坑点:如果源码中存在文件读取操作(如
fopen("key", "rb")),必须在同级目录提前构造好文件。应该写echo -n "CTFshow" > key,不写-n会额外引入换行符,破坏底层数据纯净度。
Pwn17——命令拼接注入
思路:IDA 查看附件,发现出题人在 dest 里面放了 "ls /"。或许让用户输入一个目录名(比如 tmp),拼成 "ls /tmp",然后用 system() 看目录里有什么。但使用了 strcat 来暴力拼接字符,我们只需使用分号 ; 隔断,再输入 sh 便可提取 shell。
补充:常用注入连接符 (Linux)
;:按顺序执行多条命令。&&:前面命令执行成功,才执行后面命令。||:前面命令执行失败,才执行后面命令。|(管道):将前面命令的输出作为后面命令的输入。
(踩坑点:本题通过注入弹出的 shell 是盲终端,没有 ctfshow@ubuntu:~$ 提示符。可以通过 ls 或 whoami 测试。此处无法通过键盘移动光标,回车键也无法正常发挥功能,键入时应确保无误)
pwn18 —— >> 和 > 的辨析
| 符号 | 作用 |
命令 > 文件 |
将标准输出重定向到文件中(清除原有文件中的数据) |
命令 >> 文件 |
将标准输出重定向到文件中(在原有的内容后追加) |
Pwn19 ——文件描述符与重定向
当程序的标准输出(1 号文件描述符 stdout)被 fclose() 恶意关闭时,正常的 cat 无法回显。必须利用依然敞开的报错通道(2 号文件描述符 stderr),构造命令 cat /flag 1>&2,将输出数据强行拐弯塞进报错通道打印在屏幕上。
补充:文件描述符(0 & 1 & 2)
Linux 进程默认有 3 个缺省打开的文件描述符:标准输入 0 (stdin),标准输出 1 (stdout),标准错误 2 (stderr)。
0, 1, 2 对应的物理设备一般是:键盘、显示器、显示器。
Pwn20-22 ——RELRO保护机制与GOT表
程序调用外部函数(如 puts、system)时,查阅底层的“内部通讯录”——即 .got 和 .got.plt 表(全局偏移表)。如果这两个表是可写的,我们就可以劫持 GOT 表。RELRO 保护机制就是为了把这个表锁定为只读。
底层读取出的地址(如 0000000000600f18)带有大量补位的 0。实战中约定俗成做法是去掉前导零,并加上十六进制前缀 0x(即 0x600f18)。
| RELRO 状态 | .got 运行权限 | .got.plt 运行权限 |
| No RELRO | 1 (可写) | 1 (可写) |
| Partial RELRO | 0 (只读) | 1 (可写) |
| Full RELRO | 0 (只读) | 0 (只读/不存在) |
踩坑点:
readelf 命令的大小写:
readelf -s(小写):查看符号表 (Symbols)。只能看到函数名和变量名。readelf -S(大写):查看节头表 (Section Headers)。这才能找到内存块地址 (Addr) 和权限旗标 (Flg)。
静态标志与运行环境优先级:
readelf(静态):即使看到.got带有 WA 旗标,那也只是文件在硬盘上的静态属性。checksec(动态):只要确认了 Full RELRO,运行时权限将覆盖静态权限。
Pwn23——命令行参数注入与SUID权限
思路:程序一开始用 fgets 把 Flag 偷偷读进内存,然后留了一个会调用 sigsegv_handler 的 signal(11)。程序吃启动参数 (argv[1]),并把它扔给了不检查长度的 strcpy。利用命令替换 $() 挂载超长字符串,把 strcpy “撑爆”,引发段错误触发 signal(11),借助 pwnme 程序带有的 SUID 权限得到 flag。
SUID 权限(提权跳板):当程序权限显示为
-rwsr-sr-x(带有s标志)且所有者为 root 时,进程会临时获得 root 权限。Signal 11(段错误与“复活甲”):栈溢出覆盖返回地址后,抛出 SIGSEGV。若源码中有
signal(11, handler),程序临死前会被拉去执行 handler 逻辑。
| 维度 | 标准输入 (stdin) | 命令行参数 (argv) |
| C 语言源码特征 | 使用 scanf(), gets(), read(), fgets(..., stdin) |
在 main 函数中通过 int main(int argc, char **argv) 接收。核心是处理 argv[1]。 |
| 正常人怎么用 | 终端输入 ./pwnme -> 提示录入 -> 敲字 -> 回车。 |
终端输入 ./pwnme AAAA 直接回车。 |
| 黑客注入手法 | 管道符 ` | 或 输入重定向<` |
| 攻击模板 | `python3 -c “print(‘A’*200)” | ./pwnme` |
| Linux 底层跑法 | 终端同时跑两个进程,建立管道。 | 终端先跑 Python 拿到结果,再拼成完整命令执行。 |
| 错误下场 | 喂错给 argv 程序:管道撑爆,报错 BrokenPipeError。 |
喂错给 stdin 程序:Payload 被当成杂项忽略,程序傻等键盘。 |
| 进阶补充 | 菜单题必须上 pwntools 库交互。 |
如果包含空格或特殊字符,会被切断。实战通常不考虑,因为打的是机器码。 |
Pwn24 —— pwntools 初步利用(shellcraft)
思路:没开 NX,静态分析发现读取后直接执行 call eax,而 eax 指向 buf 首地址。利用 shellcraft 自动编写 shellcode。
1 | from pwn import * |
PWN25——Ret2libc初步利用
思路:开了 NX 保护,利用程序自带函数库攻击。泄露函数真实地址,利用 LibcSearcher 搜索 system 和 /bin/sh 地址。
获取偏移值
gdb ./pwncyclic 200 > patternrun < pattern段错误时执行
info register eip得到0x6261616bcyclic -l 0x6261616b得到偏移量
泄露地址脚本
1 | from pwn import * |
完整攻击 EXP
1 | from pwn import * |
/image2.png)
通过 strings ./pwn | grep Ubuntu 确定 GCC: (Ubuntu 7.5.0-3ubuntu1~18.04)。
18.04 对应 GLIBC 2.27。
审视选项:排除 0-3 (2.19), 4 (非 Ubuntu), 6, 9 (2.17)。
输入 5 提取 flag。
Flag: ctfshow{b513b07d-d063-400c-8429-64451fa11b8b}