先存个档。
1. Linux入门
命令 | 含义 | 示例 | ||
---|---|---|---|---|
pwd | 输出当前路径 | pwd | ||
ls | 列出位于当前路径的文件 | ls -al | ||
cd xxx | 改变当前路径,前往xxx目录 | cd ~/workspace | ||
cat xxx | 直接展示xxx文件的内容 | cat hw.c | ||
* \ | grep xxx | *是其他命令,输出含有xxx的行 | cat hw.c \ | grep stdio.h |
* \ | less | 查看输出更舒服,jk上下移动q退出 | cat hw.c \ | less |
./xxx | 运行当前目录xxx可执行文件 | ./a.out |
2. 汇编基础
内存地址:在有段/偏移寄存器的语境下一般记为段:偏移
,如CS:IP
,SS:IP
,SS:BP
,DS:DI
,DS:SI
,DS:[]
小端序:低地址存放低位数据
一个程序的内存空间:
高地址 | OS Kernel Space |
---|---|
Stack | |
↓(blank) | |
Shared Libraries | |
↑(blank) | |
Heap | |
BSS | |
Data(RW)数据段 | |
Text(RX)代码段 | |
低地址 | (blank) |
栈:
SS:SP 栈顶 |
---|
SS:BP 栈帧基底 |
SS 栈底 |
寄存器总结
name | 用途 | name | 用途 |
---|---|---|---|
AX | 通常用来存放函数的返回值 | SS(Stack Seg) | 栈的段地址/基地址 |
CX | 通常用来做循环计数器 | CS(Code Seg) | 下一条指令的段地址 |
BX | DS(Data Seg) | 数据的段地址 | |
DX | SP(Stack Pointer) | 栈顶偏移地址 | |
BP(Base Pointer) | 栈的基址偏移地址 | ||
IP(Instruction Pointer) | 下一条指令的偏移地址 |
汇编指令
3. 程序装载与栈帧结构
在Linux的可执行文件ELF
ELF文件类型:
- Relocatable File(*.o):可重定位文件,用来链接的素材
- Executable File(*):可执行文件
- Shared Object File(*.so):共享目标文件,用于做动态链接库
4. 实战环境配置和工具介绍
连接远程服务器:
nc表示netcat
1 | nc node4.buuoj.cn 28487 |
使用file查看文件信息,checksec查看文件保护信息。
利用gdb调试ELF文件:
命令 | 简写 | 说明 |
---|---|---|
file | 装载一个文件,可以在gdb后加参数,效果等同 | |
kill | 终止当前调试的进程 | |
run | r | 运行当前装载的文件,运行过程中使用Ctrl + C退出程序交互,进入调试(在退出时位置) |
next | n | =step over 单步步过 |
step | s | =step into 单步步入 |
continue | c | 继续执行程序,直到下一个中断或程序结束 |
finish | fini | 运行到函数返回处 |
catch | 设置捕捉点 | |
thread | t | 查看当前程序的线程信息 |
break | b | 在当前位置设置断点 |
backtrace | k | 查看当前函数调用栈信息 |
stack | stack n 查看栈内容 | |
vmmap | 查看程序中的分段,相当于OD中的E 模块 |
5. 缓冲区溢出
编写程序时没有考虑或错误设置用户输入长度,导致用户向缓冲区输入长度超过接受变量长度,从而覆盖到其它正常数据,破坏栈帧结构。
缓冲区溢出常见的漏洞函数:
1 | char *gets(char *str); |
如果文件中有”/bin/sh”,可用以下脚本:
1 | from pwn import * |
6. Shellcode
shellcode就是能使程序调用shell的一段代码(通常为汇编级别/机器码)。一旦某种shellcode被执行,我们就能够拿到目标机器的控制权限,从而获取flag。
- system(“/bin/sh”);(?) -> execve(“/bin/sh”, 0, 0)
- 触发中断(int 0x80 / syscall)
1 | ; first.asm |
Shellcode脚本:
1 | from pwn import * |
7. ROP链构造
NX——NO Execute bit(禁止执行位)是应用在CPU上的安全技术,它支持了操作系统级别的DEP——Data Execute Prevention(数据执行保护,Microsoft)。在应用了NX的系统上,(如果可执行文件开启保护),会把内存中的区域分为只供存储指令和只供存储数据两种。NX bit 被标记在内存分页中使用的页表索引上,如果置1,则该页内存数据不允许被执行,即把所有内容作为数据处理。这样可以防范shellcode注入攻击。
ROP——Return-Oriented Programming(返回导向编程)技术,允许攻击者在开启了栈不可执行等安全保护技术的情况下,执行恶意代码。
核心思想是通过栈溢出等方式,改写栈上的控制信息(调用栈,即return address, rbp等),以控制调用栈,劫持程序控制流并执行一些针对性的命令序列(gadgets)。
gadgets主要指一些以ret结尾的小段汇编指令,它们的执行通过ret语句和栈上控制的返回地址相连,构成一条ROP链。链的功能是设置寄存器值,泄露信息,调用函数等。
7.1 Ret2Text
gadget一般存在于Text中,或者广义上存在于ELF文件中(指令部分)。将返回地址改写为能执行某些特定功能的gadget地址,构造ROP链。
辅助工具:ropper、pwntools(ELF class)
1 | from pwn import * |
如果文件中没有system函数,如果想要调用system函数,要利用到PLT表。
7.2 Ret2syscall
- 一般为静态链接的可执行文件,指令非常多,也提供了许多gadget
- 最核心gadget:syscall(int 0x80)
- 整体类似Shellcode注入
静态链接的ELF文件在IDA的函数窗口全是一片白,没有粉色的动态链接。
利用ROPgadget工具,自行生成ROP链。
7.3 PLT表和GOT表
这里的PLT表示.plt
,GOT表表示.got.plt
。.got
存放其它全局符号信息,与.got.plt
不同,与.plt
关系不大。
7.4 Ret2libc
- 对动态链接文件,一般链接glibc
- glibc链接基址未知,需要进行基址泄露
- 一般需要程序循环,可以通过ROP链构造循环
1 | from pwn import * |
8. ELF保护措施及绕过方法
8.1 ASLR
ASLR(Address space layout randomization)——地址空间配置随机化
将可执行文件、共享库、栈、堆的基址随机化,用于防范明确地址的内存破坏攻击,比如ret2libc、stack address。
应对方法:地址泄露
8.2 NX
看[7.ROP链构造]
8.3 PIE
PIE(Position-independent executable)——地址无关代码/可执行文件
无论文件被加载进内存空间的什么地址中,程序都能够正常运行。在共享库链接中有重要作用。共享库文件被动态链接到内存中,PIE使其能正确处理外部引用。在普通ELF文件上,ELF配合ASLR,使其基址不可预测,增加了攻击难度。
整个ELF文件都会被装载进一个随机偏移的连续内存空间里,只有基址变成了未知,其它都是相同的。
应对方法:Partial Writing
程序的加载以内存页(4K)为单位,基地址后3位(hex)一定为0,同一文件被加载进连续地址中。
一般利用Return Address控制跳转,可通过栈上已有地址,只修改最低3位(2B,4位)值,控制转向
8.4 Canary
Canary(Canary in a coal mine)——金丝雀
一串随机数据,放置在栈数据和控制信息之间,函数开始时被放入,退出前检验,若被修改立即终止程序,极大地防范了栈溢出攻击。
应对方法:Canary Leak
- 覆盖栈到Canary处,利用puts等函数泄露地址
- Canary最低字节为0,防止连带输出
小技巧:scanf("%d");
输入”+”不覆盖内存数据
9. Stack Pivot
假如可供泄露空间过少,或者需要整段可控的栈空间,这就需要使栈帧移向可控栈空间,控制程序执行流转向。