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了,好难的萌新赛