Aerosol Posted January 8, 2015 Report Posted January 8, 2015 In Part 1 & Part 2 of this RE blog series you saw how we reverse engineered the Dropcam and got access to the file system. In this final post of the series we'll examine some of the binaries found on the file system and play a bit with Lua code we found there. As usual we'll talk about some of the lessons learned from some failures in the analysis process as well as successes. We'll conclude with a release of a small tool that can aid reversers who are looking at Lua disassembly.The Lua code we found on the system is packed inside the Dropcam's /usr/connect binary which was obtained from the rooted Dropcam as described in our previous blog post (Part 2.) We unpacked the connect binary; it's compressed/packed with upx but that is trivial to undo. Once unpacked we loaded the binary in our trusty IDA and looked around a little bit. We noticed it was writing a file named /tmp/connect.bin and then running this command via a call to system():rm -rf /tmp/connect && mkdir /tmp/connect && tar zx -f /tmp/connect.bin -C /tmp/connect && rm /tmp/connect.binSo it looks like /usr/bin/connect is decompressing a tar.gz file hidden inside the connect binary itself. The IDA screenshot below shows the function that writes the file and then calls the shell command. This function is called with the arguments 0x8393c (the address of the connect.bin data in memory) and 0x29203 (the length of the file):We extracted the file using dd:dd if=./connect.decompressed of=connect.tar.gz bs=1 skip=473404 count=168451And then, we unpacked the .tar.gz file and took a look at what was there: $ ls -la total 808drwxrwxrwx 1 nico staff 4096 Feb 21 15:20 .drwxrwxrwx 1 nico staff 4096 Nov 11 20:35 ..-rwxrwxrwx 1 nico staff 1504 Apr 23 2013 containers.bin-rwxrwxrwx 1 nico staff 5879 Apr 23 2013 decoder.bin-rwxrwxrwx 1 nico staff 1038 Apr 23 2013 descriptor.bin-rwxrwxrwx 1 nico staff 10376 Apr 23 2013 dispatch.bin-rwxrwxrwx 1 nico staff 54727 Apr 23 2013 droptalk_pb.bin-rwxrwxrwx 1 nico staff 9360 Apr 23 2013 encoder.bin-rwxrwxrwx 1 nico staff 1243 Apr 23 2013 hello.bin-rwxrwxrwx 1 nico staff 545 Apr 23 2013 hwver.bin-rwxrwxrwx 1 nico staff 4279 Apr 23 2013 ir.bin-rwxrwxrwx 1 nico staff 879 Apr 23 2013 list.bin-rwxrwxrwx 1 nico staff 615 Apr 23 2013 listener.bin-rwxrwxrwx 1 nico staff 650 Apr 23 2013 main.bin-rwxrwxrwx 1 nico staff 2363 Apr 23 2013 monitor.bin-rwxrwxrwx 1 nico staff 708 Apr 23 2013 motion.bin-rwxrwxrwx 1 nico staff 2010 Oct 29 19:48 net.bin-rwxrwxrwx 1 nico staff 2607 Apr 23 2013 oldiags.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_01_3D_hwrev_1.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_01_3D_hwrev_2.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_02_3D_hwrev_1.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_02_3D_hwrev_2.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_03_3D_hwrev_1.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_03_3D_hwrev_2.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_04_3D_hwrev_1.bin-rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_04_3D_hwrev_2.bin-rwxrwxrwx 1 nico staff 3280 Apr 23 2013 persistence.bin-rwxrwxrwx 1 nico staff 329 Apr 23 2013 platform.bin-rwxrwxrwx 1 nico staff 3365 Apr 23 2013 platform_a5s.bin-rwxrwxrwx 1 nico staff 551 Apr 23 2013 platform_local.bin-rwxrwxrwx 1 nico staff 20750 Apr 23 2013 protobuf.bin-rwxrwxrwx 1 nico staff 191 Apr 23 2013 rtp.bin-rwxrwxrwx 1 nico staff 643 Apr 23 2013 settings.bin-rwxrwxrwx 1 nico staff 9931 Apr 23 2013 states.bin-rwxrwxrwx 1 nico staff 912 Apr 23 2013 status.bin-rwxrwxrwx 1 nico staff 3822 Apr 23 2013 streams.bin-rwxrwxrwx 1 nico staff 1505 Apr 23 2013 text_format.bin-rwxrwxrwx 1 nico staff 1525 Apr 23 2013 type_checkers.bin-rwxrwxrwx 1 nico staff 3047 Apr 23 2013 update.bin-rwxrwxrwx 1 nico staff 601 Apr 23 2013 usb.bin-rwxrwxrwx 1 nico staff 2602 Apr 23 2013 util.bin-rwxrwxrwx 1 nico staff 1468 Apr 23 2013 watchdog.bin-rwxrwxrwx 1 nico staff 3620 Apr 23 2013 wire_format.binInspecting the first .bin file we see these are Lua byte-code files. The first five bytes were those of a Lua Bytecode Header: +------------------------+| 1B | 4C | 75 | 61 | 52 | => Lua 0x52+------------------------+These files contain compiled Lua bytecode that supplements the logic in the connect binary. From the initial examination, we saw the bytecode was Lua 5.2 bytecode. The structure of a Lua bytecode file is extensively documented; we'll just cover the necessary information in this post (for a quick overview take a look at this link).Of course we'd like to know what functionality is hidden in these files so we tried every decompiler we could get our hands on. Unfortunately they all complained about the byte-code version or died trying to interpret the bytes on the files. This is because the decompilers weren't up-to-date for Lua 5.2. This version of Lua adds a couple of instructions to the VM but the semantics and the byte-code format seems to be the same.Here were some of the decompilers we tried (among others):LuaDechttps://github.com/sztupy/luadec51/wikiunluac | SourceForge.netChunkSpy: a Lua 5 binary chunk disassembler -- khmanConsidering this, we tried to hack up the files to trick the decompilers into working with our target files but alas, nothing seemed to be working, the decompilers just died with errors stating that the chunk of code did not correspond to valid Lua code. Note: Pay careful attention to endianness when hacking up byte code files. We even considered patching a tool like unluac to support Lua 5.2 bytecode as this looked like the most mature out of the ones we tried, but this wouldn't be a trivial task and would require major surgery. Unluac and others weren't going anywhere without a major patch and we didn't have much time so we went lower-level to a bytecode disassembler.Enter: LuaAssemblyTools(LAT) - https://github.com/mlnlover11/LuaAssemblyTools.This Lua library allowed us to parse and disassemble the byte-code regardless of version and/or endianness. We were able to decompile the Lua 5.2 byte-code used in the connect binary into LASM (a LAT representation of Lua VM's instructions).Now we have disassembly, but it's ugly -- like DNSSec level of ugly. So our next challenge was what to do with the dissasembled code. The way tables and constants are handled in Lua's VM is great for machine consumption but human readable it is not! How many levels of indirection can one really keep track of in their head at the same time?Using LAT's LASM Decompiler we disassembled descriptor.bin into this: ; Decompiled to lasm by LASM Decompiler v1.0; Decompiler Copyright (C) 2012 LoDC; Main code.name "".options 0 0 1 2; Above contains: Upvalue count, Argument count, Vararg flag, Max Stack Size; Constants.const "module".const "descriptor".const "FieldDescriptor".const "TYPE_DOUBLE".const 1.const "TYPE_FLOAT".const 2.const "TYPE_INT64".const 3.const "TYPE_UINT64".const 4.const "TYPE_INT32".const 5.const "TYPE_FIXED64".const 6.const "TYPE_FIXED32".const 7.const "TYPE_BOOL".const 8.const "TYPE_STRING".const 9.const "TYPE_GROUP".const 10.const "TYPE_MESSAGE".const 11.const "TYPE_BYTES".const 12.const "TYPE_UINT32".const 13.const "TYPE_ENUM".const 14.const "TYPE_SFIXED32".const 15.const "TYPE_SFIXED64".const 16.const "TYPE_SINT32".const 17.const "TYPE_SINT64".const 18.const "MAX_TYPE".const "CPPTYPE_INT32".const "CPPTYPE_INT64".const "CPPTYPE_UINT32".const "CPPTYPE_UINT64".const "CPPTYPE_DOUBLE".const "CPPTYPE_FLOAT".const "CPPTYPE_BOOL".const "CPPTYPE_ENUM".const "CPPTYPE_STRING".const "CPPTYPE_MESSAGE".const "MAX_CPPTYPE".const "LABEL_OPTIONAL".const "LABEL_REQUIRED".const "LABEL_REPEATED".const "MAX_LABEL"; Upvalues.upval '' 1 0; Instructionsgettabup 0 0 256loadk 1 1call 0 2 1newtable 0 0 25settable 0 259 260settable 0 261 262settable 0 263 264settable 0 265 266settable 0 267 268settable 0 269 270settable 0 271 272settable 0 273 274settable 0 275 276settable 0 277 278settable 0 279 280settable 0 281 282settable 0 283 284settable 0 285 286settable 0 287 288settable 0 289 290settable 0 291 292settable 0 293 294settable 0 295 294settable 0 296 260settable 0 297 262settable 0 298 264settable 0 299 266settable 0 300 268settable 0 301 270settable 0 302 272settable 0 303 274settable 0 304 276settable 0 305 278settable 0 306 278settable 0 307 260settable 0 308 262settable 0 309 264settable 0 310 264settabup 0 258 0return 0 1 0 To understand this as quick as possible we need something to make LASM a bit more sane, time to write some code to do it ourselves! Lua is a register-based virtual machine so that makes our life a little easier.We made an easy script that rewrites the LASM code into something more human readable. It organizes the disassembly to a much more readable code display form, so consider the output of this tool somewhere in the middle of the spectrum between a straight disassembler and a decompiler (restructured disassembly?)If you're interested to learn more, here are a few presentations showing the internals of a Lua VM that came in handy for this task (http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf and Programming in Lua were a huge help).The resulting code from our tool can't be compiled (so it's not a true decompiler) but it was so much easier to follow than a straight disassembly. You can find the tool published on our Github here.Here we can see the description.bin output after using our LasmRewriter.py script:function main(...)module(descriptor)regs[0] = []regs[0][TYPE_DOUBLE] = 1regs[0][TYPE_FLOAT] = 2regs[0][TYPE_INT64] = 3regs[0][TYPE_UINT64] = 4regs[0][TYPE_INT32] = 5regs[0][TYPE_FIXED64] = 6regs[0][TYPE_FIXED32] = 7regs[0][TYPE_BOOL] = 8regs[0][TYPE_STRING] = 9regs[0][TYPE_GROUP] = 10regs[0][TYPE_MESSAGE] = 11regs[0][TYPE_BYTES] = 12regs[0][TYPE_UINT32] = 13regs[0][TYPE_ENUM] = 14regs[0][TYPE_SFIXED32] = 15regs[0][TYPE_SFIXED64] = 16regs[0][TYPE_SINT32] = 17regs[0][TYPE_SINT64] = 18regs[0][MAX_TYPE] = 18regs[0][CPPTYPE_INT32] = 1regs[0][CPPTYPE_INwhT64] = 2regs[0][CPPTYPE_UINT32] = 3regs[0][CPPTYPE_UINT64] = 4regs[0][CPPTYPE_DOUBLE] = 5regs[0][CPPTYPE_FLOAT] = 6regs[0][CPPTYPE_BOOL] = 7regs[0][CPPTYPE_ENUM] = 8regs[0][CPPTYPE_STRING] = 9regs[0][CPPTYPE_MESSAGE] = 10regs[0][MAX_CPPTYPE] = 10regs[0][LABEL_OPTIONAL] = 1regs[0][LABEL_REQUIRED] = 2regs[0][LABEL_REPEATED] = 3regs[0][MAX_LABEL] = 3return regs[0]endThis gets the disassembly to the point where we can easily understand it, compared to what we had before which was just horrible. Now that we can disassemble the files we see that they control the logic of the device, but the hardware access is done at a lower level. More so, the System-on-a-Chip has some interesting features like setting up the parameters of your video input and output and the image post-processing is done by the hardware which is much more efficient.Lua on an embedded devices such as Dropcam is compact and safer to write than C, so that's a good idea from the security front. The Linux kernel and it's device drivers running on the device take care of everything real-time related and they expose this functionality to Lua the Unix way i.e. everything is a file. You can open a /dev/ file to access the stream of video and manipulate camera functionality. Everything for image conversion, filtering, etc. is taken care of in the low-level drivers. (Note: a bit more detail on this topic can be be found in SynAck's recent presentation which was published after the research you're reading in this blog-post was conducted.)This way of using Lua on embedded devices is a little different than project like eLua (eLua - eluaproject) which takes the Lua VM and make it run on small embedded devices (to check the supported CPUs click Overview - Status - eluaproject). We've seen that used on other embedded devices we hack on.Well that's the conclusion of this blog post series, we hope you got a bit of insight into reversing embedded devices. We didn't publish any 0day vulns in these posts, 0days are a given in every product if you look hard enough, this blog series was meant to give the beginner/intermediate IoT reverser some guidanceSource Quote