Skip to content

Instantly share code, notes, and snippets.

@aSmig
Last active August 3, 2025 14:26
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 Nov 19, 2020

P.S. If i manually run the app.out original, the compiled version with ./app.out the app will run.
do you know if exist some kind of protection so we can't run the .sh but only compiled app?

Again really thanks a lot
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 19, 2020

The output you showed from running sh app.out seems consistent with invisible (non-printing) characters breaking commands in the script. This would also explain why the command interpreter can't be found if it is looking for "/bin/sh\r", which really doesn't exist, instead of "/bin/sh". So, did you create the app.out and app.out-chain files in Windows? We can see all the characters in app.out with hexdump -c /media/usb1/app.out. Observing the sequence \r\n will confirm this is the issue.

Windows line endings can be fixed with cd /media/usb1; tr -d '\r' <app.out >temp; mv temp app.out. Be sure to do the same with app.out-chain.

@wes1993
Copy link

wes1993 commented Nov 19, 2020

Really really really thanks a lot!!! yes, i have used notepad++
Now works well!! sorry for my stupid distraction and if i wasting your time... :-(
For this step i have think this, what do you think?

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.

add a new file called killIOTDaemon with this lines:

#!/bin/sh
sleep 90
pkill -f IOTDaemon

and put this line in the app.out_chain:

#!/bin/sh
umount /opt/app/app.out
cd /opt/app
exec /media/usb1/killIOTDaemon &
exec ./app.out

So how can i do this steps?

  1. Run dropbear instead of telnetd.

  2. 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.

Really Really Really thanks a lot!!! Again :-D
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 20, 2020

Congrats! It is annoying how often the little (and sometimes invisible) things slow us down.

Yep, I definitely support killing IOTDaemon. Looks like it gets started by the real app.out in your firmware, so you might need to create a script like dummy-daemon.sh:

#!/bin/sh
cat >/dev/null 2>&1

Then you can mount --bind /media/usb1/dummy-daemon.sh /opt/bin/IOTDaemon. I'm just speculating that app.out might try to restart IOTDaemon at some point. So having a replacement that will run forever and do nothing should keep it happy.

The easy way to run dropbear is to find a binary already compiled for your kernel (uname -a will help identify) and drop it on your USB drive. Frequently you can find some OpenWRT or other embedded firmware which runs on the same CPU and with a close enough kernel version that no compiling is required. In case you have to compile yourself, there are many guides and tools out there for that. Dropbear may need some extra flags to use config files from /media/usb1/ instead of /etc/ but that shouldn't be too hard to sort out. Presumably connections to the NVR would be over a trusted LAN, so SSH may not be all that important.

Under no circumstances should you ever install or use a password on this device that is also used anywhere else on your network. This is regardless of whether you run telnetd or dropbear. Imagine this thing has a keylogger dumping everything you do to the manufacturer at all times. I recommend connecting devices like this to a managed switch and blocking traffic from that port to the Internet. Don't just block from the IP assigned to the NVR; setting up an interface alias with another IP only takes an instant.

Re-packing firmware is a much bigger challenge. An older project firmware-mod-kit tries to help with this process, but doesn't work with all systems. It is almost guaranteed that devices will get bricked somewhere along the process of developing your own firmware. Sometimes I have been able to recover by uboot, tftp, jtag, or pulling flash chips from boards and manually programming them. Occasionally, I still fail because of layered security measures elsewhere on the board. Expect this to be a long and painful process where you will learn a lot and probably end up with some unusable hardware.

That said, you are in a pretty good environment to practice given that you have UART and USB access on this device. Normally, the system does mount -t squashfs /dev/mtdblock4 /opt so you could try rebuilding that squashfs with mksquashfs, drop the file on your USB drive and then mkdir /opt2; mount -t squashfs /media/usb1/new-opt.bin /opt2. Then compare the two directories (diff -qr /opt /opt2). If it looks good, try mounting over the top of /opt and let it run for a while. So far no permanent changes have been made so recovery is as simple as pulling the USB drive and rebooting. Next thing is to overwrite /dev/mtdblock4 with something like dd if=/media/usb1/new-opt.bin of=/dev/mtdblock4 and cross your fingers. This is where you might get lucky or might end up with a brick. Experimenting with /opt is much safer than starting with /, but could still have all sorts of unwanted impacts, like breaking the normal firmware installation method, making it really hard to recover. And there might be other obstacles like /dev/mtdblock4 being read only until some magic bit is flipped.

If you get through all of that and end up with new squashfs images for / and /opt installed on the flash chip, you are probably happy. Unless you have multiple devices and want to modify all of them without so many risky dd steps. In this case, packaging up everything into a new firmware image would be ideal, but is still more work. The updater probably won't let you install something that looks like the same version as what is already installed. It looks like there is a basic crc32 checking but maybe not be any sort of hashing or signatures to contend with, so that makes life much easier. But this is still a pretty big task when faced with an upgrade tool where source code is not available.

Speaking of /bin/upgrade, it looks like that might be run as a daemon. If so, it is probably another thing worth killing and replacing with dummy-daemon.sh since it has some shell execution capabilities which can probably be leveraged remotely.

Anyway, I'm happy to hear that you have a working USB hack and I hope this is the start of an enjoyable adventure. Best of luck!

@wes1993
Copy link

wes1993 commented Nov 20, 2020

Again really thanks for all your help and patience.
I don’t think is a good idea edit the FS, I think I leave the USB :).

  1. For dropbear I will search on the web or do you have some compiled version?
  2. There is also a way to add ftp support? With my FW they have dropped the FTP and for me is useful!!
  3. Do you have some suggestion on how can I access the camera behind NVR from my lan?
    Some sort of bridging or something else? My goal is to access devices under NVR so I can take images of the cameras directly bypassing the NVR but the NVR still recording.

