Skip to content

Instantly share code, notes, and snippets.

@aSmig
Last active December 24, 2023 15:53
Show Gist options
  • Save aSmig/e50058a54ab85428915521f233ffa3d0 to your computer and use it in GitHub Desktop.
Save aSmig/e50058a54ab85428915521f233ffa3d0 to your computer and use it in GitHub Desktop.
How to get root on your K9608-2W 8-channel Network Video Recorder

Rooting K9608-2W

Let's say you have one of these and you want shell access for some reason, like setting the date & time programatically. By adding a couple magic files to a USB mass storage device, connecting it to your NVR and rebooting, you will be able to login via telnet.

K9608-2W Image

How to know this will work for you

Login to the web user interface of your NVR, go to Settings (wrench and screwdriver icon). If you see the following info listed in the DevInfo tab, then these instructions should work for you. This may work with other software versions too but no promises.

Dev model K9608-2W
HW version 2.1.0
SW version 2.7.13.0_22922330
Reldatetime 2018/10/26 10:58

Hack

Grab a thumb drive or whatever USB mass storage device is handy. Make sure the partition table is simple and has fat32 as the first primary partition. This is usually the default setup for a new device so you probably don't have to do anything. Mount it and get started making the three files as below in the top level directory.

me@here:/media/me/SANDISK$ echo 1000000001 > enable_log_forever
me@here:/media/me/SANDISK$ cat <<EOF>dvr_app
#!/bin/sh
/usr/sbin/telnetd &
exec /media/usb1/dvr_app_chain "\$@"
EOF
me@here:/media/me/SANDISK$ cat <<EOF>dvr_app_chain
#!/bin/sh
umount /root/dvr_app/dvr_app
exec /root/dvr_app/dvr_app "\$@"
EOF
me@here:/media/me/SANDISK$ 

Unmount/eject, pop it in the NVR, reboot the NVR, wait a bit and then telnet to it using the same IP or hostname you used to get to the web interface. Login with username root and password j1/_7sxw

me@here:~$ telnet nvr-host
Trying 192.168.31.337...
Connected to nvr-host.
Escape character is '^]'.
(none) login: root
Password:
Welcome to HiLinux.
# uptime
 05:42:38 up  2:35,  1 users,  load average: 13.88, 12.32, 11.89
# ls /
a.out    bin      etc      linuxrc  mnt      oem      root     sys      usr
a2.out   dev      lib      media    nfsroot  proc     sbin     tmp      var
# 

Why does this work?

One of the startup scripts contains the following snippet:

if [ -e $MOUNT_DIR/enable_log_forever ];then
                echo "enable log2 found."
                rv=$(cat $MOUNT_DIR/enable_log_forever)
                if [ "$rv" == "1000000001" ];then
                        if [ -e $MOUNT_DIR/dvr_app ];then
                                echo "mount bind dvr_app."
                                mount --bind $MOUNT_DIR/dvr_app /root/dvr_app/dvr_app
                        fi

In short, if the file enable_log_forever exists and contains 1000000001 then bind-mount dvr_app from the USB device over the top of the normal /root/dvr_app/dvr_app before running it later in the same startup script.

The shell won't allow us to unmount dvr_app from inside of the script running as that name, so we pass off execution to an arbitrary script called dvr_app_chain directly from the USB mountpoint. From there, we can unmount our dvr_app and run the orriginal with the arguments that were passed along the way. Any additional commands that we want to run can be added to either script.

One side effect is that logs will be written to your USB device. Rebooting without this USB device present will revert to running normally without telnetd.

Other suggestions

  • Kill run_IOTDaemon.sh. It's the script responsible for spawning IOTDaemon once a minute. This service reaches out to ngw.dvr163.com offering remote access to your video along with all sorts of other controls, including a reverse shell.
  • Change the root password. Using bind-mount, you can set up a passwd file on your USB mass storage device.
  • Run dropbear instead of telnetd.
  • Update the squashfs with all of your changes so that you don't depend on the USB mass storage device any more. For extra credit, add an NFS mount to hold larger packages.
  • Run ntpd. The clock drift on this thing is terrible. The built in mechanism for ntp is to stop recording, update clock, then start recording again. This is appropriate for a big time shift, but not so hot for regularly maintaining time sync.

