[Pentesting] Dahua (CCTV cameras) auth

Dahua CCTV cameras use proprietary protocol, replying on TCP port 37777.

Here are absent server or NAS or NVR/DVR[1] (I use a fake server Python script instead of it) and working Dahua CCTV camera. Here is Dahua camera tries to connect to a (fake) server/NAS/NVR/DVR, to auth and to upload video.

The user is 'admin' and the password (I knew beforehand) is 'Admin12345'.

Camera to (fake) server:
00000000: A0 05 00 60 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 00 00 00 00 00 00 00 00  04 02 00 01 00 00 00 00  ................

(Fake) server to camera:
00000000: B0 01 00 78 46 00 00 00  01 0E 10 00 00 00 00 00  ...xF...........
00000010: 00 00 00 00 01 00 00 00  06 00 F9 00 00 00 00 02  ................
00000020: 52 65 61 6C 6D 3A 4C 6F  67 69 6E 20 74 6F 20 30  Realm:Login to 0
00000030: 30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  0000000000000000
00000040: 30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 0D  000000000000000.
00000050: 0A 52 61 6E 64 6F 6D 3A  31 32 33 34 35 36 37 38  .Random:12345678
00000060: 39 30 0D 0A 0D 0A                                 90....

Camera to (fake) server:
00000000: A0 05 00 60 47 00 00 00  00 00 00 00 00 00 00 00  ....G...........
00000010: 00 00 00 00 00 00 00 00  04 02 00 08 00 00 A1 AA  ................
00000020: 61 64 6D 69 6E 26 26 31  30 45 34 30 39 33 39 32  admin&&10E409392
00000030: 38 39 32 34 41 38 39 45  37 37 38 44 42 45 31 38  8924A89E778DBE18
00000040: 41 46 42 36 44 38 42 38  37 36 31 31 37 33 32 33  AFB6D8B876117323
00000050: 44 31 37 45 32 37 32 46  34 32 43 32 43 44 31 35  D17E272F42C2CD15
00000060: 34 30 45 44 44 32 44                              40EDD2D

First half:  10E4093928924A89E778DBE18AFB6D8B
Second half: 876117323D17E272F42C2CD1540EDD2D

And this is a fake server Python code:

#!/usr/bin/env python3

import socket, hexdump, sys

to_send=b""
to_send+=b"\xb0\x01\x00\x78\x46\x00\x00\x00\x01\x0e\x10\x00\x00\x00\x00\x00"
to_send+=b"\x00\x00\x00\x00\x01\x00\x00\x00\x06\x00\xf9\x00\x00\x00\x00\x02"
to_send+=b"Realm:Login to 00000000000000000000000000000000\x0d\x0a"
to_send+=b"Random:1234567890\x0d\x0a\x0d\x0a"

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(('0.0.0.0', 37777))
serversocket.listen(5) # become a server socket, maximum 5 connections

while True:
    connection, address = serversocket.accept()
    buf = connection.recv(1024)
    if len(buf) > 0:
        print ("recv:")
        hexdump.hexdump(buf)
        print ("send:")
        hexdump.hexdump(to_send)
        connection.send(to_send)
        buf=connection.recv(1024)
        print ("recv:")
        hexdump.hexdump(buf)
        break

What is second half (32 nibbles or 16 bytes), I don't know. But the first half is calculated as:

str_upper(hex(md5(username:random:str_upper(hex(md5(username:realm:password))))))

This formula is similar to RTSP, but somewhat different, and string uppercase operation is also used. Also, the 'Random' term is used instead of 'nonce'.

The code to calculate this hash:

#!/usr/bin/env python3

import hashlib

username="admin"
password="Admin12345"

def hashed_password(random, realm):
    h = hashlib.md5(f"{username}:{realm}:{password}".encode("utf-8")).hexdigest().upper()
    return hashlib.md5(f"{username}:{random}:{h}".encode("utf-8")).hexdigest().upper()

realm="Login to 00000000000000000000000000000000"
random="1234567890"

print (hashed_password(random, realm))

It will print: 10E4093928924A89E778DBE18AFB6D8B.

I couldn't find a way to run Hashcat for this auth. Maybe in future it will be available.

[1] DVR - digital video recorder; NVR - network video recorder.

(the post first published at 20251202.)


List of my other blog posts.

Subscribe to my news feed,

Some time ago (before 24-Mar-2025) there was Disqus JS script for comments. I dropped it --- it was so motley, distracting, animated, with too much ads. I never liked it. Also, comments didn"t appeared correctly (Disqus was buggy). Also, my blog is too chamberlike --- not many people write comments here. So I decided to switch to the model I once had at least in 2020 --- send me your comments by email (don"t forget to include URL to this blog post) and I"ll copy&paste it here manually.

Let"s party like it"s ~1993-1996, in this ultimate, radical and uncompromisingly primitive pre-web1.0-style blog and website. This website is best viewed under lynx/links/elinks/w3m.