Thanks a lot
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 20, 2020

I don't have dropbear or ftp binaries handy. But I do have some notes on pulling stills/video.

On my model, I can pull a still .jpg from Channel 1 with http://<host>/cgi-bin/snapshot.cgi?chn=0&u=<user>&p=<pass>. Channel 2 is chn=1, and so on. Computers start counting with 0 while humans are weird and start with 1. Pulling stills with this method does not impact ongoing NVR recordings.

I can pull archived .flv video with http://<host>/cgi-bin/flv.cgi?u=<user>&p=<pass>&mode=time&chn=0&begin=<start>&end=<stop> where start and stop are UTC seconds since epoch. So 2020-11-17 15:27:43 Pacific would be 1605626863. If video isn't available for a given time/channel, I get a 404.

To get stills directly from cameras shouldn't be too hard as my NVR defaults to setting up bridging between the wireless and wired networks. If I create an alias on my host connected to the same LAN with an IP like 172.20.14.123 then I can talk directly to the cameras at their IP addresses 172.20.14.32, etc. I can see a list of the IP's in the NVR management interface. Or just use a network scanner to find them.

Not having any direct experience with your NVR, I'm just speculating that it might work similarly.

@wes1993
Copy link

wes1993 commented Nov 20, 2020

Thanks again for all your help :-D
I have tried this links but won't work with my nvr..:
http:///cgi-bin/snapshot.cgi?chn=0&u=&p=
http:///cgi-bin/flv.cgi?u=&p=&mode=time&chn=0&begin=&end=
If i put the ip address i can access and ping only the nvr from 172.20.14.1 and can't reach other cameras.. :-(

Screen of my route:
image

some suggenstion?
Again thanks a lot
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 21, 2020

I'm assuming that you have replaced and in the URLs above. These are the credentials used in the web interface on the NVR. For me, the default user is "admin". Leaving those blank really shouldn't work. and also can't be blank for me. To see what URL the web interface is using, start up Chrome and load the NVR webpage. When you are at a point where you can do something interesting, like request a still from a camera, press F12 to open the Developer Tools, select the Network tab, then go click the link of interest on the NVR page. In the developer tool-pannel, you should see the URLs being loaded in the File column. Right click and Copy when you find the one you want. There are frequently more parameters provided by the stock web interface than are actually required. So you can experiment with removing things to simplify the URL. Worst case, it will cause the NVR to crash/reboot. But there shouldn't be any harm in that. Maybe it is best to avoid late night tinkering when the NVR could be capturing video that you will want to review later.