Please post comments with suggestions or requests for this document. Have a device you want root on and don't mind sending one to me? Tweet at @octosavvi.

@wes1993
Copy link

wes1993 commented Mar 19, 2021

Another interesting tidbit in the same log file:

ENVIRONMENT: RtspServer default disable

Maybe there is an RTSP server, it's just disabled by default 🤔

Sorry,
Where is the path of this log?
P.s. I have also sent and email here:
jszc@juanvision.com
It’s the developer of this shit... no success for me, but you could try regarding to RTSP stream he write this:

Sorry, the FTP function of the recorder has been eliminated, and the RTSP of the recorder needs to be customized

I hope you will find a way

@TippyLion28
Copy link

Damn it, that's no fun :(

With regards to the log file, I found it inside the log beginning with app_

In other news, I have managed to compile dropbear and I can verify that it prints the version string and even starts a daemon when supplied with the -R argument. However, when you try to connect it crashes with cannot resolve symbol 'getrandom'

It looks like the version of uClibc bundled with the NVR is quite old or missing some component. Perhaps there's a way I can bundle the dropbear binary with its own version of uClibc? Or maybe I need to bind-mount the newer uClibc libraries. I'll investigate :)

@wes1993
Copy link

wes1993 commented Mar 19, 2021

Thanks for the update :)
Sorry but I’m not so expert... I don’t know but we can try to copy the library to the usb?

bye
Stefano

@TippyLion28
Copy link

Please don't be mistaken, I'm not an expert either :)

Maybe we can, but first I'm trying to static link the dropbear binary, then it will not need any external library :)))

@TippyLion28
Copy link

TippyLion28 commented Mar 20, 2021

Okay, static-linking the binary worked! Now when I connect, dropbear wants to create a file inside /etc/dropbear/ but it cannot (for obvious reasons)

Do you by any chance know how to create a NEW mount point within /etc rather than binding one? Then we can redirect this folder to the USB and then dropbear should work :DDDDDD

Alternatively, I could try to modify the source code of dropbear to place the files in /tmp instead :)

@wes1993
Copy link

wes1993 commented Mar 20, 2021

I don’t know because the FS is only write, I could try with mount but from what I remember won’t work... :( i this the best thing is to redirect to /tmp

@wes1993
Copy link

wes1993 commented Mar 20, 2021

P.s. goooooooddd workkkkkk!! :)

@TippyLion28
Copy link

Okay, source code modification it is then :D

As a side note, I asked Sannce support if they could provide some technical information regarding the RTSP stream. Here's what they said:

The product manager said that the RTSP steam of the system is as below:
Mainstream: rtsp://IP:port(website port)/ch0_0.264
Substream: rtsp://IP:port(website port)/ch0_1.264

But we cannot make sure it will work stably because the engineer didn't maintain it anymore.
If you want to use it, please have a try.

This reply, and the entry we saw in the log file about the RTSP server being disabled, leads me to believe that the functionality was unmaintained/unreliable and ultimately it was switched off and is therefore not accessible.

@wes1993
Copy link

wes1993 commented Mar 20, 2021

So firt we need to know, your cameras are H265 or H264?
Because this address seems related to the cameras not the NVR.
Also have you tried to access the cameras telnet?
For access the cameras just change settings of your pc and set an IP address related to subnet of the cameras, should be:
IP: 172.14.10.0 Subnet: 255.255.0.0

@wes1993
Copy link

wes1993 commented Mar 20, 2021

Did you have telegram?

@TippyLion28
Copy link

To be honest, I haven't really looked at accessing the cameras individually. I'm more interested in accessing the NVR and getting the streams from there.

If my attempts fail, I will try to give the cameras static IP addresses and find a way to route from my LAN to the camera subnet.

I do have telegram, but I'm not going to be posting my username here for privacy reasons :)

