banner



Can Two Reolink Cameras Use Same Rstp Port 554

Way back in late 2019, I dissected a Reolink B800 IP camera to demonstrate the various parts of an embedded Linux system. It's fairly prissy hardware, actually—information technology has a 4K video sensor, a microphone, power over Ethernet, and is nominally waterproof. And yes, information technology runs Linux.

Reolink B800

Information technology came in a "kit" of half dozen cameras and an NVR (a dedicated recording box that also powers the cameras). Unfortunately, the NVR is pretty anemic: information technology's clearly an existing model with slight changes to back up 4K cameras, and it struggles to support more than one viewer at a fourth dimension.

However, I bought these cameras because I believed they supported open standards such as ONVIF, then I'd simply swap the NVR for a re-create of Blue Iris running on my server. At the time, the Reolink back up page conspicuously indicated that all of their non-battery-powered cameras supported RTSP. After the system was installed, information technology became apparent that the cameras did not in fact support RTSP—the only port open on them was port 9000. And so, barely outside my render window, Reolink updated their support page to say that the cameras would only work with their eight-aqueduct NVR or proprietary viewer apps.

This was, in the immortal words of Bill and Ted, bogus. Heinous. Most non-triumphant.

Artificial enough that I decided to pwn the photographic camera, reverse engineer the protocol, and write my own software to get the video stream. The end result is a new slice of open-source software called Neolink, which allows Blue Iris, Shinobi, or other NVR software to receive video from unmodified Reolink cameras.

Hither's how I did it.

Sniffing traffic

As a first step, I fired up Wireshark and captured traffic between the camera and its official Reolink PC client1.

The login stream

The merely thing that jumped out to me was the appearance of a sync word at the beginning of each parcel, 0xf0debc0a. (In little endian, this is 0x0abcdef0.) On a lark, I Googled this, and actually establish a project on GitHub from 2015 which was attempting to recall data from Swann cameras! A quick wait at the code told me that although they share the sync discussion and parcel header, the protocols for my camera and these older cameras were very dissimilar. The payload appeared to be encrypted in my captures. Furthermore, the code wouldn't even run due to some questionable arrow juggling. Time to extract what I could—the header layout was correct—and motility on.

Dissecting traffic with Wireshark

E'er, ever spend time developing debug or analysis tools. In my feel, such tools immediately pay back your time investment by a factor of iv or more. With what I knew, I was able to write a "Baichuan" protocol dissector for Wireshark using Mika'southward crawly tutorial. This was easy and fun: Wireshark lets you lot write dissectors in Lua (disclaimer: your definition of fun may vary). Now Wireshark could show me the payload lengths and message IDs. Not much, just it was a start.

Inspecting the firmware

In club to figure out whatever encryption and/or obfuscation the protocol was using, I planned to reverse engineer the firmware. I felt pretty confident that the underlying video was using a well-known protocol (specially since the camera seemed to have dedicated video encoding hardware). So "all" I'd accept to do is contrary engineer the rest of the protocol.

Easy peasy, right?

Why doesn't this camera support RTSP?

As a quick aside, information technology's natural to wonder why this camera doesn't support RTSP and/or ONVIF. After all, plenty of other Reolink cameras do. Because I'd like to give them the benefit of the incertitude, I'll advise the possibility that Reolink ran out of storage on this camera and had to axe some features.

After all, a 16MB flash chip would toll a whole 20 cents extra. This is just a toll-saving measure and definitely not vendor lock-in, hmmm?

Pwning the photographic camera

Correct, onward. With the protocol not immediately accessible, information technology was time to crack this photographic camera open up. My previous disassembly of the photographic camera has already indicated that it uses SPI NOR wink—bog standard for a small Linux system like this. I was wanting to effort out a really neat trivial SOIC socket I had discovered and ordered on Taobao a lilliputian while dorsum. I desoldered the flash and soldered on a socket instead.

