发布时间:2025-12-09 11:50:45 浏览次数:2
本篇原创作者:Rj45
什么是缓冲区溢出?这里我借某台栈溢出靶机里面的第一道题目来解释缓冲区溢出的原理。
可以看到靶机里面有两份权限不同的文件,而我目前拿到的shell是 level0
#include <stdio.h>#include <string.h>#include <unistd.h>int main(int argc, char **argv) { uid_t uid = geteuid(); setresuid(uid, uid, uid); long key = 0x12345678; char buf[32]; strcpy(buf, argv[1]); printf("Buf is: %sn", buf); printf("Key is: 0x%08xn", key); if(key == 0x42424242) { execve("/bin/sh", 0, 0); } else { printf("%sn", "Sorry try again..."); } return 0;}1、审计:可以看到,程序一开始声明并初始化了key=0x12345678, 并声明了32字节的buf, 然后通过strcpy函数将第一个参数拷贝到buf中, 接着判断key是否为0x42424242,如是则获得shell。(levelOne的权限为level1) key已经初始化为0x12345678,如何才能将其修改为0x42424242?
2、溢出:如何将key修改为0x42424242?--通过缓冲区溢出覆盖key值为0x42424242。什么是缓冲区溢出?在样例程序中声明了一段32字节的buf,此为缓冲区, 当通过strcpy函数将输入的函数第一个参数拷贝到缓冲区的时候,由于strcpy函数为危险函数,其不会对操作对象进行任何检查,故在输入数据不超过32个字节时不会发生任何情况, 但当输入的数据超过32个字节的时候,就会发生溢出,也即所谓的缓冲区溢出。
3、覆盖:根据前面我们学习到的内容,一个程序在载入内存后,其栈区会存在函数的各种变量、参数、栈针、返回地址等,并且各种数据是相邻分布的。这就意味着,一个存在缓冲区溢出的程序,在精准控制溢出范围的情况下,可以精准覆盖内存栈区中某些特殊位置的数据。这就为利用构造了条件,也即在本样例程序中的覆盖key值为0x42424242。
4、危险函数:显而易见,在缓冲区溢出的过程中,最关键的就是strcpy函数。那么还有哪些类似strcpy的危险函数呢?
可以看到这些危险函数集中为IO函数。
1、命令
objdump -d levelOne -M intel2、反汇编 下面为样例程序的反汇编情况:
000011e9 <main>: 11e9: 8d 4c 24 04 lea ecx,[esp+0x4] 11ed: 83 e4 f0 and esp,0xfffffff0 11f0: ff 71 fc push DWORD PTR [ecx-0x4] 11f3: 55 push ebp 11f4: 89 e5 mov ebp,esp 11f6: 56 push esi 11f7: 53 push ebx 11f8: 51 push ecx 11f9: 83 ec 3c sub esp,0x3c 11fc: e8 ef fe ff ff call 10f0 <__x86.get_pc_thunk.bx> 1201: 81 c3 ff 2d 00 00 add ebx,0x2dff 1207: 89 ce mov esi,ecx 1209: e8 42 fe ff ff call 1050 <geteuid@plt> 120e: 89 45 e4 mov DWORD PTR [ebp-0x1c],eax 1211: 83 ec 04 sub esp,0x4 1214: ff 75 e4 push DWORD PTR [ebp-0x1c] 1217: ff 75 e4 push DWORD PTR [ebp-0x1c] 121a: ff 75 e4 push DWORD PTR [ebp-0x1c] 121d: e8 0e fe ff ff call 1030 <setresuid@plt> 1222: 83 c4 10 add esp,0x10 //调整栈帧 1225: c7 45 e0 78 56 34 12 mov DWORD PTR [ebp-0x20],0x12345678 //声明并初始化了一个变量,地址为ebp-0x20,数据为0x12345678 122c: 8b 46 04 mov eax,DWORD PTR [esi+0x4] 122f: 83 c0 04 add eax,0x4 1232: 8b 00 mov eax,DWORD PTR [eax] 1234: 83 ec 08 sub esp,0x8 1237: 50 push eax 1238: 8d 45 c0 lea eax,[ebp-0x40] 123b: 50 push eax 123c: e8 1f fe ff ff call 1060 <strcpy@plt> //将输入点数据拷贝到ebp-0x40 1241: 83 c4 10 add esp,0x10 //调整栈帧 1244: 83 ec 08 sub esp,0x8 1247: 8d 45 c0 lea eax,[ebp-0x40] 124a: 50 push eax 124b: 8d 83 08 e0 ff ff lea eax,[ebx-0x1ff8] 1251: 50 push eax 1252: e8 e9 fd ff ff call 1040 <printf@plt> 1257: 83 c4 10 add esp,0x10 125a: 83 ec 08 sub esp,0x8 125d: ff 75 e0 push DWORD PTR [ebp-0x20] 1260: 8d 83 14 e0 ff ff lea eax,[ebx-0x1fec] 1266: 50 push eax 1267: e8 d4 fd ff ff call 1040 <printf@plt> 126c: 83 c4 10 add esp,0x10 126f: 81 7d e0 42 42 42 42 cmp DWORD PTR [ebp-0x20],0x42424242 //判断ebp-0x20处的变量是否为0x42424242 1276: 75 18 jne 1290 <main+0xa7> //如果相同则跳转到后门函数 1278: 83 ec 04 sub esp,0x4 127b: 6a 00 push 0x0 127d: 6a 00 push 0x0 127f: 8d 83 24 e0 ff ff lea eax,[ebx-0x1fdc] //将’/bin/sh’压入栈中,作为函数参数 1285: 50 push eax //将0压入栈中作为函数参数 1286: e8 05 fe ff ff call 1090 <execve@plt> //后门 128b: 83 c4 10 add esp,0x10 128e: eb 12 jmp 12a2 <main+0xb9> 1290: 83 ec 0c sub esp,0xc 1293: 8d 83 2c e0 ff ff lea eax,[ebx-0x1fd4] 1299: 50 push eax 129a: e8 d1 fd ff ff call 1070 <puts@plt> 129f: 83 c4 10 add esp,0x10 12a2: b8 00 00 00 00 mov eax,0x0 12a7: 8d 65 f4 lea esp,[ebp-0xc] 12aa: 59 pop ecx 12ab: 5b pop ebx 12ac: 5e pop esi 12ad: 5d pop ebp 12ae: 8d 61 fc lea esp,[ecx-0x4] 12b1: c3 ret 12b2: 66 90 xchg ax,ax 12b4: 66 90 xchg ax,ax 12b6: 66 90 xchg ax,ax 12b8: 66 90 xchg ax,ax 12ba: 66 90 xchg ax,ax 12bc: 66 90 xchg ax,ax 12be: 66 90 xchg ax,ax栈中的情况如下图:
如此当填充满32个字节的数据后,在填充4个B,即达成利用条件,进入后门函数。