Running brctl show br0 will describe the bridge Iface listed in your route output above. Based on the routing table, it appears to include the 172.20.14.0/24 and 192.168.0.0/24 networks. There may be an IP/routing issue on the source computer side. Making sure you set a static IP of 172.20.14.123 or similar and that should be all you need unless the NVR is doing some. Check for packet filtering with iptables -nvL.

There does appear to be a routing issue on the NVR where it is trying to route packets destined for unknown hosts (Internet) via two different gateways: 192.168.0.20 and 192.168.0.1 which may indicate an issue with the DHCP gateway configuration on your network. With two default routes having the same Metric, some packets may be lost if one of those gateways fails. But as previously mentioned, letting this thing talk to the internet is a risk. So broken routing doesn't need to be fixed. This won't impact hosts on the wired network claiming to be in the 172.20.14.0/24 network.

Another option for talking to cameras is to route and NAT through the NVR instead of relying on bridging.

echo 1 >/proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to 172.20.14.1

Then anyone in the 192.168.0.0/24 should be able to talk to a 172.20.14.23 camera by adding a static route for 172.20.14.0/24 to the 192.168.0.x address of the NVR.

To get a little more info about what hosts your NVR sees, try arp -na to list neighbor devices (cameras) that it has been talking to recently. And ifconfig will show you the 192.168.0.x IP which you probably already know.

@wes1993
Copy link

wes1993 commented Nov 22, 2020

