写在前面的话
在一次渗透测试过程中,我们所面对的应用程序安全系数比较高,没有存在太多的错误配置,因此简单分析并没有发现安全问题。但是深入分析后,我们发现了一个运行在嵌入式设备上的Web应用程序。这个Web应用程序使用了WebSocket来实现服务器和客户端之间的通信,为了使用WebSocket,后端系统可以选择的技术有很多种,而这里使用的是Socket.io。
这个应用程序的主要功能之一就是文件上传,这也是它选择使用Socket.io-file NPM模块的原因。总而言之,这里存在一个路径遍历漏洞,将允许我们上传文件到任意系统路径中,并让Web服务器运行该文件。
如果我们可以修改ssh_config、/etc/passwd或/etc/shadow文件的话,那么这个漏洞就相当于是一个远程代码执行漏洞了,但这只能通过root权限来实现,因此我们需要想办法利用低权限用户来实现远程代码执行。
通过研究之后,我们在Socket.io-file模块中找到了一个文件类型限制绕过漏洞。在该漏洞的帮助下,我们可以绕过模块配置文件中的文件类型限制。这样一来,我们就可以上传任意文件类型,然后通过修改底层配置文件来上传适当的Shell,以实现底层系统的远程代码执行。
除此之外,Socket.io-file的上传功能也存在对输入数据处理和验证逻辑不正确的问题,这些分布在代码的各个地方。而攻击者将能够利用该问题绕过上传文件类型的限制,将所选的文件类型上传到底层系统中。
漏洞描述
Socket.io-file的默认配置下,提供了一个由WebSocket处理的上传功能。当用户尝试通过Web应用程序上传一个文件时,将会创建下列客户端请求以实现文件创建:
42["socket.io-file::createFile",{"id":"u_0","name":"testfile.mp3","size":1,"chunkSize":10240,"sent":0,"data":{}}]
为了在底层系统创建该文件,Socket.io-file的index.js代码将会被执行,下列代码将会检测文件的类型并执行后续操作:
let err = new Error('Not Acceptable file type ' + mimeType + ' of ' + filename + '. Type must be one of these: ' + this.accepts.join(', '));
return sendError(err);
}
else {
self.socket.emit(socket.io-file::complete::${id}, emitObj); self.emit('complete', emitObj);
}
}
else {
self.socket.emit(socket.io-file::complete::${id}, emitObj);
self.emit('complete', emitObj);
比如说,如果用户上传了一个名为“testfile.mp3”的文件,那么应用程序将创建一个新的.mp3文件,由于钱买你的代码只会在客户端进行检测(WebSocket请求创建之前),那么我们就可以拦截上传请求,并以应用程序修改文件名的方式来修改创建文件的文件类型。下面给出的是漏洞利用样例:
42["socket.io-file::createFile",{"id":"u_0","name":"testfile.php","size":1,"chunkSize":10240,"sent":0,"data":{}}]
为了绕过客户端限制,我们还需要将原始文件的文件类型修改问Web应用程序允许的文件类型。拦截请求之后,我们需要将文件类型修改为原始类型(.php),这样服务器端就不会进行检测了。接下来,我们就可以在底层系统创建一个.php文件了,这样也就实现了文件类型检测绕过。
除此之外,我们还可以结合路径遍历漏洞来执行攻击,我们继续往下看。
结合多个漏洞实现RCE
既然我们可以向任意服务器目录上传任意文件,那么我们就可以在特定配置下,在底层系统实现远程代码执行了。
场景1:修改配置文件
首先,我们可以修改配置文件,向Web服务器中添加恶意JavaScript代码库,然后修改index.html来加载恶意js脚本,即添加一个