基于华为开发者空间云主机的软件安全栈溢出攻击实践

B站影视 电影资讯 2025-10-27 15:10 2

摘要:本案例是在华为云主机(ARM+Linux(ubuntu 24.04 server定制版))下,进行软件安全的简单的经典栈溢出攻击实践。使用c语言编写简单具备栈溢出漏洞的代码,通过gcc携带特殊的编译原型,将源码编译链接成可执行文件,利用gdb调试工具,查找漏洞

本案例由开发者:华为2024年第三批次协同育人项目-南航金城学院-孙福清教师提供

最新案例动态,请查阅《【案例共创】基于华为开发者空间云主机的软件安全栈溢出攻击实践》「链接」。小伙伴快来领取华为开发者空间进行实操吧!

本案例是在华为云主机(ARM+Linux(ubuntu 24.04 server定制版))下,进行软件安全的简单的经典栈溢出攻击实践。使用c语言编写简单具备栈溢出漏洞的代码,通过gcc携带特殊的编译原型,将源码编译链接成可执行文件,利用gdb调试工具,查找漏洞点,从而成功执行栈溢出,将函数内部变量溢出为自己的学号。

通过本实践,使得学生建立软件安全意识,在以后的软件编程学习和工作中,重视安全编程实践,编写出更加安全的代码,减少潜在的软件安全漏洞。

说明:

登录华为开发者空间工作台,领取云主机。安装验证代码编辑器gedit。安装验证十六进制文件编辑器ghex。安装验证c代码编译gcc和调试gdb工具。编译简单栈溢出漏洞代码。调试简单栈溢出漏洞程序。栈溢出漏洞攻击实践。

本案例预计花费0元。

资源名称规格单价(元)时长(分钟)

面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。

如果还没有领取云主机进入华为开发者空间工作台界面后点击配置云主机,选择Ubuntu操作系统。

在华为开发者空间工作台界面,点击打开云主机 > 进入桌面连接云主机。

本案例需要使用文本编辑软件来进行:c语言代码的查看、编辑、修改。

Linux操作系统自带的文本编辑工具vim,对于初步接触linux操作系统的新手来说,难于适应这种命令行的使用方式。本案例使用新手友好熟练的鼠标使用方式的文本编辑器,推荐使用gedit:

gedit是一个GNOME桌面环境下兼容UTF-8的文本编辑器。它简单易用,支持良好的语法高亮,对中文支持很好,包括支持gb2312、gbk在内的多种字符编码。它还是一个自由软件,每天被数百万使用GNOME的Ubuntu、Fedora和其他Linux发行版的用户所使用。gedit因其强大的功能和灵活性,在Linux社区中广受欢迎,是许多开发者和用户首选的文本编辑器之一。

华为开发者空间 - 云主机已经预装gedit,我们可以在Terminal终端窗口中通过如下命令打开代码编辑器gedit:

gedit

若环境中未安装,可以在Terminal终端窗口中执行命令,进行安装:

sudo apt install gedit

使用gedit新建代码文件,编辑和保存。

操作步骤

通过mkdir命令创建stackoverflow目录,通过cd命令进入该目录,通过ls命令查看当前文件目录为空文件;通过gedit命令创建hello.c文件,并编辑一段hello world代码,点击gedit编辑器右上角的保存按钮;打开一个新的Terminal终端窗口,通过ls命令查看当前目录中已经新增了hello.c文件;完成上述操作后,使用gedit + 文件名命令可以再次查看已有的编辑保存的代码文件。mkdir stackoverflowcd stackoverflowlsgedit hello.c

代码:

#includeint main{ printf("hello world\n"); return 0;}

GHex 是一款为GNOME桌面环境设计的图形化十六进制编辑器,允许用户直接查看和编辑二进制文件的原始字节数据。它支持多种文件格式,能够以十六进制和 ASCII 格式显示文件内容,并提供多级撤销/重做功能。

GHex是一个简单易用且功能强大、图形化的工具,对于学习和实践软件安全技术的新手非常友好。

在华为开发者空间 - 云主机中未预装GHex,需要单独安装。安装时需要root权限安装,具体命令如下:

ghexsudo apt install ghex

