Skip to content

Instantly share code, notes, and snippets.

@aSmig
Last active April 9, 2025 07:59
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.

@aSmig
Copy link
Author

aSmig commented Nov 18, 2020

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!

@wes1993
Copy link

wes1993 commented Nov 18, 2020

Hello aSmig!!
Thanks a lot for your help :-D really thanks a lot :-D
I have put this script inside my pendrive and rebooted the NVR but fail to start and go in a bootloop.
if i go via serial console i can see the mounted script in /opt/app/app.out but if i try to execute manually from serial console the script i have this error:

cd /opt/app/
/opt/app # ./app.out
-/bin/sh: ./app.out: not found
exec /media/usb1/app.out_chain "\$@"/opt/app # app.out
-/bin/sh: app.out: not found
/opt/app # more app.out
#!/bin/sh
/usr/sbin/telnetd &
echo 'toor::0:0:root:/root:/bin/sh' >> /etc/passwd
/opt/app #

But if i try to execute the command manually works well, i have do some modification:

  1. Changed this(problem with read only FS):
echo 'toor::0:0:root:/root:/bin/sh' >> /etc/passwd
-/bin/sh: can't create /etc/passwd: Read-only file system

With this:

mount --bind /media/usb1/passwd- /etc/passwd-
mount --bind /media/usb1/passwd /etc/passwd

