概述:

在对某些厂家的IOT网关设备进行检测时,发现了一个RCE漏洞,这些漏洞存在于大多数网关设备中,漏洞点位于更新ntp中。这些漏洞的利用是需要条件的,那就是首先得登录(当然不排除有未授权的情况发生),个人感觉这个漏洞比较好玩,所以,就发出来分享一下。

当然,如果你想测试这个漏洞,最好找以前的版本,现在最新的版本大多数都已修复,祝君好运!

漏洞分析

这里以烽火某型号家庭网关为例
在NTP文件中,首先获取这几个参数的传入,

1

随后把参数传入了ntpdate文件 , bin/ntpdate ntpserver地址

2

在获取了ntpserver地址后,进行了系统命令调用

3

第一次执行调用是初始化ntpdate程序(清理关闭干扰程序),随后执行netdate程序,获取当前时间

漏洞利用

我们来看下漏洞利用点,漏洞发生在设备的时间设定功能上

4

因为这个漏洞是隐式RCE,所以没有返回,我们只能进系统进行验证。

根据以上分析,我们后台监控看下

5

可以看到,sntp成功的调用了ntpdate程序来获取时间,而且ntpserver服务器的参数是我们可以控制的。

我们向tmp目录写入test.txt文件

6

后台监控看下是否利用成功

8

在这里,我们还是来看下前端代码吧,因为代码太长,所以选择主要函数讲解。

在我们提交保存按钮后,会调用btnApply()函数,我们跟进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
function btnApply() {
var loc = 'sntpcfg.cgi?ntp_enabled=';
with( document.forms[0] ) {
if( ntpEnabled.checked ) {
loc += '1&ntpServer1=';
if( ntpServer1.selectedIndex == ntpServers.length ) {
if( ntpServerOther1.value.length == 0 ) { // == Other
alert('第一时间服务器为“其它”,可是“其它”域为空');
return;
} else {
loc += ntpServerOther1.value;
}
} else {
loc += ntpServer1[ntpServer1.selectedIndex].value;
}

loc += '&ntpServer2=';
if( ntpServer2.selectedIndex == ntpServers.length+1 ) {
if( ntpServerOther2.value.length == 0 ) { // == Other
alert('第二时间服务器为“其它”,可是“其它”域为空');
return;
} else {
loc += ntpServerOther2.value;
}
} else {
if( ntpServer2.selectedIndex > 0 )
loc += ntpServer2[ntpServer2.selectedIndex].value;
}

loc += '&ntpServer3=';
if( ntpServer3.selectedIndex == ntpServers.length+1 ) {
if( ntpServerOther3.value.length == 0 ) { // == Other
alert('第三时间服务器为“其它”,可是“其它”域为空');
return;
} else {
loc += ntpServerOther3.value;
}
} else {
if( ntpServer3.selectedIndex > 0 )
loc += ntpServer3[ntpServer3.selectedIndex].value;
}
loc += '&ntpServer4=';
if( ntpServer4.selectedIndex == ntpServers.length+1 ) {
if( ntpServerOther4.value.length == 0 ) { // == Other
alert('第四时间服务器为“其它”,可是“其它”域为空');
return;
} else {
loc += ntpServerOther4.value;
}
} else {
if( ntpServer4.selectedIndex > 0 )
loc += ntpServer4[ntpServer4.selectedIndex].value;
}
loc += '&ntpServer5=';
if( ntpServer5.selectedIndex == ntpServers.length+1 ) {
if( ntpServerOther5.value.length == 0 ) { // == Other
alert('第五时间服务器为“其它”,可是“其它”域为空');
return;
} else {
loc += ntpServerOther5.value;
}
} else {
if( ntpServer5.selectedIndex > 0 )
loc += ntpServer5[ntpServer5.selectedIndex].value;
}

loc += '&timezone_offset=' + cboTimeZone[cboTimeZone.selectedIndex].value;
loc += '&timezone=' + getTimeZoneName(cboTimeZone.selectedIndex);
loc += '&ntpWan=' + ntpWan.value;
loc += '&use_dst=0';

var ntpIntervalVal = parseInt(ntpInterval.value);
if(isNaN(ntpIntervalVal) || ntpIntervalVal < 3600 || ntpIntervalVal > 604800){
alert('同步间隔范围为3600-604800');
return;
}
loc += '&ntpInterval=' + ntpIntervalVal;
} else {
loc += '0';
}
}
loc += '&sessionKey=' + sessionKey;
var code = 'location="' + loc + '"';
eval(code);
}

函数先对是否开启自动更新时间进行判断,随后进入操作。

在我们设置完参数后,函数会把我们提交过来的参数经过几个步骤的参数拼接,然后用eval()函数进行提交操作,最后httpd调用sntp程序对参数进行操作。

7

我们可以看到,在一系列的操作中,没有任何的函数对我们提交的参数进行过滤和拦截,最后此漏洞的发生还是在过滤不严上面。

下面我们来看下华为和中兴对NTP漏洞的防范,他们的防护代码一样。。。

在我们提交ntpserver地址后,会对我们自定义的值进行检查和过滤,由isTValidName()函数进行,只有合规的参数才能进入后面操作

1
2
3
4
5
6
if(isTValidName(ntpServerOther1.value) == false)
{
AlertEx('第一级SNTP服务器的地址无效。');
return;
}
Form.addParameter('NTPServer1',ntpServerOther1.value)

我们来跟进 isTValidName()函数,可以看到,对大多数的特殊字符进行了检测,这些字符正是我们执行命令需要的,有了这个函数的检测加上后端对参数的处理,漏洞自然就不存在了。

1
2
3
4
5
6
7
8
9
10
function isTValidName(name) {
var i = 0;
var unsafeString = "\"<>%\\^[]`\+\$\,='#&:;*/{} \t";
for ( i = 0; i < name.length; i++ ) {
for( j = 0; j < unsafeString.length; j++)
if ( (name.charAt(i)) == unsafeString.charAt(j) )
return false;
}
return true;
}