安装过程中根据提示输入y完成安装。

使用gedit新建十六进制文件,编辑和保存。

操作步骤

通过ghex命令打开编辑器ghex。在ghex编辑器右上角点击菜单选择new,创建一个新的十六进制文件。编写输入一段代码,测试转化成十六进制内容。在ghex编辑器右上角点击菜单选择Save As,保存文件。完成上述操作后,使用ghex + 文件名命令可以再次查看已有的编辑保存的代码文件。ghexghex + 文件名

GCC(GNU Compiler Collection)是一个开源免费的编译器工具集合,主要用于编译多种编程语言的代码,最初是C语言编译器,后来支持C++等其他编程语言。GCC提供了丰富的编译选项,适应不同的需求。

GCC是Linux开发中不可或缺的工具,掌握它的使用方法能够帮助你更高效地进行程序开发和调试。

在华为开发者空间 - 云主机中已经预装了gcc 13.3.0版本;如果没有,可以通过如下命令安装:

gcc --versionsudo apt install gcc

操作步骤

通过gcc命令执行编译步骤“2.2 使用gedit”中创建的hello.c文件,将该文件编译成可执行文件hello.elf。通过./hello.elf命令运行编译好的hello.elf文件,从返回的日志中我们可以看出程序运行成功。gcc hello.c -o hello.elf./hello.elf

GDB(GNU Debugger)是Linux系统中一个功能强大、开源的调试工具,主要用于调试C、C++等语言编写的程序,可以查看程序运行时的变量值、执行单步调试、设置断点、检查堆栈信息,掌握它的基本用法可以帮助开发者快速定位和修复程序中的问题。通过设置断点、单步执行、查看变量和堆栈信息等功能,可以深入了解程序的运行状态。

在华为开发者空间 - 云主机中未预装gdb,需要单独安装。安装时需要root权限安装,具体命令如下:

gdbsudo apt install gdb

操作步骤

通过gcc + 调试程序命令进入调试。在调试程序时,要求gcc执行编译时在后面添加-g调试选项,被编译后的*.elf文件才能被读取其中的调试符号。如下图日志所示“No debugging symbols found in hello.elf”。通过gcc -g重新执行hello.c文件编译命令。生成新的编译后的文件hello.elf。再次执行gcc + 调试程序命令,日志输出“Reading symbols from hello.elf...”,进入程序调试模式。通过quit或exit命令退出程序调试模式。gcc -g hello.c -o hello.elfgdb hello.elf exit / quit

栈溢出漏洞通常发生在程序使用不安全的函数(如strcpy、strcat等)处理用户输入时,未对输入长度进行严格限制,导致超出缓冲区的数据覆盖了栈中的返回地址。攻击者可以通过精心构造输入数据,将返回地址修改为指向恶意代码(如Shellcode)的地址,从而实现任意代码执行。

栈溢出漏洞示例代码:

#includeint main { int nModified; char buffer[200]; //学生学号后2位尾号 xx * 4,例如0x24030850,50 * 4 = 200 nModified = 0; memset(buffer,0,sizeof(buffer)); printf("please input ...\n"); gets(buffer); printf("nModified=0xx\n",nModified); if(0x24030850 == nModified) //学生学号,栈溢出要将栈变量溢出复位为学号 { printf("congratulations,success to stack-overflow variable-nModified\n"); } else { printf("you fail to stack-overflow variable-nModified\n"); } return 0;}

示例代码中的代码语句 gets(buffer) 为不安全的函数,没有来判断输入内容长度超出buffer长度的溢出问题,会引入栈溢出漏洞。

对示例代码需要使用特殊的编译选项进行编译:

gcc [源代码文件名].c -g -w -fno-stack-protector -o [可执行文件名].elf

gcc -g:gcc -g是GCC编译器的一个选项,用于在编译时生成调试信息。这些调试信息会被嵌入到生成的可执行文件中,使得调试器(如GDB)能够提供更详细的调试支持,包括变量名、源代码行号、函数名等信息。用法示例:

gcc 1.c -g -o 1.elf

gcc -w:gcc -g编译器的一个选项,用于关闭所有警告信息。当你在编译代码时使用 -w 选项,GCC 会忽略所有警告,只输出错误信息。这意味着即使代码中存在潜在问题(如未初始化的变量、类型不匹配等),编译器也不会提醒。

