补丁分析发现Zyxel认证绕过(CVE-2022-0342)

译者注:直接英译中后语句太过拗口,在不改变原意的情况下做了些许调整
原文:Zyxel authentication bypass patch analysis (CVE-2022-0342)

几个月前,许多Zyxel设备发布了新固件来修补漏洞。随后许多新闻媒体报道了该漏洞,但没有公布技术细节。

已分配漏洞编号 CVE-2022-0342,描述如下:

An authentication bypass vulnerability in the CGI program of Zyxel USG/ZyWALL series firmware versions 4.20 through 4.70, USG FLEX series firmware versions 4.50 through 5.20, ATP series firmware versions 4.32 through 5.20, VPN series firmware versions 4.30 through 5.20, and NSG series firmware versions V1.20 through V1.33 Patch 4, which could allow an attacker to bypass the web authentication and obtain administrative access of the device.

Zyxel官方发布的信息如下:

本文作为 Zyxel audit 系列的文章,通过对固件更新包进行补丁对比,来发现漏洞成因并进行漏洞利用。

 

认证机制

在Zyxel设备中,web界面通过Apache HTTP来进行管理。其主要的配置文件为/usr/local/zyxel-gui/httpd.conf,有一行LoadModule auth_zyxel_module modules/mod_auth_zyxel.so

此Apache模块由Zyxel开发和维护,用于管理认证过程

登录过程中会生成一个authtok cookie,在下一个请求中用于用户身份验证。

 

补丁对比

用Bindiff对比两个版本的mod_auth_zyxel.so,很容易定位到修复此漏洞的函数:

Apache直接调用check_authtok函数来进行身份验证,此外,create_server_config函数也被修改。

可看到check_authtok函数中部分代码被删除:

有漏洞的伪C代码如下:

补丁版如下:

可见,若满足if判断,一些GUI相关的请求可无需认证即可直接访问,补丁版中相关代码已全部被删除。

get_server_conf函数中,读取/tmp/__HTTP_SERVER_CONFIG文件,并将内容赋值到某些变量。

随后,这些变量与传入check_authtok函数的某些结构体进行比较。

依据Apache documentation,传入函数的结构体为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;(同上,定义均在httpd.h)

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_sockaddr_t结构体的第4个元素,即apr_port_t port;(定义在apr_network_io.h

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函数访问的文件来确定(即/tmp/__HTTP_SERVER_CONFIG),可见其包含Apache HTTP服务器使用的主要端口:

 

本质原因

为何会影响认证呢?由于那个检查是在socket上进行的,可假设如下两种情况:

  • 可连接到其他端口,通过修改HTTP协议的Host字段,将请求转到不同的虚拟主机;
  • 可通过不同的端口,访问一些CGI文件(端口定义在如上的配置文件中)

理论上来讲,Apache HTTP会根据其监听的端口来保证环境的分离(不同端口对应不同的界面),因此第一个假设不太可能。

检查Apache在系统上使用了哪些端口:

通过浏览器访问TCP 8080端口,如下:

看起来与2FA有关,其配置在/var/zyxel/service_conf/httpd_twofa.conf

访问TCP 54088端口:

看起来与“屏蔽告警页面”有关,其配置在/var/zyxel/service_conf/cf_blockpage_https.conf

检查Apache HTTP的主配置文件,即/usr/local/zyxel gui/httpd.conf,可见cgi-bin目录是配置在全局区域中,意味着所有cgi都可在每个不同的虚拟主机上访问

 

漏洞利用

综上所述,漏洞利用就很简单了。基于补丁信息,可假设所有CGI脚本都可在Apache公开的所有接口上访问。

访问标准端口即80时,若cookie错误,则身份验证错误:

但是,通过非标准端口即8008访问CGI时,可绕过身份验证,从而可获取配置文件:

(完)