Changed this (with your command the app won't start):

umount /opt/app/app.out
exec /opt/app/app.out

with this:

umount /opt/app/app.out
cd /opt/app
./app.out

so the files now are:

  1. app.out:
#!/bin/sh
/usr/sbin/telnetd &
mount --bind /media/usb1/passwd- /etc/passwd-
mount --bind /media/usb1/passwd /etc/passwd
exec /media/usb1/app.out_chain "\$@"
  1. app.out_chain
#!/bin/sh
umount /opt/app/app.out
cd /opt/app
./app.out

So my last question is how can i solve the problem of execution this script automatically?
from the log seems that can mount correctly the usb:

scsi 1:0:0:0: Direct-Access     SanDisk  Ultra USB 3.0    1.00 PQ: 0 ANSI: 6
sd 1:0:0:0: [sda] 30031872 512-byte logical blocks: (15.4 GB/14.3 GiB)
sd 1:0:0:0: [sda] Write Protect is off
sd 1:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
 sda: sda1
sd 1:0:0:0: [sda] Attached SCSI removable disk
hisi-femac 10010000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
check sda...
sda found...
usb media sda found...
usb media sda1 found...
FAT-fs (sda1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
enable log not found.
enable log2 found.
mount bind app.out.
enable log2...
-f: file size (blocks)             unlimited
-t: cpu time (seconds)             unlimited
-d: data seg size (kb)             unlimited
-s: stack size (kb)                8192
-c: core file size (blocks)        unlimited
-m: resident set size (kb)         unlimited
-l: locked memory (kb)             64
-p: processes                      860
-n: file descriptors               1024
-v: address space (kb)             unlimited
-w: locks                          unlimited
-e: scheduling priority            0
-r: real-time priority             0
163840
163840
1327
os= 116 mmz= 140
116
all_mem: 256, mmz_sign: 0
0
mem_sign is 0
all_mem is 256
OK
mmz_start: 0x87400000, mmz_size: 140M
sys_config: loading out-of-tree module taints kernel.

scsi 1:0:0:0: Direct-Access     SanDisk  Ultra USB 3.0    1.00 PQ: 0 ANSI: 6
sd 1:0:0:0: [sda] 30031872 512-byte logical blocks: (15.4 GB/14.3 GiB)
sd 1:0:0:0: [sda] Write Protect is off
sd 1:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
 sda: sda1
sd 1:0:0:0: [sda] Attached SCSI removable disk
hisi-femac 10010000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
check sda...
sda found...
usb media sda found...
usb media sda1 found...
FAT-fs (sda1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
enable log not found.
enable log2 found.
mount bind app.out.
enable log2...
-f: file size (blocks)             unlimited
-t: cpu time (seconds)             unlimited
-d: data seg size (kb)             unlimited
-s: stack size (kb)                8192
-c: core file size (blocks)        unlimited
-m: resident set size (kb)         unlimited
-l: locked memory (kb)             64
-p: processes                      860
-n: file descriptors               1024
-v: address space (kb)             unlimited
-w: locks                          unlimited
-e: scheduling priority            0
-r: real-time priority             0
163840
163840
1327
os= 116 mmz= 140
116
all_mem: 256, mmz_sign: 0
0
mem_sign is 0
all_mem is 256
OK
mmz_start: 0x87400000, mmz_size: 140M
sys_config: loading out-of-tree module taints kernel.
scsi 1:0:0:0: Direct-Access     SanDisk  Ultra USB 3.0    1.00 PQ: 0 ANSI: 6
sd 1:0:0:0: [sda] 30031872 512-byte logical blocks: (15.4 GB/14.3 GiB)
sd 1:0:0:0: [sda] Write Protect is off
sd 1:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
 sda: sda1
sd 1:0:0:0: [sda] Attached SCSI removable disk
hisi-femac 10010000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
check sda...
sda found...
usb media sda found...
usb media sda1 found...
FAT-fs (sda1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
enable log not found.
enable log2 found.
mount bind app.out.
enable log2...
-f: file size (blocks)             unlimited
-t: cpu time (seconds)             unlimited
-d: data seg size (kb)             unlimited
-s: stack size (kb)                8192
-c: core file size (blocks)        unlimited
-m: resident set size (kb)         unlimited
-l: locked memory (kb)             64
-p: processes                      860
-n: file descriptors               1024
-v: address space (kb)             unlimited
-w: locks                          unlimited
-e: scheduling priority            0
-r: real-time priority             0
163840
163840
1327
os= 116 mmz= 140
116
all_mem: 256, mmz_sign: 0
0
mem_sign is 0
all_mem is 256
OK
mmz_start: 0x87400000, mmz_size: 140M
sys_config: loading out-of-tree module taints kernel.

inside my pendrive i have this log:

run_app.sh: line 176: ./app.out: not found

One more thig:
The original app.out permission:
image
The mounted permission:
image

Is possibile that we need to compile the script for execution? there is a way to edit the file in the firmware rom and flash with new modded firmware?

Thanks again a lot for all your help
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 18, 2020

Good call on bind-mounting /etc/passwd, I had forgotten /etc/ was read-only on this platform. It is safe to skip passwd- because that is just a backup file and not used for authentication. There was a typo where "\$@" should be "$@" after writing to file. But since no arguments are provided by run_app.sh when calling app.out, supplying $@ can be dropped entirely. So no problems yet, just minor clean-up. To narrow the problem down, let's add some more info to the log file.

app.out:

#!/bin/sh
echo "entering /media/usb1/app.out"
/usr/sbin/telnetd &
ps | grep telnetd
mount --bind /media/usb1/passwd /etc/passwd
ls -l /etc/passwd
cat /proc/mounts
echo "leaving /media/usb1/app.out"
exec /media/usb1/app.out_chain

app.out_chain:

#!/bin/sh
echo "entering /media/usb1/app.out_chain"
echo -n "pre-umount "
ls -l /opt/app/app.out
umount /opt/app/app.out
echo -n "post-umount "
ls -l /opt/app/app.out
cd /opt/app
pwd
echo PATH=$PATH
echo "leaving /media/usb1/app.out_chain"
exec ./app.out

You should end up with something like app_20201118_112233.log in your usb drive and it will contain the output from the added commands above. Hopefully the added information will direct you to the issue.

Compiling is not needed or possible for shell scripts. (There are some tools which turn a shell script into an executable binary, but then it is no longer a shell script.) As long as the file permissions allow execution for the user trying to run it and there is a valid command interpreter listed on the first line after the #!, then it is as executable as needed. In this case, ls -l shows three x's which means everyone can run it. And the command interpreter is /bin/sh which is a symlink to busybox. So all good there.

It is certainly possible to build a new firmware image with whatever changes you like. This is a lot more work and there is a lot more risk of getting your device stuck in an unrecoverable state. It also means a firmware update from the manufacturer would wipe out your changes. If you choose to go down this path, I wish you the best of luck but won't be able to help without matching hardware.

@wes1993
Copy link

wes1993 commented Nov 18, 2020

Again thanks a lot for all your help!! :)
Tomorrow I will try with this new files :)
I have written to make a custom FW but only if I can’t find a way to make this hack working!!
Again really really thanks a lot!! :)

Stefano

@wes1993
Copy link

wes1993 commented Nov 19, 2020

Some news:
This are the new files:
app.out

#!/bin/sh
echo "entering /media/usb1/app.out"
/usr/sbin/telnetd &
ps | grep telnetd
mount --bind /media/usb1/passwd /etc/passwd
ls -l /etc/passwd
cat /proc/mounts
echo "leaving /media/usb1/app.out"
exec /media/usb1/app.out_chai

app.out_chain

#!/bin/sh
echo "entering /media/usb1/app.out_chain"
echo -n "pre-umount "
ls -l /opt/app/app.out
umount /opt/app/app.out
echo -n "post-umount "
ls -l /opt/app/app.out
cd /opt/app
pwd
echo PATH=$PATH
echo "leaving /media/usb1/app.out_chain"
exec ./app.out

The log app_201119_093647.log:

run_app.sh: line 176: ./app.out: not found

I possibile that can't find the interpreter?
Again thanks a lot
Stefano

@aSmig
Copy link
Author

aSmig commented Nov 19, 2020

Hmm, I'm curious about your version of run_app.sh. The one I extracted from FWHI36D_20200821_W-NVR_K8208-W_3_0_8_3_9013160200.rom has ./app.out on line 175 but the error above shows it on 176. Could there be a change in run_app.sh that is breaking the bind-mount? Since you have TTL UART set up, I suggest booting without the USB drive attached, get your UART shell connected, plug in the USB drive, then run /opt/run_app.sh. It will probably throw some errors and die, but when it is done, it would be very interesting to see the output of grep bind /proc/mounts; ls -l /opt/app/app.out /media/usb1/app.out.

While you have a shell open, you can verify that the interpreter is available like so:

# ls -l /bin/sh /bin/busybox
-rwxrwxr-x 1 root root 670388 Jul 15  2019 /bin/busybox
lrwxrwxrwx 1 root root      7 Nov 17 15:26 /bin/sh -> busybox
# 

If your output doesn't look remotely similar to the above, or busybox doesn't have -rwx at the beginning of the modes (since you are running it as root, the owner of the file), then that is totally a problem. I'm betting your output will match because many /etc/init.d/ scripts are using the same interpreter and working well enough to get run_app.sh started.

And while I'm throwing out wild theories, would you happen to be editing app.out and app.out-chain on your USB drive in Windows? If so, any chance the line termination is \r\n?

@wes1993
Copy link

wes1993 commented Nov 19, 2020

Hello aSmig and thanks a lot for all your help again :-D
When i run with TTL i can't write anything, seems that the app.out once executed block TTL input but,
If i put the usb the boot stop because the app.out won't run and after that for 30 sec i can put something, after this the system will reboot...
anyway this is the app.out from my TTL output:

/opt # more run_app.sh
#!/bin/sh

#Mount usb disk
MOUNT_DIR=/media/usb1
CONFIG_DIR=/config/json
LOG_REDIRECT=0

#Restore machine in remote network
ifconfig lo up
ifconfig eth0 192.168.1.114 up

#config
mount -t jffs2 /dev/mtdblock6 /config

if [ ! -d /config/json ]; then
cp /opt/app/bin/json /config -rf
fi


# check mount status
if [ -r $MOUNT_DIR ];then
        echo "mount dir('$MOUNT_DIR') is exist now!"
else
        echo "mount dir('$MOUNT_DIR') isn't exist now!"
        mkdir $MOUNT_DIR
        sleep 2
fi

#mount usb
sleep 1
for i in a b c d e f g h i j k l
do
        echo "check sd$i..."
        if [ -e /sys/block/sd"$i" ];then
                echo "sd$i found..."
                rv=$(cat /sys/block/sd"$i"/removable)
                if [ "$rv" == "1" ];then
                        echo "usb media sd$i found..."
                        for n in 1 2 3 4 5 0
                        do
                                udisk_index=$n
                                if [ "$n" -eq "0" ];then
                                        udisk_index="";
                                fi
                                if [ -b "/dev/sd$i$udisk_index" ];then
."                                      echo "usb media sd$i$udisk_index found..--More-- (23% of 3830 bytes)
                                        mount -t vfat /dev/sd"$i"$udisk_index $MOUNT_DIR
                                        break;
                                else
                                        echo "usb media sd$i$udisk_index not found..."
                                fi
                        done
                        break
                fi
        else
          echo "sd$i... not found"
        fi
done

#check log redirect
if [ -e $MOUNT_DIR/enable_log ];then
                echo "enable log found."
                rv=$(cat $MOUNT_DIR/enable_log)
                if [ "$rv" == "1" ];then
                        touch /tmp/udisk_loging
                        echo "enable log..."
                        LOG_REDIRECT=1
                        ulimit -c unlimited
                        ulimit -a
                        echo "$MOUNT_DIR/%e_%t.core" > /proc/sys/kernel/core_pattern
                        mv $MOUNT_DIR/enable_log $MOUNT_DIR/disable_log
                fi
else
        echo "enable log not found."
fi

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/app.out ];then
                                echo "mount bind app.out."
                                mount --bind $MOUNT_DIR/app.out /opt/app/app.out
                        fi

                        touch /tmp/udisk_loging
                        echo "enable log2..."
                        LOG_REDIRECT=1
                        ulimit -c unlimited
                        ulimit -a
                        echo "$MOUNT_DIR/%e_%t.core" > /proc/sys/kernel/core_pattern
                fi
