Linux下pwn从入门到放弃
字数 2092 2025-08-25 22:58:56
Linux下PWN从入门到精通
0x00 简介
PWN在安全领域中指的是通过二进制/系统调用等方式获得目标主机的shell。随着移动端和IoT设备的流行,传统的缓冲区溢出技术再次变得重要。
0x01 必备工具
- gdb:Linux下必备的调试工具
- gdb增强插件:
- gdb-peda
- gef
- gdbinit
- pwntools:编写exp和poc的利器
- checksec:检测ELF程序的安全性和运行平台
- objdump和readelf:查看ELF程序关键信息
- IDA Pro:强大的反编译工具
- ROPgadget:ROP利用工具
- one_gadget:快速寻找libc中exec('bin/sh')的位置
- libc-database:通过泄露的函数地址查询libc版本
0x02 ELF安全机制检测
使用checksec检测ELF程序的安全措施:
-
RELRO:
- Partial RELRO:部分保护
- FULL RELRO:完全保护,无法修改GOT表
-
Stack Canary:
- 开启后不能直接覆盖返回地址
- 需要绕过方法:改写指针、泄露canary、覆盖canary
-
NX(DEP):
- 栈中数据无执行权限
- 无法使用call esp/jmp esp
- 需使用ROP技术绕过
-
PIE(ASLR):
- 地址随机化
- 未开启时基地址固定(如0x400000)
-
FORTIFY:
- 限制格式化字符串漏洞利用
- 包含%n的格式化字符串不能位于可写地址
- 使用位置参数时必须使用范围内的所有参数
0x03 调试技巧
GDB常用命令
n:执行一行源代码但不进入函数ni:执行一行汇编代码但不进入函数s:执行一行源代码并进入函数si:执行一行汇编代码并进入函数c:继续执行到下一个断点b *地址:下断点directory:加载源码目录set follow-fork-mode parent:只调试主进程stack:显示栈信息x:十六进制显示内存数据
调试脚本
未开启ASLR
def debug(addr):
raw_input('debug:')
gdb.attach(r, "b *" + addr)
开启ASLR
wordSz = 4
hwordSz = 2
bits = 32
PIE = 0
mypid=0
def leak(address, size):
with open('/proc/%s/mem' % mypid) as mem:
mem.seek(address)
return mem.read(size)
def findModuleBase(pid, mem):
name = os.readlink('/proc/%s/exe' % pid)
with open('/proc/%s/maps' % pid) as maps:
for line in maps:
if name in line:
addr = int(line.split('-')[0], 16)
mem.seek(addr)
if mem.read(4) == "\x7fELF":
bitFormat = u8(leak(addr + 4, 1))
if bitFormat == 2:
global wordSz
global hwordSz
global bits
wordSz = 8
hwordSz = 4
bits = 64
return addr
log.failure("Module's base address not found.")
sys.exit(1)
def debug(addr = 0):
global mypid
mypid = proc.pidof(r)[0]
raw_input('debug:')
with open('/proc/%s/mem' % mypid) as mem:
moduleBase = findModuleBase(mypid, mem)
gdb.attach(r, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))
0x04 泄露libc地址和版本
-
格式化字符串漏洞:
- 泄露栈数据找到libc函数地址
- 使用libc-database判断版本
- 常用目标:
__libc_start_main
-
write函数:
- 使用pwntools的DynELF计算各种地址
-
printf函数:
- 利用遇到0x00停止输出的特性
- 泄露栈中重要数据
0x05 栈溢出利用技术
无保护程序
-
传统方法:
- 将shellcode写入栈中
- 查找call esp/jmp esp指令
-
现代方法:
- 寻找程序中的system函数
- 布局栈空间调用gets(.bss)
- 调用system('/bin/sh')
-
虚表覆盖:
- 通过覆盖虚表利用漏洞
开启NX保护
- 使用ROP技术绕过
- ROPgadget工具生成利用链
- 复杂程序需手动分析
开启Canary保护
-
利用canary泄露flag:
- 覆盖libc_argv[0]为flag地址
- stack_check_fail时会打印程序名称
-
子进程泄露:
- 在一个子进程中泄露Canary
- 在另一个子进程中伪造Canary
开启PIE保护
-
printf泄露:
- 多打印栈中数据
- 计算程序基地址、libc基地址
-
write泄露:
- 使用DynELF函数
0x06 格式化字符串漏洞
-
pwntools自动化:
- FmtStr和fmtstr_payload函数
- 自动计算利用点并生成payload
-
信息泄露:
- 制造格式化字符串漏洞泄露数据
0x07 UAF漏洞
- 释放堆后未清空指针
- 导致use-after-free漏洞
0x08 任意地址写
-
单字节写:
- 即使只能写一个字节也可控制程序
-
GOT表修改:
- 控制GOT表写入
- 改变程序执行流程
-
top_chunk控制:
- 计算libc基地址
- 控制top_chunk指针
0x09 综合技巧
- 即使所有保护全开,完全控制栈仍可攻破
- 组合多种技术:信息泄露+ROP+任意地址写
- 灵活运用工具链提高效率
0x0A 学习资源
-
工具安装指南:
- gdb插件安装:http://blog.csdn.net/gatieme/article/details/63254211
-
示例题目分析:
- 虚表覆盖:http://blog.csdn.net/niexinming/article/details/78144301
- ROP利用:http://blog.csdn.net/niexinming/article/details/78259866
- Canary绕过:http://blog.csdn.net/niexinming/article/details/78681846
- 格式化字符串:http://blog.csdn.net/niexinming/article/details/78699413
- UAF漏洞:http://blog.csdn.net/niexinming/article/details/78598635