CVE-2022-47945: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: ThinkPHP Framework before 6.0.14 allows local file inclusion via the lang parameter when the language pack feature is enabled (lang_switch_on=true). An unauthenticated and remote attacker can exploit this to execute arbitrary operating system commands, as demonstrated by including pearcmd.php.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 9.8
Published: 2022-12-23T21:15Z
Repository: https://github.com/top-think/framework
Language: PHP
Files changed: src/think/Lang.php, src/think/middleware/LoadLangPack.php
Commit date: 2022-09-14 13:33:59 +0800
Commit message: 删除废弃方法 优化多语言检测

Diff: diff --git a/src/think/Lang.php b/src/think/Lang.php
index 5f16c464b..b89108482 100644
--- a/src/think/Lang.php
+++ b/src/think/Lang.php
@@ -285,60 +285,6 @@ class Lang
         }
 
         return $value;
     }
 
-    /**
-     * 自动侦测设置获取语言选择
-     * @deprecated
-     * @access public
-     * @param Request $request
-     * @return string
-     */
-    public function detect(Request $request): string
-    {
-        // 自动侦测设置获取语言选择
-        $langSet = '';
-
-        if ($request->get($this->config['detect_var'])) {
-            // url中设置了语言变量
-            $langSet = strtolower($request->get($this->config['detect_var']));
-        } elseif ($request->header($this->config['header_var'])) {
-            // Header中设置了语言变量
-            $langSet = strtolower($request->header($this->config['header_var']));
-        } elseif ($request->cookie($this->config['cookie_var'])) {
-            // Cookie中设置了语言变量
-            $langSet = strtolower($request->cookie($this->config['cookie_var']));
-        } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
-            // 自动侦测浏览器语言
-            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
-            if ($match) {
-                $langSet = strtolower($matches[1]);
-                if (isset($this->config['accept_language'][$langSet])) {
-                    $langSet = $this->config['accept_language'][$langSet];
-                }
-            }
-        }
-
-        if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
-            // 合法的语言
-            $this->range = $langSet;
-        }
-
-        return $this->range;
-    }
-
-    /**
-     * 保存当前语言到Cookie
-     * @deprecated
-     * @access public
-     * @param Cookie $cookie Cookie对象
-     * @return void
-     */
-    public function saveToCookie(Cookie $cookie)
-    {
-        if ($this->config['use_cookie']) {
-            $cookie->set($this->config['cookie_var'], $this->range);
-        }
-    }
-
 }
diff --git a/src/think/middleware/LoadLangPack.php b/src/think/middleware/LoadLangPack.php
index d6bf6a462..9550c9008 100644
--- a/src/think/middleware/LoadLangPack.php
+++ b/src/think/middleware/LoadLangPack.php
@@ -68,37 +68,37 @@ class LoadLangPack
         // 自动侦测设置获取语言选择
         $langSet = '';
 
         if ($request->get($this->config['detect_var'])) {
             // url中设置了语言变量
-            $langSet = strtolower($request->get($this->config['detect_var']));
+            $langSet = $request->get($this->config['detect_var']);
         } elseif ($request->header($this->config['header_var'])) {
             // Header中设置了语言变量
-            $langSet = strtolower($request->header($this->config['header_var']));
+            $langSet = $request->header($this->config['header_var']);
         } elseif ($request->cookie($this->config['cookie_var'])) {
             // Cookie中设置了语言变量
-            $langSet = strtolower($request->cookie($this->config['cookie_var']));
+            $langSet = $request->cookie($this->config['cookie_var']);
         } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
             // 自动侦测浏览器语言
-            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
-            if ($match) {
-                $langSet = strtolower($matches[1]);
-                if (isset($this->config['accept_language'][$langSet])) {
-                    $langSet = $this->config['accept_language'][$langSet];
-                }
+            $langSet = $request->server('HTTP_ACCEPT_LANGUAGE');
+        }
+
+        if (preg_match('/^([a-z\d\-]+)/i', $langSet, $matches)) {
+            $langSet = strtolower($matches[1]);
+            if (isset($this->config['accept_language'][$langSet])) {
+                $langSet = $this->config['accept_language'][$langSet];
             }
         }
 
         if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
             // 合法的语言
-            $range = $langSet;
-            $this->lang->setLangSet($range);
+            $this->lang->setLangSet($langSet);
         } else {
-            $range = $this->lang->getLangSet();
+            $langSet = $this->lang->getLangSet();
         }
 
-        return $range;
+        return $langSet;
     }
 
     /**
      * 保存当前语言到Cookie
      * @access protected

Vulnerable code:     /**
     * 自动侦测设置获取语言选择
     * @deprecated
     * @access public
     * @param Request $request
     * @return string
     */
    public function detect(Request $request): string
    {
        // 自动侦测设置获取语言选择
        $langSet = '';

        if ($request->get($this->config['detect_var'])) {
            // url中设置了语言变量
            $langSet = strtolower($request->get($this->config['detect_var']));
        } elseif ($request->header($this->config['header_var'])) {
            // Header中设置了语言变量
            $langSet = strtolower($request->header($this->config['header_var']));
        } elseif ($request->cookie($this->config['cookie_var'])) {
            // Cookie中设置了语言变量
            $langSet = strtolower($request->cookie($this->config['cookie_var']));
        } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
            // 自动侦测浏览器语言
            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
            if ($match) {
                $langSet = strtolower($matches[1]);
                if (isset($this->config['accept_language'][$langSet])) {
                    $langSet = $this->config['accept_language'][$langSet];
                }
            }
        }

        if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
            // 合法的语言
            $this->range = $langSet;
        }

        return $this->range;
    }

    /**
     * 保存当前语言到Cookie
     * @deprecated
     * @access public
     * @param Cookie $cookie Cookie对象
     * @return void
     */
    public function saveToCookie(Cookie $cookie)
    {
        if ($this->config['use_cookie']) {
            $cookie->set($this->config['cookie_var'], $this->range);
        }
    }

            $langSet = strtolower($request->get($this->config['detect_var']));
            $langSet = strtolower($request->header($this->config['header_var']));
            $langSet = strtolower($request->cookie($this->config['cookie_var']));
            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
            if ($match) {
                $langSet = strtolower($matches[1]);
                if (isset($this->config['accept_language'][$langSet])) {
                    $langSet = $this->config['accept_language'][$langSet];
                }
            $range = $langSet;
            $this->lang->setLangSet($range);
            $range = $this->lang->getLangSet();
        return $range;
Fixed code:             $langSet = $request->get($this->config['detect_var']);
            $langSet = $request->header($this->config['header_var']);
            $langSet = $request->cookie($this->config['cookie_var']);
            $langSet = $request->server('HTTP_ACCEPT_LANGUAGE');
        }

        if (preg_match('/^([a-z\d\-]+)/i', $langSet, $matches)) {
            $langSet = strtolower($matches[1]);
            if (isset($this->config['accept_language'][$langSet])) {
                $langSet = $this->config['accept_language'][$langSet];
            $this->lang->setLangSet($langSet);
            $langSet = $this->lang->getLangSet();
        return $langSet;