The photographic camera was at present pwned, permanently—in that location was null I could do to brick it (I could always just reflash it with flashrom) and there was nothing Reolink could exercise to cease me from running my own code on it (since I had command of the showtime educational activity executed, if need be2).

The socketed flash

With the flash now conveniently socketed, I dumped it and used binwalk to inspect the layout. Hither's the flash layout—the nice round byte offsets that engineers tend to pick.three Bootloader, Linux uImage, squashfs rootfs, and JFFS2 persistent segmentation.

            DECIMAL       HEXADECIMAL     DESCRIPTION -------------------------------------------------------------------------------- 67266         0x106C2         eCos RTOS string reference 1769472       0x1B0000        uImage header 3604480       0x370000        Squashfs filesystem, little endian 7798784       0x770000        JFFS2 filesystem, niggling endian                      

This too lines upwards nicely with this visualization of the flash epitome, made by a neat trivial tool called BinVis.

The flash layout

binwalk also conveniently offers to unpack the filesystems it finds.

Low hanging fruit first

My first order of business was to detect the bodily camera binary. It was pretty easy to notice; it's sitting in its own directory at /mnt/app/dvr. The dvr binary also had an accompanying dvr.xml, which looked like a configuration file. A lilliputian grepping subsequently, and… sure enough, here were the magic words ONVIF and RTSP!

                          # ....              push_server              =              "pushx.reolink.com"              push_server_port              =              "9501"              support_3gnet              =              "0"              support_intelligence              =              "0"              support_smartsearch              =              "0"              support_onvif              =              "0"                              support_rtsp                =                "0"                            support_bc              =              "1"              support_3dnr_config_interface              =              "i"              default_3dnr_config              =              "1"              # ....                      

I suspected these were simply feature flags dictating to the software which features to enable. What if I just changed these zeros to ones?

Rick Sanchez changing a 1 to a 0

Rebuilding the rootfs wasn't quite as like shooting fish in a barrel as extracting it, but I cobbled together a command through trial and error. I wanted to become the squashfs format exactly the same as the one reported by binwalk, and then that I could be certain that the stock kernel would mount it. Since I was more often than not flight blind without a UART console, I didn't want any trouble.

            $ mksquashfs new-squashfs-root/ new-squashfs.img -comp xz -b 262144 -all-root -noappend $ dd if=new-squashfs.img of=pwned.bin bs=1 seek=$((0x370000)) conv=notrunc $ flashrom -p ft2232_spi:type=2232H,port=A -w pwned.bin                      

Alas, information technology was not to exist this piece of cake. The camera exhibited a remarkable lack of behavioral reform: no new ports opened, nothing.

For comparison, I downloaded and unpacked a firmware update for a different Reolink camera that did back up RTSP. The dvr binary for that camera was nearly 8 megabytes, while my victim'due south was but a footling over 3. Clearly, the engineers compiled out the unneeded bits.

Getting root

Fine. If Reolink has compiled the extra functionality out, then the least the camera could do is give me a beat out. While I was here, I decided I'd make some, ahem, extra modifications.

A quick Google did non yield prebuilt binaries of the tools I was looking for. Instead, I checked out a fresh copy of Buildroot and rapidly prepare information technology upwards for a baseline MIPS configuration with static linking, then asked for copies of gdbserver, busybox with all the fixin's, and strace:

            $ make qemu_mips32r2el_malta_defconfig $ make menuconfig  # (with appropriate edits made) $ make busybox gdb strace                      

Thirty minutes after, I had my tools. In the rootfs tree that binwalk had extracted, at that place's the usual assortment of startup scripts in /etc/init.d. With my tricked-out Busybox copied to /bin, and a symlink created named telnetd, I added an actress line to one of the startup scripts:

                          # Get a shell              /bin/telnetd -l /bin/sh                      

Fingers crossed, I reinstalled the flash and powered the photographic camera.

            $ telnet 192.168.i.187 Trying 192.168.one.187... Connected to 192.168.i.187. Escape character is '^]'.  /mnt/app #                      