@wes1993
Copy link

wes1993 commented Mar 20, 2021

Enter here:
https://t.me/joinchat/VTgYk81SmxQ2YTlk

@aSmig, also you if you want could enter here for help us to add RTSP ecc

@TippyLion28
Copy link

I've done it! Dropbear compiled and running on the DVR :)

I had to modify the dropbear source in order to generate hostkeys on the USB instead of /etc/dropbear

If anyone is interested how I did this, I used a buildroot package override to point to a different folder, then I copied the original source from buildroot/output/dropbear into that folder

Then I modified default_options.h and changed the default file paths to point to the USB, and built using that modified source.

When it comes to running the binary file on the DVR, simply use the -R flag, and it'll generate those keys on-the-fly :)

If anyone wants the binary, let me know and I can toss it over :)

@TippyLion28
Copy link

Update on the RTSP situation:

I was able to access the cameras directly by connecting to the NVR's WiFi, and then pointing VLC directly to the camera. Finally!

The way I did this was by logging into the NVR and doing cat /tmp/wps_ap.conf and grabbing the passphrase for the WiFi network from there.

Then I connected up my laptop and set a static IP. Each camera was exposing telnet, RTSP, a Web UI and something on port 8000 which I'm not quite sure about.

Username was admin and password was blank for the Web UI and RTSP, telnet password was j1/_7sxw as mentioned earlier in the thread.

RTSP stream URL was /ch0_0.264 as mentioned by Sannce support team (thanks guys)

My solution will probably be to build some kind of bridge between the NVR subnet and the rest of my LAN using something like a Raspberry Pi. Then I'll probably end up writing a mobile-friendly Web viewer for the streams in React :)

@wes1993
Copy link

wes1993 commented Mar 31, 2021

