赘述
这里先用几句话简单的介绍下漏洞成因:
- 前端使用js来校验 ping相关的参数
- 后端直接使用
snprintf
来构造执行指令 后端构造的具体指令为
ipping -c 1 -s %d -w %d %s -I %s
-c
:ping的次数-s
:ping的数据包大小-w
:ping的timeout%s
(第二个):用户输入的host-I
:ping使用的网卡
- 注入host就可以命令执行:
ipping -c 1 -s 64 -w 1 $(host) -I 192.168.0.12
0x1 漏洞成因-前端
- 1 首先后台页面的 ping入口只是在前端校验
- 2 核心实现文件为
pingNTraceRoute.htm
,其中startDiag
函数实现了ipping
的相关逻辑,关键代码如下所示
function startDiag() {
...
if ($.id("ipping").checked) {
if (isNaN(icmpPkts) || icmpPkts < 1 || icmpPkts > 50) {
$.alert(ERR_DIAG_PING_COUNT);
$.id("l_ping_pkt").focus();
return
}
//执行 ipping 的关键注入
ipping = $.act(ACT_GET, IPPING_DIAG, null, null, ["dataBlockSize", "timeout", "numberOfRepetitions", "host", "X_TP_ConnName", "diagnosticsState"]);
if (!$.exe()) {
ipping.diagnosticsState = "Requested";
ipping.dataBlockSize = parseInt($.id("l_ping_pkt_size").value, 10);
ipping.timeout = parseInt($.id("l_ping_pkt_time").value, 10);
ipping.numberOfRepetitions = parseInt($.id("l_ping_pkt").value, 10);
ipping.host = $.id("l_addr").value;
ipping.X_TP_ConnName = test_conn;
pktSize = parseInt($.id("l_ping_pkt_size").value, 10) + 8;
pingtimeout = parseInt($.id("l_ping_pkt_time").value, 10);
$.act(ACT_SET, IPPING_DIAG, null, null, ipping);
if (!$.exe()) {
$.id("testButton").disabled = true;
$.addLoading($.id("testButton"));
var gotResult = 1;
var i = 1;
$.auto(function() {
ipping = $.act(ACT_GET, IPPING_DIAG, null, null, null);
if (!$.exe()) {
if (i > icmpPkts) {
result = "";
disNewLine(result, "");
result = "---" + ipping.X_TP_IPAddress + " ping statistics ---";
disNewLine(result, "");
result = ipping.numberOfRepetitions + " packets transmitted, " + ipping.successCount + " packets received," + Math.round(ipping.failureCount * 100 / ipping.numberOfRepetitions) + "% packet loss";
disNewLine(result, "");
if (ipping.minimumResponseTime != 65535) {
result = "round-trip min/avg/max = " + ipping.minimumResponseTime / 1000 + "/" + ipping.averageResponseTime / 1000 + "/" + ipping.maximumResponseTime / 1000 + " ms";
disNewLine(result, "")
}
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
}
if (gotResult) {
$.act(ACT_OP, ACT_OP_IPPING);
if (!$.exe()) {
gotResult = 0;
return true
}
return false
}
if (printHeader) {
result = "PING " + ipping.host;
if ($.ifip(ipping.host, true) == 0) {
result = result + " (" + ipping.host + "): " + ipping.dataBlockSize + " data bytes";
disNewLine(result, "");
result = "";
disNewLine(result, "");
printHeader = 0
} else {
if ($.ifip(ipping.X_TP_IPAddress, true) == 0) {
result = result + " (" + ipping.X_TP_IPAddress + "): " + ipping.dataBlockSize + " data bytes";
disNewLine(result, "");
result = "";
disNewLine(result, "");
printHeader = 0
}
}
}
if ("Error_CannotResolveHostName" == ipping.diagnosticsState) {
result = "PING: unknown host: " + ipping.host;
disNewLine(result, "");
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
} else {
if ("Requested" == ipping.diagnosticsState) {
gotResult = 0;
return true
} else {
if ("None" == ipping.diagnosticsState) {
gotResult = 1;
var res = pktSize + " bytes from " + ipping.X_TP_IPAddress + ": icmp_seq=" + i + " ttl=128 time=" + ipping.X_TP_ResponseTime / 1000 + " ms";
disNewLine(res, "");
res = "";
i++
} else {
if ("Complete" == ipping.diagnosticsState) {
if (strstr(ipping.X_TP_Result, "Request timed out.") != -1) {
result = "Request timed out.";
disNewLine(result, "")
} else {
result = pktSize + " bytes from " + ipping.X_TP_IPAddress + ": icmp_seq=" + i + " ttl=128 time=" + ipping.X_TP_ResponseTime / 1000 + " ms";
disNewLine(result, "")
}
result = "";
disNewLine(result, "");
result = "---" + ipping.X_TP_IPAddress + " ping statistics ---";
disNewLine(result, "");
result = ipping.numberOfRepetitions + " packets transmitted, " + ipping.successCount + " packets received," + Math.round(ipping.failureCount * 100 / ipping.numberOfRepetitions) + "% packet loss";
disNewLine(result, "");
if (ipping.minimumResponseTime != 65535) {
result = "round-trip min/avg/max = " + ipping.minimumResponseTime / 1000 + "/" + ipping.averageResponseTime / 1000 + "/" + ipping.maximumResponseTime / 1000 + " ms";
disNewLine(result, "")
}
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
} else {
if ("Error_Internal" == ipping.diagnosticsState) {
gotResult = 1;
disNewLine(ipping.X_TP_Result, "")
} else {
gotResult = 1;
disNewLine(ipping.X_TP_Result, "");
i++
}
}
}
}
}
ipping.diagnosticsState = "Requested";
ipping.X_TP_Result = "";
$.act(ACT_SET, IPPING_DIAG, null, null, ["X_TP_Result=", "diagnosticsState=Requested"]);
if (!$.exe()) {}
}
},
500)
}
}
} else {
if ($.id("traceroute").checked) {
var maxHop;
var lqtmp;
maxHop = parseInt($.id("l_tr_hop").value, 10);
if (isNaN(maxHop) || maxHop < 1 || maxHop > 30) {
$.alert(ERR_DIAG_TTL);
$.id("l_tr_hop").focus();
return
}
tracert = $.act(ACT_GET, TRACEROUTE_DIAG, null, null, ["maxHopCount", "timeout", "numberOfTries", "host", "dataBlockSize", "X_TP_ConnName", "diagnosticsState", "X_TP_HopSeq"]);
var HopCount = 0;
var tmp;
var loopTime = 500;
var traceHost;
var tpktsize;
if (!$.exe()) {
tracert.diagnosticsState = "Requested";
tracert.host = $.id("l_addr").value;
tracert.dataBlockSize = 64;
tracert.timeout = 5;
tracert.numberOfTries = 1;
tracert.maxHopCount = parseInt($.id("l_tr_hop").value, 10);
tracert.X_TP_ConnName = test_conn;
tracert.X_TP_HopSeq = 0;
HopCount = parseInt($.id("l_tr_hop").value, 10);
traceHost = $.id("l_addr").value;
tpktsize = 64;
maxHop = parseInt($.id("l_tr_hop").value, 10);
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {
$.id("testButton").disabled = true;
$.addLoading($.id("testButton"));
var i = 0;
$.act(ACT_OP, ACT_OP_TRACERT);
if ($.exe()) {
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return
}
$.auto(function() {
tracert = $.act(ACT_GET, TRACEROUTE_DIAG, null, null, ["diagnosticsState", "X_TP_HopSeq", "X_TP_Result"]);
if (!$.exe()) {
if ((tracert.X_TP_HopSeq == 0) && (tracert.X_TP_Result != "") && printHeader) {
printHeader = 0;
if (strstr(tracert.X_TP_Result, "traceroute") >= 0) {
disNewLine(tracert.X_TP_Result, ""); ++tracert.X_TP_HopSeq;
loopTime = 500;
tracert.X_TP_Result = "";
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {
return true
}
} else {
if (tracert.diagnosticsState == "Complete" || tracert.diagnosticsState == "None" || tracert.diagnosticsState == "Requested") {
lqtmp = tracert.X_TP_Result
}
var ip = $.act(ACT_GET, TRACEROUTE_DIAG, null, null, ["X_TP_IPAddress"]);
if (!$.exe()) {
result = "traceroute to " + traceHost + " (" + ip.X_TP_IPAddress + "), " + maxHop + " hops max, " + tpktsize + " byte packets";
disNewLine(result, "");
disNewLine(lqtmp, ""); ++tracert.X_TP_HopSeq;
loopTime = 500;
tracert.X_TP_Result = "";
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {
return true
}
}
}
}
if ((tracert.diagnosticsState != "Requested") && (tracert.diagnosticsState != "None") && (tracert.diagnosticsState != "Complete")) {
disNewLine(tracert.X_TP_Result, "");
tracert.X_TP_Result = "";
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {}
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
} else {
if (tracert.diagnosticsState == "Complete") {
disNewLine(tracert.X_TP_Result, "");
tracert.X_TP_Result = "";
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {}
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
}
}
if (tracert.X_TP_Result != "") {
disNewLine(tracert.X_TP_Result, "");
loopTime = 500; ++tracert.X_TP_HopSeq;
tracert.X_TP_Result = "";
$.act(ACT_SET, TRACEROUTE_DIAG, null, null, tracert);
if (!$.exe()) {
return true
}
}
loopTime += 500;
if (tracert.X_TP_HopSeq == HopCount) {
disNewLine(tracert.X_TP_Result, "");
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
}
if (loopTime >= 150000) {
result = "traceroute to (" + traceHost + ") failed!";
disNewLine(result, "");
$.id("testButton").disabled = false;
$.removeLoading($.id("testButton"));
return false
}
}
},
500)
}
}
}
}
}
- 3 主要是利用host参数进行注入
0x2 漏洞成因-后端
- 实现
ACT_OP_IPPING
的函数位于libcmm.so
中的.text:0009A210 oal_startPing:
- 核心代码以及漏洞点如下
int __fastcall oal_startPing(int a1)
{
int v3; // $v0
int v4; // $v0
int v5; // $v0
int v6; // $v0
int v7; // [sp+28h] [-418h] BYREF
int v8[4]; // [sp+2Ch] [-414h] BYREF
char str_cmd[1028]; // [sp+3Ch] [-404h] BYREF
memset(str_cmd, 0, 1024);
memset(v8, 0, sizeof(v8));
v7 = 0;
util_findSystemProc("ipping", 0, (int)str_cmd, (int)"ipping", &v7);
if ( v7 )
{
cdbg_printf(
8,
"oal_startPing",
87,
"The previous ping to (%s) haven't complete, please wait a few minutes",
a1 + 268);
return 1;
}
memset(str_cmd, 0, 1024);
if ( *(_BYTE *)(a1 + 236) && !oal_intf_getIfAddr(a1 + 236, v8) )
{
cdbg_printf(8, "oal_startPing", 98, "Get interface(%s)'s IP failed!", a1 + 236);
return 1;
}
v3 = strlen(str_cmd);
if ( *(_BYTE *)(a1 + 204) )
snprintf(
&str_cmd[v3],
1024 - v3,
"ipping -c 1 -s %d -w %d %s",
*(_DWORD *)(a1 + 256),
*(_DWORD *)(a1 + 260),
(const char *)(a1 + 268));
else
snprintf(
&str_cmd[v3],
1024 - v3,
"ipping -c %d -s %d -w %d %s ",
*(_DWORD *)(a1 + 264), // ping的次数
*(_DWORD *)(a1 + 256), // ping的数据包大小
*(_DWORD *)(a1 + 260), // ping的timeout
(const char *)(a1 + 268)); // 域名->host
if ( util_netChkLegalIpAddr((int)v8) )
{
v4 = strlen(str_cmd);
snprintf(&str_cmd[v4], 1024 - v4, " -I %s", v8);
}
else if ( *(_BYTE *)(a1 + 236) )
{
v5 = strlen(str_cmd);
snprintf(&str_cmd[v5], 1024 - v5, " -I %s", a1 + 236);
}
v6 = strlen(str_cmd);
snprintf(&str_cmd[v6], 1024 - v6, (const char *)&off_B4C58);
util_execSystem("oal_startPing", (int)str_cmd);// 直接执行指令
return 0;
}
- 首先注入
host
到a1 + 268
- 接着程序使用
snprintf
和host
构造出str_cmd
- 然后
util_execSystem
直接执行str_cmd
- ps:
str_cmd
长度为1024,利用强度还是很高的
0x2 漏洞利用
- 利用bp构造简单的数据包即可达成利用
- 1 首先构造host数据包
- 2 接着触发指令
- 3 查看文件
/var/tmp/k44
可以发现执行成功
0x3 exp
- 完整的利用代码如下
...
0x4 搜集
web/js/lib.js
https://www.shodan.io