Win 2000中格式化字符的安全问题
即使只有一点C语言基础的人也会printf()函数,实际上C语言教科书上通常的第一个程序就是“Hello, World!”,Kernighan and Ritchie在《The C Programming Language》中引发的惯例。
#include <stdio.h> void main(void) { printf("\nHello,World!\n\n"); } 这并没有完,在C语言中,当编译并运行这个程序向屏幕打印“Hello, World!”并不是简单的向屏幕输出字符串。和相关的程序fprintf(),vprintf() 以及 sprintf()等一样,就想在print后面加上“f”,这些实际上是打印格式。格式化部分允许程序员控制显示文本的样式。可以通过代替特殊的格式字符来显示值或数据,比如,要显示整型的变量“dVal”的值,就可以使用下面的格式化字符: printf(“The value is %d”,dVal); 打印的时候,%d就被dVal的值所代替。如果程序员想用十六进制显示同样值: printf(“The value in decimal is %d and in hexadecimal is %x”,dVal,dVal); 这里%d表示十进制的dVal值,%x表示十六进制的dVal的值。下面是集中特殊的格式化字符: %c 单字符格式 %d 十进制整型 (pre ANSI) %e,%E 指数形式的 float or double %f 十进制 float or double %I 整型 (like %d) %o 八进制整型 %p 地址指针 %s 字符串 %x,%X 十六进制整型 当然,功能不仅限于怎么控制显示的数据类型,而且也能控制显示的宽度和队列等。 一个格式字符%n没有列在上面,因为有特殊用途,但是它存在的格式化字符安全问题也非常严重。%n用于把前面打印的字符数记录到一个变量中。也用于统计格式化的字节数,这当然需要一个空间来存储这个数字,因此程序需要为此分配内存,例如下面的代码: 1. #include <stdio.h> 2. int main() 3. { 4. int bytes_formatted=0; 5. char buffer[28]=”ABCDEFGHIJKLMNOPQRSTUVWXYZ”; 6. printf(“%.20x%n”,buffer,&bytes_formatted); 7. printf(“\nThe number of bytes formatted in the previous printf statement was %d\n”,bytes_formatted); 8. return 0; 9. } 编译后输出显示为: 0000000000000012ff64 The number of bytes formatted in the previous printf statement was 20 在第四行申明了一个int类型的变量bytes_formatted,在第六行,格式化字符表示20个字符应该按十六进制 (“%.20x”) 进行格式化,%n则把值20写到bytes_formatted变量中。这意味着已经写了一个值到另外的内存空间中。现在我们不讨论编译者写数值或者写地址的影响,而讨论那种通过通过某种方式在操作这些值的时候造成了缺陷(溢出),如果这样成功的话,可能获得超过程序的执行控制。 在程序员试图传递一个字符串到一个使用格式化字符的格式函数中,就可能发生溢出情况。参考下面的程序。 #include <stdio.h> void main(int argc, char *argv[]) { int count = 1; while(argc > 1) { printf(argv[count]); printf(“ “); count ++; argc --; } } 编译并运行后显示如下程序: Prompt: myecho hello hello Prompt: myecho this is some text this is some text So it justs spits back what we feed in – or does it? Try: Prompt: myecho %x%x 112ffc0 注意到myecho %x%x,并没有按照原本的意思打印出来,却显示的十六进制数?原因正是因为这些属于格式化字符,它们被传递给printf()函数却没有用函数来解释这些字符,被认为是格式化字符。安全的写法应该是 printf(“%s”,argv[count]); 而不是: printf(argv[count]); 一个攻击者能够怎么利用呢?他们使用 “%n”格式化字符,能写任意值到他们选定的内存中!如果实现了,就能够控制程序的执行。例如,在Intel上,能就可以重写堆栈中的地址,并指向他们的攻击代码,这可以执行任意目的的程序。这种格式化字符漏洞利用起来需要考虑使用函数、操作系统和处理器类型。 Windows 2000 / Intel 下的格式化字符漏洞问题 考虑下面有漏洞的代码: #include <stdio.h> int main(int argc, char *argv[]) { char buffer[512]=""; strncpy(buffer,argv[1],500); printf(buffer); return 0; } 这个程序拷贝第一个参数到一个缓冲区,然后简单地把缓冲区传递给 printf, 有问题的代码是这一行: printf(buffer); 因为可以提供一个格式化字符作为第一个参数,而被传递给 printf() ,假设这个程序编译后叫 printf.exe。 攻击者现在会试图用提供的地址来重写堆栈中函数的返回地址,提供的地址可以指向攻击代码(shell code)。要达到这样的目的,需要得到格式化打印的确切字节数,用来匹配需要用的地址。 例:如果攻击者的攻击代码在地址0x0012FF40处,那么,就要让 printf 表达式格式0x0012FF40个字节,格式化字符串就可以是: c:\>printf %.622496x%.622496x%n 这就让1244992字节被printf表达式格式化打印,这个数字的十六进制就是0x0012FF40。但是目前并不完善,攻击者需要把exploit代码也放进去,这需要占据字节数。因此,要产生shell ,在windows 2000中这至多需要 40字节的 exploit code ,因此,需要修改格式化字符串放入代码就需要从622496中减去40。 就变成: c:\>printf AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%.622496x%.622456x%n 在这个例子中,攻击者只是简单地用字符“A”替代攻击代码。现在可以运行它但是可能发生非法存取问题,因为程序试图写的地址0x41414141可能没有初始化。当这个问题出现的时候调试程序,正如看到的,不愉快的一行是: 上一篇: Windows 2000 公钥基础结构详解 下一篇: 修改注册表加强Win 2000安全 更多相关文章
|
推荐文章
精彩文章
|