CVE-2018-18546: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: ThinkPHP 3.2.4 has SQL Injection via the order parameter because the Library/Think/Db/Driver.class.php parseOrder function mishandles the key variable.
CWE description: 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.
CVSS v3 base score: 9.8
Published: 2018-10-21T01:29Z
Repository: https://github.com/top-think/thinkphp
Language: PHP
Files changed: ThinkPHP/Library/Think/Db/Driver.class.php
Commit date: 2018-10-08 21:54:51 +0800
Commit message: 改进

Diff: diff --git a/ThinkPHP/Library/Think/Db/Driver.class.php b/ThinkPHP/Library/Think/Db/Driver.class.php
index 1a67399..a3b1283 100644
--- a/ThinkPHP/Library/Think/Db/Driver.class.php
+++ b/ThinkPHP/Library/Think/Db/Driver.class.php
@@ -756,33 +756,33 @@ abstract class Driver
     {
         if (empty($order)) {
             return '';
         }
         $array = array();
+        if (is_string($order) && '[RAND]' != $order) {
+            $order = explode(',', $order);
+        }
+
         if (is_array($order)) {
             foreach ($order as $key => $val) {
                 if (is_numeric($key)) {
-                    if (false === strpos($val, '(') && false === strpos($val, ';')) {
-                        $array[] = $this->parseKey($val);
-                    }
-                } elseif (false === strpos($key, ')') && false === strpos($key, '#')) {
-                    $sort    = in_array(strtolower($val), array('asc', 'desc')) ? ' ' . $val : '';
+                    list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
+                } else {
+                    $sort = $val;
+                }
+
+                if (preg_match('/^[\w]+$/', $key)) {
+                    $sort    = strtoupper($sort);
+                    $sort    = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
                     $array[] = $this->parseKey($key, true) . $sort;
                 }
             }
         } elseif ('[RAND]' == $order) {
             // 随机排序
             $array[] = $this->parseRand();
-        } else {
-            foreach (explode(',', $order) as $val) {
-                if (preg_match('/\s+(ASC|DESC)$/i', rtrim($val), $match, PREG_OFFSET_CAPTURE)) {
-                    $array[] = $this->parseKey(ltrim(substr($val, 0, $match[0][1]))) . ' ' . $match[1][0];
-                } elseif (false === strpos($val, '(') && false === strpos($val, ';')) {
-                    $array[] = $this->parseKey($val);
-                }
-            }
         }
+
         $order = implode(',', $array);
         return !empty($order) ? ' ORDER BY ' . $order : '';
     }
 
     /**

Vulnerable code:                     if (false === strpos($val, '(') && false === strpos($val, ';')) {
                        $array[] = $this->parseKey($val);
                    }
                } elseif (false === strpos($key, ')') && false === strpos($key, '#')) {
                    $sort    = in_array(strtolower($val), array('asc', 'desc')) ? ' ' . $val : '';
        } else {
            foreach (explode(',', $order) as $val) {
                if (preg_match('/\s+(ASC|DESC)$/i', rtrim($val), $match, PREG_OFFSET_CAPTURE)) {
                    $array[] = $this->parseKey(ltrim(substr($val, 0, $match[0][1]))) . ' ' . $match[1][0];
                } elseif (false === strpos($val, '(') && false === strpos($val, ';')) {
                    $array[] = $this->parseKey($val);
                }
            }
Fixed code:         if (is_string($order) && '[RAND]' != $order) {
            $order = explode(',', $order);
        }

                    list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
                } else {
                    $sort = $val;
                }

                if (preg_match('/^[\w]+$/', $key)) {
                    $sort    = strtoupper($sort);
                    $sort    = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';