else
        echo "enable log2 not found."
fi

if [ -e $MOUNT_DIR/enable_core_dump ];then
        echo "enable core_dump found."
        ulimit -c unlimited
        ulimit -a
        echo "$MOUNT_DIR/%e_%t.core" > /proc/sys/kernel/core_pattern
fi

if [ -e $MOUNT_DIR/enable_demo_video ];then
        echo "enable enable_demo_video found."
        touch /tmp/enable_demo_video
fi

if [ -e /config/gdb_open ];then
        echo "open GDB debug."
        ulimit -c unlimited
        ulimit -a
        echo "/opt/rec/a1/%e_%t.core" > /proc/sys/kernel/core_pattern
fi

#restore factory
if [ -e $MOUNT_DIR/restore_factory_config_is_dangerous -a -r ${CONFIG_DIR} ];then
        echo "restore flag found."
        rm ${CONFIG_DIR}/* -rf
        mv $MOUNT_DIR/restore_factory_config_is_dangerous $MOUNT_DIR/restore_factory_config_is_dangerous_disable
fi

cat /proc/sys/net/core/rmem_default
cat /proc/sys/net/core/rmem_max
cat /proc/sys/vm/min_free_kbytes

###3MB缓存太大了??
#echo 3000000 > /proc/sys/net/core/rmem_default
#echo 3000000 > /proc/sys/net/core/rmem_max

值1024/2048,这个值OS内存小时不要设置太大否则可用内存会更加少--More-- (79% of 3830 bytes)
echo 2048 > /proc/sys/vm/min_free_kbytes
echo 150 > /proc/sys/vm/vfs_cache_pressure
# set stack size
ulimit -s 4096

#load driver
cd /opt/module
if [ "$LOG_REDIRECT" == "1" ]; then
    sh load -i
else
    sh load -i -hubreset
fi

#######for test
#rmmod wdt

hwclock -s

if [ -e "/config/debug" ];then
echo "do you want to run app.out(y or n)?"
exit 1;#####for test
read -t 1 -n 1 char
fi

if [ "$char" == "n" ];then
        login
fi

mount -t tmpfs none /opt/rec

if [ -x /opt/bin/daemon_start.sh ];then
/opt/bin/daemon_start.sh &
else
/opt/bin/daemon_server &
fi

#run_app
cd /opt/app/
if [ "$LOG_REDIRECT" == "1" ];then
        CURTIME=`date +%y%m%d_%H%M%S`
        LOG_FILE=app_$CURTIME.log
        ./app.out > "$MOUNT_DIR/$LOG_FILE" 2>&1
else
        ./app.out
fi

The folder opt

drwxrwxrwx   19 root     root           238 Nov 14  2018 ..
-rwxrwxrwx    1 root     root          3830 Aug 21 04:31 run_app.sh
drwxrwxrwx    2 root     root           155 Aug 21 04:31 ppp-bak
drwxrwxrwx   10 root     root           141 Aug 21 04:31 .
drwxrwxrwx    3 root     root           152 Aug 21 04:31 web
drwxrwxrwx    2 root     root           561 Aug 21 04:31 sbin
drwxrwxrwx    5 root     root            77 Aug 21 04:31 module
drwxrwxrwx    2 root     root           453 Aug 21 04:31 lib
drwxrwxrwx    2 root     root           321 Aug 21 04:31 bin
drwxrwxrwx   12 root     root           215 Aug 21 04:31 app
drwxrwxrwt    2 root     root            40 Nov 19 19:48 rec

The output of /opt/run_app.sh

/opt/run_app.sh
mount: mounting /dev/mtdblock6 on /config failed: Device or resource busy
mount dir('/media/usb1') is exist now!
check sda...
sda found...
usb media sda found...
usb media sda1 found...
mount: mounting /dev/sda1 on /media/usb1 failed: Device or resource busy
enable log not found.
enable log2 found.
mount bind app.out.
enable log2...
-f: file size (blocks)             unlimited
-t: cpu time (seconds)             unlimited
-d: data seg size (kb)             unlimited
-s: stack size (kb)                8192
-c: core file size (blocks)        unlimited
-m: resident set size (kb)         unlimited
-l: locked memory (kb)             64
-p: processes                      860
-n: file descriptors               1024
-v: address space (kb)             unlimited
-w: locks                          unlimited
-e: scheduling priority            0
-r: real-time priority             0
163840
163840
2048
os= 116 mmz= 140
116
all_mem: 256, mmz_sign: 0
0
mem_sign is 0
all_mem is 256
OK
mmz_start: 0x87400000, mmz_size: 140M
insmod: can't insert './hi_module/sys_config.ko': File exists
insmod ./hi_module/hi_osal.ko mmz=anonymous,0,0x87400000,140M anony=1 || report_error
insmod: can't insert './hi_module/hi_osal.ko': File exists
******* Error: There's something wrong, please check! *****

grep bind /proc/mounts; ls -l /opt/app/app.out /media/usb1/app.out

/ # grep bind /proc/mounts; ls -l /opt/app/app.out /media/usb1/app.out
-rwxr-xr-x    1 root     root           235 Nov 19 10:35 /media/usb1/app.out
-rwxr-xr-x    1 root     root           235 Nov 19 10:35 /opt/app/app.out

ls -l /bin/sh /bin/busybox

/ # ls -l /bin/sh /bin/busybox
-rwxrwxrwx    1 root     root        670388 Jul 15  2019 /bin/busybox
lrwxrwxrwx    1 root     root             7 Nov  2  2018 /bin/sh -> busybox

Seems that the script can mount correctly the app.out script but refuses to run, after boot with usb see the screen below:

cd /opt/app/
/opt/app # pwd
/opt/app
/opt/app # ls -latr
-rwxrwxrwx    1 root     root           970 Aug 21 04:31 user.conf
drwxrwxrwx    2 root     root           542 Aug 21 04:31 static
drwxrwxrwx    7 root     root           100 Aug 21 04:31 resource
drwxrwxrwx    3 root     root            27 Aug 21 04:31 luiengine
drwxrwxrwx    6 root     root           684 Aug 21 04:31 icon
drwxrwxrwx    2 root     root            60 Aug 21 04:31 font
drwxrwxrwx    7 root     root            84 Aug 21 04:31 dialog
-rwxrwxrwx    1 root     root          7228 Aug 21 04:31 ddns.conf
drwxrwxrwx    2 root     root           152 Aug 21 04:31 config_ini
drwxrwxrwx    3 root     root            27 Aug 21 04:31 bin
drwxrwxrwx   10 root     root           141 Aug 21 04:31 ..
drwxrwxrwx    2 root     root            35 Aug 21 04:31 loss
drwxrwxrwx    2 root     root            36 Aug 21 04:31 audio
drwxrwxrwx   12 root     root           215 Aug 21 04:31 .
-rwxr-xr-x    1 root     root           235 Nov 19 10:35 app.out
/opt/app # more app.out
#!/bin/sh
echo "entering /media/usb1/app.out"
/usr/sbin/telnetd &
ps | grep telnetd
mount --bind /media/usb1/passwd /etc/passwd
ls -l /etc/passwd
cat /proc/mounts
echo "leaving /media/usb1/app.out"
exec /media/usb1/app.out_chain/opt/app #

Seems that can't find the interpreter.. :-( below some other ways to call the script:

/ # cd /opt/app
/opt/app # ./app.out
-/bin/sh: ./app.out: not found

/opt/app # sh app.out
entering /media/usb1/app.out
: not foundne 3:
 failed: No such file or directory on /etc/passwd
: No such file or directory
': No such file or directorys
leaving /media/usb1/app.out
app.out: exec: line 9: /media/usb1/app.out_chain: not found

/opt/app # exec app.out
-/bin/sh: exec: line 4: app.out: not found


No success... :-(, df-h seems that mount it's ok:

df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 1.0M      1.0M         0 100% /
tmpfs                    54.4M         0     54.4M   0% /dev
tmpfs                    54.4M         0     54.4M   0% /tmp
tmpfs                    54.4M         0     54.4M   0% /media
tmpfs                    54.4M         0     54.4M   0% /var/run
tmpfs                    54.4M         0     54.4M   0% /var/lock
/dev/mtdblock4           10.8M     10.8M         0 100% /opt
/dev/mtdblock6          704.0K    240.0K    464.0K  34% /config
/dev/sda1                14.3G     16.4M     14.3G   0% /media/usb1
/dev/sda1                14.3G     16.4M     14.3G   0% /opt/app/app.out
none                     54.4M         0     54.4M   0% /opt/rec

Really really thanks again a lot for all your help :-D

Best regards
Stefano

@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

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