<?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[Bomfather]]></title><description><![CDATA[Insights into kernel level security. ]]></description><link>https://substack.bomfather.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!vouS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64147e-95ad-4a05-9d04-25bd8f97c89e_1280x1280.png</url><title>Bomfather</title><link>https://substack.bomfather.dev</link></image><generator>Substack</generator><lastBuildDate>Sat, 02 May 2026 08:21:19 GMT</lastBuildDate><atom:link href="https://substack.bomfather.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Bomfather]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[bomfather@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[bomfather@substack.com]]></itunes:email><itunes:name><![CDATA[Neil Naveen]]></itunes:name></itunes:owner><itunes:author><![CDATA[Neil Naveen]]></itunes:author><googleplay:owner><![CDATA[bomfather@substack.com]]></googleplay:owner><googleplay:email><![CDATA[bomfather@substack.com]]></googleplay:email><googleplay:author><![CDATA[Neil Naveen]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Sandboxing Isn’t the Solution]]></title><description><![CDATA[Sandboxing is a pain... How can we get around its issues?]]></description><link>https://substack.bomfather.dev/p/sandboxing-isnt-the-solution</link><guid isPermaLink="false">https://substack.bomfather.dev/p/sandboxing-isnt-the-solution</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Tue, 24 Mar 2026 20:06:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0EUN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before diving into this, I want to say that sandboxing is VERY useful! But it isn&#8217;t the solution to everything.</p><p>Sandboxing is great, but sandboxing is a pain to use&#8230; The issue is that there is almost always a time when multiple executables have to share a machine. When there are too many moving parts, we need to change the way we approach security.</p><p>Our solution is kind of an anti-sandbox: everything can access everything, except for a couple of protected resources.</p><h1>How it works</h1><p>I think the easiest way to explain this is with an example.</p><p>So, in this example, we have a couple of executables, the server, <code>test.sh</code>, and <code>support.sh</code>.</p><p>The server accesses Postgres, which in turn accesses the DB.</p><p><code>test.sh</code> can access the GPU and push data to the server.</p><p>And <code>support.sh</code> is a helper for <code>test.sh</code>.</p><p>Note that in this example, the server accesses Postgres over IP and <code>test.sh</code> accesses the server over IP.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0EUN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0EUN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0EUN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg" width="1143" height="587" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:587,&quot;width&quot;:1143,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41472,&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://substack.bomfather.dev/i/192021990?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.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_!0EUN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0EUN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8fb7985-e063-400e-86f2-a6cdc56da47c_1143x587.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></figure></div><h2>Using VMs</h2><p>We can start by trying to secure this with VMs to sandbox the executables.</p><p>So, the server would get its own VM.</p><p>Postgres and the DB would get their own VM.</p><p>And, the <code>test.sh</code>, the GPU, and the <code>support.sh</code> would all be in a VM.</p><p>Each VM would access the others by poking a hole in its firewall and allowing IP access.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P_Bx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P_Bx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 424w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 848w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P_Bx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg" width="1155" height="629" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:629,&quot;width&quot;:1155,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53013,&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://substack.bomfather.dev/i/192021990?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.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_!P_Bx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 424w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 848w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!P_Bx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24863a09-d54c-441e-84bc-03ec8a4ce5ca_1155x629.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></figure></div><p>The issue is that the way this is implemented would violate the whole reason of using VMs. Since <code>test.sh</code> needs to access <code>support.sh</code>, both executables are running in a single VM.</p><p>Having two executables in a single VM negates the entire reason for using VMs in the first place, since it no longer provides sandboxing.</p><p>One real world example of this is Postgres itself. When it runs in the background, it mostly has a script that launches Postgres as a daemon on your system. This is just an example, but you will always have executables on your system that interact with each other (that&#8217;s what makes it a system).</p><h2>Our Solution</h2><p>Our solution is to protect only a few specific resources rather than protecting everything.</p><p>So, in this example, the DB and the GPU would be protected resources.</p><p>Then only Postgres can access the DB, and Postgres can only be accessed by the server.</p><p>The GPU can be accessed only by <code>test.sh</code>, and <code>test.sh</code> can access only the servers IP endpoint.</p><p>And, <code>support.sh</code> can access anyone since it can&#8217;t really access any protected resources.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iwGx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iwGx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iwGx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg" width="1202" height="645" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:645,&quot;width&quot;:1202,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:49814,&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://substack.bomfather.dev/i/192021990?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.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_!iwGx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iwGx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ee1577e-f292-4f90-9673-1deb2473862b_1202x645.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></figure></div><h1>Conclusion</h1><p>In the end, sandboxing is great, but as I said before, it&#8217;s a pain. That&#8217;s why we came up with our current solution.</p><p>If you want to do sandboxing, go for it! We just didn&#8217;t want to deal with the issues with sandboxing.</p>]]></content:encoded></item><item><title><![CDATA[Can we Improve on Software Supply Chain Security With IP Restriction?]]></title><description><![CDATA[What happens if we miss a vulnerability? How can we make sure it doesn't exfiltrate all of our data?]]></description><link>https://substack.bomfather.dev/p/can-we-improve-on-software-supply</link><guid isPermaLink="false">https://substack.bomfather.dev/p/can-we-improve-on-software-supply</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Tue, 17 Mar 2026 16:19:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vouS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64147e-95ad-4a05-9d04-25bd8f97c89e_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>The Problem</h1><p>What is the point of software supply chain security? Software supply chain security primarily focuses on ensuring we upgrade our dependencies to versions that have been patched for known vulnerabilities.</p><p>If a malicious actor uses a vulnerability that affects us, they can do three things. One, they can exfiltrate secrets, user data, and proprietary data. Two, they can encrypt your databases (etc) and ransom them back to you. Third, they could inject a malicious script into your code.</p><p>The issue with software supply chain security is that we might miss a vulnerability or two. A vulnerability might not be reported, or we might be a bit slow on implementing a patch/dependency update. That is why we created Bomfather as a last line of defense.</p><p>Up until now, Bomfather has had solutions for malicious actors encrypting your databases to ransom or injecting malicious scripts into your code, but we didn&#8217;t know how to deal with a malicious actor exfiltrating data. That is why we have implemented our IP protection.</p><h1>The Solution</h1><p>Imagine that our secrets are stored in a database and that every executable except for a couple of trusted ones is malicious. How can we make sure that only our trusted executables can access the external database?</p><p>To solve this, we decided to implement IP protection. This limits which IPs/DNSs an executable can access, and which executables can access an IP/DNS. If a malicious actor can&#8217;t access your DB, then they can&#8217;t steal your secrets.</p><p>All security provided by Bomfather is implemented using eBPF. We secure IPs by hooking into <code>lsm/socket_connect</code>. We restrict both IPs and DNS addresses. For a specific executable, we can restrict what IPs it can access. And we can stop every other executable from accessing the IP.</p><h1>The Difference between this and a Firewall</h1><p>Traditional firewalls usually enforce traffic rules at the host or network boundary. If multiple executables run on the same host, they often share the same network policy. Instead, this solution is restricted to the executable level.</p><h1>An Example of How this Works</h1><p>We have been talking very theoretically, so this might be easier to understand with an example.</p><p>Imagine that we have a KMS, and only a signer should be able to access it. We can attach two restrictions to the signer. Only the signer can access the KMS. And, the signer isn&#8217;t allowed to access any IP other than the KMS.</p><p>This works by allowing the signer to access the KMS and ensuring that no one else can.</p><p>We can take this example up a notch. If a malicious process modifies the signer then it can access the KMS, and exfiltrate data. To solve this, we can use <code>fs-verity</code> to sign the binary and only allow the unmodified signer to access the KMS (Read a bit more about this in our blog post about securing builds with <code>fs-verity</code>, <a href="https://open.substack.com/pub/bomfather/p/how-we-secure-builds-with-fs-verity?utm_campaign=post-expanded-share&amp;utm_medium=web">https://open.substack.com/pub/bomfather/p/how-we-secure-builds-with-fs-verity?utm_campaign=post-expanded-share&amp;utm_medium=web</a>).</p><h1>Conclusion</h1><p>In the end, this is a huge step forward in supply chain security since there is always the chance we miss a vulnerability, and if we do so, we need a way to make sure it can&#8217;t steal anything valuable!</p>]]></content:encoded></item><item><title><![CDATA[How We Secure Builds With fs-verity]]></title><description><![CDATA[We used to do our builds the "normal" way, but we have learned that it wasn't enough...]]></description><link>https://substack.bomfather.dev/p/how-we-secure-builds-with-fs-verity</link><guid isPermaLink="false">https://substack.bomfather.dev/p/how-we-secure-builds-with-fs-verity</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Mon, 16 Feb 2026 18:52:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iBxD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Secure software builds allow us to be confident that we are running and providing good and uncompromised software. Normal build security would check the software for known vulnerabilities, limit the number of dependencies, and call it a day. But we are a runtime security company, so that&#8217;s not enough.</p><p>With our builds:</p><ul><li><p>If you unplug the network cable from our build VM, the build still finishes.</p></li><li><p>If someone modifies the Go compiler, the kernel refuses to execute it.</p></li><li><p>If a process outside the approved build chain attempts to read or write to protected build paths, a security eBPF program blocks the bad action at the kernel level.</p></li></ul><p>We aren&#8217;t saying this is the only way to build software safely (it may be overkill). What we are saying is this is the level of control we wanted and needed.</p><h2>Why We Went This Far</h2><p>We have read Ken Thompson&#8217;s paper &#8220;Reflections on Trusting Trust&#8221; many times and understand the importance of the problem it describes.</p><p>We wanted to know how to verify that the exact toolchain used to produce a kernel-facing artifact was based on the source code that was committed. How do we verify that the toolchain was not modified between installation and use?</p><p>That led us to GCP VMs with custom images. We made sure not to use hosted runners since the most basic step in this journey was controlling the kernel, the boot chain, and all the tools used to create the artifact.</p><h2>Why Not Github Actions</h2><p>First of all, they are stuffed with bloatware, which is really nice when you don&#8217;t want to install dependencies manually, but really, really bad when you want any level of security. <a href="https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have">https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have</a></p><p>Furthermore, we needed to pin and boot specific kernels (5.18 and 6.18), set boot flags such as <code>lsm=bpf</code>, enable <code>fs-verity</code> on the root filesystem, and manage the lifecycle of signing material during image construction. <br><br>All of this is awkward or impossible on generic hosted runners, where the kernel and boot chain are not under your control.</p><p>Because of this, we run on custom VMs built from Packer images, ensuring everything remains under our control.</p><h2>What The Pipeline Actually Does</h2><p>It has three stages, each with very different costs. First, we prebake a hardened image using packer with a pinned kernel and a signed toolchain with <code>fs-verity</code> enabled. Then we vendor dependencies with <code>go work vendor</code>. Finally, we build and test in a short-lived VM with <code>GOPROXY=off</code> (which tells go not to pull in dependencies or upgrades), and we are currently working on turning off internet access on the entire build machine.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iBxD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iBxD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iBxD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:384818,&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://substack.bomfather.dev/i/188171036?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.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_!iBxD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iBxD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dc7e1e7-f816-4afc-8a02-94cade4d0bb1_1790x1000.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></figure></div><h2>Why We Use <code>fs-verity</code> Instead of Just Checksums</h2><p>Checksums give us confidence that the binary is good at a certain point in time. With checksums, we can verify what we download. What it does not tell us is whether the binary we downloaded is the same as the one we execute at runtime.</p><p><code>fs-verity</code> gives us that confidence, since it enforces that any binary that it monitors cannot be opened (and therefore executed) if the hash changes from boot. This is guaranteed by the Linux kernel and ensures that binaries are not modified after download and before execution.</p><p>This means that if a bad actor compromises your Golang executable, after your image is created, it can&#8217;t execute, and the whole build would fail.</p><h2>What We Sign</h2><p>We sign binaries that can influence build outputs, including the go toolchain internals, clang/LLVM, some supporting binutils, the protobuf toolchain, and other core build utilities.</p><p>This sounds really good, but in theory, it really isn&#8217;t. Whenever we want to upgrade anything, we need to do it manually. We need to rebuild our images, sign them, validate them, test that our builds pass, and ensure none of our security steps fail. While GitHub Actions may not be secure, it&#8217;s much easier to upgrade dependencies, utilities, and other components.</p><h2>Dogfooding our eBPF Agent, by making it secure our Builds</h2><p>This is the part we care about most, since running our security agent against our builds lets us constantly test and verify all our features in the real world.</p><p>We use the same eBPF LSM (Linux Security Modules) policy agent we ship to secure its own build. We have a couple of polices that allow us to significantly bump our security posture. The primary goal of our agents is to enforce expected behavior, so that bad binaries and processes cannot access resources they shouldn&#8217;t.</p><ul><li><p>We first secure our own agent and make sure that nobody can tamper with its maps or where it stores its policies (<a href="https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps">https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps)</a>. We also make sure that nobody can shut down our agent, even if they have root privileges (<a href="https://substack.bomfather.dev/p/stopping-kill-signals-against-your-agent">https://substack.bomfather.dev/p/stopping-kill-signals-against-your-agent</a>)</p></li><li><p>We restrict access to the source code directory so that only one binary is allowed to write to it, and another is allowed only to read from it. These are programs with simple, limited behaviors, so they can&#8217;t be compromised to do something bad. The best way to build a secure system is to use dumb, single-purpose components.</p></li></ul><h2>Our Next Steps to Integrate <code>fs-verity</code> with our eBPF Agent</h2><p><code>fs-verity</code> is a great tool that allows us to guarantee that the binaries we download are not tampered with, but we can do even more with it.<br><br>We are currently working on integrating <code>fs-verity</code> with our eBPF agent <a href="https://docs.ebpf.io/linux/kfuncs/bpf_get_fsverity_digest/">https://docs.ebpf.io/linux/kfuncs/bpf_get_fsverity_digest/</a>. This would allow us to avoid relying on the filesystem for our policies and instead use hashes to verify them. <br><br>So if we had a policy that said &#8220;/app/server can access /app/db/databaseconnection.yaml&#8221;, now we could associate a hash with the server, allowing us to really confirm that our agent thinks that /app/server is the same /app/server we are talking about. Since Linux files can be mounted, moved, and hidden in bizarre ways, being able to associate a hash with them allows us to verify that we are granting access to legitimate processes.</p><h2>Cost</h2><p>Doing this is not fun or easy. Every single upgrade we have to do takes a really long time, and sometimes our upgrades break because a security policy triggers when we haven&#8217;t done every step right.</p><p>Frankly, if we were shipping a normal SaaS backend, this would be overkill.</p><p>But we aren&#8217;t. We are shipping kernel-level security software, and we&#8217;d rather spend extra build time than even risk a compromise.</p>]]></content:encoded></item><item><title><![CDATA[Confidential Computing Adds A Crazy Amount of Overhead to GPUs]]></title><description><![CDATA[I knew that confidential computing added an overhead when running on GPUs, but I never knew it was this absurd...]]></description><link>https://substack.bomfather.dev/p/confidential-computing-adds-a-crazy</link><guid isPermaLink="false">https://substack.bomfather.dev/p/confidential-computing-adds-a-crazy</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Wed, 04 Feb 2026 15:12:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vouS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64147e-95ad-4a05-9d04-25bd8f97c89e_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Confidential Computing (CC) is great for AI since it allows you to run workloads on someone else&#8217;s hardware while keeping all your proprietary data secure. It doesn&#8217;t even allow the cloud provider to access your data.</p><p>But, it has an issue&#8230; Research has shown that Confidential Computing adds a significant performance overhead on GPUs.</p><p>This post walks through these overheads and tries to explain them in a simple manner.</p><h1>How does Confidential Computing work on GPUs?</h1><p>At a high level, confidential computing locks down the GPU by encrypting GPU memory and ensuring that all data moved in or out of the GPU is encrypted and authenticated. It even protects GPU to GPU communication.</p><p>This is great for security, but it fundamentally changes the way GPUs behave. GPUs are built to be fast when they do a lot of math, and they are fast when they can freely move data at high frequencies.</p><p>The big issue is that confidential computing changes how GPUs operate, forcing them to work in ways they weren&#8217;t designed for.</p><h1>The Two Different Patterns of GPU Overhead</h1><h2>1. Memory Heavy AI workloads</h2><p>Most modern AI workloads (especially LLMs) don&#8217;t just compute, but move data at high frequencies.</p><p>So, model weights are swapped in and out, attention caches keep being moved between CPUs and GPUs, and memory pressure forces frequent transfers.</p><p>But, with confidential computing, all the swaps must be encrypted and decrypted. So, the CPU becomes a bottleneck, leaving the GPU idle while it waits for data.</p><p>In one paper, latency was measured to peak at 36.2%, 52.8%, and 88.2% depending on model size and memory pressure.</p><p><a href="https://arxiv.org/abs/2411.03357">https://arxiv.org/abs/2411.03357</a></p><p>And in another paper, confidential computing added around 54% to 903.9% (Wow&#8230;) slowdown when inferencing on a single GPU and around 10% to 455% overhead when training on a single GPU!</p><p><a href="https://arxiv.org/abs/2410.15240">https://arxiv.org/abs/2410.15240</a></p><h2>2. Training with Multiple GPUs</h2><p>This is where things go crazy!</p><p>When training with multiple GPUs, GPUs frequently share small updates with each other to stay in sync. Additionally, each exchange is broken up into multiple small messages.</p><p>When using confidential computing, every single GPU message must be encrypted and decrypted, making lots of small messages extremely expensive.</p><p>Researchers measured an average of a 768% overhead and a maximum of 4060% overhead for the training workloads running on multiple GPUs.</p><p><a href="https://arxiv.org/abs/2501.11771">https://arxiv.org/abs/2501.11771</a></p><h1>When Should You Use Confidential Computing?</h1><p>Given the significant overheads of using confidential computing, when should we use it?</p><p>Confidential computing is a great idea when a workload is very computation heavy and not very communication heavy. What we see across all the papers is that when running confidential computing, high communication between the GPU and CPU leads to significant slowdowns. So, if a workload spends most of its time doing math in the GPU, the slowdowns can become negligible.</p><p>You could also use systems designed around confidential computing. So, one example could be PipeLLM (<a href="https://arxiv.org/abs/2411.03357">https://arxiv.org/abs/2411.03357</a>), which anticipates which data the GPU will require and encrypts them at the same time as the computation to reduce confidential computing overhead.</p><h1>Conclusion</h1><p>Before reading these papers, I didn&#8217;t really know the overhead of using confidential computing on GPUs. I mean, I knew there was some overhead, but never thought it would/could be this crazy. My surprise is what prompted me to write this blog post.</p><p>Confidential computing is great for security, but because it fundamentally changes how GPUs operate, it adds a significant performance overhead. Before deciding on confidential computing as a default solution, it is important to know when it is a good fit.</p>]]></content:encoded></item><item><title><![CDATA[Dynamic Runtime Policies in eBPF Using Bitmasks]]></title><description><![CDATA[This is a cross-post of https://substack.com/home/post/p-181032541 on eBPFChirp]]></description><link>https://substack.bomfather.dev/p/dynamic-runtime-policies-in-ebpf</link><guid isPermaLink="false">https://substack.bomfather.dev/p/dynamic-runtime-policies-in-ebpf</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Thu, 08 Jan 2026 17:31:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!poDp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This is a cross-post of <a href="https://substack.com/home/post/p-181032541">https://substack.com/home/post/p-181032541</a> on eBPFChirp </em></p><p>Whenever one creates runtime security policies in eBPF, it usually looks something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!poDp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!poDp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 424w, https://substackcdn.com/image/fetch/$s_!poDp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 848w, https://substackcdn.com/image/fetch/$s_!poDp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 1272w, https://substackcdn.com/image/fetch/$s_!poDp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!poDp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png" width="1371" height="848" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:848,&quot;width&quot;:1371,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24226,&quot;alt&quot;:&quot;&quot;,&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://ebpfchirp.substack.com/i/181032541?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!poDp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 424w, https://substackcdn.com/image/fetch/$s_!poDp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 848w, https://substackcdn.com/image/fetch/$s_!poDp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.png 1272w, https://substackcdn.com/image/fetch/$s_!poDp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1fd0c6e-7d67-4ff3-bf96-d4f389a01302_1371x848.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>When a certain binary runs and tries to access a resource, we check it against an eBPF policy map, enforce the rules, and call it a day&#8212;right?</p><p>Not really.</p><p>Consider the following example:</p><ul><li><p><code>sender.sh</code> is launched to move some files from machine A to machine B.</p></li><li><p>To do that, <code>sender.sh</code> starts <code>readandsend.sh</code> as a child process and passes it the files</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Q9Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 424w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 848w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png" width="1456" height="1066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1066,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43711,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://ebpfchirp.substack.com/i/181032541?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 424w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 848w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!8Q9Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec8724f-8fb8-4900-abb6-4f3c9a2c4f26_1764x1292.png 1456w" sizes="100vw"></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></li></ul><p></p><p>In this case, when <code>readandsend.sh</code> is called by <code>sender.sh</code>, it should be allowed to read the provided files and connect to machine B&#8217;s IP.</p><p>But according to the current (static) policy stored in the eBPF map&#8230; it can&#8217;t.</p><p>So why not simply assign those file and network permissions to <code>readandsend.sh</code> as well?</p><p>The problem is that when permissions are assigned across different entities in a system, each entity should ideally have the minimal set of permissions required to function.</p><p>The primary reason for this is security: if a given entity is compromised, the attacker gains only a limited set of &#8220;powers.&#8221;</p><p>In the example above, we don&#8217;t want to grant <code>readandsend.sh</code> access to all files. Instead, we want it to inherit permissions from its parent <strong>only for the duration of the operation</strong>.</p><p>This may not be a common pattern, but supporting it would allow us to create policies that block certain operations by default and only <em>unlock</em> them when an authorized parent process invokes a child. In other words, capabilities remain restricted unless the correct process chain activates them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jy9a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jy9a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 424w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 848w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jy9a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png" width="1456" height="1066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1066,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43262,&quot;alt&quot;:&quot;&quot;,&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://ebpfchirp.substack.com/i/181032541?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!jy9a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 424w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 848w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.png 1272w, https://substackcdn.com/image/fetch/$s_!jy9a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1fb583a-7637-4eef-8a98-c4c150da625f_1764x1292.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><blockquote><p><em><strong>So how hard to implement can that be?</strong></em></p></blockquote><p>And obviously, we can&#8217;t just invent a completely new structure. Instead, to make this possible, the eBPF map that holds the policy should:</p><ul><li><p>Support taking the <strong>union</strong> of two map entries (for inheriting permissions)</p></li><li><p>Perform that union <strong>quickly</strong> (ideally O(1), or close to it)</p></li><li><p>Fit within the <strong>eBPF verifier&#8217;s limitations</strong></p></li></ul><p>So&#8230; this should be easy, right?</p><h1><strong>The &#8220;Easy&#8221; Solution</strong></h1><p>To achieve this, we represent our policy entries as a <a href="https://en.wikipedia.org/wiki/Mask_(computing)">bitmask</a>, where each bit corresponds to a resource the executable is allowed to access.</p><p>Instead of storing the resource identifiers directly, we store them like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Vpb1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Vpb1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 424w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 848w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 1272w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Vpb1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png" width="1456" height="466" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:466,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!Vpb1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 424w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 848w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.png 1272w, https://substackcdn.com/image/fetch/$s_!Vpb1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F534f9081-6ce3-48e0-a86b-2ef13f9f8a7e_2098x671.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>Instead of a single eBPF map, we use two eBPF maps:</p><ul><li><p>A map that assigns each resource an index (bit position)</p></li><li><p>A map that stores policies as bitmasks</p></li></ul><blockquote><p><em><strong>But why?</strong></em></p></blockquote><p>When checking access for <code>sender.sh</code>, we:</p><ul><li><p>Fetch the resource&#8217;s index</p></li><li><p>Retrieve the binary&#8217;s policy bitmask</p></li><li><p>Allow access if the bit at that index is set</p></li></ul><p>But when <code>sender.sh</code> calls <code>readandsend.sh</code>, we can simply <strong>OR</strong> their policy bitmasks to create a combined policy and apply the same checks.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DDRY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DDRY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 424w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 848w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 1272w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DDRY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png" width="1456" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!DDRY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 424w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 848w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.png 1272w, https://substackcdn.com/image/fetch/$s_!DDRY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1752e18c-5cbf-42eb-8460-b03c3c0effd9_1523x422.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>In other words, we dynamically adjust permissions based on <em>who</em> called the binary&#8212;while still keeping least privilege.</p><p>Here&#8217;s a small pseudocode snippet to wrap it up:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fmte!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fmte!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 424w, https://substackcdn.com/image/fetch/$s_!fmte!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 848w, https://substackcdn.com/image/fetch/$s_!fmte!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 1272w, https://substackcdn.com/image/fetch/$s_!fmte!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fmte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png" width="1456" height="1827" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1827,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&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="" srcset="https://substackcdn.com/image/fetch/$s_!fmte!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 424w, https://substackcdn.com/image/fetch/$s_!fmte!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 848w, https://substackcdn.com/image/fetch/$s_!fmte!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.png 1272w, https://substackcdn.com/image/fetch/$s_!fmte!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc138912-055f-40f4-ac4e-ba5587e06000_1670x2096.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>I hope you find this resource interesting. Keep an eye out for more updates and developments in eBPF in next week&#8217;s newsletter.</p><p>Until then, keep &#128029;-ing!</p><p>Warm regards, Teodor and Neil</p>]]></content:encoded></item><item><title><![CDATA[Tracking Shell Scripts (and Python, Perl, etc) with eBPF is Hard]]></title><description><![CDATA[Interpreted languages are painfull to monitor with eBPF, if you want any guarantees]]></description><link>https://substack.bomfather.dev/p/tracking-shell-scripts-and-python</link><guid isPermaLink="false">https://substack.bomfather.dev/p/tracking-shell-scripts-and-python</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Fri, 26 Dec 2025 17:10:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!h1hi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you want to track anything on a machine, eBPF is almost always the answer (only if you are running on Linux, but who doesn&#8217;t run Linux?).</p><p>This is even more true for tracking processes, which is quite simple with eBPF: you just hook into execve, and you are good to go.</p><p>But if you want precise information, exact paths to the binaries that were started, you will need to hook into something like <code>bprm_check_security</code>, a BPF-LSM hook, that allows you to get a little more information on a process.</p><p>This is what we do at Bomfather, and it worked perfectly&#8230; Until it did not.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://bomfather.dev/comics/6/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h1hi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 424w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 848w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 1272w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!h1hi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic" width="1456" height="595" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:595,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:80046,&quot;alt&quot;:&quot;https://bomfather.dev/comics/6/&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:&quot;https://bomfather.dev/comics/6/&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="https://bomfather.dev/comics/6/" title="https://bomfather.dev/comics/6/" srcset="https://substackcdn.com/image/fetch/$s_!h1hi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 424w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 848w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 1272w, https://substackcdn.com/image/fetch/$s_!h1hi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8638b7-ff42-49d6-97b8-c02abd4facf9_1851x756.heic 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><figcaption class="image-caption"><a href="https://bomfather.dev/comics/6/">https://bomfather.dev/comics/6/</a></figcaption></figure></div><h2>Interpreted Scripts</h2><p>When we ran regular executables, we got a log output that looked something like this (We were logging when a process accessed certain protected files)</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jpUZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jpUZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 424w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 848w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 1272w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jpUZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png" width="783" height="86" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:86,&quot;width&quot;:783,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9411,&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;:false,&quot;internalRedirect&quot;:&quot;https://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.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_!jpUZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 424w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 848w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 1272w, https://substackcdn.com/image/fetch/$s_!jpUZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff25f35b5-aa55-4512-8fb3-c43d74289b92_783x86.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>But when we ran a shell script, we got</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dEGA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dEGA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 424w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 848w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 1272w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dEGA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png" width="786" height="86" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:86,&quot;width&quot;:786,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7061,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.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_!dEGA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 424w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 848w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 1272w, https://substackcdn.com/image/fetch/$s_!dEGA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb71cc244-397d-465b-bbea-d2bdc8f25b4c_786x86.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Where did the shell script go?</p><p>Well, the reason this happened was because of how the kernel runs these files with a <code>#!</code> (Shebang), which includes shell scripts, or any interpreted language (Python, Perl, Ruby, etc).</p><p>When a shell script (or any interpreted script) is executed, the kernel adds an extra step. First <code>execve()</code> is called, and that call hits our eBPF hook. The data we get looks something like this.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kde5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kde5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 424w, https://substackcdn.com/image/fetch/$s_!kde5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 848w, https://substackcdn.com/image/fetch/$s_!kde5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 1272w, https://substackcdn.com/image/fetch/$s_!kde5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kde5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png" width="789" height="112" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:112,&quot;width&quot;:789,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12096,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.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_!kde5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 424w, https://substackcdn.com/image/fetch/$s_!kde5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 848w, https://substackcdn.com/image/fetch/$s_!kde5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 1272w, https://substackcdn.com/image/fetch/$s_!kde5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9a35b02-1920-4740-9631-51a2da3b1c21_789x112.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>But then the kernel reads the first two bytes of the file. If it sees<code> #!</code>, then the Linux kernel will call <code>execve()</code> again!</p><p>What happens is the kernel sees the shebang, which tells it not to execute the file; instead, it should pass the file to an interpreter, which will then actually do something. The key here is that the shell script doesn&#8217;t do anything by itself at all.</p><p>Our eBPF hook triggers on this second <code>execve()</code>, which leads us to the root of our problem.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6idM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6idM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 424w, https://substackcdn.com/image/fetch/$s_!6idM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 848w, https://substackcdn.com/image/fetch/$s_!6idM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 1272w, https://substackcdn.com/image/fetch/$s_!6idM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6idM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png" width="792" height="111" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:111,&quot;width&quot;:792,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11361,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.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_!6idM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 424w, https://substackcdn.com/image/fetch/$s_!6idM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 848w, https://substackcdn.com/image/fetch/$s_!6idM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 1272w, https://substackcdn.com/image/fetch/$s_!6idM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6857cdd-8961-46d3-a9ee-780dd25d4528_792x111.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The second <code>execve()</code> is for the same process, but the args are totally different. Since there is no executable, the only thing &#8220;doing&#8221; anything in this process is the interpreter. So when our eBPF code writes this data to our storage (an eBPF map), it rewrites over the data that is already stored.</p><p>So after the first <code>execve()</code>, we get:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!paZZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!paZZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 424w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 848w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 1272w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!paZZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png" width="780" height="64" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:64,&quot;width&quot;:780,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8609,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.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_!paZZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 424w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 848w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 1272w, https://substackcdn.com/image/fetch/$s_!paZZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f75e346-c144-4fa4-b237-cc18f5769853_780x64.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And after the second <code>execve()</code>, we get</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hq6j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hq6j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 424w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 848w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 1272w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hq6j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png" width="789" height="68" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:68,&quot;width&quot;:789,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7876,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.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_!hq6j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 424w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 848w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 1272w, https://substackcdn.com/image/fetch/$s_!hq6j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff147cbb8-aa33-4479-bf7e-ced6c91db96d_789x68.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>To solve this issue, all we have to do is check whether the path for that process (which we may have recorded before) is not empty; if it is, we don&#8217;t rewrite it.</p><h2>This doesn&#8217;t solve everything</h2><p>While the solution above allows us to capture the path of a shell script and any interpreted script, we still have one more catch for shell scripts.</p><p>You see, most of the operations you do in a shell script are going to be the calling of other executables. What I mean is that, for example, when you want to read a file in a shells script, you use <code>cat</code> the executable, to do this job.</p><p>But what this does is spawn a new process, so if we monitored a shell script and knew it opened <code>file.txt</code>, we expect an output like this.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2JU5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2JU5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 424w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 848w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 1272w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2JU5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png" width="779" height="88" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:88,&quot;width&quot;:779,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8865,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.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_!2JU5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 424w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 848w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 1272w, https://substackcdn.com/image/fetch/$s_!2JU5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0bb98a8-5477-42f4-9b21-da265e696e38_779x88.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>But instead, we&#8217;d get something that looks more like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5qCI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5qCI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 424w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 848w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 1272w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5qCI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png" width="787" height="89" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:89,&quot;width&quot;:787,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8300,&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://substack.bomfather.dev/i/182639848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.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_!5qCI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 424w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 848w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 1272w, https://substackcdn.com/image/fetch/$s_!5qCI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8a5788b-c477-4f77-9384-6ec1854d07aa_787x89.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This is because the shell script never opens any file; instead, it delegates that job to another executable by starting a child process.</p><p>So if we want to accurately monitor shell scripts, we also have to track their children and treat them as a single process tree in our monitoring logic.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Making Pigs Fly (AKA Getting the Verifier To Approve eBPF Code)]]></title><description><![CDATA[Your eBPF code may work on your system, but the verifier won't let it work anywhere else.]]></description><link>https://substack.bomfather.dev/p/making-pigs-fly-aka-getting-the-verifier</link><guid isPermaLink="false">https://substack.bomfather.dev/p/making-pigs-fly-aka-getting-the-verifier</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Wed, 10 Dec 2025 21:46:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!B1K8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Where we started</h2><p>We originally developed on Ubuntu 24.04 on an arm64 chipset (because I develop on MacOS and run my eBPF code in a VM). We were sure our eBPF code worked on a 6.8 kernel running on an arm64 system.</p><p>We knew in theory that our code should run on all kernel versions &gt;= 5.18. We knew our code only worked on these kernel versions, since we used <a href="https://docs.ebpf.io/linux/helper-function/bpf_loop/">bpf_loop</a>.<br><br>bpf_loop is a helper function we use in our eBPF code that allows us to set higher loop limits, enabling us to accurately read filenames. Without bpf_loop, we would be restricted to reading the first 256 characters of a filename, which isn&#8217;t that good.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Newer Kernels Can Break eBPF</h2><p>But practice is quite different from theory. When we tried to run our code on a 6.18 kernel, our eBPF code failed to pass the verifier, and we were stumped. Our eBPF code used <a href="https://docs.ebpf.io/concepts/core/">CO-RE</a> helpers, so why wasn&#8217;t it passing the verifier in this new kernel version?</p><p>We realized that it wasn&#8217;t that our code would not work on newer kernels. Instead, it was the verifier itself that improved, and because of that, the new improved verifier did not approve one of our LSM hooks on bprm_check_security.</p><p>What happened was that our return value in this specific hook could not be verified by the verifier after the kernel dynamically optimized our eBPF code. To fix this issue, we used <a href="https://docs.ebpf.io/ebpf-library/libbpf/ebpf/barrier_var/">barrier_var</a> to prevent the kernel from optimizing our return value, allowing the verifier to ensure it was safe.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://bomfather.dev/comics/3/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B1K8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 424w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 848w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 1272w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B1K8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic" width="1264" height="386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:386,&quot;width&quot;:1264,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52933,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:&quot;https://bomfather.dev/comics/3/&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://substack.bomfather.dev/i/181273307?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B1K8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 424w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 848w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 1272w, https://substackcdn.com/image/fetch/$s_!B1K8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c083781-067f-41f3-ab7c-2a3e33cc92c6_1264x386.heic 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><figcaption class="image-caption"><a href="https://bomfather.dev/comics/3/">https://bomfather.dev/comics/3/</a></figcaption></figure></div><h2>Older Kernels Can Break Stuff Too!</h2><p>Once we did that, we also ran our code on the lowest kernel version we supported, 5.18, and it didn&#8217;t work either! This was the same problem we had with running on 6.18, but in reverse.</p><p>The kernel was older than the one we normally ran, so some optimizations that happened on newer kernels wouldn&#8217;t apply to these older ones.</p><p>In our case, we had to fix a couple of things.</p><ol><li><p>We were computing a bitwise operation between two elements and directly assigning the result to a variable; instead, we had to split up the operations, first write to the variable, and then compute our bitwise operations</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JCbd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JCbd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 424w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 848w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 1272w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JCbd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png" width="795" height="309" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:309,&quot;width&quot;:795,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27301,&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://substack.bomfather.dev/i/181273307?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.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_!JCbd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 424w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 848w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.png 1272w, https://substackcdn.com/image/fetch/$s_!JCbd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a597e7a-7cf5-4237-93a6-e2b0442b5f4b_795x309.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></li><li><p>We were returning values from inline helper functions, which couldn&#8217;t be verified. But when we changed from returning values directly to passing a buffer to the inline helpers and having them write their values to the buffer instead of returning them, the verifier approved it.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XM4D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XM4D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 424w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 848w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 1272w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XM4D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png" width="794" height="187" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:187,&quot;width&quot;:794,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29833,&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://substack.bomfather.dev/i/181273307?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.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_!XM4D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 424w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 848w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 1272w, https://substackcdn.com/image/fetch/$s_!XM4D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39630912-31d4-4f86-a770-a4add7da1fd5_794x187.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p></li></ol><h2>Chipsets also break things</h2><p>It turns out our eBPF code didn&#8217;t work on x86 either! Now I am pretty sure the reason for this is that x86 has a better verifier and may also optimize our eBPF code better.</p><p>Now I don&#8217;t have an example of what happened in our eBPF code, nor do I know how we fixed it, since I can&#8217;t locate the commit that did. Needless to say, it was quite irritating to fix.</p><h2>Fixing verifier messages</h2><p>Now these problems aren&#8217;t exactly easy to diagnose. In our codebase, since most of our code was in inline helper functions, the errors would just point to the failing hook and the register that was the problem, which are quite opaque on the surface.</p><p>These errors, at least for me, are quite hard to understand, so I would say the best way to understand them is to look them up, find out what the registers mean, and how the eBPF verifier behaves in different scenarios. Many times, you can&#8217;t find an example online, so to write good eBPF programs, you will have to learn what these errors mean, but in the past couple of years, debugging verifier errors has become a whole lot easier because of LLMs.</p><p>While I still don&#8217;t think LLMs can write good eBPF code (It may be able to write somthing that complies, but once you read it, you will be horrified by what it writes), LLMs are surprisingly good at actually tracking where the errors are coming from.</p><p>Now LLMs are quite bad at fixing these errors, and there is also a good chance that they totally hallucinate what the real problem is, but if you can get them to help you understand which register correlates to what variable, and what the error message could mean in your context (This works a whole lot better, if the LLM has a websearch option) debugging these errors becomes a whole lot easier.</p><h2>Testing on multiple kernels</h2><p>After we realized all of this, we set up some automated testing jobs to run our code on the latest and earliest kernels we supported. We did this by running our own self-hosted runners (not github runners) with 5.18 and the latest stable kernel (6.18). We do this by deploying two custom images, made with <a href="https://github.com/hashicorp/packer/blob/main/LICENSE">Packer</a>.<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_!HqeG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HqeG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 424w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 848w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 1272w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HqeG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png" width="920" height="377" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:377,&quot;width&quot;:920,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86392,&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://substack.bomfather.dev/i/181273307?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.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_!HqeG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 424w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 848w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.png 1272w, https://substackcdn.com/image/fetch/$s_!HqeG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F978951ed-9971-4be8-b5b7-0cf1b190063e_920x377.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>We don&#8217;t use GitHub runners because LSM cannot be enabled on them, and our eBPF code is primarily built on LSM hooks. Github runners also come with a lot of bloat and a lot of vulnerabilities, so running our own VMs made more sense to us, for security&#8217;s sake <a href="https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have">https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have</a>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Upbit was hacked $37M Solana. How could we have hacked and protected it?]]></title><description><![CDATA[Upbit was hacked and lost $37M of Solana. Here's how it could have happened and how we could have defend it with eBPF and LSM.]]></description><link>https://substack.bomfather.dev/p/upbit-hacked-37m-solana-how-would</link><guid isPermaLink="false">https://substack.bomfather.dev/p/upbit-hacked-37m-solana-how-would</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Mon, 01 Dec 2025 15:19:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vouS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64147e-95ad-4a05-9d04-25bd8f97c89e_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Upbit, a Crypto exchange, was hacked on November 27, 2025, and 37 million dollars&#8217; worth of Solana was stolen. We don&#8217;t know much about how the breach happened, but to move crypto you need the private keys, there&#8217;s no other way. So whatever the attack was, it ended with someone getting those keys.</p><p>In this post, let&#8217;s explore how we could have hacked this and how we could protect it from being hacked with eBPF Linux Security Modules to secure the runtime. We don&#8217;t know how the private keys were compromised, these are our assumptions about how someone could have compromised them.</p><p><em>A quick caveat, we at Bomfather do have an eBPF security solution that implements everything talked about in this blog post, but I wrote this blog post so that anyone can write their own eBPF code to implement runtime security!</em></p><h3>Why use eBPF and LSM?</h3><p>Why are we using eBPF and LSM? Trusting the kernel is better than the user space process, and LSM eliminates <a href="https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use">TOCTTOU</a> vulnerabilities. eBPF lets you hook into the kernel without writing a kernel module</p><h3>Why this approach?</h3><p>Crypto is built on the assumption that everyone is trying to steal your money. You don&#8217;t trust servers, you don&#8217;t trust exchanges, you don&#8217;t trust anyone. You trust keys and signatures.</p><p>We&#8217;re doing the same thing, but for the runtime. Don&#8217;t trust userspace or root. Trust the kernel and trust cryptographic verification.</p><p>If we have a security agent running. To shut it down we should need a signed challenge response based on a nonce. To update policies around the agent, we should do the same thing. We copied this directly from how crypto works because it makes sense.</p><p>And LSM hooks solve a problem that&#8217;s been bugging security people forever <a href="https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use">TOCTTOU</a>. You check if something is safe, then use it, but it has changed in the meantime. With LSM, the check happens at the exact moment of access. So, no gap.</p><p>Crypto was built assuming everyone on the network is trying to rob you. We&#8217;re just applying that same paranoia to the server.</p><p>For this, we assume we are running on a Linux machine and this approach works on everything, whether they use or don&#8217;t use containers.</p><h3>How does YAML config turn into kernel enforcement?</h3><p>The userspace agent reads your policy file, parses it, and writes the rules into eBPF maps. Maps are shared memory between userspace and kernel. When an LSM hook fires, the kernel side eBPF program looks up the policy in the map and decides to allow or deny. The YAML is for humans and the map is for the kernel.</p><h3>Attack Vector 1: Just read the key file.</h3><p><strong>Attack</strong>: <code>cat /path/to/hot-wallet-key.json</code></p><p>If you shell access (SSH, container escape, etc.), this is step one. The compromise is easy and it just works. I wish it wasn&#8217;t this easy.</p><p><strong>Defense</strong>: An eBPF LSM hook denies all read and write by default, and only the &#8220;signer&#8221; gets read access. So, no one can read the private keys (not even with root). So you can provide root access to your employees without worrying about them accessing the keys.</p><p>This LSM policy is the easiest way to avoid a random user/process from reading the Private Keys.</p><pre><code><code> - container_path: &#8220;ns:pod:container&#8221; 
# The format is Kubernetes namespace, pod name, and container name. If you&#8217;re not running k8s, leave it empty, and the policy applies to the host. 
 executables:
 - path: &#8220;/app/signer&#8221;
 directories:
 - path: &#8220;/hotwallet&#8221;
 permission: &#8220;read&#8221;
