sprintf注入
今天刷到了ctfshow萌新赛的一个题,学到了一个新知识点,在这里整理学习下关于sprintf函数的注入
题目概述
sprintf()函数
定义和语法
sprintf()函数是将格式化的字符串写入到变量中
语法
sprintf(format,arg1,arg2,arg++)
_arg1*、_arg2*、*++* 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 _arg1_,在第二个 % 符号处,插入 _arg2_,依此类推。
注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “$“ 组成
从官网找到了一些函数的详细用法
参数
描述
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 字符串中第一个 % 符号处的参数。
arg2
可选。规定插到 format 字符串中第二个 % 符号处的参数。
arg++
可选。规定插到 format 字符串中第三、四等 % 符号处的参数。
返回值
已格式化的字符串。
占位符
%1$s表示替换的为String类型
%1$d表示替换的为int类型
数字表示替换字符串中要替换的位置,若一个字符串要替换两个int类型,在替换位置分别写%1$d和%2$d.
%s 、%d为缩写方式,只替换一个位置,可以这么写
漏洞成因
当我们在sprintf的format参数里输入%的时候,他的后面如果加了一个\转义符,那%\就会被认为是和%c,%b之类的一种类型,他会把%\当作一种类型去匹配后面arg传来的参数,但是这个类型没有定义,所以规定匹配空,这就可能导致\被吃掉
在本题中:
方法一:
传入?name=admin&pass=%1$’ or 1=1–+
这里的%1$’会首先被addslashes在’前加上转义号\来转义掉’,就会出现%1$\’,这样这里的%1$\就会被当作一种不存在的类型被匹配为空,最后得到:
?name=admin&pass=’ or 1=1–+
形成万能密码绕过登录
方法二:
或者匹配使用%c匹配相应的ascii字符,例如:
如果format中的格式化符为%1$c,则表示第一个参数位置的类型为%c,再来看一下上面的代码,如果我们输入的name为39并且是以%c的形式,那就是相当于是单引号,我这里构造payload: name=39&pass=1%1$c1=1%23 还原一下就相当于输入name=39&pass=1’ 1=1%23
还是能达到万能密码的效果
其他
这道题一开始我的想法是利用宽字节注入
传入传入%df’,可以看到存在宽字节注入成功的回显,从回显的逻辑上来看万能密码注入是成功的,但是并没有显示成功的页面,后来询问了大佬才明白,这道题设置了数据库gbk编码,虽然在前端显示了,但是注入不进数据库,所以这道题的宽字节注入思路行不通