sprintf漏洞
sprintf
函数
函数功能
sprintf
是 PHP 中一个格式化字符串的函数,功能是根据指定的格式化字符串,将一系列参数转换并格式化为一个新的字符串
函数语法
1 |
|
format
:是必需参数,规定字符串以及如何格式化其中的变量,如果format
字符串中不包含任何占位符(如%s
、%d
等),那么可以不需要额外的arg
参数arg1, arg2,...
:可选参数,要被插入到format
字符串中指定位置的参数,可以有多个
参数 | 描述 |
---|---|
format | 必需。规定字符串以及如何格式化其中的变量。可能的格式值: %% - 返回一个百分号 % %b - 二进制数 %c - ASCII 值对应的字符 %d - 包含正负号的十进制数(负数、0、正数) %e - 使用小写的科学计数法(例如 1.2e+2) %E - 使用大写的科学计数法(例如 1.2E+2) %u - 不包含正负号的十进制数(大于等于 0) %f - 浮点数(本地设置) %F - 浮点数(非本地设置) %g - 较短的 %e 和 %f %G - 较短的 %E 和 %f %o - 八进制数 %s - 字符串 %x - 十六进制数(小写字母) %X - 十六进制数(大写字母) 附加的格式值。必需放置在 % 和字母之间(例如 %.2f): + (在数字前面加上 + 或 - 来定义数字的正负性。默认情况下,只有负数才做标记,正数不做标记) ‘ (规定使用什么作为填充,默认是空格。它必须与宽度指定器一起使用。例如:%’x20s(使用 “x” 作为填充)) - (左调整变量值) [0-9] (规定变量值的最小宽度) .[0-9] (规定小数位数或最大字符串长度) 注释:如果使用多个上述的格式值,它们必须按照以上顺序使用 |
arg1 | 如果format 中有占位符(如 %s 、%d 等),这个参数是必需的,规定插到 format 字符串中第一个 % 符号处的参数 |
arg2 | 可选,规定插到 format 字符串中第二个 % 符号处的参数 |
常见用法
- 格式化数字
1 |
|
- 拼接字符串和变量
1 |
|
- 格式化日期
1 |
|
sprintf
函数存在的漏洞
占位符漏洞
原理
格式化字符串函数在处理占位符时,会按照特定规则将传入的参数替换到占位符所在的位置,当用户能够控制格式化字符串(包含占位符)或部分参数时,就可能出现安全问题。
攻击者可以利用特殊构造的格式化字符串来读取内存中的敏感信息、修改内存数据、甚至执行任意代码。
%1$
%1$
是格式化字符串中的一种高级占位符用法,在格式化字符串中,%
通常是占位符的起始符号。%1$
中的 1
代表参数列表中的第 1 个参数(编号从 1 开始),$
是分隔符,它的作用是明确指定要使用的参数序号。借助这种方式,你能够精确控制在格式化字符串中使用哪个参数进行替换,还可以多次使用同一个参数。
1 |
|
arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “$” 组成
1 |
|
%2\$s
表示将第 2 个参数(即 "b"
)以字符串类型插入,%1\$s
表示将第 1 个参数(即 "a"
)以字符串类型插入。所以最终输出 a=b, b=a
。 其中 \
是转义字符,用来转义 $
,防止它被 shell 等环境提前解析 ,确保 sprintf
函数能正确识别占位符。 而 %2$s
里的 2
指明用第 2 个参数,s
表明参数类型为字符串。
sprintf
底层实现代码
1 |
|
通过底层实现代码可以发现,sprintf()
方法就是对15种类型做了匹配,15种类型以外的就直接break了没有做任何处理,所以就会导致一个问题:
如果我们输入%
\或者%1$\
,他会把反斜杠当做格式化字符的类型,然而找不到匹配的项那么%\
,%1$\
就因为没有经过任何处理而被替换为空。
因此sprintf
注入的原理就是用一个15种类型之外的\
来代替格式字符类型让函数替换为空,则%1$\'
后面的单引号就能闭合前面的单引号。
此外,还要注意**函数addslashes()
**作用是返回在预定义字符之前添加反斜杠的字符串。预定义字符是单引号'
、双引号"
、反斜杠\
、NULL
。比如下面的代码,这样就造成了得结果是我们无法在注入的过程当中使用单引号(’)
题目
题目来源
NSSCTF–git泄露
这是这题的后半部分的考点,源码如下
1 |
|
审计代码,这段代码通过GET
请求的name
和pass
参数,构造一条SQL
查询语句,从 user
表中筛选出符合特定条件的记录。
payload
如下
1 |
|
addslashes()
将’转义成' 则passpass=123%1$'
最终构造的SQL语句如下
1 |
|
可以看到跳转到wjbh.php
这个页面了,虽然表面上看着是Not Found
,但这实际是出题人写来混淆的,抓包可以知道状态码是200,查看源码可以看到flag
的位置在/flag
但是访问后却发现又回到最初的界面了
那就去抓包看看
发现cookie
请求头中有一个参数file
,对其值进行 Hex 解码
解出flag.txt
,访问又是什么都没有,再继续抓包
没什么区别,看到之前的cookie
参数file
,想到是不是用来读取flag.txt
这个文件的,但是没有读到,要是给它加上伪协议读取呢
还是不行
想到之前的提示在/flag
文件中,那就读取这个文件
1 |
|
1 |
|
这就出flag了,好难的萌新赛