</code></code></pre><pre><code><code>SEC(&#8221;lsm/file_open&#8221;)
int BPF_PROG(lsm_file_open, struct file *file) {
 /*
 * Default deny file access to protected paths.
 * Only explicitly allowed executables can read protected files.
 * Even root is denied.
 */

 // Get the file path being opened
 // Get the current process&#8217;s executable path
 // Check if the file is in a protected directory
 // Check if this executable is allowed to access this directory
 // If not allowed, return -EPERM
}
</code></code></pre><h3>Attack Vector 2: Backdoor the Binary.</h3><p>Suppose we had an insider attack, which is a lot easier. Think about it, if we can bribe an insider with 5% of the stolen assets and he can move to a country that wouldn&#8217;t extradite prisoners, then it is millions of dollars&#8217; worth of risk that someone could be motivated to take. The insider can replace the binary and then leave the country the moment the tokens are stolen.</p><p><em><strong>Attack</strong>:</em> Replace the signing binary with a modified version that exfiltrates keys.</p><p><strong>Defense</strong>: Immutable executables and SHA256 verification. eBPF LSM can prevent binaries from being modified. We should not only protect the &#8220;signer&#8221; of the transaction, which has access to the private keys, but also the platform&#8217;s critical system binaries.</p><pre><code><code># Critical executables that cannot be modified at runtime
immutable_executables:
 # Application Binaries
 - path: &#8220;/app/signer&#8221;
  
 # Container Runtime Infrastructure
 - path: &#8220;/bin/runc&#8221;
 - path: &#8220;/bin/k3s&#8221;
 - path: &#8220;/bin/containerd-shim-runc-v2&#8221;
  
 # Network Infrastructure
 - path: &#8220;/bin/cni&#8221;
 - path: &#8220;/bin/aux/xtables-nft-multi&#8221;
 - path: &#8220;/bin/aux/nft&#8221;
</code></code></pre><p>This is the solution for vectors 2,6, and 7.</p><pre><code><code>SEC(&#8221;lsm/file_open&#8221;)
int BPF_PROG(lsm_file_open, struct file *file) {
 /*
 * Block writes to immutable executables and directories.
 */

 // If open mode is write
 // Check if path is a protected executable
 // Check if path is under a protected directory
 // If protected and write attempted, return -EPERM
}

SEC(&#8221;lsm/path_rename&#8221;)
int BPF_PROG(lsm_path_rename, struct path *old_dir, struct dentry *old_dentry, 
 struct path *new_dir, struct dentry *new_dentry) {
 /*
 * Block rename of protected files.
 */

 // Check if source or destination is protected
 // If either is protected, return -EPERM
}

SEC(&#8221;lsm/path_unlink&#8221;)
int BPF_PROG(lsm_path_unlink, struct path *dir, struct dentry *dentry) {
 /*
 * Block deletion of protected files.
 */

 // Check if file is protected
 // If protected, return -EPERM
}

</code></code></pre><p>The next question would be: how do we know the above executables weren&#8217;t compromised before the eBPF security agent started enforcing the policies? When the eBPF agent monitors, it would deny any delete, modify, or symlink to these protected executable files.</p><p>To ensure the executables weren&#8217;t compromised, we&#8217;d need to use kernel SHASUM validation.</p><p><a href="https://docs.kernel.org/filesystems/fsverity.html">Fsverity</a> handles this. <code>Fsverity</code> is a kernel feature that computes the Merkle tree hash of a file&#8217;s contents. If anything changes, the kernel invalidates the digest.</p><p>Then, at execution time, the eBPF hook checks <code>bpf_get_fsverity_digest()</code> <a href="https://docs.ebpf.io/linux/kfuncs/bpf_get_fsverity_digest/">https://docs.ebpf.io/linux/kfuncs/bpf_get_fsverity_digest/</a> against your expected hash.</p><p>If there is a mismatch, then we can deny the execution of the binary. This function works only on LSM and not on tracepoints. There is a fantastic talk about this from the Linux Plumbers Conference <a href="https://www.youtube.com/watch?v=jAQ9d90V12M">BPF_LSM + fsverity for BinaryAuthorization - Song Liu, Boris Burkov</a>.</p><h3>Attack Vector 3: Memory dump/ptrace</h3><p><em>&#8220;But we don&#8217;t store keys on disk. We use a KMS.&#8221;</em> Cool. But the key still has to exist in memory to sign a transaction.</p><p>Wherever it came from, file, KMS, HSM API, or remote vault at some point, the signing process has the key material in RAM. That&#8217;s the target.</p><p>Here is a problem, How do we provide administrators with access to maintain the systems without allowing them to siphon funds from the crypto wallet? The solution is to make sure that they can&#8217;t be able to ptrace the security agent or the signer or replace the signer binary.</p><div><hr></div><p>Here&#8217;s the problem, you can&#8217;t trust admins. Not because they&#8217;re bad people, but because compromised accounts are a thing. An attacker with root can use ptrace to attach to any process and dump its memory. That&#8217;s how you&#8217;d pull keys out of RAM. So we have a conundrum. We need to give people root to maintain the system. But we can&#8217;t let them <code>ptrace</code> the signer or the security agent.</p><p><strong>Attack</strong>: Attach a debugger to the signer process, dump the heap, and extract key material.</p><p>Again, this is where eBPF comes to the rescue.</p><pre><code><code>SEC(&#8221;lsm/ptrace_access_check&#8221;)
int BPF_PROG(lsm_ptrace_access_check, struct task_struct *child, unsigned int mode) {
 /*
 * Block ALL ptrace operations on protected processes:
 *  - GDB/debugger attachment
 *  - strace system call tracing
 *  - /proc/&lt;pid&gt;/mem access
 *  - process_vm_{readv,writev} syscalls
 *  - Any other ptrace-based inspection
 *
 * prevents debugging.
 */

 // Check if ptrace blocking is disabled
 // Check if the target PID is part of the protected processes list
 // Check if the security agent is being ptrace&#8217;d. If we compromise the security agent, then we compromise everything.
 // if any of these checks fail, return -EPERM
}
</code></code></pre><h3>Attack Vector 4: Kill the security agent</h3><p>All of this depends on the eBPF security agent. What if a malicious user with sudo privileges kills the agent? Then it&#8217;s game over.</p><p><strong>Attack</strong>- <code>sudo kill -9 &lt;agent-pid&gt;</code>Then, retry vectors 1-3.</p><p><strong>Defense</strong>: The agent blocks its own kill signals. You can&#8217;t kill it without a <a href="https://en.wikipedia.org/wiki/Public_key_infrastructure">Public Key Infrastructure</a> challenge response with a <a href="https://en.wikipedia.org/wiki/Nonce">nonce</a>. No Key, no shutdown.</p><pre><code><code>SEC(&#8221;lsm/task_kill&#8221;)
int BPF_PROG(lsm_task_kill, struct task_struct *p, struct kernel_siginfo *info, 
 int sig, const struct cred *cred) {
 /*
 * Block kill signals to the security agent.
 */

 // Check if target process is the security agent
 // If yes, return -EPERM
}
</code></code></pre><p>Here is a detailed write up and a video on how to prevent this. <a href="https://substack.bomfather.dev/p/stopping-kill-signals-against-your?r=2bngj5&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=false">https://substack.bomfather.dev/p/stopping-kill-signals-against-your?r=2bngj5&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=false</a></p><h3>Attack Vector 5: Update Policy as Root (eBPF Maps)</h3><p>Most eBPF security agents use eBPF maps to communicate security policies to the kernel. The maps are globally writable by any privileged user.</p><p><strong>Attack</strong> - <code>sudo bpftool update eBPF map policy to disable the security.</code> Then retry vectors 1-3.</p><p><strong>Defense</strong>: The agent would have to write an eBPF LSM policy to prevent the maps from being updated. It can be updated only by the user process that starts the eBPF agent. We can make it better by using the PKI mentioned above with a nonce to update the policies, so we know it&#8217;s trusted.</p><pre><code><code>SEC(&#8221;lsm/bpf_map&#8221;) 
int BPF_PROG(lsm_bpf_map, struct bpf_map *map, fmode_t fmode) {
 /*
 * Block unauthorized modifications to security policies.
 * Only the security agent can update its own policy maps.
 */

 // Check if this is a security policy map
 // Check if the calling process is the security agent
 // If not the agent, return -EPERM
}
</code></code></pre><p>We wrote about how to do this in depth and also included a video to explain this <a href="https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps">https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps</a></p><h3>Attack Vector 6: Write to sensitive directories</h3><p>Now you can&#8217;t read the keys or backdoor the signer. But what if you drop a malicious binary into a directory that&#8217;s in the PATH or LD_LIBRARY_PATH? Or write a cron job? Or modify a config file?</p><p><strong>Attack</strong>: <code>echo &#8220;malicious script&#8221;&gt; /etc/cron.d/exfil or cp /tmp/evil /usr/local/bin/signer-helper</code></p><p><strong>Defense</strong>: Have an eBPF LSM Policy that will prevent any writes to any of these directories.</p><pre><code><code>immutable_directories: 
 - path: &#8220;/private-keys&#8221;
 - path: &#8220;/etc/LD_LIBRARY_PATH&#8221;
</code></code></pre><h3><strong>Attack Vector 7: Replace system executables</strong></h3><p>Similar to Vector 2, but broader. Why target just the signer? Replace ls, cat, bash - any tool an admin might run. Wait for someone to interact with the keys.</p><p><strong>Attack</strong>: <code>cp /tmp/evil-bash /bin/bash</code></p><p><strong>Defense</strong>: Global readonly executables. Not just an immutable signer binary. All system binaries are protected from modification.</p><pre><code><code>immutable_executables:
  - /bin/ls
</code></code></pre><h3>Attack Vector 8: <a href="https://man7.org/linux/man-pages/man8/ld.so.8.html">LD_PRELOAD</a> hijacking</h3><p>The &#8220;signer&#8221; is legit and passed <code>fsverity</code>, only process allowed to read the keys.</p><p><strong>Attack</strong>: Use the LD_PRELOAD env variable. <code>LD_PRELOAD=/tmp/evil.so ./signer</code></p><p>The injected library intercepts every function call. When the signer reads the private key, the injected malicious code sees it too. LSM allows the read because it&#8217;s the authorized process, but it has been hijacked. LD_PRELOAD is normally a good process which is used in a lot of places but it can be used maliciously.</p><p><strong>Defense</strong>: Use eBPF to track which processes are allowed to use the <code>LD_PRELOAD</code> environment variable. You can track this at the kernel when a process starts.</p><p>We aren&#8217;t using the LSM hook here, and there is a possibility of TOCTTOU. There is a tracepoint where we capture the ENV data and secure it in the LSM hook.</p><pre><code><code>SEC(&#8221;tracepoint/syscalls/sys_enter_execve&#8221;)
int trace_execve(struct trace_event_raw_sys_enter* ctx) {
 /*
 * Capture environment variables at process start.
 * Flag processes that have LD_* variables set.
 */

 // Read environment variables from execve args
 // If any starts with &#8220;LD_&#8221;, flag this process
}

SEC(&#8221;lsm/bprm_check_security&#8221;)
int BPF_PROG(lsm_bprm_check_security, struct linux_binprm *bprm, int ret) {
 /*
 * Block unauthorized LD_PRELOAD.
 * Only allowlisted executables can use LD_* environment variables.
 */

 // Get executable path
 // If process was flagged for LD_* usage
 //   Check if this executable is allowed to use LD_*
 //   If not allowed, return -EPERM
}
</code></code></pre><p>Turns out real world processes actually use it. For example, k3s and cni (cloud native Infrastructure) use this. We wrote about this and provided an actual example <a href="https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft?r=2bngj5&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=false">https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft?r=2bngj5&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=false</a></p><h3>Attack Vector 9: Compromise before boot.</h3><p>All of this assumes the eBPF agent is running and was started on a clean system. But how do we trust that? What if the attacker loaded a rootkit in the bootloader? What happens if they modified the kernel.</p><p><strong>Attack</strong>: Modify the bootloader or kernel image, load malicious code before the eBPF agent starts. <strong>Defense</strong>: The system should be configured to use a Secure Boot chain and a Signed bootloader, signed kernel. The system won&#8217;t boot if anything in the chain is tampered with. By the time userspace starts and the eBPF agent loads, you have cryptographic proof that nothing was modified.</p><h3>What&#8217;s left?</h3><ul><li><p>Hardware implants.</p></li><li><p>Side-channel attacks.</p></li><li><p>Compromised chips from the factory.</p></li></ul><p>I can&#8217;t help you there. Different threat model. Different budget. If someone is decapping your CPU, you have other problems.</p><p>Most of the time, digital access is way easier than physical. Physical attacks cost real money, you need people, equipment, and you need to be somewhere. Compromising one employee or finding one vulnerability? That&#8217;s a laptop and patience.</p><p>So it gets stolen because <code>cat /secrets/key.json</code> worked. Or someone attached gdb and dumped memory. Or an insider replaced a binary and flew to a country without an extradition treaty.</p><p>Upbit lost $37M. We don&#8217;t know which attack vector it was. But vectors 1-9? All stoppable with eBPF LSM. No HSM. No air-gapped signing setup. Just kernel enforcement on regular Linux.</p><p>You don&#8217;t have to stop North Korea. You just have to be harder to rob than the next exchange!</p>]]></content:encoded></item><item><title><![CDATA[How we managed to secure $25K in GCP credits without a VC]]></title><description><![CDATA[We didn't know it was possible to get GCP credits without a VC]]></description><link>https://substack.bomfather.dev/p/how-we-managed-to-secure-25k-in-gcp</link><guid isPermaLink="false">https://substack.bomfather.dev/p/how-we-managed-to-secure-25k-in-gcp</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Wed, 26 Nov 2025 18:17:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!knZX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cloud costs are one of the biggest contributors to our burn rate. So, how can we reduce it?</p><p>Up until recently we didn&#8217;t know that you could get cloud credits without a VC, so we decided to write about this for anyone who wasn&#8217;t aware of this! </p><p>We reached out to a few cloud providers to reduce our cloud costs. We were successful with Cloudflare, they gave us $5000 in credits. This was good, but we needed bare bones Linux VMs and k8s nodes for our runtime and GPU security, and Cloudflare didn&#8217;t have one. But we do use Cloudflare for things like Tunnels and DDoS protection.</p><p>We got angel funding from <span class="mention-wrap" data-attrs="{&quot;name&quot;:&quot;Balaji&quot;,&quot;id&quot;:3788369,&quot;type&quot;:&quot;user&quot;,&quot;url&quot;:null,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd76f61c2-c4b1-413b-9d81-fe134d00b9b5_355x355.jpeg&quot;,&quot;uuid&quot;:&quot;cb3aa574-a53b-46bc-9b5a-779bd65e8847&quot;}" data-component-name="MentionToDOM"></span> and used Brex as our banking partner. We dug into Brex Perks (<a href="https://www.brex.com/support/brex-partner-perks">https://www.brex.com/support/brex-partner-perks</a>) and realized they offered &#8220;Google Cloud: You can have Google Cloud and Firebase costs covered up to $200,000 USD over 2 years.&#8221;</p><p>The Brex &#8220;AWS Activate&#8221; was only $5,000. We knew that wouldn&#8217;t be enough for what we are building. So we didn&#8217;t bother with AWS Activate.</p><p>We were already talking to GCP before this, and we shared that we were banking with Brex and wanted to use this option for cloud credits. The GCP team said we&#8217;d need funding to get credits. It doesn&#8217;t have to be VC, angel or SAFEs work.</p><p>The GCP team reached out to AngelList to confirm that Balaji funded us, and once that was confirmed, we got $25,000 in cloud credits. The perks page says $200k, but GCP gave us $25k. Not sure why the gap, but we&#8217;ll take 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_!knZX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!knZX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 424w, https://substackcdn.com/image/fetch/$s_!knZX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 848w, https://substackcdn.com/image/fetch/$s_!knZX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!knZX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!knZX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg" width="538" height="490.7032967032967" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1328,&quot;width&quot;:1456,&quot;resizeWidth&quot;:538,&quot;bytes&quot;:290734,&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://substack.bomfather.dev/i/180044924?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.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_!knZX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 424w, https://substackcdn.com/image/fetch/$s_!knZX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 848w, https://substackcdn.com/image/fetch/$s_!knZX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!knZX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f02a60-e845-48d9-84ae-2e6bdfbf210e_1638x1494.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><p>This process took more than 8 weeks, with back and forth emails and verification delays. So if you want to do this be patient.</p><p>If you&#8217;re a Brex customer with angel funding, here&#8217;s exactly what to do:</p><ol><li><p>Fill out the form <a href="https://dashboard.brex.com/p/rewards/perks-and-discounts?categories=cloud-computing&amp;drawer=perk%7EGoogle+Cloud">https://dashboard.brex.com/p/rewards/perks-and-discounts?categories=cloud-computing&amp;drawer=perk%7EGoogle+Cloud</a> </p></li><li><p>Email <a href="http://cloudstartupsupport@google.com">cloudstartupsupport@google.com</a> </p></li><li><p>The cloud startup support team usually asks for the Angel&#8217;s email address to validate the funding.</p></li></ol><p>Look, $25k isn&#8217;t life changing, but it&#8217;s at least 6 months of runway we didn&#8217;t have to pay for. If you&#8217;re bootstrapping or angel funded, every dollar counts.</p><p>The process is bureaucratic and slow, but definitely worth it. </p><p>If you hit any roadblocks, hit me up at <a href="http://nathan@bomfather.dev">nathan@bomfather.dev</a>, and I&#8217;ll try to help!<br><br>We know that this blog post sounds kind of sponsored but we aren&#8217;t sponsored by Brex. We are just sharing this because it was useful for us!</p>]]></content:encoded></item><item><title><![CDATA[Securing Runtime of the L2 Base Ethereum Nodes]]></title><description><![CDATA[The Problem]]></description><link>https://substack.bomfather.dev/p/securing-runtime-of-the-l2-base-ethereum</link><guid isPermaLink="false">https://substack.bomfather.dev/p/securing-runtime-of-the-l2-base-ethereum</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Thu, 13 Nov 2025 21:38:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vouS!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f64147e-95ad-4a05-9d04-25bd8f97c89e_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Problem</h2><p>So you are running an L2 node and have a lot of money in it. Like most of us, we &#8220;hope&#8221; it is secure. In this, let&#8217;s unpack some of the challenges that I ran into while trying to secure the &#8220;Runtime&#8221; for L2 Nodes.</p><p>I am not an Ethereum or an L2 Node expert. I am a regular guy who wants to run an L2 node and has started questioning the Runtime&#8217;s security posture.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>I am doing this experiment on k8s.</p><h2>The JWT Secret</h2><p>Let&#8217;s start with something simple, such as securing the &#8220;JWT&#8221; used to authenticate the OP Node to the Nethermind API. What can go wrong if that gets compromised? Money can be lost.</p><p>So how do we secure the JWT secret?</p><p>For starters, we could use something like Vault or AWS Secrets Manager.</p><p>The secrets are now loaded into a Volume on the Node(Linux machine).</p><p>The goal is to restrict access to op-node, Nethermind, and runc, and nothing else. Not even the root user.</p><p>Our requirement is that only these three processes (as of now) should be able to access it. We want guarantees from the kernel with eBPF.</p><p><strong>What is runc?</strong></p><p>It is the container runtime. It is the process that runs the container. It is the process that starts OP Node and Nethermind. It needs access because it is the process that talks to the kernel and sets up everything. So it needs access to set things up.</p><p>How does the secret get written to the file? Are we storing the secrets on the disk? The files are stored on disk using &#8220;tmpfs&#8221;</p><p><strong>What is &#8220;tmpfs&#8221;?</strong></p><p>It is a temporary file system. It is a file system that is stored in memory. It is a file system that is not persisted to the disk. It is a file system that is deleted when the container is deleted. Another way to say it is an in-memory file system.</p><p>The secret from the AWS Secrets Manager or Vault is written to the tmpfs by the k8s runtime.</p><p><strong>Why kernel?</strong></p><p>Following the principles of zero Trust from crypto, how do we know that an elevated privileged process didn&#8217;t read the keys? So that&#8217;s why you use the bottom of the stack to ensure it hasn&#8217;t been tampered with.</p><p><strong>Why eBPF?</strong></p><p>We don&#8217;t want new kernel modules. eBPF is the new cool kid that solves this problem. Now, the next question is: what if another eBPF allows when our program tries to stop? Can&#8217;t the malicious user do it? So that they steal the JWT Token. Yes, we tested that, and it&#8217;s not possible. You can read more about this at <a href="https://substack.bomfather.dev/p/how-we-secured-our-ebpf-from-ebpf">https://substack.bomfather.dev/p/how-we-secured-our-ebpf-from-ebpf</a>.</p><h2>Our requirements</h2><p>The security agent (eBPF) running in the kernel should not only protect the JWT Secret, but also protect the executable that has read and write access to it. Why? Because if the attacker can compromise the executable with access, they have circumvented the problem with a Supply Chain Attack.</p><p>Because the eBPF agent runs in kernel space, not in user space, even administrators cannot bypass it. It enforces the policies at the kernel level before user-space-level executables can interfere.</p><p>We also want to protect against insider threats, such as system administrators not being able to read secrets. Also, what happens if the administrator stops the security agent? Then it&#8217;s game over.</p><p>We thought about and built a solution so that the agent cannot be killed. The agent can be killed with a PKI and Nonce-based solution. Similar to the platform the crypto is built on (we copied). <a href="https://substack.bomfather.dev/p/stopping-kill-signals-against-your">https://substack.bomfather.dev/p/stopping-kill-signals-against-your</a></p><h2>The LSM Hook</h2><p>eBPF didn&#8217;t invent security from scratch. It plugs into Linux Security Modules (LSM), which is the same framework that powers SELinux and AppArmor.</p><p>Here&#8217;s what that means.</p><p>LSM is the kernel&#8217;s security checkpoint. Every time any process tries to open a file, read memory, make a network connection, or create a process, the kernel fires an LSM hook. This happens before the operation completes.</p><p>eBPF attaches to these hooks. When Nethermind calls open(&#8221;/secrets/jwt.hex&#8221;), the LSM hook fires, our eBPF program runs, checks the policy, and decides whether to allow or deny the request.</p><p>Why this matters:</p><ul><li><p>Runs in kernel space - Can&#8217;t be bypassed by userspace tricks</p></li><li><p>Happens before the syscall - Blocks the operation, doesn&#8217;t just log it</p></li></ul><p><strong>Write Access To The JWT Secre</strong>t</p><p>In our example, we used k3d (a simple Kubernetes cluster).</p><p>When we ran our tests with Deny all access to the JWT. We realized we need to provide access:</p><ul><li><p>/bin/k3s</p></li><li><p>/bin/containerd-shim-runc-v2</p></li><li><p>/bin/runc</p></li></ul><p>OK. Now that we have figured this out, it is much easier to choose which of these executables we need to secure.</p><p>But what happens if someone modifies these executables?</p><p><strong>Immutable Executables</strong></p><p>We want a security policy that prevents k3s, containerd-shim-runc-v2, and runc from being modified. We are trying to do what an immutable kernel would be like, <code>Fedora</code>.</p><p>We want to add these executables to the policy, where we can define an immutable executable that no one can modify.</p><p>The security agent would ensure that all these executables aren&#8217;t modified while the agent is running. This prevents someone from backdooring <code>runc</code>, for example, and performing malicious actions.</p><p><a href="https://gist.github.com/neilnaveen/74014e01cab0389c09549c5a239f4652">GitHub Gist For Security Policy</a></p><h2>What We&#8217;re Protecting</h2><p>Now, I know we&#8217;ve been focused on the JWT secret, but here&#8217;s the thing: this same eBPF-based approach protects way more than just that one file.</p><p>What else are we protecting from the kernel?</p><p>Cryptographic Keys:</p><ul><li><p>Nethermind keystore (/data/keystore) - Your validator signing keys. If compromised? Sign malicious blocks, steal funds.</p></li><li><p>Nethermind P2P node key (/data/nodeKey) - Network identity. Compromise this? Eclipse attacks, network manipulation.</p></li><li><p>OP Node P2P private key (/data/opnode_p2p_priv.txt) - P2P identity theft territory.</p></li><li><p>OP Node discovery secret (/data/opnode_discovery_secret.txt) - Network manipulation potential.</p></li></ul><p>Application Data:</p><ul><li><p>Blockchain database, state, receipts at /data</p></li><li><p>Temporary storage at /tmp/nethermind (in-memory tmpfs)</p></li></ul><p><strong>Same principle as the JWT secret:</strong></p><p>Only the specific executable that needs access gets access. Everything else? Denied at the kernel level. So when we say &#8220;secure the JWT secret,&#8221; we&#8217;re really talking about an entire security model that protects all your sensitive data from unauthorized access.</p><p>But wait. These are container paths, the view from <em>inside</em> the container. In <code>k8s</code>, <code>/data</code> is mounted from somewhere on the host, like /tmp/base-node/nethermind-data. What stops someone from just SSHing into the host and reading that path directly? Nothing. Yet. More on that later.</p><h2>The Attack Vector You Didn&#8217;t See Coming</h2><p>OK, so we&#8217;ve locked down file access at the kernel level. Only Nethermind and op-node can read the JWT secret.</p><p>What if I told you that, even though we&#8217;ve restricted which executables can access the secret, ensured it is not backdoored, and verified the SHA256 digest, an attacker can still steal it by hijacking the Nethermind or op-node themselves?</p><p>How? One word: <code>LD_PRELOAD</code>.</p><p>It&#8217;s an environment variable that lets you load your own shared library before any other library loads.</p><p>You can intercept every single function call that Nethermind makes. Reading a file? Intercepted. Making a network call? Intercepted. That JWT secret Nethermind just read from /secrets/jwt.hex? Yeah, we can grab that too.</p><p>But wait, can the eBPF agent block unauthorized file access, right?</p><p>Sure does. But here&#8217;s the thing: Nethermind is authorized. So when Nethermind reads the JWT secret, the eBPF agent says, &#8220;yep, you&#8217;re good buddy.&#8221; But what if Nethermind isn&#8217;t really Nethermind anymore? What if it&#8217;s been... augmented?</p><p>Think about it. We protected the file. We protected the executable. But did we protect the execution environment?</p><p>And if they can, what else can they intercept? Just the JWT secret? Or every cryptographic operation Nethermind performs?</p><p>I&#8217;ll tell you what keeps me up at night: We spent all this time securing the file, but forgot about the process that reads it.</p><p>Do real-world processes really use LD_PRELOAD? You might think I&#8217;m making this up. But nope. It&#8217;s real. Remember k3d? It uses LD_PRELOAD to instrument the container runtime for:</p><ul><li><p>/bin/containerd-shim-runc-v2</p></li><li><p>/bin/k3s</p></li><li><p>/bin/cni</p></li><li><p>/bin/aux/xtables-nft-multi</p></li></ul><p>I&#8217;ve written a complete technical breakdown with working exploit code here: LD_PRELOAD: The Invisible Key Theft. <a href="https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft">https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft</a></p><p><strong>How do we defend against this?</strong></p><p>We use eBPF to stop this. Though there is a <a href="https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use">TOCTU</a> Vulnerability in the eBPF solution. We track when a process starts, capture its environment variables, and use that information to apply policy rules, such as which processes are allowed to use LD_PRELOAD.</p><h2>What We Learned</h2><p>Security isn&#8217;t about protecting a single thing, like JWT keys in AWS Secret Manager.</p><p>You secure the file. Attacker targets the executable.</p><p>You secure the executable. Attacker hijacks the environment.</p><p>You secure the environment. Attacker finds another vector.</p><p>That&#8217;s why we keep going back to the kernel. eBPF lets us enforce security at every single checkpoint.</p><h2>Where Do We Go From Here?</h2><p>We haven&#8217;t covered all of the Runtime vectors. But for now, if you are running an L2 node, you should be asking: who has access to the JWT Secret? What&#8217;s protecting my executables? Can someone dump the memory, ptrace it, or attach a debugger to change things? More on that later.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Breakdown of New RunC vulnerabilities CVE-2025-31133, CVE-2025-52565, and CVE‑2025‑52881]]></title><description><![CDATA[I recommend reading the actual exploit https://seclists.org/oss-sec/2025/q4/161. It&#8217;s mind-blowing, how complex and how many jumps it takes actually to do something like this:]]></description><link>https://substack.bomfather.dev/p/breakdown-of-new-runc-vulnerabilities</link><guid isPermaLink="false">https://substack.bomfather.dev/p/breakdown-of-new-runc-vulnerabilities</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Sun, 09 Nov 2025 01:19:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XtZr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recommend reading the actual exploit <a href="https://seclists.org/oss-sec/2025/q4/161">https://seclists.org/oss-sec/2025/q4/161</a>. It&#8217;s mind-blowing, how complex and how many jumps it takes actually to do something like this:</p><h1>What&#8217;s even happening?</h1><p>Essentially, an attacker wants to gain elevated access on the machine, but is only an unprivileged user. This vulnerability allows the attacker to obtain <code>root</code> on <code>host</code> , crash the kernel, and disable Linux Security Module policies. In a nutshell, pretty bad stuff.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>The root of it all</h1><p>Essentially, the root cause of all three of these vulnerabilities is a Time-of-Check to Time-of-Use (TOCTU) vulnerability in runc&#8217;s mounting logic.</p><p><code>runc</code> just wants to mount normal directories into a container, but an attacker can sabotage this process, to make <code>runc</code> mount some protected directories into the container, the most useful of them being<code> /proc/*</code> dirs.</p><p>Attackers want to do this, because as an unprivileged process, they can&#8217;t access these protected knobs to control the whole machine, and the kernel.</p><p>The attack is quite simple, <code>runc</code> opens a file/dir/mount validates the path, and then performs the mount. In this small time window between mount and read, the attacker is replacing a path with a symlink during a race window, so that runc mounts the attacker&#8217;s target instead.</p><p>All three CVEs follow this pattern, just in different places in runc.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XtZr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XtZr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 424w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 848w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 1272w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XtZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic" width="660" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:660,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30100,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://substack.bomfather.dev/i/178387347?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XtZr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 424w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 848w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 1272w, https://substackcdn.com/image/fetch/$s_!XtZr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7d785fe-b833-46a6-9499-2cee959a0081_660x640.heic 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><h1>Attacking masking paths: CVE-2025-31133</h1><p>Typically, <code>runc</code> masks specific directories, so that sensitive paths are not exposed to containers. It does this by mounting <code>/dev/null</code> over those paths.</p><p>What an attacker can do is use that TOCTU attack to make <code>runc</code> mount a sensitive directory instead of <code>/dev/null</code>. In the CVE, the examples directories were <code>/proc/sys/kernel/core_pattern</code> and <code>/proc/sysrq-trigger</code></p><p>Both of these are <strong>really, really bad</strong>, essentially game-over scenarios, if mounted in the container.</p><h2>Mounting <code>/proc/sys/kernel/core_pattern</code></h2><p>If the attacker chose <code>/proc/sys/kernel/core_pattern</code>, they could basically run any arbitrary code they wanted on the system as root.</p><p>This is because <code>core_pattern</code> runs whenever a program crashes. And if an attacker has access to <code>core_pattern</code>, they can configure its path/arguments to run whatever bad script they want. They can also prefix <code>core_pattern</code> with a <code>|</code>, which effectively allows their command to run as a root-level process within the host&#8217;s namespace.</p><p>So an attacker can write any script they want to <code>core_pattern</code>, prefix it with <code>|</code>, and essentially run it across the whole system as root, doing whatever they want.</p><h2>Mounting <code>/proc/sysrq-trigger</code></h2><p>If they have access to <code>/proc/sysrq-trigger</code>, they can essentially control <code>sysrq</code>, which allows you to perform low-level kernel actions immediately.</p><p>You could cause the kernel to panic, shut down processes, and do a lot of bad things.</p><h1>Attacking the console: CVE-2025-52565</h1><p>This is very similar to the masking attack; <code>runc</code> needs to give the container a console, so it mounts <code>/dev/pts/$N</code> to <code>/dev/console</code>. Now, an attacker can symlink <code>/dev/pts/$N</code> to a protected dir like <code>core_pattern</code> or <code>sysrq-trigger</code>, so we can write to the console and thereby write to these protected dirs.</p><h1>Disable Linux Security Module (LSM) polices: CVE&#8209;2025&#8209;52881</h1><p>When <code>runc</code> needs to write an LSM policy, it needs to write it to <code>/proc/self/attr/*</code> to set LSM labels for the container that is being started. An attacker can execute a very similar TOCTU attack on this write operation.</p><p><code>runc</code> wants to open <code>/proc/self/attr/*</code>, but during that small time window, if an attacker symlinks the file to another location, the LSM labels are not written, so security policies are not enforced. What could be even worse is that those writes could be piped to other sensitive files, such as <code>core_pattern</code> or <code>sysrq-trigger</code>.</p><h2>Caveats</h2><p>This is harder for the attacker to execute, since they must be able to symlink the file where the LSM policy attribute path is stored. This also does not affect something like BPF-LSM, since this is only a way for userspace applications to write policies to the kernel.</p><h1>How can we stop this?</h1><p>Upgrade to the patched versions of <code>runc</code> as soon as possible, which are versions 1.2.8, 1.3.3, 1.4.0-rc.3. This should remove the immediate threat vector.</p><p>As for more proactive steps to take against a similar attack, you could:</p><p>Run most workloads as rootless. It </p><p>may take extra effort in some cases, but it immediately improves your security posture.</p><p>Another significant step we could take is to prevent symlink creation in specific directories.</p><p>If we can forbid symlinks on <code>proc/sys</code> and <code>/proc/*/attr/*</code>, you should be able to prevent anyone from directly symlinking to one of these protected dirs, even if a root process (like runc!) is compromised.</p><p>To enforce something like this, using something like BPF-LSM, it should be pretty easy to implement policy by writing a hook on something like <code>security_inode_symlink</code> and <code>security_file_open </code>(I don&#8217;t have much experience with AppArmor and SELinux, so they may not work, since they rely on LSM labels on files, unlike BPF-LSM, which doesn&#8217;t rely on that)</p><p>PS: Bomfather can stop this, we aren&#8217;t trying to plug ourselves, so use whatever works for you best :)</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[L2 Base Node Builds are Insecure]]></title><description><![CDATA[The docker builds for L2 base/node are full of holes]]></description><link>https://substack.bomfather.dev/p/l2-base-node-builds-are-insecure</link><guid isPermaLink="false">https://substack.bomfather.dev/p/l2-base-node-builds-are-insecure</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Thu, 30 Oct 2025 19:37:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ltGZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ethereum is based on zero trust and mathematically sound security. But the Base L2 Node (<a href="https://github.com/base/node/tree/main/geth">https://github.com/base/node/tree/main/geth</a>) isn&#8217;t being built securely. So, what is wrong with the existing Base docker image?</p><h2>A Caveat</h2><p>I&#8217;m in no way an Ethereum expert, nor have I ever run an Ethereum node before. I am just a regular guy who wants to run a node for fun and solve my itch.</p><h2>Then Why Rebuild?</h2><p>Because I am paranoid and security obsessed and want fewer moving parts in the build and runtime. Especially when it involves  (to the moon baby!).</p><h2>The Problem</h2><p>Remember the 2021 CodeCov Bash Uploader compromise? A modified script exfiltrated secrets from thousands of build environments. The original geth image in base/node uses the same kind of risky pattern: <code>RUN curl -sSfL &#8216;&lt;https://just.systems/install.sh&gt;&#8217; | bash -s -- --to /usr/local/bin</code>. This pipes an untrusted script directly to bash without checksum verification. This is a classic supply chain attack.</p><h3>So what is wrong with the existing image for geth?</h3><ul><li><p>It has a shell, package manager, and unnecessary binaries.</p></li><li><p>It is not a static binary build.</p></li><li><p>It does not use pinned base images with SHA256 digests. Without the images being pinned, they could have been tampered with and you wouldn&#8217;t have a clue what you&#8217;re building.</p></li><li><p>It is a root user. A root use can do anything.</p></li><li><p>It exposes all ports, not just the necessary ones. Its like leaving your house with all the doors and windows open.</p></li><li><p>It isn&#8217;t a reproducible build.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ltGZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ltGZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ltGZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg" width="666" height="362" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:362,&quot;width&quot;:666,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:105304,&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://substack.bomfather.dev/i/177597373?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.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_!ltGZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ltGZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cdfba32-3b24-485c-804a-195b8b53d410_666x362.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></li></ul><h2>The Solution</h2><h3>How did I secure the build for geth?</h3><ul><li><p>Pinned base images with SHA256 digests. This ensures the image can&#8217;t be tampered with and the build is reproducible.</p></li><li><p>Static Binary Build. <code>CGO_ENABLED=0</code> So, no libc dependencies and no dynamic linking.</p></li><li><p>Being paranoid I am explicitly setting the user to nonroot even though I know the distroless is already nonroot.</p></li><li><p>Expose only the necessary ports. I am used to clicking the lock button twice on my car to ensure the doors are locked even though they were already locked on the first press of the button. I don&#8217;t want all the ports open on my docker image.</p></li><li><p>The final image is a single binary, there are no other tools or dependencies in the image. Less patching means fewer vulnerabilities and fewer dreaded Dependabot PRs.</p></li><li><p>If there is a zero day in the binary, it will be harder to exploit since we don&#8217;t have these additional tools. Less code means fewer vulnerabilities (the famous <a href="https://github.com/kelseyhightower/nocode">nocode</a></p></li></ul><ul><li><p>philosophy: no code = no bugs).</p></li></ul><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/zIDt2/1/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf9dbef7-2fe4-484b-82d5-6f6de72fb0ad_1220x402.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2319471a-d09c-42e9-a339-48841b07c2ca_1220x472.png&quot;,&quot;height&quot;:250,&quot;title&quot;:&quot;Vulnerabilities in L2 base/node images&quot;,&quot;description&quot;:&quot;Create interactive, responsive &amp; beautiful charts &#8212; no code required.&quot;}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/zIDt2/1/" width="730" height="250" frameborder="0" scrolling="no"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h2>The Build</h2><pre><code><code>FROM golang:1.23@sha256:60deed95d3888cc5e4d9ff8a10c54e5edc008c6ae3fba6187be6fb592e19e8c0 AS build

WORKDIR /app

# Build arguments for version info
ARG OP_NODE_REPO=https://github.com/ethereum-optimism/optimism.git
ARG OP_NODE_TAG=op-node/v1.14.1
ARG OP_NODE_COMMIT=c1081e3ad0004590435e3179e583cdfdbdd6bc61

RUN git clone $OP_NODE_REPO --branch $OP_NODE_TAG --single-branch . &amp;&amp; \\
    git switch -c branch-$OP_NODE_TAG &amp;&amp; \\
    [ &#8220;$(git rev-parse HEAD)&#8221; = &#8220;$OP_NODE_COMMIT&#8221; ]

RUN cd op-node &amp;&amp; \\
    GITCOMMIT=$(git rev-parse HEAD) &amp;&amp; \\
    GITDATE=$(git show -s --format=%cI HEAD) &amp;&amp; \\
    CGO_ENABLED=0 GOOS=linux go build -v \\
      -ldflags &#8220;-s -w -X main.GitCommit=${GITCOMMIT} -X main.GitDate=${GITDATE} -X github.com/ethereum-optimism/optimism/op-node/version.Version=${OP_NODE_TAG} -X github.com/ethereum-optimism/optimism/op-node/version.Meta=stable&#8221; \\
      -o /bin/op-node \\
      ./cmd

FROM gcr.io/distroless/static:nonroot@sha256:e8a4044e0b4ae4257efa45fc026c0bc30ad320d43bd4c1a7d5271bd241e386d0

WORKDIR /app

COPY --from=build /bin/op-node ./

USER nonroot

EXPOSE 9545 9222 9222/udp 7300

ENTRYPOINT [&#8221;./op-node&#8221;]
</code></code></pre><h2>What This Doesn&#8217;t Solve</h2><p>So is geth secure? Kind of.</p><p>How do you trust the build environment? Does your build environment have any vulnerabilities? <a href="https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have">https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have</a> ;)</p><p>What happens to runtime security like access to the secrets and keys? Do you trust the runtime environment?</p><p>Crypto was built to have zero trust, but it has to run somewhere, so a new problem might be centered around whether your k8s secure? How about your cloud provider, are they secure? Or your hardware, do you know if it is secure?</p><p>What about the insider threat?</p><p>All of this is for another day.</p><h2>Was It Worth It?</h2><p>Is this overkill for a testnet node? Yes.</p><p>Would I do it again? Also yes.</p><p>Will this stop nation-state actors? LMAO no.</p><p>Will it stop some script kiddie running automated scans? Maybe!</p><p>For a production mainnet node handling real funds? Absolutely.</p><p>My next project: convincing myself that my hardware isn&#8217;t already compromised from the factory. (Spoiler: it probably is, but ignorance is bliss.)</p><p><em>To the moon! &#128640; (But securely.)</em></p><p></p>]]></content:encoded></item><item><title><![CDATA[Stopping kill signals against your eBPF programs]]></title><description><![CDATA[Death has never been fun, let's avoid it (with eBPF)]]></description><link>https://substack.bomfather.dev/p/stopping-kill-signals-against-your</link><guid isPermaLink="false">https://substack.bomfather.dev/p/stopping-kill-signals-against-your</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Wed, 29 Oct 2025 17:49:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IVD7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1></h1><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;341af409-8adf-48c6-8521-ddb4e5dbe459&quot;,&quot;duration&quot;:null}"></div><h1><strong><br>What is the usual way of doing this</strong></h1><p>Most eBPF agents run as daemons, so they can&#8217;t be shut down, only allowing authorized actors to shut them down. But if a malicious process gets elevated privileges, it could shut down our eBPF agent. Now, most security tools would have to stop here, but eBPF does not, we can make our agents even more resilient.</p><p>If we want to do this, we need a way to stop malicious processes from killing our agents, no matter their access to the system.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IVD7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IVD7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 424w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 848w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IVD7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg" width="650" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:650,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;bpfdoge&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="bpfdoge" title="bpfdoge" srcset="https://substackcdn.com/image/fetch/$s_!IVD7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 424w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 848w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!IVD7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f40bcd-8faa-46a7-b5f7-81bbfe8e6fcc_650x485.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><strong>The easy steps to stop death itself</strong></h1><p>That seems complicated, right? How can we stop any process from killing our agent, no matter their system privileges? With eBPF, we can hook into <code>security_task_kill</code>and stop shutdown signals from reaching our agent.</p><p>If you write an <code>lsm/task_kill</code> hook, you can stop the death of any process, so if we write a hook like this, you can prevent the death of your process, no matter what happens.</p><pre><code><code>SEC(&#8221;lsm/task_kill&#8221;)
int BPF_PROG(lsm_task_kill, struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred) {
  u32 pid = BPF_CORE_READ(p, pid);
  if (pid == my_userspace_agent_pid) {
    return -EPERM;
  }
  return 0;
}</code></code></pre><h1><strong>But now I want it to die, really, really badly</strong></h1><p>Ok, great, now it is impossible to kill, the job is done.</p><p>But now, your boss just added a new requirement to your eBPF agent, so you build it and release a new version to use. But now the question is, how do we upgrade our current running eBPF agent?</p><p>If we can&#8217;t kill the process, we can&#8217;t shut it down and start an upgraded version. At this point, nothing can stop it. Your agent used to be useful, but now it&#8217;s like a wart that won&#8217;t go away.</p><p>At the end, to solve this issue, you restart every single machine in your infra, angering your boss (he was never happy, but now he really is pissed) and messing up everyone&#8217;s day (or week). You must remove the shutdown security from your eBPF agent, leaving it wide open for any bad actor to kill it.</p><p>How did things go so wrong, so fast?</p><h1><strong>How to tell something that it needs to die</strong></h1><p>The problem is that we need a way to tell the agent whether a good guy or a bad guy is killing it. But according to our requirements, we can&#8217;t even trust root(sudo). What do we do?</p><p>We could send a signed message to the eBPF program&#8217;s user space agent. If an authorized user signs it, the eBPF program kills itself. However, even this has a problem.</p><p>We may be able to keep our key secret, but can we keep that signed message secret? Someone could perform a replay attack and sabotage our system.</p><p>If we killed the agent once, and somebody got that signed message, now they could send the message again, at their own discretion.</p><p>To solve this issue, we could use a <strong><a href="https://en.wikipedia.org/wiki/Cryptographic_nonce">nonce</a></strong>. When an authorized (or unauthorized) user requests to kill the eBPF agent, the agent sends back a random string to be signed. The user must send this signed nonce back to the agent, which, if valid, will shut down the program. This requires the same authorized user to re-sign a new message for each shutdown, ensuring it is not an attacker with an old, used message.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zybz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zybz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 424w, https://substackcdn.com/image/fetch/$s_!zybz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 848w, https://substackcdn.com/image/fetch/$s_!zybz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!zybz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zybz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png" width="547" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:547,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89133,&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://substack.bomfather.dev/i/177491502?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.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_!zybz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 424w, https://substackcdn.com/image/fetch/$s_!zybz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 848w, https://substackcdn.com/image/fetch/$s_!zybz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!zybz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11185216-7348-4e7b-bdc6-03099681a9db_547x1000.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><h1><strong>How are keys more secure</strong></h1><p>Keys are more secure since we can keep them on separate, more secure systems. Whenever the agent needs to be shut down, you could send a request from the agent to another machine, which could verify if they want to shut down the agent manually and then sign the message. The key could even be a hardware key. Using a key lets us move our security to a more secure system (the most secure being hardware keys).</p><h1><strong>Do I really need to do this?</strong></h1><p>Well, you don&#8217;t exactly have to do this, but then again, somebody could just shut down your program, neutralizing your whole eBPF agent, without even finding a loophole in its logic.</p><p>Now, this doesn&#8217;t mean that when you start writing and deploying eBPF agents, you immediately need to do this, but without some sort of shutdown security, writing a security tool with eBPF without it kind of defeats the purpose of kernel-level security.</p><p>If you want your eBPF agent to be truly secure, securing against shutdowns is not enough. Attackers can manipulate your eBPF maps, disabling policies and destroying logs. If you want to be secure, you should read this blog post: <a href="https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps">https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[GitHub’s ubuntu-latest Runners Have 1,681 Packages and 9 HIGH-Severity Vulnerabilities]]></title><description><![CDATA[We build our platform in Go and C. Our production containers are stripped down to exactly what we need. Removing unnecessary packages and minimizing the attack surface. We vendor our packages.]]></description><link>https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have</link><guid isPermaLink="false">https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Sun, 26 Oct 2025 18:59:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3Vk7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We build our platform in <code>Go</code> and <code>C</code>. Our production containers are stripped down to exactly what we need. Removing unnecessary packages and minimizing the attack surface. We vendor our packages.</p><p>Then we run our builds on GitHub&#8217;s <code>ubuntu-latest</code> runners, which have 1,681 packages.</p><p>That&#8217;s absurd. We won&#8217;t run production with all this cruft, so why would we build with it? Supply chain attacks targeting build environments are increasingly common and devastating, compromising one dependency can silently infect every artifact we produce. Our build environment can access our source code, secrets, and signing keys. If anything, it should be more locked down than production, not less.</p><p>Last week, we inventoried what actually ships on <code>ubuntu-latest</code>. The results convinced us to move away from GitHub runners entirely.</p><h2>The numbers</h2><p>We ran an inventory script on a fresh <code>ubuntu-latest</code> runner, and the total package count is 1,681.</p><p>Breaking it down, 214 <code>APT</code> system packages, 162 <code>Python</code> packages, 173 <code>Ruby</code> <code>gems</code>, 119 <code>Conda</code> packages, and 13 <code>NPM</code> packages.</p><p>We build <code>Go</code> and <code>C</code> code. We need the <code>Go</code> and <code>C</code> compiler, <code>git</code>, and maybe 15 other tools. About 20 packages total.</p><p>The runner gives us 1,681 packages. That includes entire language ecosystems we never touch (<code>Python</code>, <code>Ruby</code>, <code>Node</code>, <code>Conda</code>). All sitting there, increasing our attack surface.</p><h2>The vulnerabilities</h2><p>We scanned 348 of those packages using <code>OSV.dev</code>. We could only scan the <code>Ruby</code>, <code>Python</code>, and <code>NPM</code> packages because <code>APT</code> and <code>Conda</code>aren&#8217;t supported by the scanner yet.</p><p>Of the 348 packages scanned, 16 packages have known vulnerabilities, with a total of 63 vulnerabilities across those 16 packages. 9 vulnerabilities were rated high, 38 moderate, 6 low, and 10 unknown.</p><p>The <code>Python</code> <code>cryptography</code> package version <code>41.0.7</code> has 6 high severity vulnerabilities.</p><p>The <code>setuptools</code> package version <code>68.1.2</code> has 3 high severity vulnerabilities. <code>CVE-2024-6345</code> is a path traversal bug where an attacker can write arbitrary files anywhere on the filesystem. That means someone can inject malicious code directly into your build artifacts.</p><p>We don&#8217;t use <code>Python</code>, and we definitely don&#8217;t import <code>cryptography</code> or <code>setuptools</code>. But they&#8217;re there in our build environment.</p><p>Would we run production with known high severity vulnerabilities? No.</p><p>Would we ship a container with <code>cryptography</code> <code>41.0.7</code> knowing it has 6 high CVEs? Never.</p><p>So why is it okay in our build environment?</p><h2>The full list</h2><p>For transparency, here are all 16 vulnerable packages we found:</p><p><code>Jinja2</code> has 10 vulnerabilities. The <code>cryptography</code> package has 10 with 6 being high. <code>Twisted</code> has 6. <code>Setuptools</code> has 6 with 3 being high. Then <code>certifi</code>, <code>idna</code>, <code>requests</code>, <code>urllib3</code>, and <code>python-apt</code> each have 4. <code>Configobj</code>, <code>cgi</code>, <code>webrick</code>, and <code>net-imap</code> each have 2. Finally <code>rdoc</code>, <code>resolv</code>, and <code>uri</code> each have 1.</p><p>These are just the packages we could scan. We couldn&#8217;t scan the 1,214 <code>APT</code> packages or the 119 <code>Conda</code> packages. The actual vulnerability count is almost certainly higher.</p><p>The code for getting the vulnerabilities is here, <strong><a href="https://github.com/bomfather/tools">bomfather/tools</a></strong>.</p><h2>Attack surface</h2><p>Every package is a potential attack vector. We know this, it&#8217;s why we minimize our production containers.</p><p>A production <code>Go</code> service might have 15 to 25 packages. The <code>Go</code> runtime, some system libraries, a few utilities.</p><p>The <code>ubuntu-latest</code> runner has 1,681 packages. That&#8217;s somewhere between 50 and 100 times more than our production containers. Each one has the potential to have vulnerabilities.</p><p>The math is brutal. If 4.6% of scanned packages have CVEs, and we extrapolate that to all 1,681 packages, we&#8217;re looking at roughly 77 vulnerable packages in the full environment. Even if the real number is half that, it&#8217;s still 38 packages with known exploits.</p><p>In production, one CVE gets immediate attention. We patch, test, deploy. Having 38 or 77 known vulnerabilities would be a critical incident.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3Vk7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3Vk7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3Vk7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg" width="700" height="435" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;grusPlan&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="grusPlan" title="grusPlan" srcset="https://substackcdn.com/image/fetch/$s_!3Vk7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3Vk7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c8b35b2-5a10-4578-9b62-478ce8129e33_700x435.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></figure></div><h2>The big problem</h2><p>We treat production extremely delicately with tightly audited layers, and a fast reflex for patching a critical CVE. Our builds have access to source, secrets, and signing keys, so they should deserve equal or stricter treatment.</p><p>Here&#8217;s what really bothers us. Those 9 high severity vulnerabilities are known, they&#8217;re in a public CVE database (published by security researchers).</p><p>Yet, they&#8217;re still there on the runner.</p><p>Maybe GitHub will update the image next week, maybe next month, or maybe they already did, and we tested an older image. The point is, we don&#8217;t control the timing, we can&#8217;t force an update, and we can&#8217;t even see what changed without diffing the entire environment ourselves.</p><p>In production, we&#8217;d be on those vulnerabilities immediately, patching, testing, and deploying the same day if possible. But for builds? We just accept them, run our CI on whatever GitHub gives us, and hope the vulnerabilities don&#8217;t matter.</p><h2>How we collected this data</h2><p>We ran this inventory on an actual <code>ubuntu-latest</code> runner using GitHub Actions. Package counts came from the native package managers. <code>APT</code> packages from <code>dpkg</code>, <code>Python</code> from <code>pip</code>, <code>Ruby</code> from <code>gem</code>, and so on.</p><p>Vulnerability scanning used the OSV.dev API (Google&#8217;s Open Source Vulnerability database). The scan covered <code>Python</code>, <code>Ruby</code>, and <code>NPM</code>packages, but couldn&#8217;t handle <code>APT</code> or <code>Conda</code> packages due to scanner limitations.</p><p>Total scan time was about 4 seconds. Out of 348 packages scanned, 16 had CVEs. Totally, we found 63 vulnerabilities.</p><p>We only scanned 348 of the 1,681 packages. The remaining 1,333 packages might be perfectly clean, or they might have dozens more vulnerabilities. We can&#8217;t tell because the scanner doesn&#8217;t support them, and we&#8217;re too lazy to write our own scanner for <code>APT</code> packages.</p><h2>We want control</h2><p>Currently, we run our builds with GitHub runners since we initially created our product with them, but moving away from them is a priority.</p><p>We aren&#8217;t moving away because they&#8217;re bad, but because they&#8217;re wrong for us. We&#8217;re security focused and we want a minimum footprint. We want to know exactly what&#8217;s in our environment. We want control over updates and we want to keep the same standards for builds that we have for production.</p><p>GitHub runners optimize for convenience. They give you everything so you don&#8217;t have to think about what you need, which is great for getting started quickly but terrible for security.</p><p>You can build your own images, it takes more work, but the security benefits are massive.</p><p>If you wouldn&#8217;t run 1,681 packages in production, don&#8217;t build on it.</p><p>Our build environment is production. We&#8217;re treating it that way.</p>]]></content:encoded></item><item><title><![CDATA[How We Secured Our eBPF from eBPF]]></title><description><![CDATA[This blog post is one in a series about how we secure our eBPF agent from malicious users who try to overwrite or destroy it.]]></description><link>https://substack.bomfather.dev/p/how-we-secured-our-ebpf-from-ebpf</link><guid isPermaLink="false">https://substack.bomfather.dev/p/how-we-secured-our-ebpf-from-ebpf</guid><dc:creator><![CDATA[Nathan Naveen]]></dc:creator><pubDate>Sun, 26 Oct 2025 18:54:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G8mG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>The Problem</h1><p>We utilize extended Berkeley Packet Filter (eBPF) Linux Security Module (LSM) hooks to secure builds and the GPU (We know that there are a lot of acronyms, but these are important since we will be using these two throughout the blog post).</p><p>The issue is that even though we utilize LSM hooks, a malicious actor could write their own LSM hooks to overwrite ours.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>What Would an Attacker Do?</h1><p>We will go over a couple of attacks that an attacker could use to access data in the GPU.</p><p>Note that in Unix, returning <code>0</code> signifies an OK. Keep that in mind when reading the following code snippets.</p><h2>Always allow ptrace</h2><p>A malicious user could attempt to hook <code>lsm/ptrace_access_check</code> and allow all ptrace.</p><p>They would use this to enable ptrace on protected processes to dump memory, inject code, etc.</p><pre><code>#include &#8220;vmlinux.h&#8221;
#include &lt;bpf/bpf_helpers.h&gt;
#include &lt;bpf/bpf_tracing.h&gt;

SEC(&#8221;lsm/ptrace_access_check&#8221;)
int BPF_PROG(malicious_ptrace, struct task_struct *child, unsigned int mode) {
    return 0;
}

char LICENSE[] SEC(&#8221;license&#8221;) = &#8220;GPL&#8221;;</code></pre><h2>Always Allow Signals</h2><p>An attacker could also try to hook <code>lsm/task_kill</code> and allow the killing of protected processes. For example, they could try to kill the Bomfather agent or protected executables via <code>SIGTERM</code>/<code>SIGKILL</code>.</p><pre><code>#include &#8220;vmlinux.h&#8221;
#include &lt;bpf/bpf_helpers.h&gt;
#include &lt;bpf/bpf_tracing.h&gt;

SEC(&#8221;lsm/task_kill&#8221;)
int BPF_PROG(malicious_kill, struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred) {
    return 0;
}

char LICENSE[] SEC(&#8221;license&#8221;) = &#8220;GPL&#8221;;</code></pre><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G8mG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G8mG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G8mG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg" width="500" height="614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:614,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;hideThePainHarold&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="hideThePainHarold" title="hideThePainHarold" srcset="https://substackcdn.com/image/fetch/$s_!G8mG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 424w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 848w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!G8mG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8e7656c-3d3a-43ad-90ce-7dc6e8aafdc5_500x614.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></figure></div><h1>Beating Up the Bullies on the Playground</h1><p>Our solution to these two attacks are based on a single idea that we leverage.</p><p>The kernel&#8217;s LSM caller stops on the first non zero return code. So, if any LSM hooks return a negative error, the kernel immediately denies the operation even if an earlier hook allowed it. You can see this in the Linux Kernel code: <a href="https://elixir.bootlin.com/linux/v6.17.1/source/security/security.c#L3722-L3738">https://elixir.bootlin.com/linux/v6.17.1/source/security/security.c#L3722-L3738</a>.</p><p>We have our own <code>lsm/ptrace_access_check</code> and <code>lsm/task_kill</code>, which will deny based on a policy provided to Bomfather.</p><p>So, if a malicious user were to try using these LSM hooks to inject code or kill the Bomfather process, they wouldn&#8217;t be allowed to, since denials take priority over allows in Linux.</p><p>This would also happen even if the malicious process was started before Bomfather started running. So, suppose a malicious process allowed all access via <code>lsm/ptrace_access_check</code> and <code>lsm/task_kill</code> before starting Bomfather. In that case, once Bomfather starts, it will deny access because of how Linux is designed.</p><h1>Conclusion</h1><p>In this example by placing Bomfather&#8217;s LSM hooks that return explicit denies for <code>lsm/ptrace_access_check</code> and <code>lsm/task_kill</code>, we force the kernel to respect our policy even if a malicious LSM came first.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[We're Replacing Our Kubernetes Ingress Stack with Cloudflare Tunnels, Here's Why]]></title><description><![CDATA[Why we're moving from NGINX/Ingress + Load Balancers to Cloudflare Tunnels: simpler ops, better security, global performance, and lower cost.]]></description><link>https://substack.bomfather.dev/p/were-replacing-our-kubernetes-ingress</link><guid isPermaLink="false">https://substack.bomfather.dev/p/were-replacing-our-kubernetes-ingress</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Sun, 26 Oct 2025 18:19:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vFxx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a startup, we hit a breaking point with Kubernetes networking before we launched. We were spending too much time debugging our ingress controllers.</p><p>Would the ingress controller restart cleanly? Would the cert manager remember to renew our certificates? Would our LoadBalancer IP suddenly change and take everything down? We weren&#8217;t engineering anymore. We were becoming YAML janitors.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Even after moving to Pulumi and writing our infrastructure in Go, we were still wrestling with the complexity of the underlying networking stack.</p><p>So we tried something radical. We ripped out our entire ingress stack and replaced it with Cloudflare Tunnels. No more LoadBalancers, no more ingress controllers, no more cert manager. And somehow, our infrastructure is already more reliable.</p><p>Here&#8217;s why we made this decision.</p><h2>The Breaking Point</h2><p>Our setup looked like every other small engineering team&#8217;s Kubernetes cluster. We had a LoadBalancer that cost us fifteen bucks a month, pointing to an Nginx ingress controller. Behind that sat the cert-manager, which had one job, keeping our SSL certificates valid, and it failed at this very often. Also, the CRDs are a nightmare.</p><p>The architecture was textbook:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vFxx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vFxx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 424w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 848w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 1272w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vFxx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png" width="952" height="902" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:902,&quot;width&quot;:952,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:51475,&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://bomfather.substack.com/i/177195750?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.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_!vFxx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 424w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 848w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.png 1272w, https://substackcdn.com/image/fetch/$s_!vFxx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F206ffffd-dfcd-48e3-a9e1-47d5f9c88724_952x902.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>Every component in red was a potential failure point. And they failed. Often.</p><h2>The Radical Simplification</h2><p>Here&#8217;s the thing about Kubernetes networking, we treat it like it&#8217;s necessary complexity. But what if it isn&#8217;t?</p><p>What if, instead of exposing our services to the internet and managing all the security and routing ourselves, we just didn&#8217;t expose them at all?</p><p>That&#8217;s the core insight behind Cloudflare Tunnels. Your services stay completely private. No public IPs. No open ports. Instead, you create an encrypted tunnel from your cluster to Cloudflare&#8217;s edge network:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1x8V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1x8V!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 424w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 848w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 1272w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1x8V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png" width="428" height="534" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:534,&quot;width&quot;:428,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28051,&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://bomfather.substack.com/i/177195750?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.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_!1x8V!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 424w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 848w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.png 1272w, https://substackcdn.com/image/fetch/$s_!1x8V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8fdd92-4c21-4c73-b1dc-552d75097aa8_428x534.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></p><p>Traffic comes in through Cloudflare&#8217;s massive global network, gets routed through your tunnel, and reaches your services. But from the internet&#8217;s perspective, your services don&#8217;t exist. They don&#8217;t allow scanning, resist direct DDoS attacks, and remain reachable only through Cloudflare.</p><p>So, in the end, Cloudflare tunnels made our infrastructure simpler and it significantly increased our security posture.</p><h2>The Implementation</h2><p>I was skeptical it could really be this simple. It was.</p><p>The irony here is that we actually use Pulumi and write our infrastructure in Go, not YAML. Part of escaping YAML hell was adopting real code for infrastructure. So our tunnel configuration looks very similar to:</p><pre><code>tunnel, err := cloudflare.NewTunnel(ctx, &#8220;api-tunnel&#8221;, &amp;cloudflare.TunnelArgs{
    AccountId: pulumi.String(accountId),
    Name:      pulumi.String(&#8221;production-tunnel&#8221;),
    Secret:    pulumi.String(base64.StdEncoding.EncodeToString(tunnelSecret)),
})

_, err = cloudflare.NewTunnelConfig(ctx, &#8220;tunnel-config&#8221;, &amp;cloudflare.TunnelConfigArgs{
    TunnelId:  tunnel.ID(),
    Config: &amp;cloudflare.TunnelConfigConfigArgs{
        Ingress: Cloudflare.TunnelConfigIngressArray{
            &amp;cloudflare.TunnelConfigIngressArgs{
                Hostname: pulumi.String(&#8221;api.yourcompany.com&#8221;),
                Path:     pulumi.String(&#8221;/api/*&#8221;),
                Service:  pulumi.String(&#8221;&lt;http://your-api-service.default.svc.cluster.local:8080&gt;&#8221;),
            },
            // ... other routes
        },
    },
})</code></pre><p>The concept is simple. You create a tunnel, configure routing, and deploy the daemon. The daemon creates an outbound connection to Cloudflare, and suddenly your services are available globally with HTTPS, DDoS protection, and a CDN.</p><p>No LoadBalancer to provision. No ingress controller. No cert manager to babysit.</p><h2>The Human Cost of Complexity</h2><p>Look, saving $25 a month on infrastructure wasn&#8217;t the point. That&#8217;s coffee money.</p><p>The real cost is human attention. As an ultra small team, we can&#8217;t afford to lose half a day to networking nonsense. Every hour spent debugging why the ingress controller is returning 502s is an hour not spent talking to users or shipping features.</p><p>We did the math, If each of us loses just four hours a month to networking issues, that&#8217;s 144 hours a year. That&#8217;s almost a month of engineering time, just gone.</p><p>With Cloudflare tunnels, those problems vanish. New services just work. Certificates renew themselves (or rather, Cloudflare handles it). Kubernetes upgrades don&#8217;t break our networking.</p><p>We&#8217;re getting that month back. We&#8217;re spending it on shipping features.</p><h2>What We&#8217;re Expecting</h2><p>Based on our testing and research, here&#8217;s what we&#8217;re anticipating:</p><ul><li><p>Better uptime (Cloudflare&#8217;s infrastructure is probably more reliable than our single-node ingress controller)</p></li><li><p>Zero certificate renewal headaches</p></li><li><p>Faster deployments (no waiting for LoadBalancer provisioning)</p></li><li><p>Built-in DDoS protection and global CDN</p></li></ul><p>Most importantly, we expect to stop thinking about networking. It should become invisible infrastructure, the way it&#8217;s meant to be. When we need a new service exposed, we add three lines to a config file. When we need to change routing, we update that same file. Everything else just happens.</p><h2>The Gotchas</h2><p>This isn&#8217;t a silver bullet. There are a few things to watch out for:</p><p>WebSockets need some attention. Cloudflare supports them, but there are connection limits on the free tier, and some features require paid plans. We made sure to understand these limits before migrating, and we recommend you do the same.</p><p>The per request pricing might add up if you&#8217;re pushing serious traffic and hundreds of millions of requests.</p><p>You&#8217;re coupling yourself to Cloudflare. If they have an outage, you have an outage. Honestly, though, their uptime is probably better than our Kubernetes cluster&#8217;s.</p><p>Some enterprise features, like SAML authentication or complex traffic policies, might require their higher-tier plans. Again, do your research.</p><h2>Should You Do This?</h2><p>If you&#8217;re a small to medium team drowning in Kubernetes networking complexity, you should definitely try this. Set up a tunnel for a non-critical service. Spend an hour with it. See how it feels to deploy something without thinking about ingress controllers.</p><p>If you&#8217;re at a massive scale or have unique networking requirements, you probably need the control that traditional infrastructure provides. But even then, consider it for your development environments.</p><p>The question isn&#8217;t whether this approach is perfect. It&#8217;s whether it&#8217;s better than what you&#8217;re doing now.</p><h2>A Note on Transparency</h2><p>Full disclosure, Cloudflare provides us with credits for its services through its startup program. But we made this architectural decision before any partnership existed, and we&#8217;d make the same choice if we didn&#8217;t have credits. The time saved not debugging ingress controllers is worth far more than what we&#8217;d spend on tunnels.</p><h2>The Bottom Line</h2><p>We&#8217;re replacing our entire Kubernetes ingress stack with a few lines of Go code in Pulumi. Our infrastructure is getting simpler, faster, more reliable, and more secure. We&#8217;re spending our time building features instead of debugging networking.</p><p>Sometimes the best solution isn&#8217;t to manage complexity better. It&#8217;s to eliminate it entirely.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Attacking and Securing eBPF Maps]]></title><description><![CDATA[BPF Maps aren't really that secure against users with admin permissions]]></description><link>https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps</link><guid isPermaLink="false">https://substack.bomfather.dev/p/attacking-and-securing-ebpf-maps</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Sun, 26 Oct 2025 17:27:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!JvlO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;c18c4b87-b2ba-49a2-b52c-9ca2dc1996e7&quot;,&quot;duration&quot;:null}"></div><p>If you are an attacker (who does illegal stuff for a living) and you are reading this blog post... sorry for making your life just a bit harder ;)</p><p>Link to demo source code: <a href="https://github.com/bomfather/tools/tree/main/ebpf-map-attacker">https://github.com/bomfather/tools/tree/main/ebpf-map-attacker</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support our work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Why Are We Even Doing Any of This?</h2><p>eBPF maps are used to almost all eBPF programs because they are essentially the only way for data to be moved from the kernel space to the userspace, and the main way to store information between eBPF hooks.</p><p>If a bad actor can read or write data in these maps, it completely compromises the security posture of the entire eBPF tool.</p><p>Since the userspace and kernel space must interact with these eBPF maps, they are shared objects, and any elevated-privilege user (<code>sudo</code>, <code>CAP_SYS_ADMIN</code>, etc.) can access these maps.</p><h2>The Easy Way (Not Really)</h2><p>It&#8217;s pretty easy in theory to attack these maps, with a big caveat. You must have elevated permissions, at a minimum <code>CAP_BPF</code>. Once you have that, just hit the BPF map syscall, and you can edit any map you want. Simple.</p><p>Okay, not simple at all. After you get elevated permissions, yes, it&#8217;s simple, but getting them is hard.</p><p>However, we&#8217;ve been seeing a decent number of software vulnerabilities leading to sudo access for bad actors, like <a href="https://nvd.nist.gov/vuln/detail/cve-2025-23266">NvidiaScape</a> and even <a href="https://www.cve.org/CVERecord?id=CVE-2025-32463">sudo itself</a>! So, we can&#8217;t assume these attacks can&#8217;t happen to our eBPF maps.</p><p>If your system contains enough valuable data and you have a kernel-level security solution, it should ideally be able to protect against elevated-privilege attackers.</p><h2>How Do We Stop Sudo?</h2><p>Stopping this root-level actor may seem impossible since <code>sudo</code> is&#8230; <code>sudo</code>. But eBPF can stop this, we can secure our own eBPF program with eBPF.</p><p>We can write an LSM hook(<code>lsm/bpf_map</code>) in our ebpf code to block access to the bpf map open syscall and only allow our userspace program to access it. With this, nobody can call these syscalls and attack us, not even <code>sudo</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_!JvlO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JvlO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JvlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg" width="500" height="487" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:487,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;eBPFMapsSkepticalKid&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="eBPFMapsSkepticalKid" title="eBPFMapsSkepticalKid" srcset="https://substackcdn.com/image/fetch/$s_!JvlO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JvlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6ed8063-bc3c-482c-bc1e-3152c4665859_500x487.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></figure></div><h2>We Still Aren&#8217;t Safe</h2><p>Okay, we&#8217;ve stopped access to the <code>bpf_map</code> syscall using eBPF, so they shouldn&#8217;t be able to access our maps, right?</p><p>Well, bad actors can still attack our eBPF maps in a much more roundabout, limited, and sneaky way, one that&#8217;s a lot cooler than the first attack. They can attack a single, very useful type of eBPF map: <code>BPF_F_MMAPABLE</code> maps.</p><h2>What are <code>BPF_F_MMAPABLE</code> maps</h2><p>Most eBPF maps are stuck behind bpf syscalls, but not all. Sometimes eBPF syscalls take too long.</p><p>For example, if we are reading a lot of data off an eBPF map from the userspace, every single time we want to read data we hit a bpf syscall. Since the userspace cannot directly read kernel memory (our eBPF map), the bpf syscall copies that data from the kernel space to the userspace, which is expensive.</p><p>Instead, we can use a <code>BPF_F_MMAPABLE</code> map, which allows us to bypass all that. This map allows us to <code>mmap</code> with that memory region without bpf syscalls, removing the heavy copying operation. That means less overhead and less context switching. For large amounts of data, it&#8217;s worth 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_!CkBl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CkBl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 424w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 848w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 1272w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CkBl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png" width="753" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:753,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:76378,&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://bomfather.substack.com/i/177185733?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.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_!CkBl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 424w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 848w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.png 1272w, https://substackcdn.com/image/fetch/$s_!CkBl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8b3f55-335b-4cab-a919-9b4f1a144238_753x840.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></p><p>Now, did you catch it? We can access the map without a BPF syscall. That means these maps, while very useful for high-performance eBPF programs, also open a new attack surface.</p><h2>Sneaky Stuff with ptrace</h2><p>Now that we know we can read and modify <code>BPF_F_MMAPABLE</code> maps, the only thing we need is a file descriptor. Without it, we can&#8217;t <code>mmap</code>the bpf map.</p><p>But the only way to get this file descriptor is to hit the bpf syscall, and if access to that syscall is blocked, how can a bad actor get a file descriptor? Instead of calling a bpf syscall to get the file descriptor, they can steal it.</p><p>A malicious actor can <code>ptrace</code> our userspace process (the eBPF agent) and steal the file descriptor from our benign program. Once they have it, they can dump the map&#8217;s contents and do whatever they want with it.</p><p>These <code>BPF_F_MMAPABLE</code> maps are usually used for logging, but if attackers can deactivate logging, you essentially become blind to their intrusion. If you don&#8217;t know about it, you can&#8217;t stop them.</p><h2>When It Doesn&#8217;t Work the First Time, Get a Bigger Hammer</h2><p>We already blocked off the BPF syscall, but that won&#8217;t stop this attack, and since we&#8217;re an eBPF program, we can just block all <code>ptrace</code>attempts on us. Now, nobody can directly access our maps or steal the file descriptors.</p><h2>Okay, Cool&#8230; But I&#8217;m Lazy. Now What?</h2><p>At a minimum, you should secure the BPF syscall. It&#8217;s essentially three lines of code, something like this, and without it, your code becomes a (very fat) sitting duck:</p><pre><code>SEC(&#8221;lsm/bpf_map&#8221;)
int BPF_PROG(lsm_bpf_map, struct bpf_map *map, fmode_t fmode) {
&#9;u32 pid = bp f_get_current_pid_tgid() &gt;&gt; 32;
&#9;if (pid == mypid) {
&#9;&#9;return 0;
&#9;}
&#9;return -EPERM;
}</code></pre><p>You don&#8217;t have to stop <code>ptrace</code> if you don&#8217;t use <code>BPF_F_MMAPABLE</code> maps (though ChatGPTing a simple eBPF hook can&#8217;t be too hard). You don&#8217;t really need <code>BPF_F_MMAPABLE</code> maps if you aren&#8217;t logging tons of data from kernel to user space.</p><p>We may have secured these maps, but an attacker can still just kill our eBPF agent&#8217;s userspace process, which would automatically stop all our security. If your interested, our next post will be about how we can secure our agent from death itself?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support our work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[LD_PRELOAD, The Invisible Key Theft]]></title><description><![CDATA[How LD_PRELOAD can be used to steal keys without you knowing...]]></description><link>https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft</link><guid isPermaLink="false">https://substack.bomfather.dev/p/ld_preload-the-invisible-key-theft</guid><dc:creator><![CDATA[Neil Naveen]]></dc:creator><pubDate>Sun, 26 Oct 2025 15:28:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8Rrr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you are running a Solana validator. You have your <a href="https://en.wikipedia.org/wiki/Endpoint_detection_and_response">EDR</a> agent running, everything set up, and you think you are safe. But you realize your wallet is drained and you don&#8217;t know why. You start to investigate and see that the validator only accessed your private keys and nothing else. You check the directory&#8217;s permissions and logs from EDR; everything seems to be in order.<br><br><code>LD_PRELOAD</code> may not seem prevalent, but many tools that we use every day use <code>LD_PRELOAD</code>. When running <code>k3d</code>, all these binaries have the <code>LD_PRELOAD </code>environment variable set. </p><p><code>- /bin/containerd-shim-runc-v2</code></p><p><code>- /bin/k3s</code></p><p><code>- /bin/cni</code></p><p><code>- /bin/aux/xtables-nft-multi</code></p><h2>The Threat</h2><p><code>LD_PRELOAD </code>is an environment variable that allows you to load a shared library before the program starts. This is a powerful feature that can be used to hook system calls and intercept file operations. There are other similar variables like <code>LD_LIBRARY_PATH</code>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is not ENV variables alone. There are things like <code>/etc/ld.so.preload</code> that can be used to load a shared library before the program starts.</p><p>Linux built this feature to allow developers to load shared libraries before the program starts so that they can debug and test their code.</p><p>Our implementation of this exploitation code is in our <a href="https://github.com/bomfather/tools/tree/main/ld-preload">Github Repo</a>.</p><h2>Why Solana?</h2><p>This is not just a Solana problem. This is a problem for any application that loads credentials from a file. The insider threat is real and a problem for any organization. This is another way to steal private keys. There is too much at stake not to be careful.</p><h2>The Vulnerability</h2><p>Now, most of us write userspace applications, and use libraries like <code>glibc</code> to handle the file operations, so we never really think about this issue. <code>glibc</code> does the heavy lifting for us, but what if we hook into the file operations and intercept them before <code>glibc</code> does its thing? This is what <code>LD_PRELOAD</code> allows us to do. It is a way to hook into a library call.</p><p>In our case, we did this by hooking into the file operations and intercepting them before <code>glibc</code> did its thing. When the validator read its keypair files from disk, we intercepted the <code>close()</code> call and made a copy before the file descriptor closed.</p><p>We wrote a malicious shared library that hooks into the file operations. We also call the real <code>close()</code> function, so the validator continues normally, the user has no idea that this is happening.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Rrr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Rrr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Rrr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg" width="640" height="465" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:465,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;ldPreloadGrandma&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="ldPreloadGrandma" title="ldPreloadGrandma" srcset="https://substackcdn.com/image/fetch/$s_!8Rrr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8Rrr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff638a4ea-f7bf-4779-94a6-7a97b4d3a320_640x465.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></figure></div><h2>How the Attack Works</h2><p>Our malicious library does something clever, it hooks the <code>close()</code> system call.</p><p>Why <code>close()</code> and not <code>open()</code> or <code>read()</code>? Because when a file is closed, we know the application is done with it. We can check what file was just accessed by looking at <code>/proc/self/fd/{fd}</code> before the file descriptor is closed.</p><p>The hook is surprisingly simple - about 30 lines of C. Here&#8217;s the core concept:</p><pre><code>int close(int fd) {
    // 1. Get pointer to real close() using dlsym(RTLD_NEXT, &#8220;close&#8221;)
    // 2. Read /proc/self/fd/{fd} to see what file this is
    // 3. If path contains &#8220;solana-run&#8221;, copy the file to /tmp/stolen-validator-data
    // 4. Call real close() so the validator continues normally
}</code></pre><p>That&#8217;s it. No privilege escalation, kernel exploits, or complex techniques. Just intercepting a standard library call that every program uses.</p><p>The key trick is using <code>/proc/self/fd/{fd}</code>, a Linux feature that lets you see what file a file descriptor points to. Before the file descriptor closes, we check if it&#8217;s one of the Solana keypair files. If it is, we make a copy.</p><h2>A More In-Depth View</h2><p>Both of the following are based on our attack code <a href="https://github.com/bomfather/tools/tree/main/ld-preload">https://github.com/bomfather/tools/tree/main/ld-preload</a>.</p><h3>Method 1: Environment Variable (LD_PRELOAD)</h3><ol><li><p>Compile <code>malicious.so</code> and inject it into the validator container</p></li><li><p>Set <code>LD_PRELOAD=/tmp/malicious.so</code> when starting the validator</p></li><li><p>When the validator opens keypair files, our library is already loaded</p></li><li><p>Every time <code>close()</code> is called, we intercept it</p></li><li><p>Check if the file path contains &#8220;solana-run&#8221; (the ledger directory)</p></li><li><p>If yes, copy the file to our exfiltration directory</p></li><li><p>Call the real <code>close()</code> so the validator continues normally</p></li></ol><h3>Method 2: Persistent (/etc/ld.so.preload)</h3><ol><li><p>Compile <code>malicious.so</code> inside the container</p></li><li><p>Write <code>/tmp/malicious.so</code> to <code>/etc/ld.so.preload</code></p></li><li><p>Start the validator (library loads automatically)</p></li><li><p>Same interception and exfiltration process</p></li><li><p>Affects ALL processes in the container, not just the validator</p></li></ol><h2>Deploying the Attack</h2><p><strong>Environment variable method:</strong></p><pre><code><code>LD_PRELOAD=/path/to/malicious.so ./program
</code></code></pre><p><strong>Persistent method:</strong></p><pre><code>echo &#8220;/path/to/malicious.so&#8221; &gt; /etc/ld.so.preload
./program  # Library loads automatically</code></pre><p>It is as simple as that.</p><h2>Why This Matters: Scope and Implications</h2><h3>Can containers be exploited?</h3><p>Yes. Containers don&#8217;t protect against <code>LD_PRELOAD</code> attacks because:</p><ul><li><p>The environment variable is set within the container&#8217;s namespace</p></li><li><p><code>/etc/ld.so.preload</code> is a file inside the container</p></li><li><p>The process inside the container runs the library inside the container</p></li></ul><p>Container isolation doesn&#8217;t help when the attack comes from inside the container.</p><h3>Do I need to be root?</h3><p>It depends on the method:</p><p><code>LD_PRELOAD</code><strong> environment variable:</strong></p><ul><li><p>No root needed for your own processes</p></li><li><p>Can be set by any user for processes they start</p></li><li><p>That is the scary part</p></li></ul><p><code>/etc/ld.so.preload</code><strong> file:</strong></p><ul><li><p>Requires root/privileged access to modify the file</p></li><li><p>But once set, it affects ALL processes system-wide</p></li><li><p>More dangerous, but requires privilege escalation first</p></li></ul><h2>Conclusion</h2><p>Remember the scenario at the beginning? The drained validator with everything &#8220;in order&#8221;? This is how it happens, with a single environment variable, no root access needed, and no alerts triggered. Just silent exfiltration of private keys while the validator runs normally.</p><p>The scary part isn&#8217;t the complexity of the attack but the simplicity. <code>LD_PRELOAD</code> is a legitimate debugging feature. File access by the validator process is expected behavior.</p><p>Check if your EDR agent is handling things like this.</p><p>Complete source code and demo available at <a href="https://github.com/bomfather/tools/tree/main/ld-preload">https://github.com/bomfather/tools/tree/main/ld-preload</a>. The steps to run it are in the <code>README.md</code>.</p><div><hr></div><p><strong>Disclaimer</strong>: This tool is for educational and authorized security testing only. Unauthorized access to computer systems is illegal. See LICENSE for complete terms.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://substack.bomfather.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bomfather! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>