光标
1 | echo -e "\033[?25l" 隐藏光标 |
LibcSearcher
安装
1 | git clone https://github.com/lieanu/LibcSearcher.git |
1 | from LibcSearcher import * |
ret2csu
因为是edi,而binsh在libc里面的地址是0x7f开头的八位,所以不能直接用
1 | def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0): |
64位传参
参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9
栈帧
查看内存命令
用gdb查看内存
格式: x /nfu
说明
x 是 examine 的缩写
n表示要显示的内存单元的个数
f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。
u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节
修改pwngdb或者peda
1 | gedit /.gdbinit |
python函数函数
数字和字符串转换
chr(x ) 将一个整数转换为一个字符
ord(x ) 将一个字符转换为它的整数值
查看变量类型
type()函数
命令
ascii码的字符打印出来 printf “\037”
用字符的ascii码执行程序:printf “\023\342” | ./可执行文件
追踪
ltrace ./可执行文件
gdb使用peda和pwngdb
1 | gedit ~/.gdbinit |
linux初始化root密码
1 | sudo passwd |
ubuntu 安装pip
1 | # 1. 更新系统包 |
生成指定数目字符串
cyclic 100
检测机制
Full RELRO
不能覆盖got表
malloc、realloc、free函数在开始时会查看对应的hook变量是否为空,不为空则调用变量中的地址,寻找malloc_hook、realloc_hook、free_hook
泄漏cannary
Canary设计为以字节”\x00”结尾,本意是为了保证Canary可以截断字符串。
因为存在栈溢出,所以可以覆盖地位的’\x00’,让canary随着前面的数据一起输出。
内存存放情况:
局部变量 |
---|
canary |
ebp |
返回地址 |
参数 |
所以,有canary的栈题。
如果数据是[rbp - n]
- 第一次泄漏填充的长度就是rbp - 0x8(canary)+ 1(覆盖那个‘\x00’)
- 第二次填充的长度rbp - 0x8 + canary
partial write绕过pie
partial write就是利用了PIE技术的缺陷。我们知道,内存是以页载入机制,如果开启PIE保护的话,只能影响到单个内存页,一个内存页大小为0x1000,那么就意味着不管地址怎么变,某一条指令的后三位十六进制数的地址是始终不变的。因此我们可以通过覆盖地址的后几位来可以控制程序的流程。
由于地址的后3位一样,所以覆盖的话至少需要4位,那么倒数第四位就需要爆破,爆破范围在0到0xf
各种bin
fastbin
0x20 - 0x80
后进先出
使用单链表对空闲堆块进行连接
只有bk
单向链表
small bin
0x20 - 0x400(1024)
先进先出
bk和fd
双向链表
larage bin
大于0x400
bk和fd、找到下一个和他大小不同的堆块
根据large bin的大小,用fd_nextsize和bk_nextsize按大小排序连接
利用:实现任意地址写堆地址
unsortedbin
双向链表
chunk被释放后,如果大小不再fastbin内,会先放到unsortedbin中
在申请内存的时候,如果大小不是fastbin大小的内存,并且在smallbin中没有找到合适的chunk,就会从unsortedbin中查找。
堆里用
篡改size域
chunk extend => chunk overlap
篡改prev_size域和prev_in_use域
unlink
attack:
FD = target - 12
BK = target - 8
target = target -12
house of einherjar
类似unlink、后向合并检查不严
篡改fd指针
fastbin attack
就是fd控制,那么它指向的地址(要被认为是一个chunk,可以通过size域的检查)
tcache attack
2.27里面加入的,更快
但是从堆块去内存的时候,没有对size进行检查(不知道地址是否合法)
attack:
篡改fd指针
tcache struct attack
tcache_struct:
- tcache_count;
- tcache_entry;
attack:篡改tcache_entry->任意地址分配
chunk overlap
篡改bk指针
unsorted bin attack
向地址里面写入libc
bk = target - 0x10(64位)
不良影响:unsortedbin被污染,用它分配内存可能有错
libc
在64位下是0x7f开头
1 | libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so") |
如果题目中给出了libc文件,用ida打开,搜索字符串,搜索version
如果题目没有给出libc文件,需要一个个尝试
堆的话试试UAF啥的
要么2.23、要么2.27、2.31就喷他
栈溢出那就leak一下就行了
题目给定
查看版本
1 | strings libc.so | grep GNU |
查看该版本与本地是否相同
1 | diff libc.so /lib/x86../libc-2.27.so |
Binary files libc-2.27.so and /lib/x86_64-linux-gnu/libc-2.27.so differ
表示该libc与本地环境不同,故调试的时候,需要加
1 | io = process(['./bin'],env={"LD_PRELOAD":"./libc-2.23.so"}) |
来加载,其中[‘./bin’]替换为你需要调试的二进制文件名,”./libc-2.23.so”替换成你需要加载的目标libc,这样本地调试就可以通过目标libc进行了。
寄存器参数
%rdi, %rsi, %rdx, %rcx, %r8, %r9
docker
进入容器的几种方法:
1、exec
通过docker ps 查看需要进入的容器pid
执行
docker exec -it 246f35c432de /bin/bash
退出容器,不会关闭容器,一般使用这个方法。
2、attach
通过docker ps 查看需要进入的容器pid
docker attach pid
退出容器会关闭容器,不推荐
pwn出题环境
DynELF
DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,其基本代码框架如下。
1 | p = process('./xxx') |
addr就是可以泄漏内存的地址
比如说write函数和put函数的输出参数
setbuf
由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。
global max fast
global max fast是决定使用fast bin管理的chunk的最大值
使用:
改写global max fast后,处理特定大小的chunk,进而可以在arena往后的任意地址写入一个堆地址
markdown的页内跳转
1 | 1. 先定义一个锚(id) |
exp脚本
开头
1 | #!/usr/bin/python |
elf和libc区别
1 | libc = ELF('./libc-2.23.so') |
got和plt是程序的,也就是ELF(‘./程序’)
symbols是libc的,也就是ELF(‘./libc文件’)
接收字符串
canary
1 | p.recvuntil("\x7f")[-6:] |
show得到的
1 | show() |
sh
system(‘sh’)也可以得到shell
plt表和got表
注意plt表可执行不可写,got表可写不可读:
ptl表的地址:0x400620 - 0x4006C0附近
got表地址在0x602000 - 0x602060
- .got
GOT(Global Offset Table)全局偏移表。这是「链接器」为「外部符号」填充的实际偏移表。
- .plt
PLT(Procedure Linkage Table)程序链接表。它有两个功能,要么在 .got.plt
节中拿到地址,并跳转。要么当 .got.plt
没有所需地址的时,触发「链接器」去找到所需地址
- .got.plt
这个是 GOT 专门为 PLT 专门准备的节。说白了,.got.plt 中的值是 GOT 的一部分。它包含上述 PLT 表所需地址(已经找到的和需要去触发的)
可控内存
- bss段:进程按页分配内存,分配给 bss 段的内存大小至少一个页 (4k,0x1000) 大小。一般 bss 段的内容用不了这么多的空间,并且 bss 段分配的内存页拥有读写权限。
- heap:需要泄漏堆地址
shellcode
1 | #pwntools提供的 |
ljust
前面是大小,后面是填充的字符串
1 | payload.ljust(0x100+0x8, "a") |
调试
~/ctf-challenges/pwn/stackoverflow/stackprivot/X-CTF Quals 2016 - b0verfl0w
更新源和软件
1 | sudo apt-get update #更新源 |
main_arena_offset
使用如下工具:
https://github.com/Coldwave96/LibcOffset
1 | libc_base = main_arena - main_arena_offset |
offset_unsortedbin_main_arena
1 | context.binary = "./babyheap_0ctf_2017"#要指定 |
1 | main_arena = unsortedbin_addr - offset_unsortedbin_main_arena |
allocate
calloc
calloc同malloc类似只是会将申请到的堆块内容清0。所以常规的unsorted bin信息泄露的方式不可行。需要使用堆溢出进行配合
hook
malloc_hook
最常见也是最容易的一种堆利用方法。
malloc函数会首先检查malloc_hook的值,若不为0则会调用他。若我们能通过内存写入malloc_hook即可实现任意地址跳转
通过fastbin_attack攻击malloc_hook。
fastbin在分配时并不检查对齐情况,将fastbin的fd设置为__malloc_hook-0x23,触发fastbin attack分配得到malloc_hook上方内存空间,向malloc_hook进行写入one_gadget得到权限。
realloc_hook
一种很巧妙的利用方法。有些情况下one_gadget因为环境原因全部都不可用,这时可以通过realloc_hook来调整堆栈环境使one_gadget可用。
realloc函数在函数起始会检查realloc_hook的值是否为0,不为0则跳转至realloc_hook指向地址。
realloc_hook同malloc_hook相邻,故可通过fastbin attack一同修改两个值。
free_hook
同malloc_hook类似,在调用free函数时会先检验free_hook的值。
但是free_hook上方都是0字节。不能直接通过fastbin_attack进行攻击,可以通过修改top
free_hook上方,之后申请内存至free_hook修改为system地址。
fastbin数组在top chunk指针上方。可以通过free fastbin chunk修改fastbin数组的值使的fastbin attack可以实现。 存在限制要求堆的地址以0x56开头
https://bbs.pediy.com/thread-246786.htm#msg_header_h1_0
tcache attack
内存申请:
在内存分配的 malloc 函数中有多处,会将内存块移入 tcache 中。
(1)首先,申请的内存块符合 fastbin 大小时并且在 fastbin 内找到可用的空闲块时,会把该 fastbin 链上的其他内存块放入 tcache 中。
(2)其次,申请的内存块符合 smallbin 大小时并且在 smallbin 内找到可用的空闲块时,会把该 smallbin 链上的其他内存块放入 tcache 中。
(3)当在 unsorted bin 链上循环处理时,当找到大小合适的链时,并不直接返回,而是先放到 tcache 中,继续处理。
tcache 取出
在内存申请的开始部分,首先会判断申请大小块,在 tcache 是否存在,如果存在就直接从 tcache 中摘取,否则再使用_int_malloc 分配。
tcache posioning
通过覆盖 tcache 中的 next,不需要伪造任何 chunk 结构即可实现 malloc 到任何地址。
可以看出 tcache posioning
这种方法和 fastbin attack 类似,但因为没有 size 的限制有了更大的利用范围。
tcache dup
可以对同一个 chunk 多次 free
查看ubuntu版本
- cat /proc/version
- uname -a
- lsb_release -a
ropgadget
1 | ROPgadget --binary rop -- ropchain #静态寻找rop链 |