PDF 版本下载:抓住“新代码”的影子 —— 基于GoAhead系列网络摄像头多个漏洞分析
Author:知道创宇404实验室 Date:2017/03/19
一.漏洞背景
GoAhead作为世界上最受欢迎的嵌入式Web服务器被部署在数亿台设备中,是各种嵌入式设备与应用的理想选择。当然,各厂商也会根据不同产品需求对其进行一定程度的二次开发。
2017年3月7日,Seebug漏洞平台收录了一篇基于GoAhead系列摄像头的多个漏洞。该漏洞为Pierre Kim在博客上发表的一篇文章,披露了存在于1250多个摄像头型号的多个通用型漏洞。其在文章中将其中一个验证绕过漏洞归类为GoAhead服务器的漏洞,但事后证明,该漏洞却是由厂商二次开发GoAhead服务器产生的。于此同时,Pierre Kim将其中两个漏洞组合使用,成功获取了摄像头的最高权限。
二.漏洞分析
当我们开始着手分析这些漏洞时发现GoAhead官方源码不存在该漏洞,解开的更新固件无法找到对应程序,一系列困难接踵而至。好在根据该漏洞特殊变量名称loginuse和loginpas,我们在github上找到一个上个月还在修改的门铃项目。抓着这个“新代码”的影子,我们不仅分析出了漏洞原理,还通过分析结果找到了漏洞新的利用方式。
由于该项目依赖的一些外部环境导致无法正常编译,我们仅仅通过静态代码分析得出结论,因此难免有所疏漏。如有错误,欢迎指正。:)
1.验证绕过导致的信息(登录凭据)泄漏漏洞
作者给出POC: curl http://ip:port/system.ini?loginuse&loginpas
根据作者给出的POC,我们进行了如下测试:
可以看出,只要url
中含有loginuse
和loginpas
这两个值即无需验证。甚至当这两个值对应的账号密码为空或者为错误的zzzzzzzzzzzzzz
时均可通过验证。
看到这里,我们大致可以判断出验证loginuse
和loginpas
的逻辑问题导致该漏洞的出现。于是,在此门铃项目中直接搜索loginuse
定位到关键函数。
/func/ieparam.c
第6407-6485
行AdjustUserPri
函数如下:
unsigned char AdjustUserPri( char* url )
{
int iRet;
int iRet1;
unsigned char byPri = 0;
char loginuse[32];
char loginpas[32];
char decoderbuf[128];
char temp2[128];
memset( loginuse, 0x00, 32 );
memset( loginpas, 0x00, 32 );
memset( temp2, 0x00, 128 );
iRet = GetStrParamValue( url, "loginuse", temp2, 31 );
//判断是否存在loginuse值,并将获取到的值赋给temp2
if ( iRet == 0x00 )
{
memset( decoderbuf, 0x00, 128 );
URLDecode( temp2, strlen( temp2 ), decoderbuf, 15 );
memset( loginuse, 0x00, 31 );
strcpy( loginuse, decoderbuf );
}
//如果存在,则将temp2复制到loginuse数组中
memset( temp2, 0x00, 128 );
iRet1 = GetStrParamValue( url, "loginpas", temp2, 31 );
//判断是否存在loginpas值,并将获取到的值赋给temp2
if ( iRet1 == 0x00 )
{
memset( decoderbuf, 0x00, 128 );
URLDecode( temp2, strlen( temp2 ), decoderbuf, 15 );
memset( loginpas, 0x00, 31 );
strcpy( loginpas, decoderbuf );
}
//如果存在,则将temp2复制到loginpas数组中
if ( iRet == 0 )
{
if ( iRet1 == 0x00 )
{
//printf("user %s pwd:%s
",loginuse,loginpas);
byPri = GetUserPri( loginuse, loginpas );
//如果两次都获取到了对应的值,则通过GetUserPri进行验证。
return byPri;
}
}
memset( loginuse, 0x00, 32 );
memset( loginpas, 0x00, 32 );
memset( temp2, 0x00, 128 );
iRet = GetStrParamValue( url,