XMEye RSA key authorization

oldStrogg

n3wb
Feb 17, 2023
3
0
Estonia
Good day gentlemen's.

I have 8mPx XM camera.
It work OK. I own Admin/Password/IP.
Mobile app and PC soft XMEye is OK, but for my reasons I need write Python script to access it.

Using Wireshark I can see next:

PC XMEye soft --> Cam.
POST Login Request on
Cam -->
{ "Ret":100,
"Salt":"XIqJlRnt",
"PublicKey":"8ADA6A1BB3E91A9C1BB3 --- 256chars long string (128hex) --- 4329AEB2A4F,010001",
"LoginEncryptionType":"RSA/MD5_8",
"DataEncryptionType":"AES" }

PC -->
{"Name":"Login","LoginEncryptionType":"RSA",
"User":"31e0c47c846 --- 128hex long data --- 768708f076165e36b3088f0077c4be1a7",
"Sign":"46cd96b5c2d --- 128hex long data --- 736995bdb40977146b2f7f",
"VERK":"15c8e727314 --- 128hex long data --- 985e13bd426b7509202c8a982f92baa0e",
"DTAK":"0bc43d916e0 --- 128hex long data --- 4e8dcdd03bdeff27669df153737e9e",
"Salt":"XIqJlRnt"}

Login OK. PC connected...

Question:
It is possible, based on this information and known password, write Python script to access Camera? Emulate login sequence.
 
Maybe. I was off to bed gotta get up early. So not sure this will work for you as it is. I mean will need to look it over and change some values that meets your needs like your password I already setup the IP as you have it listed..

The hardest part is correctly mimicking VERK and DTAK. These may include device UUIDs, timestamps, random nonces, or challenge-responses derived from the app.
If you can intercept these values from the real app once with Wireshark while using the known password, and replay them, you may get in.
Once logged in, future communication is likely AES encrypted, using a session key returned by the login response.

Python:
import requests
import hashlib
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import json
import base64

camera_ip = "192.168.1.20"
username = "admin"
password = "your_known_password"

# 1. Request salt and public key
resp = requests.post(f"http://{camera_ip}/cgi-bin/login.cgi", data='{}')
data = resp.json()

salt = data['Salt']
pubkey_raw = data['PublicKey']
login_type = data['LoginEncryptionType']

# 2. Parse Public Key
key_parts = pubkey_raw.split(',')
modulus_hex = key_parts[0]
exponent_hex = key_parts[1]

modulus = int(modulus_hex, 16)
exponent = int(exponent_hex, 16)

rsa_key = RSA.construct((modulus, exponent))
cipher = PKCS1_v1_5.new(rsa_key)

# 3. Hash password
md5 = hashlib.md5()
md5.update((password + salt).encode('utf-8'))
password_hash = md5.hexdigest().upper()

# 4. Encrypt fields
def encrypt_field(value):
    padded = value.encode().ljust(128, b'\x00')  # ensure 128 bytes
    encrypted = cipher.encrypt(padded)
    return encrypted.hex().upper()

user_enc = encrypt_field(username)
sign_enc = encrypt_field(password_hash)
verk_enc = encrypt_field("whatever_VERK_is")  # this needs to be replicated from real app behavior
dtak_enc = encrypt_field("whatever_DTAK_is")  # same here

login_payload = {
    "Name": "Login",
    "LoginEncryptionType": "RSA",
    "User": user_enc,
    "Sign": sign_enc,
    "VERK": verk_enc,
    "DTAK": dtak_enc,
    "Salt": salt
}

# 5. Send login request
login_resp = requests.post(f"http://{camera_ip}/cgi-bin/login.cgi", json=login_payload)
print(login_resp.text)
 
  • Like
Reactions: oldStrogg
Thank you for your time, sir.

1. Is it correct, that hashed should be only password (# 3. Hash password). Username, and other values, encrypted as it is? "user_enc = encrypt_field(username)" not "user_enc = encrypt_field(username_hash)" ?

At any combination I try, camera give me error. Depends of I use
cipher = PKCS1_v1_5.new(rsa_key) # { "Ret":136, "Tip":"pwd salt error2" }
cipher = PKCS1_OAEP.new(rsa_key) # { "Ret":136, "Tip":"pwd salt error1" } 1 better then 2 :)

2. I try to get values of VERK and DTAK. I can Login cam from web page at Chrome. As I understand Chrome, somehow get this values. More likely from 3rd source (cloud service). But I can't capture this part with Wireshark.
Some protocol? Where I should dig?

Best regards
 
Yes you are correct, Only the password is hashed with MD5 and the salt. THe username, hashed password Siign VERK, and DTAK are each encrypted with RSA as raw strings. No extra hashing or transformation unless the protocol specifies otherwise.

So the python
user_enc = encrypt_field(username) Plain admin -> RSA
sign_enc = encrypt_field(password_hash) MD5 (password+salt) -> RSA

Should not hash the username. Just encrypt the raw string as is with RSA..


Error messages sounds like the best bet would be
cipher = PKCS_v1_5.new(rsa_key)

About the VERK and DTAK think you are 100% on track, these values are almost never generated locally.

Common behavior in XM-based devices like XMEye.

When using the XMEye app the web plugin. the login sequence goes,
Connect to cloud server Like cloud.xmeye.net
get public key + salt from the cloud
cloud helps generate VERK and DTAK or at least provides some parts of it

So if you are testing from local browser, even when pointing to the cams local IP, the browser side JS or plugin may secretly communicate with cloud APIs in parallel and that is where the VERK and DTAK originate..


So where to dig?

Wireshark won't always see this because Chrome likely using HTTPS, could be using WebSockets, XHR or even WebAssembly.. Could be CORS-restricted making direct access tricky..

Ideas to capture hidden VERK/DTAK
Open browser Dev Tools F12, Network
Look for XHR, Fetch or WebSocket calls
Filter by domain: see if request go to xmeye.net or coudsee.net or unknown hosts
Inspec the request/response payloads, you may find VERK/DTAK in plain JSON

IF you have Burp Suite might want to use that..

Another option is if able to Reverse Engineer the JavaScript,
On the camera's login page view-source: and any included .js files, look for calls like
CryptoJS.MD5
RSAKey()
getLoginPayload()
 
  • Like
Reactions: oldStrogg