Mwahahaha.

I am root

Reversing the protocol

What to practice with my newfound ability? I planned to start with a static assay of the firmware, first reverse technology the encryption scheme. If I got stuck, I could interrogate the photographic camera binary as it executed.

Once I could undo the encryption, I'd be able to see what the bodily protocol was like.

Static analysis with Ghidra

The traditional hobbyist tool for static analysis, IDA free edition, is no skilful here, because my binary is for MIPS, which the free IDA refuses to disassemble. Instead, the tool of choice is Ghidra, an astoundingly good open-source reverse engineering suite released by the NSA. Now, normally saying "I ran a binary the NSA gave me" will get you laughed out of the room. But Ghidra has been open up source for a while at present, so I feel reasonably safe installing it from the Curvation Linux repository.

The Ghidra website

If this website looks like information technology'due south made by a authorities bureau, it'southward because it is.

Ghidra is crawly. Seriously, this is a piece of software you'd accept to pay $10000 for, and it should be your go-to for reverse engineering work. In addition to the disassembler and analyzer, Ghidra also includes a decompiler, which prints pseudo-C code instead of leaving you lot excavation through MIPS associates. It also re-analyzes in realtime as you lot annotate function arguments with type information and names. These features easily cut my time spent reverse applied science in half.

So, armed with Ghidra and a false sense of confidence stemming from never having washed any reverse engineering science before, I went spelunking in the Baichuan binaries.

Strings: a bounty of information

There'due south a reason most contrary engineers start by examining the strings in an unknown binary—it's a technique that works. In my example, checking for strings in the desktop client and the firmware'southward server yielded debug print statements, function names (which Ghidra automatically annotated), and a couple other oddball strings that I'll talk about in a minute. Both codebases were conspicuously congenital around a shared proprietary "BCSDK" library.

Searching for crypt yielded a couple of candidate functions that purported to perform encryption:

Functions matching crypt search

Well, no RSA here, or anything resembling "real" encryption, except AES. (And I could find any AES keys embedded in the app in short club.)

Just what'due south this string stuck nonchalantly in the middle of the others?

A string referencing Charlie

That'southward not a function proper noun. I hit observe-references and read the lawmaking that uses information technology.

The Charlie Scrambler

Ghidra took me to this gem of a function:

The Charlie Scrambler in all its glory

Technically, this is the decryption role; there's another one that works in reverse.

It only takes a 2nd to sympathize what'southward going on hither: the "encryption" scheme is simply:

  • XOR the data with the string Charlie is the designer of P2P!!, so
  • mix upwards the bytes.

This isn't encryption. This is simply a scrambler.

Bravo, Charlie, your "blueprint" is permanently encoded in this protocol for all time. Don't curlicue your ain crypto, kids.

Unfortunately, the Charlie Scrambler is but called from UDP functions (see the cross-reference at the bottom of the Ghidra window). This meant it wasn't my pigeon; my photographic camera uses TCP. At this point I had no idea which of the other "encryption" functions were the right one for my camera, so it was fourth dimension to bring out my next weapon.

Dynamic analysis with gdb

With my shell access waiting, my next move was to adhere a debugger and command the dvr program remotely from my workstation using my cross-compiled gdbserver and strace tools. My Busybox included an FTP server and a TCP wrapper:

            $ /bin/busybox tcpsvd -vE 0.0.0.0 21 ftpd / -w -A tcpsvd: listening on 0.0.0.0:21                      

With this setup I could push whatever tools I wanted to the camera filesystem, even though I hadn't packed them into the firmware. I went through this procedure manually nigh twice before it became really tedious. This is the kind of matter it's possible to automate with expect, a Tcl (!) program that pretends to exist a console user. I scripted these interactions, which reduced the connect, tool push button, and gdb setup to a simple:

