杂七杂八的pwn
栈,即堆栈,是一种具有一定规则的数据结构,它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶。
也就是esp到ebp之间
代码段:存放可执行程序的代码,可读不可写
数据段:存放程序中已经初始化的静态(全局)变量,可读写
bss段:存放程序中未初始化的静态(全局)变量,可读写
堆(heap):存放动态分配的内容,需要程序猿手动分配和释放
栈(stack):存放局部变量,如函数的参数、返回地址、局部变量等,有系统自动分配和释放
要背记的指令
gedit 文件名.py
chomd 777 文件名
remote是远程,process是本地
recvline 或recvuntil #使程序停止到某一行
ROP的命令: ROPgadget –binary 文件名 –sting ‘要找的字符串’
Ropgatget命令
命令: ROPgadget –binary 文件名 –only “pop|ret” | grep rdi
命令: ROPgadget –binary 文件名 –only “pop|ret” | grep rsi
命令: ROPgadget –binary 文件名 –only ‘pop|ret’ | grep ‘eax’ //寻找控制 eax 的 gadgets
命令: ROPgadget –binary 文件名 –only “pop|ret”
命令: ROPgadget –binary 文件名 –only ‘int’ 查找有int 0x80的地址
该工具除了可以用来查找 ret/rdi的地址,还可以用来查找一些字符串的地址
命令: ROPgadget –binary 文件名 –string ‘/bin/sh’
命令: ROPgadget –binary 文件名 –string ‘/sh’
命令: ROPgadget –binary 文件名 –string ‘sh’
命令: ROPgadget –binary 文件名 –string ‘cat flag’
命令: ROPgadget –binary 文件名 –string ‘cat flag.txt’
直接利用程序中的片段拼凑rop链 (必须是静态编译的情况)
命令:ROPgadget –binary 文件名 –ropchain
寻找某一个文件的位置:find -name 文件名
#asm()将接受到的字符串转变为汇编码的机器代码,而shellcraft可以生成asm下的shellcode,如:asm(shellcraft.sh())
查看程序运行情况:context(arch = ‘amd64’, os = ‘linux’,log_level = ‘debug’)(写在脚本中,等于是运用了工具)
关于格式化漏洞的知识点
好了,接下来说一下找到的偏移如何用,构造payload:p32(system_got)+b”%6$s”,此时system的got表地址会存在栈上,
也就是printf的第六个参数位置;而%6$s表示打印出第六个参数作为地址指向的内容,所以此时该payload就会打印出system的真实地址。
再构造payload:p32(system_got)+b”%6$n”,%6$n表示往第六个参数指向的内存中写4个字节宽的内容,而写的数值是print已经打印的
内容长度,printf此时打印的长度是p32(system_got)的长度,也就是4,所以此时该payload就会改写system真实地址为4。
上面的讲解是以32位题目为例,在遇到64位时会有些不同,这里先总结一下。首先64位前6个参数是以寄存器传参,且字长为8,
所以此时寻找偏移的payload应该这样构造:aaaaaaaa-%p-%p-%p-%p-%p-%p。另外由于寄存器传参的原因,
从第6个格式化符号开始才会打印栈上的内容。最后一个重点是,由于字长为8,64的地址存在零,
所以想要利用偏移来泄露地址内容或者改写其内容的话,目的地址不能放在格式化符号之前,否则printf在遇到零字节时会被截断,
此时应将目的地址放在格式化符号后面。
参数在栈上的地址=ebp-偏移量
本地的libc查找就是ldd +文件名
堆相关的知识:
add,就是简单的创建一个chunk
在创建堆时有一个结构体,这个结构体大概是这样的:
struct pr_heap
{
double alloc_or_not; #0或者1,表示是否分配(0表示没有分配,1表示分配)
double size; #创建chunk的大小
void *heap; #chunk的内存地址
};
寄存器用途约定
(1)64位汇编传参,当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。 > 当参数为7个以上时,
前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。
(2)64位有16个寄存器,32位只有8个。但是32位前8个都有不同的命名,分别是e _ ,而64位前8个使用了r代替e,也就是r _。
e开头的寄存器命名依然可以直接运用于相应寄存器的低32位。而剩下的寄存器名则是从r8 - r15,其低位分别用d,w,b指定长度。
(3)32位使用栈帧来作为传递的参数的保存位置,而64位使用寄存器,分别用rdi,rsi,rdx,rcx,r8,r9作为第1-6个参数。rax作为返回值
(4)64位没有栈帧的指针,32位用ebp作为栈帧指针,64位取消了这个设定,rbp作为通用寄存器使用
(5)64位支持一些形式的以PC相关的寻址,而32位只有在jmp的时候才会用到这种寻址方式。
用于存储传入参数:rdi,rsi,rdx,rcx,r8,r9 用作函数参数,依次对应第1参数,第2参数…
用于存放返回结果:%rax。
用于数据存储:rbx,rbp,r12,r13,14,15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
r10,r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
系统调用的知识
系统调用号,即 e/rax 应该为 0xb
第一个参数,即 e/rbx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 e/rcx 应该为 0
第三个参数,即 e/rdx 应该为 0
调用系统函数时会通过内联汇编代码插入int 0x80的中断指令,(不仅会插入中断指令,还会将系统调用编号设置给 %eax 寄存器)
#pop rbx,rbp,r12,r13,r14,r15
#rbx should be 0,
#rbp should be 1,enable not to jump
#r12 should be the function we want to call
#rdi=edi=r15d
#rsi=r14
#rdx=r13
中级rop利用了在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,但是大多数时候,我们很难找到每一个寄存器对应的 gadgets。
这时候,我们可以利用 x64 下的 __libc_csu_init 中的 gadgets。
重指向:
exec 1>&0(输出重指向),exec 0<&0(输入重指向)
代替libcseacher的网站:
libc.blukat.me
随机数的知识点:
libc库一样,种子一样,生成的随机数一样
canary:
- Canary值在rbp到rsp之间(并不一定是rbp-8的位置)
- Canary值以0x00结尾,如果程序没有漏洞但栈上面刚好是一个满的字符串,这个0x00可以当做截断,避免被打印出来
- Canary值如果被改写,程序会崩溃
- 一般为4/8位
ubuntu18后system要进行栈对齐
需要加个ret平衡栈,用来进行栈对齐,栈平衡问题,ubuntu18之后system会出现这个问题,有题目可知,这道题就部署在ubuntu18上
flag可以藏于环境变量中(env可以查看所有环境变量)
one_gadget的使用:
one_gadget libc的版本
bytes.fromhex ():
Python 中使用 bytes.fromhex () 把十六进制转换为字节的函数
函数fini_array:
程序执行返回后会执行fini_array中内容
patchelf的使用:
patchelf –replace-needed libc.so.6 ./libc.so.6 ./pwn