今天刷到了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编码,虽然在前端显示了,但是注入不进数据库,所以这道题的宽字节注入思路行不通