An added benefit of this setup is that I could stick any gdb commands I wanted to run at startup at the finish of the script, instead of writing a defended GDB script. These dynamic printf commands but impress in the GDB console when the camera hits a breakpoint, helpful for knowing which functions are beingness called without halting the camera:

            send_gdb "dprintf *0x478908, \"_Nets_Without_Password_Login_V20\\n\"" send_gdb "dprintf *0x4780ac, \"FUN_004780ac\\n\"" send_gdb "dprintf *0x6310f0, \"Md5_string_encrypt\\n\""                      

Feeding the watchdog

After attaching and halting the dvr daemon, the camera promptly crashed and reset. After some quick investigation, the camera had a watchdog enabled at /dev/watchdog—a very common setup for embedded devices. I was doing open heart surgery on this software—I didn't need some 2-fleck peripheral wandering past and hit information technology with an AED!

Busybox ships with a watchdog minder, so I simply fired it upward:

Trouble solved.

Charlie strikes once more

After my breakpoint was hit, I knew which encryption function I was dealing with: Nets_XmlEncryption. This was immediately practiced news, because after figuring out the encryption, I was probable dealing with nice ordinary XML, not some crazy half-baked handbag of C structs.

I took a look at the decompiled function in question in Ghidra, annotating as I went. Sure did look familiar…

The Nets_XmlEncryption disassembly

No engineering lessons had been learned during the redesign, and the influence of Charlie was alive and well! Here is the Charlie Scrambler back in force, but without the mixing step, and with a shorter central.

Fine, whatever. Implementing the Scrambler in my Wireshark dissector didn't take very long—although I was briefly baffled by a header of varying length depending on the message blazon. Once implemented, I was greeted with this glorious sight:

The decrypted login message in Wireshark

