CVE-2021-44962: Slic3r libslic3r STL File GCode::extrude() Out-of-bounds Read Vulnerability

7 minute read

Published:

CVE Number

CVE-2021-44962

Summary

An out-of-bounds read vulnerability exists in the GCode::extrude() functionality of Slic3r libslic3r 1.3.0 and Master Commit b1a5500. A specially crafted stl file could lead to information disclosure. An attacker can provide a malicious file to trigger this vulnerability.

Tested Versions

Slic3r libslic3r 1.3.0 Slic3r libslic3r Master Commit b1a5500

Tested Environment

Ubuntu 16.04.7 LTS (GNU/Linux 4.15.0-142-generic x86_64)

Product URLs

https://slic3r.org/

Details

When reading in an .stl file with the --export-gcode argument, the GCode::extrude() function tries to access the second to last element of paths.back().polyline.points on line 430 in the file GCode.cpp.

// make a little move inwards before leaving loop
if (paths.back().role == erExternalPerimeter && this->layer != NULL && this->config.perimeters > 1) {
    // detect angle between last and first segment
    // the side depends on the original winding order of the polygon (left for contours, right for holes)
    Point a = paths.front().polyline.points[1];  // second point
    Point b = *(paths.back().polyline.points.end()-3);       // second to last point
    if (was_clockwise) {
        // swap points
        Point c = a; a = b; b = c;
    }

    double angle = paths.front().first_point().ccw_angle(a, b) / 3;

    // turn left if contour, turn right if hole
    if (was_clockwise) angle *= -1;
    [...]   

Following the chain of member and element accesses, we find that paths is a vector of class ExtrusionPath, namely std::vector<ExtrusionPath>. Each of the vector elements has a public member Polyline polyline, which in turn stores a vector of class Point, std::vector<Point>.

class ExtrusionPath : public ExtrusionEntity
{
public:
    Polyline polyline;
    ExtrusionRole role;
    /// Volumetric velocity. mm^3 of plastic per mm of linear head motion
    double mm3_per_mm;
    /// Width of the extrusion.
    float width;
    /// Height of the extrusion.
    float height;
    [...]

Though by definition, a polyline should consist of more than 2 points, the vlnerability is triggered because for some input .stl files, the length of std::vector<Point> may be equal to or less than 2. Therefore, this vulnerability can cause an unexpected access on heap, leading to potential information leakage.

Crash Information

==87315==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300089e0b0 at pc 0x00000052b879 bp 0x7ffc8ed1b0d0 sp 0x7ffc8ed1a880
READ of size 16 at 0x60300089e0b0 thread T0
    #0 0x52b878 in __asan_memcpy (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x52b878)
    #1 0xf08e45 in Slic3r::GCode::extrude(Slic3r::ExtrusionLoop, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/GCode.cpp:430:19
    #2 0xf0fd1b in Slic3r::GCode::extrude(Slic3r::ExtrusionEntity const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, double) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/GCode.cpp:468:22
    #3 0xf475a4 in Slic3r::PrintGCode::_extrude_perimeters[abi:cxx11](std::map<unsigned long, Slic3r::ExtrusionEntityCollection, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, Slic3r::ExtrusionEntityCollection> > >&) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/PrintGCode.cpp:742:38
    #4 0xf3c0a1 in Slic3r::PrintGCode::process_layer(unsigned long, Slic3r::Layer const*, std::vector<Slic3r::Point, std::allocator<Slic3r::Point> > const&) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/PrintGCode.cpp:696:36
    #5 0xf2956a in Slic3r::PrintGCode::output() /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/PrintGCode.cpp:295:27
    #6 0x9b5aec in Slic3r::Print::export_gcode(std::ostream&, bool) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/Print.cpp:692:39
    #7 0x9b611f in Slic3r::Print::export_gcode(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/Print.cpp:704:11
    #8 0xb0d7cd in Slic3r::SimplePrint::export_gcode(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) /home/bug/LabSpace/Slic3rFuzzer/xs/src/libslic3r/SimplePrint.cpp:40:18
    #9 0x56dc91 in Slic3r::CLI::run(int, char**) /home/bug/LabSpace/Slic3rFuzzer/src/slic3r.cpp:362:27
    #10 0x57a4e8 in LLVMFuzzerTestOneInput /home/bug/LabSpace/Slic3rFuzzer/src/slic3r.cpp:546:11
    #11 0x46443a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x46443a)
    #12 0x454e07 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x454e07)
    #13 0x45a801 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x45a801)
    #14 0x480912 in main (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x480912)
    #15 0x7fa614e4383f in __libc_start_main /build/glibc-S7Ft5T/glibc-2.23/csu/../csu/libc-start.c:291
    #16 0x4536e8 in _start (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x4536e8)

0x60300089e0b0 is located 1 bytes to the right of 31-byte region [0x60300089e090,0x60300089e0af)
allocated by thread T0 here:
    #0 0x55dca2 in operator new(unsigned long) (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x55dca2)
    #1 0x7fa615e54498 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x11f498)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/bug/LabSpace/FuzzerRun/Slic3rFuzzer/build/slic3r+0x52b878) in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c068010bbc0: fd fd fa fa fd fd fd fd fa fa fd fd fd fa fa fa
  0x0c068010bbd0: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd
  0x0c068010bbe0: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
  0x0c068010bbf0: fd fa fa fa fd fd fd fa fa fa fd fd fd fa fa fa
  0x0c068010bc00: fd fd fd fd fa fa 00 00 00 00 fa fa fd fd fd fd
=>0x0c068010bc10: fa fa 00 00 00 07[fa]fa 00 00 00 00 fa fa fd fd
  0x0c068010bc20: fd fd fa fa fd fd fd fd fa fa fd fd fd fa fa fa
  0x0c068010bc30: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fa
  0x0c068010bc40: fa fa fd fd fd fa fa fa fd fd fd fd fa fa fd fd
  0x0c068010bc50: fd fa fa fa fd fd fd fd fa fa fd fd fd fd fa fa
  0x0c068010bc60: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd 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
==87315==ABORTING

GDB Trace

bug@ubuntu:~/LabSpace/gdb_NormalRun/Slic3r/build$ gdb ./slic3r
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./slic3r...done.
(gdb) b GCode.cpp:430
Breakpoint 1 at 0x662fa2: file /home/bug/LabSpace/gdb_NormalRun/Slic3r/xs/src/libslic3r/GCode.cpp, line 430.
(gdb) r --export-gcode ../../input.stl --output ../../output.gcode
Starting program: /home/bug/LabSpace/gdb_NormalRun/Slic3r/build/slic3r --export-gcode ../../input.stl --output ../../output.gcode
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
CLI   ERR: Arrange: 1, center: 1
Infilling layers
Processing triangulated mesh
[New Thread 0x7ffff6f4e700 (LWP 88895)]
[New Thread 0x7ffff6f4e700 (LWP 88896)]
[Thread 0x7ffff6f4e700 (LWP 88895) exited]
[Thread 0x7ffff6f4e700 (LWP 88896) exited]
[New Thread 0x7ffff6f4e700 (LWP 88897)]
[Thread 0x7ffff6f4e700 (LWP 88897) exited]
[New Thread 0x7ffff6f4e700 (LWP 88898)]
[New Thread 0x7ffff6f4e700 (LWP 88899)]
[Thread 0x7ffff6f4e700 (LWP 88898) exited]
Preparing infill
[New Thread 0x7ffff6f4e700 (LWP 88900)]
[Thread 0x7ffff6f4e700 (LWP 88899) exited]
[Thread 0x7ffff6f4e700 (LWP 88900) exited]
[New Thread 0x7ffff6f4e700 (LWP 88901)]
Generating skirt
Generating brim
Exporting G-Code...
[Thread 0x7ffff6f4e700 (LWP 88901) exited]
Thread 1 "slic3r" hit Breakpoint 1, Slic3r::GCode::extrude (
    this=0x7fffffff9490, loop=..., description="external small perimeter",
    speed=15)
    at /home/bug/LabSpace/gdb_NormalRun/Slic3r/xs/src/libslic3r/GCode.cpp:430
430         Point b = *(paths.back().polyline.points.end()-3);       // second to last point
(gdb) c 10
Will ignore next 9 crossings of breakpoint 1.  Continuing.
Thread 1 "slic3r" hit Breakpoint 1, Slic3r::GCode::extrude (
    this=0x7fffffff9490, loop=..., description="external perimeter", speed=-1)
    at /home/bug/LabSpace/gdb_NormalRun/Slic3r/xs/src/libslic3r/GCode.cpp:430
430         Point b = *(paths.back().polyline.points.end()-3);       // second to last point
(gdb) p paths.back().polyline.points.size()
$1 = 2

Timeline

2021-11-19 - Vendor Disclosure
2022-01-16 - Public Release

Credit

Discovered by Yuanhaur Chang, Yujie Wang, and Ning Zhang