本案例中使用是去除掉编译时候输出的很多信息,避免给大家带来困惑。实际工作中不建议使用。用法示例:

gcc 1.c -w -o 1.elf

gcc -fno-stack-protector: 是 GCC 编译器的一个选项,用于禁用堆栈保护功能(Stack Smashing Protector,SSP)。

堆栈保护是一种安全机制,用于检测和防止堆栈缓冲区溢出攻击,是一种编译器级别的安全特性,用于检测堆栈缓冲区溢出。它通过在堆栈帧中插入一个“保护值”(canary value),并在函数返回时检查该值是否被篡改。如果保护值被篡改,程序会检测到堆栈溢出并终止运行,从而防止潜在的安全漏洞。

-fno-stack-protector选项用于禁用堆栈保护功能。这意味着编译器不会在生成的代码中插入堆栈保护机制。禁用堆栈保护功能,这可能会提高程序的性能,但会降低安全性。在大多数情况下,建议保持堆栈保护启用,以防止潜在的安全漏洞。用法示例:

gcc 1.c -fno-stack-protector -o 1.elf

gcc -o: 是 GCC 编译器的一个常用选项,用于指定输出文件的名称。默认情况下,GCC 会将编译生成的可执行文件命名为 a.out(在 Linux 和 Unix 系统中),但通过 -o 选项,你可以自定义输出文件的名称,此处将可执行文件名的后缀名命名成elf,标识这个文件为linux的可执行程序文件。用法示例:

执行编译

操作步骤

通过gedit overflow.c命令创建overflow.c文件;进入gedit代码编辑器,将章节“1.1 C栈溢出漏洞代码”中的代码复制到overflow.c文件;点击gedit代码编辑器右上角的保存按钮,将代码保存到overflow.c代码文件;打开一个新的Terminal终端窗口,通过cd stackoverflow/进入到stackoverflow目录,通过ls命令查看当前目录中已新增overflow.c代码文件;通过gcc overflow.c -g -w -fno-stack-protector -o overflow.elf命令使用特殊编译选项,编译栈溢出漏洞代码,禁用栈溢出保护,将overflow.c代码文件,编译成overflow.elf可执行文件。再次使用ls命令查看,stackoverflow目录中已新增overflow.elf可执行文件,程序编译成功。

命令:

gedit overflow.ccd stackoverflow/lsgcc overflow.c -g -w -fno-stack-protector -o overflow.elf

注:在执行编译步骤时,日志输出“/home/developer/stackoverflow/overflow.c:9:(.text+0x2c): 警告: the `gets' function is dangerous and should not be used.”。此处已关闭其他编译输出信息,只输出最危险的告警信息。

在 Linux 系统中,通过命令行执行程序有多种方式,具体取决于程序的类型、位置以及是否需要指定路径,最常见的集中方法:

直接执行可执行文件

如果程序是一个可执行文件(如 ELF 格式的二进制文件),并且它位于环境变量 $PATH 中的某个目录下,可以直接输入程序名来执行.

例如:ls 是一个常见的 Linux 命令,它位于 /bin/ls 或 /usr/bin/ls 等 $PATH 中的目录下。直接输入 ls,系统会自动找到并执行。

ls -l使用相对路径或绝对路径执行可执行文件

如果程序不在 $PATH 中,或者你想明确指定路径,可以使用相对路径或绝对路径来执行程序,例如:

./hello.elf

./ 是一个常见的路径表示方式,用于指代当前目录。当 ./ 出现在命令行中时,它通常用于执行当前目录下的某个文件,告诉系统:在当前目录下找到名为 program 的文件并执行它。

如果直接输入文件名(不带路径),例如ls命令,系统会按照环境变量 $PATH 中定义的路径顺序查找可执行文件。使用 ./ 可以明确指定文件位于当前目录,避免与 $PATH 中的其他同名文件混淆。

找到文件后,还需要判断文件是否为可执行文件elf,当前用户是否具备执行权限等等。

直接执行脚本文件

如果程序是一个脚本文件(如 Bash 脚本、Python 脚本等),满足:确保脚本文件具有执行权限(chmod +x script.py),脚本文件第一行指定了正确的解释器,那么就可以直接类似./script.sh 或者./hello.py方式执行脚本。

通过解释器执行脚本文件

如果脚本文件没有执行权限,或者不想修改脚本的权限,可以直接通过解释器执行脚本,例如:bash script.sh 或者 python3 hello.py

执行编译出来的栈溢出漏洞的代码程序

输入内容不够,程序提示栈溢出失败,未能正确将栈变量溢出为指定的学号。

设置断点break有多种方式:

在函数入口处设置断点:break 函数名,如:break main在某个文件的特定行设置断点:break 文件名:行号,如 break 1.c:20

本案例就需要简单的设置main函数入口即可。

gdb调试命令:运行run设置断点后,调试命令run运行调试的程序停留在设置断点处。
gdb调试命令:执行到下一行代码next一直执行next命令,运行到下一行代码语句,执行到gets(buffer);时,根据gets要求键盘输入,输入后继续执行到下一行代码语句。gdb调试命令:查看变量、内存等在 GDB 中,print 命令用于查看变量、表达式或内存地址的值。它是调试过程中最常用的命令之一,可以帮助你检查程序的状态、变量的值以及内存内容。print nModified # 以默认的十进制方式查看变值 print /x nModified # 以十六进制方式查看变量值 print &nModified, print &buffer # 查看变量的内存地址
gdb调试命令:设置变量值

在GDB中,你可以使用 set 命令来修改变量的值。这在调试过程中非常有用,例如当你需要测试不同的输入条件、修复变量的错误值或验证程序的行为时。

用法:

set=set nModified = 0x24030850

:要修改的变量名。

:新的值,可以是常量、表达式或其他变量的值

gdb调试命令:继续执行continue在 GDB 中,continue 命令用于从当前暂停点继续程序的执行,直到遇到下一个断点或程序结束。

栈溢出原理

栈攻击信息

获取函数栈内变量buffer以及nModified的内存地址,就能精心设计输入内容。

在使用 GDB(GNU Debugger)调试程序时,可以通过以下命令查看变量的地址:

查看全局变量或静态变量的地址 ,info address 这将显示变量的地址。查看局部变量的地址 对于局部变量,GDB 提供了 print 命令的扩展功能,可以使用 & 操作符来获取变量的地址,print & 这将输出变量的地址。

gdb调试获取栈内变量地址:无源码只有可执行程序时,只能通过gdb调试获取

print获取函数栈内变量的内存地址(示例):

print /x &buffer$1 = 0xffffffffec40print /x &nModified$2 = 0xffffffffed0c

根据调试的攻击信息,计算溢出条件:

可以看出,当gets输入的内容大小为0xcc+4,输入内容的最后4个字节将正好覆盖掉栈内变量nModified,只要精心设计 输入内容最后四个字节,就能将栈内变量溢出覆盖为任意值。

经过前面的计算,栈溢出攻击时键盘输入的内容字节数量很多,并且最后4个字节还是特殊的非ASCII码内容(个人学号- 0x24030850),键盘将无法输入。

在 Linux 命令行中,

./overflow.elf

根据前面的设计,输入文件的大小为0xcc + 4 = 204 + 4字节,最后四个字节指定内容0x24030850。要注意字节序,华为云主机(ARM+Ubuntu 24.04 Server定制版)为小字节,因此编辑十六进制时顺序要按字节颠倒: 50 08 03 24

启动GHex,新建一个十六进制文件 input.hex

操作步骤

在Terminal终端窗口通过ghex命令打开十六进制编辑器ghex,打开右上角菜单选择New;在ghex编辑器中任意输入204个字节,然后输入50 08 03 24四个字节,点击右上角菜单选择Save As,编辑十六进制文件名称为input.hex;新打开一个Terminal终端窗口,通过ls命令确认input.hex文件在当前目录中,执行栈溢出漏洞代码程序,将输入文件重定向给程序。输出日志“congratulations,success to stack-overflow variable-nModified”。./overflow.elf

至此,成功完成安全栈溢出攻击。

来源:华为云开发者联盟

相关推荐