在我们学习c语言的时候我们就知道在输出或者输入的时候需要使用%s%d等等格式化字符,此处不过多介绍,详情可以去看看c语言的基础知识。
此处放出一些常见的格式化字符串函数。
这里拿printf格式化字符举例,在glibc库中它的相关代码如下:
可以看出它从输出流种会将输出的内容按照我们设置的format进行格式化输出。
这道题目我是用64位进行编译的,我们审计代码得知,题目告诉你a的地址,只要我们通过格式化字符串漏洞修改a的值为32就可以getshell,我们也知道可以通过%x$n+p64(a_addr)修改值,那么我们该如何计算这个偏移x呢?
此处我们也可以通过pwndbg自带的fmtarg进行计算
首先我们在printf的地方打下断点
然后c运行后在输入出随便输入字符aaaa
随后停在因为之前打了断点,在printf出停下,发现aaaa返回地址在0x7fffffffdb90 输入fmtarg 0x7fffffffdb90 即可计算出偏移为8
我们也可以通过上面的两张图方法计算出偏移
附上编译好的bin程序和exp
链接:https://pan.baidu.com/s/11VvBozTXEZKs3ownh4grqg <br />提取码:hjtp
1.
2.
int
printf(const char
*
format
, ...);
3.
int
fprintf(
FILE
stream, const char
format
, ...);
4.
int
dprintf(
int
fd, const char
*
format
, ...);
5.
int
sprintf(char
str
, const char
format
, ...);
6.
int
snprintf(char
str
, size_t size, const char
format
, ...);
1.
2.
int
printf(const char
*
format
, ...);
3.
int
fprintf(
FILE
stream, const char
format
, ...);
4.
int
dprintf(
int
fd, const char
*
format
, ...);
5.
int
sprintf(char
str
, const char
format
, ...);
6.
int
snprintf(char
str
, size_t size, const char
format
, ...);
字符 |
类型 |
使用 |
d |
4-byte |
整形 |
u |
4-byte |
无符号整形 |
x |
4-byte |
十六进制 |
s |
4-byte ptr |
字符串 |
c |
1-byte |
字符 |
f |
8-byte |
浮点数 |
n |
?-byte |
将%n之前已经打印字符个数赋值给偏移处指针所指向的地址位置 |
字符 |
类型 |
使用 |
hh |
1-byte |
字符char |
h |
2-byte |
短整数short int |
l |
4-byte |
长整型 |
ll |
8-byte |
长长整型 |
void mian(){
char
*
format
=
"%s"
;
char
*
arg1
=
"Hello!I‘m ReStr0!"
;
printf(
format
,arg1);
}
此处是格式化字符串的使用方式
void mian(){
char
*
format
=
"%s"
;
char
*
arg1
=
"Hello!I‘m ReStr0!"
;
printf(
format
,arg1);
}
此处是格式化字符串的使用方式
当我们运行它时
printf(
"%03d.%03d.%03d.%03d"
,
127
,
0
,
0
,
1
);
/
/
"127.000.000.001"
2.
printf(
"%.2f"
,
1.2345
);
/
/
1.23
3.
printf(
"%#010x"
,
3735928559
);
/
/
0xdeadbeef
5.
printf(
"%s%n"
,
"01234"
, &n);
/
/
n
=
5
当我们运行它时
printf(
"%03d.%03d.%03d.%03d"
,
127
,
0
,
0
,
1
);
/
/
"127.000.000.001"
2.
printf(
"%.2f"
,
1.2345
);
/
/
1.23
3.
printf(
"%#010x"
,
3735928559
);
/
/
0xdeadbeef
5.
printf(
"%s%n"
,
"01234"
, &n);
/
/
n
=
5
/
*
*
*
我们在正常的对格式化字符输出时大都使用printf(
*
format
,
*
arg);
此种形式进行输出,但是部分程序员在开发的使用,为了省事使用了,printf(
*
format
);进行输出
为了方便对比,我将在下面贴出正常和存在格式化字符漏洞的写法。
*
*
*
/
错误:
void main(){
char
str
[
1024
];
scanf(
%
s,&
str
);
printf(
%
s);
}
正确:
void main(){
char
str
[
1024
];
scanf(
%
s,&
str
);
printf(
%
s,
str
);
}
/
/
但是如果我们正常输入字符的情况下,此时两个都是可以正常输出我们需要的字符串,但是当我们将
%
x作为arg键入后,
/
/
错误的代码会将此处的地址打印出来,通过
%
n操作符我们可以修改指定地址的数据以达到劫持程序流的目的。
/
/
而且此时因为数据长的很长,我们可以输入很多的格式化字符,来泄露我们需要的地址或者其他信息(canary等)。
/
/
最常见的就是通过格式化字符串漏洞泄露libc进行计算基址,泄露canary 进行bypass或者通过格式化字符串漏洞进行对got表地址某几位的改写。
/
*
*
*
我们在正常的对格式化字符输出时大都使用printf(
*
format
,
*
arg);
此种形式进行输出,但是部分程序员在开发的使用,为了省事使用了,printf(
*
format
);进行输出
为了方便对比,我将在下面贴出正常和存在格式化字符漏洞的写法。
*
*
*
/
错误:
void main(){
char
str
[
1024
];
scanf(
%
s,&
str
);
printf(
%
s);
}
正确:
void main(){
char
str
[
1024
];
scanf(
%
s,&
str
);
printf(
%
s,
str
);
}
/
/
但是如果我们正常输入字符的情况下,此时两个都是可以正常输出我们需要的字符串,但是当我们将
%
x作为arg键入后,
/
/
错误的代码会将此处的地址打印出来,通过
%
n操作符我们可以修改指定地址的数据以达到劫持程序流的目的。
/
/
而且此时因为数据长的很长,我们可以输入很多的格式化字符,来泄露我们需要的地址或者其他信息(canary等)。
/
/
最常见的就是通过格式化字符串漏洞泄露libc进行计算基址,泄露canary 进行bypass或者通过格式化字符串漏洞进行对got表地址某几位的改写。
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
a;
/
/
[rsp
+
Ch] [rbp
-
74h
] BYREF
char
str
[
100
];
/
/
[rsp
+
10h
] [rbp
-
70h
] BYREF
memset(
str
,
0
, sizeof(
str
));
a
=
16
;
printf(
"ReStr0 tell you %p\n"
, &a);
__isoc99_scanf(
"%s"
,
str
);
printf(
str
);
if
( a
=
=
32
)
{
puts(
"success"
);
system(
"/bin/sh"
);
}
else
{
puts(
"failure"
);
}
return
0
;
}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
a;
/
/
[rsp
+
Ch] [rbp
-
74h
] BYREF
char
str
[
100
];
/
/
[rsp
+
10h
] [rbp
-
70h
] BYREF
memset(
str
,
0
, sizeof(
str
));
a
=
16
;
printf(
"ReStr0 tell you %p\n"
, &a);
__isoc99_scanf(
"%s"
,
str
);
printf(
str
);
if
( a
=
=
32
)
{
puts(
"success"
);
system(
"/bin/sh"
);
}
else
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课