Again, I highly recommend writing plugins for Wireshark. You tin exercise it in Lua (or C if yous're that hardcore), and information technology only takes a couple hours to accept a really nice debug tool.

A brief history of the Baichuan protocol

I won't diameter you lot with the gory details, simply I'll summarize my findings. The Baichuan protocol has had several iterations over the years. The very oldest seem to be UDP-based, using a proprietary SDK chosen TUTK, illicit copies of which can conveniently be found on GitHub. This is no longer used; information technology'southward not even present in the camera code.

The adjacent variant is indeed a plaintext "bag of structs", which consists of a header and a torso specified past a bulletin ID in the header. This "legacy variant" is briefly used on the B800 then that both clients can negotiate an upgrade to the "modern variant," which is the scrambled XML-based scheme you see above.

On acme of this, modernistic letters tin can optionally accept a payload. A certain XML message switches the entire message ID into "binary mode," which supplies a raw data stream in subsequent messages. When the client sends a video-start control, the camera replies with a binary stream containing raw H.265 video. On summit of that, the payload can too exist more encrypted XML, carve up from the main XML for some reason.

On the whole, it'south actually quite a hurting to parse.

Once my Wireshark dissector was humming along, it was time to write a new client. I wanted my software to be fast, loftier-level, and correct, since it would exist part of a security system setup.

That's right: I rewrote Reolink'south software in Rust.

Rust evangelism strike force badge

Well, not exactly. Neolink is a new client completely from scratch. Information technology speaks the aforementioned Baichuan protocol as the photographic camera, and it extracts the video and forwards it to some other existent NVR client similar Blue Iris over RTSP. The parsing code is somewhat hairy4, but other than that, information technology'south straightforward.

Getting the video information

Here's all the client is capable of correct now. It's pretty easy to read if you want to await at the source.

  1. Transport a legacy login message to get the photographic camera to "upgrade" to modern letters. Note: this uses apparently MD5 encryption for your countersign, another, um, interesting design choice. Use a password that isn't in a rainbow table!
  2. Ship a modern login message to actually authenticate to the camera.
  3. Ship a showtime video request:
                          permit                            start_video                            =                            Bc::new_from_xml(                                          BcMeta              {                                          msg_id:              MSG_ID_VIDEO,                                          client_idx:              0,                                          encrypted:              true,                                          class:              0x6414,                                          },                                          BcXml              {                                          preview:              Some(Preview              {                                          version:              xml_ver(),                                          channel_id:              0,                                          handle:              0,                                          stream_type:              "mainStream".to_string(),                                          }),                                          ..Default::default()                                          });                                                        sub_video.send(start_video)?;                                    
  1. Spit out the binary data when it's received:
                          loop                            {                                                        permit                            msg                            =                            sub_video.rx.recv_timeout(self.rx_timeout)?;                                                        if                                          let                            BcBody::ModernMsg(ModernMsg              {              binary:              Some(binary),              ..              })                            =                            msg.body              {                                          data_out.write_all(binary.as_slice())?;                                          }                            }                                    

Wrap it in maroon and white, er, RTSP5

For this part of the program I reached for Gstreamer, which ships with an RTSP server. Gstreamer is… complex. However, their examples are fantastic; they even provided a sample RTSP server in Rust!

The general approach for feeding Gstreamer data from an arbitrary part of your plan is to use a block called an appsrc. This lets yous get a callback whenever Gstreamer wants information, or alternatively just push data whenever you take some and permit Gstreamer handle scheduling information technology. The latter approach is the i I went with hither, since the camera doesn't wait for a signal to send video data.

I wrapped everything in a Gstreamer pipeline:

            appsrc proper noun=baichuan is-live=truthful emit-signals=imitation max-bytes=0 ! h265parse ! rtph265pay name=pay0                      

Testing it with Blue Iris

The moment of truth arrived… could Blue Iris connect to my RTSP server and actually brandish the video?

Y'all bet.

The Neolink stream in Blue Iris

I present… me! In glorious 4K!

Future work

I've been soak-testing Neolink for a while now and I think it's pretty stable. Going forward I'thousand packaging information technology up as a real Windows service (not a control line program) to run alongside Blue Iris on my server. Go wait at it and download information technology for yourself!

I'm also interested in getting Neolink working with other "NVR only" Reolink cameras, of which there are quite a few. And so far I haven't purchased any other hardware, so if you have i of these cameras, please become in touch and so we can exam it. It might Only Work out of the box. Port scan your cameras! If they have port 9000 bachelor, chances are good that they speak the Baichuan protocol.

This project was a "only right" intro to reverse technology. Depression-security systems like these let you teach yourself the principles without actively trying to thwart opposite engineering. I taught myself a lot, and I promise it provides a lot of value for folks who own these cameras.

Finally, some shameless cocky-promotion: embedded Linux systems are actually pretty approachable! If you lot'd like to learn how to do this kind of thing, you might exist interested in my Mastering Embedded Linux series, designed to help you lot get an adept in hacking low-cost embedded Linux systems just like this photographic camera.

If you enjoyed this, you tin subscribe to my web log updates, or leave a comment below. Thank you for reading!


  1. The Reolink support forums always claim that their pages were "updated eight hours agone." This is obvious nonsense. They exercise update fairly frequently, but non constantly. ↩︎

  2. This is exactly the bespeak of secure boot schemes, where the immutable kicking ROM validates code confronting immutable encryption keys, preventing tampering. Needless to say, this camera does not implement such niceties. ↩︎

  3. binwalk also turns up a agglomeration of simulated positives, which are easy to ignore considering they don't accept nice round offsets. ↩︎

  4. Seriously, having packet decode be stateful is just ridiculous. ↩︎

  5. Aye, I'm a proud Mississippi State University bulldog. Yous as well tin can attend and become a computer engineer! ↩︎

Source: https://www.thirtythreeforty.net/posts/2020/05/hacking-reolink-cameras-for-fun-and-profit/

Posted by: carterawye2001.blogspot.com

0 Response to "Can Two Reolink Cameras Use Same Rstp Port 554"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel