Zyxel USG/ZyWALL系列固件版本4.20至4.70、USG FLEX系列固件版本4.50至5.20、ATP系列固件版本4.32至5.20、VPN系列固件版本4.30至5.20、NSG系列固件版本的CGI程序存在身份验证绕过漏洞V1.20 到 V1.33 补丁 4,这可能允许攻击者绕过 Web 身份验证并获得设备的管理访问权限。
Zyxel 如何管理 HTTP 身份验证
在 Zyxel 设备上,Web 界面通过 Apache HTTP 服务器进行管理。主要配置文件是 /usr/local/zyxel-gui/httpd.conf 其中包含以下行:
LoadModule auth_zyxel_module modules/mod_auth_zyxel.so
此Apache 模块由 Zyxel 开发和维护,并管理身份验证过程。
登录过程会生成一个名为“authtok”的 cookie,用于在下一个请求中对用户进行身份验证。
比较补丁
将mod_auth_zyxel.so的两个版本与Bindiff进行比较,可以很容易地识别修复问题的修改例程:
“check_authtok”函数是Apache直接调用的函数,用于验证身份验证;“create_server_config”也已修改。
根据分析,可以看到部分代码已被删除:
易受攻击的伪代码是:
打补丁的是:
基本上,在某些情况下与神秘的“非 GUI 访问”相关的允许无需身份验证直接访问的所有代码都已被删除。
例程“get_server_conf”获取 /tmp/__HTTP_SERVER_CONFIG 文件的内容并将其放入一些变量中:
然后将这些变量与Apache 传递给“check_authtok”函数的数据结构的一部分进行比较。
根据Apache 文档,传递给函数的数据结构是“request_rec”,它在 http://svn.apache.org/repos/asf/httpd/httpd/trunk/include/httpd.h中定义
比较期间使用的第一个指针是“request_rec + 4”,这意味着 32 位处理器上结构的第二个元素(希望编译器优化没有混淆数据结构的顺序)。在这种情况下“conn_rec *connection;”:
struct request_rec {
/** The pool associated with the request */
apr_pool_t *pool;
/** The connection to the client */
conn_rec *connection;
/** The virtual host for this request */
server_rec *server;
/** Pointer to the redirected request if this is an external redirect */
request_rec *next;
/** Pointer to the previous request if this is an internal redirect */
request_rec *prev;
访问的第二个指针是“conn_rec”结构的第 4 个元素,即“apr_sockaddr_t *local_addr;” 在同一文件中定义:
struct conn_rec {
/** Pool associated with this connection */
apr_pool_t *pool;
/** Physical vhost this conn came in on */
server_rec *base_server;
/** used by http_vhost.c */
void *vhost_lookup_data;
/* Information about the connection itself */
/** local address */
apr_sockaddr_t *local_addr;
/** remote address; this is the end-point of the next hop, for the address
* of the request creator, see useragent_addr in request_rec
*/
apr_sockaddr_t *client_addr;
访问的第三个指针是apr_network_io.h文件中定义的 apr_sockaddr_t 的第 4 个元素,它是“apr_port_t端口;” :
239 struct apr_sockaddr_t {
240 /** The pool to use... */
241 apr_pool_t *pool;
242 /** The hostname */
243 char *hostname;
244 /** Either a string of the port number or the service name for the port */
245 char *servname;
246 /** The numeric port */
247 apr_port_t port;
248 /** The family */
249 apr_int32_t family;
因此,与 get_server_conf() 函数的结果进行比较的数据是收到请求的本地端口。
也可以通过查看“get_server_conf”函数访问的文件来确认这一点,该文件包含 Apache HTTP 服务器使用的主要端口:
问题的根本原因
我们需要了解为什么该检查会影响身份验证。由于检查是在套接字上进行的,我们可以假设两种不同的场景:
-
我们可以连接到其他端口,通过修改HTTP协议的“Host”头,去到不同的虚拟主机;
-
我们可以从一些虚拟主机访问一些 CGI,这些虚拟主机可通过与控制文件中存在的端口不同的端口访问。
从理论上讲,Apache HTTP Server 应该根据监听发生的端口和接口来保证环境的分离,因此我们的第一个假设不太可能是正确的。
让我们检查一下 Apache 在我们的系统上使用了哪些端口:
通过浏览器访问 TCP 端口 8008 我们得到:
貌似是2FA相关的东西,配置在/var/zyxel/service_conf/httpd_twofa.conf文件中:
访问 TCP 端口 54088 我们得到这个页面:
好像是和blocking alert page相关的东西,配置在/var/zyxel/service_conf/cf_blockpage_https.conf文件中:
通过查看Apache HTTP Server的主配置文件(/usr/local/zyxel-gui/httpd.conf)可以看到“cgi-bin”目录配置在全局区域,也就是说所有的CGI将可以在每个不同的虚拟主机上访问:
开发
将迄今为止发现的所有信息放在一起,利用起来非常容易。基于补丁,我们可以假设所有 CGI 都可以在 Apache 公开的所有接口上访问。
使用无效 cookie 通过标准端口访问 CGI 将返回身份验证错误:
但是通过非标准端口访问相同的 CGI 将提供对 CGI 的完全访问权限,因此也可以访问设备配置:
原文始发于微信公众号(Khan安全攻防实验室):Zyxel 认证绕过补丁分析 (CVE-2022-0342)