@aSmig
Hello again aSmig, i need your help again :-(
I have bought another NVR same Chinese stuff (Because i don't want to change the cameras) and i can't manage to use your script.
If i put enable_log_forever i could have telnet (also if i just put enable_log_forever) but won't mount dvr_app, in serial console i have this log:

check sda...
sda found...
usb media sda found...
usb media sda1 found...
restore flag 1 not found.
enable log not found.
enable log2 found.
mount bind dvr_app.
mount: mounting /media/usb1/dvr_app on /root/dvr_app/dvr_app failed: No such file or directory
enable log2...

Did you have some suggestion?
the file dvr_app it's clear nothing there is inside.

Best Regards
Stefano

@wes1993
Copy link

wes1993 commented Apr 1, 2021

News:
I have found a way to access the video stream of the NVR:
You need to use this info here:
https://gist.github.com/maxious/c8915a436b532ab09e61bf937295a5d2

The admin password must be empty (If someone could help me to send also the password it's awesome)

the link is:
http://:/bubble/live?ch=0&stream=0

@quinn-madson
Copy link

@TippyLion28 could you make the dropbear binary available please?

@TippyLion28
Copy link

@quinn-madson I've uploaded a zip here

Use at your own risk, I was having stability issues with my NVR kit while I had the USB plugged in. I'm not sure if that was caused by debug mode or the dropbear binary.

I can't remember if the conf file was neccesary. I believe I copied it from an openwrt ipk while I was fiddling with the NVR kit. It's probably fine to leave that off the stick.

@quinn-madson
Copy link

@TippyLion28 Thank you!

@wes1993
Copy link

wes1993 commented May 17, 2021

@aSmig

Hi wes1993, I took a look at your firmware. It is using a trivial XOR obfuscation. The repeating 64 bytes at the end of the file can be used as the key, starting with 0x5090, ending with 0x8040. Once you remove the XOR layer, you can use binwalk to extract the image normally. Your version of run_app.sh does a bind map on "app.out" to "/opt/app/app.out" instead of "dvr_app". With a few updates to the above, this should get you a root shell.

me@here:/media/me/SANDISK$ echo 1000000001 > enable_log_forever
me@here:/media/me/SANDISK$ cat <<EOF>app.out
#!/bin/sh
/usr/sbin/telnetd &
echo 'toor::0:0:root:/root:/bin/sh' >> /etc/passwd
exec /media/usb1/app.out_chain "\$@"
EOF
me@here:/media/me/SANDISK$ cat <<EOF>app.out_chain
#!/bin/sh
umount /opt/app/app.out
exec /opt/app/app.out "\$@"
EOF
me@here:/media/me/SANDISK$ 

Since the shadow file matches, you might want to check out the Giovision GV-SNVR section of this DEFCON 26 IoT village writeup. I did a quick check with openssl passwd -1 -salt 0dJv11NZ '' and didn't get a match, so adding the alternate password-less root account "toor" above seemed like a good measure. This is obviously not a good long-term method, but should get you in the door.

Drop a comment to let us know how it goes!

Hello again :-D
I have one question, could you help me to decrypt this FW file?
http://download.dvr163.com/NVR/normal--NVR/16lu%20NVR/FWHI3616N6_20200708_NVR_N8216-2_2_8_9_13_23D22310.rar

I'm trying to install the FW via flash programmer but i think that the .ROM i different from .BIN that i have exported from the chip.

And could you help me to know how can i do the decrypt?
Best regards
Stefano

@aSmig
Copy link
Author

aSmig commented May 18, 2021

@wes1993

I have one question, could you help me to decrypt this FW file?
http://download.dvr163.com/NVR/normal--NVR/16lu%20NVR/FWHI3616N6_20200708_NVR_N8216-2_2_8_9_13_23D22310.rar

It doesn't look like there is any crypto or even encoding on that one. After RAR extracting, it looks like a standard image.

I'm trying to install the FW via flash programmer but i think that the .ROM i different from .BIN that i have exported from the chip.

Have a look at the first few bytes of the image you extracted and compare to the .rom to see if a header has been added. It is a fairly common practice for some bytes at the beginning of the image to designate what hardware the following image can run on. If 48 bytes into the .rom looks about the same as the .bin you captured, then just trim off those bytes with something like this: dd if=downloaded.rom of=toflash.bin iflag=skip_bytes skip=48

Good luck!

@wes1993
Copy link

wes1993 commented May 18, 2021

Thanks a lot @aSmig for your help :-D

This is my dumped FW, i have seen but seems different but i'm not so expert :-( could you check also if for you it just the header ?

https://mega.nz/file/hQElSIrb#SzdfXfHFugwuCQy8zLw2cXBa9mh8DzyPPLqQwroHRkw

Best regards
Stefano

@aSmig
Copy link
Author

aSmig commented May 19, 2021

@wes1993

The dump file you uploaded didn't unpack cleanly for me using an older Binwalk v2.1.0. Based on the filename, I'm assuming you dumped twice and compared the results to make sure you got a good dump. Might be worth checking again to be sure you don't get stuck without a recovery method after some failed write attempts.

Your dump file is 16MB. The FW you downloaded is 16.25MB. Since the flash chip on your device likely holds 16MB, the download has an extra 256KB that is not going to fit in the flash. There may be a second chip on the board that gets the extra 256KB or maybe it is just ignored. I haven't dug into the stock update tool for this device so I don't know what the process looks like. But since we have your example dump to compare with, let's take a look.

My approach to lining things up is to identify a few unique segments and find the file offsets for those segments using binwalk or manual exploration:

Section Download Dump Notes
uBoot env 0x00090000 0x00050000
Kernel 0x00120000 0x00060000 Both are 4.9.37
Filesystem headers 0x003a0000 0x00298ccb, 0x002e0000
SquashFS 0x003a0040, 0x004e0400 0x00298d0b, 0x002e0040, 0x003d6040, 0x003e0000
NVR Id not included 0x00fe0000 xor encoded with the same key we saw before

Short story: these don't really line up. This means that the partition scheme is inconsistent between firmwares, or the update mechanism writes various blocks of the downloaded firmware per instructions in the header. Maybe even both. Looking at the uBoot env, the addresses configured by SPI Flash (sf) commands don't match between devices.

Looking closer at the first section of the downloaded image, we can see the string "UBOOTENV". 52 bytes after the start of that string is a 32-bit Little-Endian 0x00090000. The next string is "KERNEL" and 52 bytes after the start of that string is 0x00120000. Then "ROOTFS" and 0x003a0000. This all lines up perfectly with the offsets we found above. So we are definitely looking at a header section listing offsets in the file to find various important segments. The 32-bits following each file offset looks like a byte count for the data to find in that segment. Other information in the header may indicate where to write this data to on the target device. At the end of the header there is what looks like an md5sum followed by padding that ends at exactly 256KB. So that solves the extra 0.25MB mystery. And since the downloaded firmware image doesn't include a bootloader, you definitely can't just write the image byte-for-byte onto your device. You could try carving the kernel and filesystem out of the download and overwrite on your device, but there are lots of opportunities for this to fail. Especially given the Id segment in your dump telling the device that it is a K8208-W, listing the serial number, and other unique identifiers.

Random interesting thing from your dump 0x00ff2000 appears to be regex for matching MAC addresses starting with upper or lower case:
08:3A:2F Guangzhou Juan Intelligent Tech Joint Stock Co.,Ltd
9C:A3:A9 Guangzhou Juan Optical and Electronical Tech Joint Stock Co., Ltd

@wes1993
Copy link

wes1993 commented May 22, 2021

@aSmig
Thanks a lot for your beautiful analysis and help!! Really needed and appreciated!!
Sorry for the delay in the reply but I have a really busy days at work :(
I’m not so expert and I have read many times your reply, from what you have told to me you think that the problem is that the firmware are inconsistent and we can’t flash because probably we have some problem with uboot it’s correct?
I have also downloaded an old firmware could you check if this firmware is more consistent?

http://download.dvr163.com/NVR/normal--NVR/V2.8.9.3/FWHI3636N6_20191216_NVR_N8236_2_8_9_3_23752920.zip

http://download.dvr163.com/NVR/normal--NVR/V2.8.9.3/FWHI3625NL_20191216_NVR_N8225_2_8_9_3_24C32810.zip

really really really thanks a lot!!
Best regards
Stefano

@glsmith86
Copy link

glsmith86 commented Oct 9, 2022

Hi!

I have a NVR, K8208-3WS. I want to unpack firmware file:
http://download.dvr163.com/0606/nvr/FWHI102_20220523_W-NVR_K8208-3WS_3_2_4_0_0x62102106_RELEASE.rom

Ubuntu file command result: data. Binwalk result: 1866866 0x1C7C72 MySQL ISAM index file Version 11

Can somebody help me?

@aSmig
Copy link
Author

aSmig commented Oct 30, 2022

Hello @glsmith86,

The K8208-3WS firmware you linked uses the same XOR obfuscation as mentioned back in this prior comment. We can tell by looking at:

$ xxd FWHI102_20220523_W-NVR_K8208-3WS_3_2_4_0_0x62102106_RELEASE.rom | tail
00d816d0: 9c5c 5d9d 5f9f 9e5e 5a9a 9b5b 9959 5898  .\]._..^Z..[.YX.
00d816e0: 8848 4989 4b8b 8a4a 4e8e 8f4f 8d4d 4c8c  .HI.K..JN..O.ML.
00d816f0: 4484 8545 8747 4686 8242 4383 4181 8040  D..E.GF..BC.A..@
00d81700: 5090 9151 9353 5292 9656 5797 5595 9454  P..Q.SR..VW.U..T
00d81710: 9c5c 5d9d 5f9f 9e5e 5a9a 9b5b 9959 5898  .\]._..^Z..[.YX.
00d81720: 8848 4989 4b8b 8a4a 4e8e 8f4f 8d4d 4c8c  .HI.K..JN..O.ML.
00d81730: 4484 8545 8747 4686 8242 4383 4181 8040  D..E.GF..BC.A..@
00d81740: 5090 9151 9353 5292 9656 5797 5595 9454  P..Q.SR..VW.U..T
00d81750: 9c5c 5d9d 5f9f 9e5e 5a9a 9b5b 9959 5898  .\]._..^Z..[.YX.
00d81760: 8848 4989 4b8b 8a4a 4e8e 8f4f            .HI.K..JN..O

The repeating pattern of bytes (in this case, starting with 0x5090 and ending with 0x8040) at the end of a firmware image is a clear indicator of XOR obfuscation. Since we would commonly see null bytes at the end of an image, and since XOR is its own inverse, those exact bytes are the XOR key. One of the many tools which can decode this is xortool-xor, which can be installed with pip3 install xortool. Run like this:

$ xortool-xor -f FWHI102_20220523_W-NVR_K8208-3WS_3_2_4_0_0x62102106_RELEASE.rom -h 509091519353529296565797559594549c5c5d9d5f9f9e5e5a9a9b5b99595898884849894b8b8a4a4e8e8f4f8d4d4c8c44848545874746868242438341818040 >decode.rom

We know it worked when we get good clean strings out, like this:

$ strings decode.rom | head
9021160200
jn_uboot
uenv
jn_kernel
jn_rootfs
jn_app
jn_logo
IPL_
$  R
:P! 

From there, run binwalk as usual, binwalk -eMr decode.rom and it will produce the directory _decode.rom.extracted/ containing all extracted files.

@glsmith86
Copy link

Hello @aSmig,

Thanks for your help. After I extract the firmware, I can modify your script and I can login to the NVR. The mentioned password not working, but I can add a password-less account by the modfied startup script.

@glsmith86
Copy link

Hi all!

How can I add an swap file or partition on K8208-3WS device?
Free memory is low and I want to write an logger script on it.

@yennor
Copy link

yennor commented Mar 7, 2023

I've also got a K8208-3WS device (brand HIseeu). Firmware 3.2.4.9M from 2023-01-15 11:19:31.
I was able to "decrypt' the firmware of it. and look a bit through it.
The Web Interface is completely unworking. There are links to swf files which don't exist anymore. /cgi-bin/ calls which don't work.
But as it seems, there is websocket service listening on port 10000 of the nvr, and there is javascript code how to connect to it, and stream data from it (live and recordings). In main.js there is code, how to use the library (import.js). They tried to implement it with a fixed ip address, the resulting error in the developer console made me find the code ;-). It propably also needs the JMuxer library as it seems. I've tried to get it working, but only had like 10 minutes. I don't know if those files are the same on all k8208-3ws, or if there are differences? Main.js looks like the apprentice took a shot on it, gave up in the middle of the way, but decided to publish it anyway.
Anyway, if those javascript files properly implement the protocol, it should be possible to implement a working web user interface :-). Unfortunately I won't have time in the foreseeable future, but I post the files here for if anybody else is bored and has too much time ;-)
The files are also under squashfs-root-0/web

  • import.js : includes a lot of stuff... probably all the imported libraries... interesting is the function: "448: function(t, exports, n) {", which defines apip2p, and implements the protocol to talk to that websocket.
  • main.js: example how to use the above library. Needs JMuxer or similar. JMuxer receives the h264 stream from the above library, and displays it in a

files not really needed

  • build.js: i think that's part of the old API beeing used over a webservice behind /netsdk/ . As it seems most information you can get over it, but no recordings :-(. There actually could be endpoints for the recording which aren't listened in the file. But it's a pain in the ass going through the binary which provides the web service to find the different endpoints. The infos which where gotten over this API afterwards where send to a SWF file, to display the recordings or live streams. But this file propably is not needed anymore.
  • index.html

Edit: I've noticed I can only upload image and video files ??? will post a link tomorrow. now I go to bed

@yennor
Copy link

yennor commented Mar 7, 2023

mmhh ok... propably not the best way, but works for the moment.
https://gist.github.com/yennor/52a38821442cf0af5aa2b186879d1c80
and there download zip, then you get the files... sorry, was too lazy to open a new repository and urgently need to call a client...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment