GhostScript in the Shell czyli CVE-2023-36664

O GhostScripcie

GhostScript to otwartoźródłowy interpreter PostScripta i plików pdf. Nie jest popularny jako bezpośrednio używane narzędzie, ale jest wykorzystywany w wielu innych programach jak InkScape i LibreOffice.

CVE-2023-36664 dotyczy GhostScripta do wersji 10.01.2. Z uwagi na swój zakres, podatność otrzymała CVSS 7.8. Luka ma doprowadzić do wykonania kodu z pominięciem mechanizmu kontroli uprawnień wbudowanego w interpreter.

Przejdźmy do analizy

Poprawka naprawiająca omawiany błąd została wprowadzona przez commit 5f56c6f6f989816fc9cc671116740acecbed5b6c. Jego opis rozjaśnia mechanizm exploitacji.

Bug 706761: Don't "reduce" %pipe% file names for permission validation

For regular file names, we try to simplfy relative paths before we use them.

Because the %pipe% device can, effectively, accept command line calls, we
shouldn't be simplifying that string, because the command line syntax can end
up confusing the path simplifying code. That can result in permitting a pipe
command which does not match what was originally permitted.

W języku PostScript można wywoływać popen poprzez składnię:

(%pipe%polecenie) (w) file

Zmieniony fragment kodu funkcji gp_validate_path_len:

-    if (gp_file_name_reduce(path, (uint)len, buffer, &rlen) != gp_combine_success)
-        return gs_error_invalidfileaccess;
-    buffer[rlen] = 0;

+    /* "%pipe%" do not follow the normal rules for path definitions, so we
+       don't "reduce" them to avoid unexpected results
+     */
+    if (path[0] == '|' || (len > 5 && memcmp(path, "%pipe", 5) == 0)) {
+    ...

    while (1) {
        switch (mode[0])
        {
        case 'r': /* Read */
            code = validate(mem, buffer, gs_permit_file_reading);
            break;
        case 'w': /* Write */
            code = validate(mem, buffer, gs_permit_file_writing);
            break;

validate sprawdza czy ścieżka jest na liście bezpiecznych lokalizacji, do których powinniśmy mieć dostęp (jak /tmp). Problem stanowi fakt, że walidowana jest ścieżka uprzednio przetworzona przez gp_file_name_reduce. Co robi ta funkcja?

* Combine a file name with a prefix.
* Concatenates two paths and reduce parent references and current
* directory references from the concatenation when possible.
* The trailing zero byte is being added.

Jeśli spojrzymy na jej wywołania, łatwo zauważyć, że ta ścieżka kodu jest używana do weryfikacji uprawnień między innymi w pipe_fopen. Oznacza to, że atakujący może w trywialny sposób napisać payload obchodzący zabezpieczenie i uruchamiający plik wykonywalny z dowolnej lokalizacji.

Co robić, jak żyć?

Skoro dostępna jest łatka, to rozwiązanie jest jedno - łatać. Kwestią czasu jest aktywna exploitacja omawianej podatności przez złośliwych ludzi, tych mniej i bardziej ogarniętych.

Proof of Concept