Thanks again a lot for your help !! :-D
The first commad brctl show br0:
image
The second command told me not found iptables -nvL :-(
there is some other command to do this:

Then anyone in the 192.168.0.0/24 should be able to talk to a 172.20.14.23 camera by adding a static route for 172.20.14.0/24 to the
192.168.0.x address of the N

This is the best way for my configuration, i have also assigneed a correct ip to my pc bu i can't contact the cameras, only the NVR via 172.20.14.1.

Some other info:

image

image


Via F12 from the webpage of NVR i have only this:
F12:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="icon" href="./favicon.ico" type="image/x-icon" />
  <link rel="stylesheet" href="./dist/style.css">
  <title></title>
</head>

<body>
  <div id="app"></div>
  <script type="text/javascript" src="view1.js"></script>
  <script src="./dist/build.js"></script>
  <script src="./dist/import.js"></script>
  <script src="./main.js"></script>
  <!-- <script type="text/javascript" src="xml.js"></script> -->
  <script type="text/javascript" src="swfobject.js"></script>
  <script>
  </script>
</body>

</html>

The view1.js:

//var dvr_camcnt = Cookie.get("dvr_camcnt");;
var dvr_clientport;
var dvr_ocx;
var dvr_type = "sub";;
var iSetAble, iPlayBack;
var b = true;
var osd_status;
function nvrPlayerInit_IE(dvr_usr, dvr_pwd, dvr_channel) {
	dvr_ocx = document.getElementById("client_ocx");
	try {
		if (dvr_ocx.Version && versionCompare(dvr_ocx.Version, '1, 1, 0, 4') >= 0) {
			dvr_ocx.style.height = '100%'
			document.getElementsByClassName('mask_ipcam')[0].style.display = "none"

		} else {
			document.getElementsByClassName('mask_ipcam')[0].style.display = "block"
			throw '请下载最新版本的控件!';
		}
	} catch (e) {
		b = false;
		load_attract();
	}
	dvr_ocx.m_channel = dvr_channel
	dvr_ocx.SetInfoDispMode(0);
	dvr_ocx.EnableSoundAll(false);
	// $('div.mask_ipcam').css('padding-top',$('.mask_ipcam').height()/2+'px');
	document.getElementsByClassName('mask_ipcam')[0].style['padding-top'] = document.getElementsByClassName('mask_ipcam')[0].offsetHeight / 2 + 'px'
	if (b) {
		dvr_ocx = document.getElementById("client_ocx");
		if (dvr_ocx.GetChannelNum == null) {
			// alert(language_find("alert_OCX_error"));	
			return;
		}
		console.log(window.location.hostname + "," + window.location.port+","+dvr_usr+","+dvr_pwd);
		var ret = dvr_ocx.CheckConnect(window.location.hostname, parseInt(window.location.port == "" ? "10000" : window.location.port), dvr_usr, dvr_pwd);
		console.log(ret)
		// if(ret != true)
		// {
		// 	alert("alert_Connect_error");
		// }
	}
};
function nvrPlayerInit_webKit(dvr_usr, dvr_pwd) {
	dvr_type = "sub";

	var flashvars =
		{
			player_max: 16,
			usr: dvr_usr,
			pwd: dvr_pwd
		};
	var params =
		{
			player_max: 16,
			allowFullScreen: "true"
		};
	var attributes =
		{
			id: "viewer",
			name: "viewer"
		};
	swfobject.embedSWF("JaViewer.swf?player_max=4", "flashcontent", "100%", "100%", "10.2.0", "expressinstall.swf", flashvars, params, attributes);
}

window.onunload = function () {
	dvr_ocx.CloseAll();
};
window.onbeforeunload = function () {
	dvr_ocx.CloseAll();
};


function get_file_name(full_path) {
	var arr = full_path.split("/");
	return arr[arr.length - 1];
}

function ptz_send(cmd) {
	var chn = dvr_ocx.GetSelectChl();
	if (chn == -1) {
		alert("select chn");
		return;
	}

	var xmldoc = loadXMLString("<juan ver=\"0\" squ=\"abcdef\" dir=\"0\" enc=\"1\"><ptzctrl usr=\"" + dvr_usr + "\" pwd=\"" + dvr_pwd + "\" chn=\"" + chn + "\" cmd=\"" + cmd + "\" param=\"0\" /></juan>");
	var xmlstr = toXMLString(xmldoc);
	var ptzctrl_node;
	var errno_attr;
	var errno_value;


	$.ajax({
		type: "GET",
		url: "/cgi-bin/gw.cgi",
		processData: false,
		cache: false,
		data: "xml=" + xmlstr,
		async: true,
		beforeSend: function (XMLHttpRequest) {
			//alert("beforeSend");
		},
		success: function (data, textStatus) {
			//alert("recv:" + data);
			xmldoc = loadXMLString(data);
			ptzctrl_node = xmldoc.selectSingleNode("/juan/ptzctrl");
			if (ptzctrl_node != null) {
				errno_attr = ptzctrl_node.attributes.getNamedItem("errno");
				if (errno_attr != null) {
					errno_value = errno_attr.nodeValue;
					if (errno_value != "0") {
						alert("error!errno=" + errno_value);
					}
				}
			}
		},
		complete: function (XMLHttpRequest, textStatus) {
			//alert("complete:" + textStatus);
		},
		error: function (XMLHttpRequest, textStatus, errorThrown) {
			alert(language_find("alert_Communication_error_please_refresh_or_try_again_later"));
		}
	});
}

function pic_btn_down(img) {
	 dvr_ocx = document.getElementById("client_ocx");
	var file_name = get_file_name(img.src).split("_")[0];
	img.src = "images/" + file_name + "_2.jpg";
	switch (img.id) {
		case "pb_review":
			location.href = "playback.html";
			break;
		case "pb_settings":
			location.href = "settings.html";
			break;
		case "pb_1":
			if (dvr_ocx.GetCurDiv() != 0) {
				dvr_ocx.SetDispWndDivMode(0);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_4":
			if (dvr_ocx.GetCurDiv() != 1) {
				dvr_ocx.SetDispWndDivMode(1);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_9":
			if (dvr_ocx.GetCurDiv() != 2) {
				dvr_ocx.SetDispWndDivMode(2);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_16":
			if (dvr_ocx.GetCurDiv() != 3) {
				dvr_ocx.SetDispWndDivMode(3);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_16":
			if (dvr_ocx.GetCurDiv() != 3) {
				dvr_ocx.SetDispWndDivMode(3);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_25":
			if (dvr_ocx.GetCurDiv() != 4) {
				dvr_ocx.SetDispWndDivMode(4);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_36":
			if (dvr_ocx.GetCurDiv() != 5) {
				dvr_ocx.SetDispWndDivMode(5);
			}
			else {
				dvr_ocx.ChangePage(true);
			}
			break;
		case "pb_ptz_up":
			ptz_send(0);
			break;
		case "pb_ptz_left":
			ptz_send(2);
			break;
		case "pb_ptz_auto":
			ptz_send(8);
			break;
		case "pb_ptz_right":
			ptz_send(3);
			break;
		case "pb_ptz_down":
			ptz_send(1);
			break;
		case "pb_ptz_zd_i":
			ptz_send(9); //PTZ_CMD_IRIS_OPEN
			break;
		case "pb_ptz_zu_i":
			ptz_send(10); //PTZ_CMD_IRIS_CLOSE
			break;
		case "pb_ptz_zd_f":
			ptz_send(14); //PTZ_CMD_FOCUS_NEAR
			break;
		case "pb_ptz_zu_f":
			ptz_send(13); //PTZ_CMD_FOCUS_FAR
			break;
		case "pb_ptz_zd_z":
			ptz_send(12); //PTZ_CMD_ZOOM_IN
			break;
		case "pb_ptz_zu_z":
			ptz_send(11); //PTZ_CMD_ZOOM_OUT
			break;
		case "pb_conn_all":
			//		dvr_ocx.OpenAll();
			for (var i = 0; i < dvr_camcnt; i++) {
				dvr_ocx.OpenStream(i, dvr_type == "main" ? 0 : 1);
			}

			break;
		case "pb_disconn_all":
			for (var i = 0; i < dvr_camcnt; i++) {
				ret = dvr_ocx.GetChannelStatus(i);
				if (ret == true) {
					//dvr_ocx.OpenChannel(i);
					dvr_ocx.CloseStream(i);
				}
			}

			break;
		default:
			var ret;
			var chn = -1;
			var prefix = "chn_status_";
			if (img.id.substring(0, prefix.length) == prefix) {
				chn = parseInt(img.id.split("_")[2], 10);
				ret = dvr_ocx.GetChannelStatus(chn);
				if (ret == true) {
					dvr_ocx.CloseStream(chn);
				}
				else {
					dvr_ocx.OpenStream(chn, dvr_type == "main" ? 0 : 1);
				}
				//dvr_ocx.OpenChannel(chn);

			}
			break;
	}
}

function pic_btn_up(img) {
	var file_name = get_file_name(img.src).split("_")[0];
	img.src = "images/" + file_name + "_3.jpg";

	switch (img.id) {
		case "pb_ptz_up":
		case "pb_ptz_left":
		case "pb_ptz_right":
		case "pb_ptz_down":
		case "pb_ptz_zd_i":
		case "pb_ptz_zu_i":
		case "pb_ptz_zd_f":
		case "pb_ptz_zu_f":
		case "pb_ptz_zd_z":
		case "pb_ptz_zu_z":
			ptz_send(15); //PTZ_CMD_STOP
			break;
		default:
			break;
	}
}

var dvr_mute_all = false;
function audio_sys() {
	dvr_mute_all = !dvr_mute_all;
	dvr_ocx.EnableSoundAll(dvr_mute_all);
	if (dvr_mute_all == false) {
		$("#pb_mute_all")[0].src = "images/audio_close.jpg";
	}
	else {
		$("#pb_mute_all")[0].src = "images/audio_open.jpg";
	}
}

function switch_stream(chs,stream) {
	// dvr_type = $("#lst_type")[0].value;
	// for (var i = 0; i < dvr_camcnt; i++) {
	// 	ret = dvr_ocx.GetChannelStatus(i);
	// 	if (ret == true) {
	// 		//dvr_ocx.OpenChannel(i);
			// dvr_ocx.CloseStream(i);
			// dvr_ocx.OpenStream(i, dvr_type == "main" ? 0 : 1);
	// 	}
	// }
	dvr_ocx.CloseStream(chs);
	dvr_ocx.OpenStream(chs, stream == 0 ? 0 : 1);

	//	location.href = "view1.html?type=" + $("#lst_type")[0].value;
}

function versionCompare(ver1, ver2) {
	ver1array = ver1.replace(/\ /g, '').split(',');
	ver2array = ver2.replace(/\ /g, '').split(',');
	sv1 = parseInt(ver1array[3]) + parseInt(ver1array[2]) * 100 + parseInt(ver1array[1]) * 10000 + parseInt(ver1array[0]) * 1000000;
	sv2 = parseInt(ver2array[3]) + parseInt(ver2array[2]) * 100 + parseInt(ver2array[1]) * 10000 + parseInt(ver2array[0]) * 1000000;
	if (sv1 > sv2) {
		return 1;
	}
	else if (sv1 == sv2) {
		return 0;
	}
	else if (sv1 < sv2) {
		return -1;
	}
}

function load_attract() {
	$('div.mask_ipcam').show();
	var btn = false;
	var timer = setInterval(function () {
		if (!btn) {
			$('a.blink').css('color', '#E6AF14');
			btn = true;
		} else {
			$('a.blink').css('color', '#666');
			btn = false;
		}
	}, 500)

}```


Thanks a lot for your help
Stefano

@wes1993
Copy link

wes1993 commented Nov 23, 2020

I have found the FW and the root password also for the IP camera.
The camera FW is here: http://help.dvr163.com/index.php/572011%E7%B3%BB%E5%88%97%E5%8D%87%E7%BA%A7%E8%BD%AF%E4%BB%B6

I can't find a rtsp or something else stream link, only this:
https://gist.github.com/maxious/c8915a436b532ab09e61bf937295a5d2
But i don't understand how ca i use the link in the wiki.
Thanks a lot
Stefano

@iotola
Copy link

iotola commented Dec 16, 2020

I found this discussion really helpful. I have pretty much done the same as wes1993.
I would like to change the root password. So I was going to bind a new shadow file but there is no passwd command implemented on mine, so I cant create the hash to put in the new shadow file.
Does either of you know how I can make a hash to replace the default root without the passed command implemented?

@aSmig
Copy link
Author

aSmig commented Dec 17, 2020

It is very strange to not have the passwd command. It is normally included in busybox, so they had to set compile options to remove it.

To generate a password hash on another system: openssl passwd -5 on any *NIX based system will ask you for a password and generate a hash based on SHA-256. This will be quite a long hash, so not very friendly to type in, but much more secure than older crypt and MD5. If you want a weaker hash that is shorter, just drop the -5.

Copy link

ghost commented Jan 10, 2021

thanks for this. I was sent a "Victure NK200 CCTV Kit" from Amazon (free for leaving a review) - kit consists of NVR and 4 cameras. The NVR is K8208-W and the IP Cameras are IPG5223-W or IPG5222-W.
Here are my app.out and app.out_chain files:

app.out:

#!/bin/sh
/usr/sbin/telnetd &
mount --bind /media/usb1/passwd /etc/passwd
mount --bind /media/usb1/dummy_daemon.sh /bin/upgrade
mount --bind /media/usb1/dummy_daemon.sh /opt/sbin/hostapd
mount --bind /media/usb1/dummy_daemon.sh /opt/bin/IOTDaemon
for pid in $(pidof iot.Daemon cmd_start.sh upgrade hostapd); do kill -9 $pid; done
exec /media/usb1/app.out_chain

app.out_chain:

#!/bin/sh
ls -l /opt/app/app.out
umount /opt/app/app.out
cd /opt/app
exec ./app.out

All is working well so far, am keeping my eye on netstat output and since the dummy daemon replacement, I see no connections to the Internet

I'm trying to gain root access to the cameras but no joy so far. Only port 80 is open. I've found a Windows App (IPCAMSUITE) that can upgrade firmware, but each time this fails - the camera does appear to take management commands by HTTP, the upgrade does start but when the device reboots, it's still on the same version and the app says failed. In preparation I have a copy of the ROM which I hacked to start a telnet daemon. Short of opening the device up to access UART, I'm out of ideas - what can I do?
This is the camera's ROM: http://download.dvr163.com/IPC/WUXIAN%20%20IPC/IPCAKV3_20200601_IPCAMERA_AK19EV3_3_3_1_57501633.zip

Thanks for all the hard work in this discussion!

@wes1993
Copy link

wes1993 commented Jan 14, 2021

thanks for this. I was sent a "Victure NK200 CCTV Kit" from Amazon (free for leaving a review) - kit consists of NVR and 4 cameras. The NVR is K8208-W and the IP Cameras are IPG5223-W or IPG5222-W.
Here are my app.out and app.out_chain files:

app.out:

#!/bin/sh
/usr/sbin/telnetd &
mount --bind /media/usb1/passwd /etc/passwd
mount --bind /media/usb1/dummy_daemon.sh /bin/upgrade
mount --bind /media/usb1/dummy_daemon.sh /opt/sbin/hostapd
mount --bind /media/usb1/dummy_daemon.sh /opt/bin/IOTDaemon
for pid in $(pidof iot.Daemon cmd_start.sh upgrade hostapd); do kill -9 $pid; done
exec /media/usb1/app.out_chain

app.out_chain:

#!/bin/sh
ls -l /opt/app/app.out
umount /opt/app/app.out
cd /opt/app
exec ./app.out

All is working well so far, am keeping my eye on netstat output and since the dummy daemon replacement, I see no connections to the Internet

I'm trying to gain root access to the cameras but no joy so far. Only port 80 is open. I've found a Windows App (IPCAMSUITE) that can upgrade firmware, but each time this fails - the camera does appear to take management commands by HTTP, the upgrade does start but when the device reboots, it's still on the same version and the app says failed. In preparation I have a copy of the ROM which I hacked to start a telnet daemon. Short of opening the device up to access UART, I'm out of ideas - what can I do?
This is the camera's ROM: http://download.dvr163.com/IPC/WUXIAN%20%20IPC/IPCAKV3_20200601_IPCAMERA_AK19EV3_3_3_1_57501633.zip

Thanks for all the hard work in this discussion!

Hello,
for access via telnet try this:
root
j1/_7sxw
I will wait your reply if work :-D
I have also some infor on how we can capture the stream of the camera
Regards
Stefano

@wes1993
Copy link

wes1993 commented Jan 14, 2021

Hello Again :-D
I have one question, did someone have foud a way to switch from 8 cams to 16 cams?
I have seen that the CPU is the same so i think it's only a software limitation.
Some suggestion?
Best Regards
Stefano

Copy link

ghost commented Jan 15, 2021

the telnet port is closed, so no telnet :(

@iotola
Copy link

iotola commented Jan 18, 2021

Mine is also a Victure NK200 CCTV, and my cameras also only seem to have port 80 open, although my cameras appear to be different to the one you listed. So no telnet on mine either.

@aSmig
Copy link
Author

aSmig commented Jan 21, 2021

@salfordfred, it looks like the TTL UART has a getty running from inittab, so you should be able to physically open up the camera and get root that way. Root password in the firmware image is j1/_7sxw as mentioned by @wes1993. Also in the firmware image you linked, I see that telnetd is available and is even started by /etc/init.d/rcS so maybe something kills it later in the boot process? Maybe try spamming connections to port 23 during boot and see if telnet is ever open. There are plenty of other issues with the firmware including custom binaries that have shell execs like /usr/bin/anyka_ipc, /usr/bin/IOTDaemon and /sbin/nk_upgrade. Not to mention all the various shell scripts running as root with unsanitized input, like DHCP lease parsers, etc. But I'm lazy, so I would just open up a camera and go straight for UART. If nothing else, this would accelerate identifying vulnerabilities which can be accessed remotely across the same model.

Other interesting strings in /usr/bin/anyka_ipc:

/mnt/tf/debug.ini
telnetd &

Which could hint that if the debug.ini file exists on the TF storage, then telnetd will be started. Does your model camera have a Micro-SD card slot? Seems likely to be a dev feature not in production, but worth checking.

Copy link

ghost commented Jan 21, 2021

Thanks for the hints, I also noticed telnetd in rcS, but of course its not running afterwards. There is no micro SD slot, perhaps internally there is? I'll see if I can crack one open without breaking it!

@TippyLion28
Copy link

I was able to use these instructions to get into a similar whitelabel DVR from Amazon https://www.amazon.co.uk/gp/aw/d/B06VTHXDY7/
branded as Sannce.

I'm looking to find the Main Stream so I can create a mobile-friendly user interface for it. The app is horrible and slow :)

Does anyone know where I might find the 1080p stream for this thing? The app uses port 10000 and the Web UI is on port 80. Haven't found anything like an RTSP stream. Just a low-quality snapshot.cgi ;)

@wes1993
Copy link

wes1993 commented Mar 18, 2021

Unfortunately for me no... :(
But if you will find a way please write here or tell me :)

Best Regards
Stefano

@TippyLion28
Copy link

Aww that's a shame... My other idea was to figure out how to get that 172 subnet routed so that devices on my LAN can access the cameras individually. And hope that the camera IP addresses stay the same :D

As you mentioned earlier, iptables doesn't exist on this box so that might prove tricky :(

@wes1993
Copy link

wes1993 commented Mar 18, 2021

The only way for me is connect the camera via the Ethernet cable so i have the IP of my lan then connect the cameras with NVR.
But you won't use the WiFi only ethernet
Bye
Stefano

@TippyLion28
Copy link

Just a thought, would it be possible to route the log files to /dev/null or something? Just to avoid the log files growing and potentially getting too big, and avoiding clutter on the USB?

@wes1993
Copy link

wes1993 commented Mar 18, 2021

For this I have created a script I will attach the script tomorrow but the root is not so useful because the NVR gui is really limited... :(

@TippyLion28
Copy link

Ah awesome cheers, that'll be handy. I think the root is very useful because it gives us an extra level of control, and it lets us poke around and look for things like stream URLs :))

I'm sure I'll think of plenty of other ways to put this root shell to good use :)

@wes1993
Copy link

wes1993 commented Mar 18, 2021

I hope you could find a way and please write here if you find something.
From what I have seen there is an app and Everything in inside this app.
I can’t find the stream etc and the software is JavaScript (shit) that works only with explorer.. I have also tried with wireshark but nothing.... only the camera I have found the stream link but with a custom html header not simple and is H265 only so you couldn’t use wit normal browser

@TippyLion28
Copy link

TippyLion28 commented Mar 19, 2021

I'll keep poking and prodding and I'll log my findings here. I've spoken to Sannce support and they have been as useful as a chocolate teapot... They said there's no way to view the stream directly in VLC... I'm still determined to find a way :D

Interesting stuff in one of the log files:

�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/moo"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/snapshot"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/flv.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/bubble/live"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/jscript.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/gw.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/snapshot.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/sp.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/upload.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/upgrade_rate.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/cgi-bin/online_upgrade.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/index.cgi"
�[1;31m[     httpd.c:  43]�[0m Add a new cgi "/debug.cgi"

@TippyLion28
Copy link

I'm particularly interested in /bubble/live, jscript.cgi and debug.cgi

Bubble/live and jscript.cgi seems to hang with their responses, maybe they want specific parameters before they sends a reply.

debug.cgi is telling me username or password mismatch. even when supplying the admin username and password with the u and p parameters

@TippyLion28
Copy link

Another interesting tidbit in the same log file:

ENVIRONMENT: RtspServer default disable

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

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