<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>lavafroth</title><link>https://lavafroth.is-a.dev/</link><description>Recent content on lavafroth</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 03 Feb 2026 17:14:13 +0530</lastBuildDate><atom:link href="https://lavafroth.is-a.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Easy SSH Tunnel</title><link>https://lavafroth.is-a.dev/post/easy-ssh-tunnel/</link><pubDate>Tue, 03 Feb 2026 17:14:13 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/easy-ssh-tunnel/</guid><description>&lt;p&gt;Sometimes I forget the correct syntax to tunnel a port through SSH but I don&amp;rsquo;t want to read through a bunch of AD infested websites.&lt;/p&gt;
&lt;p&gt;This simple tool outputs the command to run on the local machine.&lt;/p&gt;
&lt;p&gt;You can edit the local port, remote host and port, and traffic direction.&lt;/p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
 &lt;meta charset="UTF-8" /&gt;
 &lt;title&gt;Easy SSH tunnel&lt;/title&gt;
 &lt;meta name="viewport" content="width=device-width,initial-scale=1" /&gt;
 &lt;meta name="description" content="" /&gt;
 &lt;style&gt;
 * { font-family: monospace; }
 body {
 --purple-dark: oklch(29.3% 0.136 325.661);
 --purple-light: lab(78.5378% 39.3533 -32.9615);
 	background: var(--purple-dark);
 }

 input {
 	outline: none;
 	color: #eee;
 	border: none;
 	background: var(--purple-dark);
 }

 .post-content &gt; div {
 	margin: 1rem auto;

 	label:not([for=direction]) {
 		padding: .5rem;
 		background: var(--purple-light);
 color: var(--purple-dark);
 	}
 }

 .grid {
 	display: grid;
 	grid-template-columns: repeat(3, minmax(0, 1fr));
 	grid-template-rows: repeat(3, minmax(0, 1fr));
 	 * { text-align: center; }

 	label[for=direction] {
 			user-select: none;
 			grid-row: span 2 / span 2;
 grid-column-start: 2;
 			color: #eee;
 			&amp;::after {
 				display: block;
 				content: '&lt;';
 			}
 		}

 		input[type="checkbox"] {
 opacity: 0;
 position: absolute;
 pointer-events: none;
 		}

 	&amp;:has(input:checked) {
 		label[for=direction]::after {
 			content: '&gt;';
 		}
 	}
 }

 #remotehost {
			grid-column: span 2 / span 2;
 }

 #command {
 		background: oklch(45.2% 0.211 324.591);
 		margin-top: 1rem;
 		padding-inline: 1rem;
 		color: #eee;
 }

 #copy {
 background: var(--purple-light);
 color: var(--purple-dark);
 padding-inline: 1rem;
 float: right;
 &amp;:active {
 background: oklch(45.2% 0.211 324.591);
 color: #eee;
 }
 }
 &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

 &lt;div class="grid"&gt;

 &lt;label for="localport"&gt;Local port&lt;/label&gt;
 &lt;label for="direction"&gt;Traffic direction&lt;/label&gt;
 &lt;label for="remoteport"&gt;Remote port&lt;/label&gt;

 &lt;input id="localport" type="number" min="0" max="65535" step=1 placeholder=8888&gt;
 &lt;input type=checkbox id="direction"&gt;
 &lt;input id="remoteport" type="number" min="0" max="65535" step=1 placeholder=8888&gt;
 &lt;label for="remotehost"&gt;Connect to&lt;/label&gt;
 &lt;input id="remotehost" type="text" placeholder="user@remote.host"&gt;
 &lt;/div&gt;

 &lt;div id='command'&gt;&lt;/div&gt;
 &lt;button id='copy'&gt;⧉ Copy&lt;/button&gt;
 &lt;/div&gt;
&lt;/body&gt;

&lt;script&gt;
 const localPort = document.querySelector('#localport');
 const remotePort = document.querySelector('#remoteport');
 const remoteHost = document.querySelector('#remotehost');
 const direction = document.querySelector('#direction');
 const command = document.querySelector('#command');
 const copy = document.querySelector('#copy');
 const valueOrPlaceholder = (v) =&gt; v.value || v.placeholder;

 function updateCommand() {
 const directionString = direction.checked ? "R" : "L";
 command.textContent = `ssh -f -N -${directionString} ${valueOrPlaceholder(localPort)}:localhost:${valueOrPlaceholder(remotePort)} ${valueOrPlaceholder(remoteHost)}`;
 }

 function validatePort(e) {
 const code = e.keyCode;
 if (code &lt; 48 || code &gt; 57 || e.target.value * 10 + (code - 48) &gt; 65535) {
 e.preventDefault()
 }
 }

 for (const variable of [localPort, direction, remoteHost, remotePort]) {
 variable.addEventListener('input', updateCommand);
 }

 for (const portVariable of [localPort, remotePort]) {
 portVariable.addEventListener('keypress', validatePort);
 }

 copy.addEventListener('click', (_) =&gt; navigator.clipboard.writeText(command.textContent))


 updateCommand()
 &lt;/script&gt;
&lt;/html&gt;</description></item><item><title>Step to the 💗 beat</title><link>https://lavafroth.is-a.dev/art/drawhearts/</link><pubDate>Sun, 18 Jan 2026 08:06:06 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/drawhearts/</guid><description>&lt;p&gt;My first procedurally generated animation using shaders.&lt;/p&gt;
&lt;p&gt;The shader can be visualized with &lt;code&gt;glslViewer&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uniform&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec2&lt;/span&gt; u_mouse;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uniform&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec2&lt;/span&gt; u_resolution;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uniform&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; u_time;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; main (&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec2&lt;/span&gt; st &lt;span style="color:#f92672"&gt;=&lt;/span&gt; gl_FragCoord.xy&lt;span style="color:#f92672"&gt;/&lt;/span&gt;u_resolution.xy;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; aspect &lt;span style="color:#f92672"&gt;=&lt;/span&gt; u_resolution.x&lt;span style="color:#f92672"&gt;/&lt;/span&gt;u_resolution.y;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; st.x &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; aspect;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// positioning shenanigans&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; st.x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; st.x &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1.8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; st.y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; st.y &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.9&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; r &lt;span style="color:#f92672"&gt;=&lt;/span&gt; st.x&lt;span style="color:#f92672"&gt;*&lt;/span&gt;st.x &lt;span style="color:#f92672"&gt;+&lt;/span&gt; st.y&lt;span style="color:#f92672"&gt;*&lt;/span&gt;st.y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; abs(st.x)&lt;span style="color:#f92672"&gt;*&lt;/span&gt;st.y;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; duration &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3.0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; bin &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; scaled_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fract(u_time&lt;span style="color:#f92672"&gt;/&lt;/span&gt;duration);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; loop &lt;span style="color:#f92672"&gt;=&lt;/span&gt; scaled_time &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;float&lt;/span&gt; r_disc &lt;span style="color:#f92672"&gt;=&lt;/span&gt; floor((r &lt;span style="color:#f92672"&gt;+&lt;/span&gt; loop&lt;span style="color:#f92672"&gt;*&lt;/span&gt;loop)&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; bin;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec3&lt;/span&gt; deep_pink &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec3&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0.917&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0.235&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0.478&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec3&lt;/span&gt; light_pink &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec3&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0.976&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0.780&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0.803&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (r_disc &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gl_FragColor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt;(deep_pink, &lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec3&lt;/span&gt; color &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mix(deep_pink, light_pink, r_disc);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gl_FragColor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt;(color,&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I tinkered around for quite a while before discovering that I can intersect two &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;xy&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; skewed ellipses
with the absolute value operator. Here&amp;rsquo;s my custom equation for the heart shape.&lt;/p&gt;</description></item><item><title>Working With LUKS File Stashes</title><link>https://lavafroth.is-a.dev/post/working-with-luks-file-stashes/</link><pubDate>Thu, 01 Jan 2026 07:24:36 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/working-with-luks-file-stashes/</guid><description>&lt;p&gt;LUKS is an incredible solution for encrypting entire partitions in Linux.
Often times, however, we can&amp;rsquo;t afford to create new partitions inside a disk
without having to completely format the drive anew.&lt;/p&gt;
&lt;p&gt;This post will guide you through the process of creating and working
with LUKS container files that are encrypted at rest and can be decrypted on
demand with knowledge of the passphrase.&lt;/p&gt;
&lt;h2 id="creating-the-image-base"&gt;Creating the image base&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;head --bytes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;4G /dev/urandom &amp;gt; stash.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="format-the-image"&gt;Format the image&lt;/h2&gt;
&lt;p&gt;The image can be formatted by either including the header in the image itself or
keeping a detached header.&lt;/p&gt;</description></item><item><title>Algebraic Python Enums</title><link>https://lavafroth.is-a.dev/post/algebraic-python-enums/</link><pubDate>Sun, 02 Nov 2025 19:08:46 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/algebraic-python-enums/</guid><description>&lt;p&gt;University has compelled me to use Python despite my preference for Rust,
primarily due to the machine learning and data science hype. One
Rust feature that I dearly miss is enumerable data types
that can encapsulate various other data types.&lt;/p&gt;
&lt;p&gt;Although Python has the answer to creating structs as
&lt;a href="https://peps.python.org/pep-0557/"&gt;dataclasses&lt;/a&gt;, including support for
&lt;a href="https://peps.python.org/pep-0636/"&gt;structural match expressions&lt;/a&gt; in recent
versions, most tutorials will suggest &lt;code&gt;Union&lt;/code&gt; types as the equivalent to Rust&amp;rsquo;s
enums.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I highly encourage you to try out the code snippets and follow along with this article.
Use the collapse explanation button to copy multiple code blocks in one go.&lt;/p&gt;</description></item><item><title>NixOS Notes to Self</title><link>https://lavafroth.is-a.dev/post/nixos-notes-to-self/</link><pubDate>Sun, 14 Sep 2025 18:31:52 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/nixos-notes-to-self/</guid><description>&lt;p&gt;A dedicated post collecting solutions to minor NixOS headaches.&lt;/p&gt;
&lt;h2 id="nixos-rebuild-shows-no-network-activity"&gt;&lt;code&gt;nixos-rebuild&lt;/code&gt; shows no network activity&lt;/h2&gt;
&lt;p&gt;On rare occasions, a system rebuild will get stuck while downloading a package from a source.
No network activity, no timeout, no writes to the nix store.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@rahu /h/u/dotfiles (main)# nixos-rebuild boot --flake .
building the system configuration...
[0/9 built, 1/0/1 copied (0.0/681.0 MiB), 0.0/670.5 MiB DL] fetching linux-firmware-20250808-zstd from https://cache.nixos.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This issue persists if the build command is simply rerun.
According to @manveru from the NixOS discourse, &lt;code&gt;nix-daemon&lt;/code&gt; has a bug where it might
not close a connection to the source, with no timeout context. Force kill it with&lt;/p&gt;</description></item><item><title>Privacy Policy</title><link>https://lavafroth.is-a.dev/privacy/</link><pubDate>Sat, 30 Aug 2025 09:00:14 +0530</pubDate><guid>https://lavafroth.is-a.dev/privacy/</guid><description>&lt;p&gt;This site &lt;em&gt;does not&lt;/em&gt; use cookies or &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; any third party resources to track you.
The site theme changes based on your theme preference propagated by your browser.&lt;/p&gt;
&lt;p&gt;Posts here can be viewed without javascript excepting live demos. The only feature relying on javascript is the search.&lt;/p&gt;</description></item><item><title>Guide: Changing Recents Provider on /e/OS</title><link>https://lavafroth.is-a.dev/post/changing-recents-provider-on-eos/</link><pubDate>Wed, 20 Aug 2025 09:55:43 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/changing-recents-provider-on-eos/</guid><description>&lt;h1 id="overview"&gt;Overview&lt;/h1&gt;
&lt;p&gt;Over the past month I have been daily driving my new phone, the Nothing CMF 1 flashed with /e/OS after I unlocked its bootloader. It&amp;rsquo;s a very pleasant experience except for the default Bliss launcher (home app).&lt;/p&gt;
&lt;p&gt;Reasons I do not prefer it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iOS like feel&lt;/li&gt;
&lt;li&gt;Icons can&amp;rsquo;t be rearranged&lt;/li&gt;
&lt;li&gt;Pull down from top opens search instead of notification shade&lt;/li&gt;
&lt;li&gt;Consumes a lot of RAM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started using Lawnchair as my default launcher but this did not change the recents provider (quickstep) from BlissLauncher to Lawnchair.&lt;/p&gt;</description></item><item><title>✨</title><link>https://lavafroth.is-a.dev/art/sparkles/</link><pubDate>Tue, 15 Jul 2025 11:52:20 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/sparkles/</guid><description/></item><item><title>Easy grep to detect stripped Go binaries</title><link>https://lavafroth.is-a.dev/post/detecting-stripped-go-binaries/</link><pubDate>Fri, 13 Jun 2025 08:39:58 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/detecting-stripped-go-binaries/</guid><description>&lt;p&gt;A couple of days ago when I was reading the &lt;a href="https://tip.golang.org/doc/gc-guide"&gt;guide to the Go garbage collector&lt;/a&gt;, I came across the following excerpt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When all else fails, the Go GC provides a few different specific traces that provide much deeper insights into GC behavior. These traces are always printed directly to STDERR, one line per GC cycle, and are configured through the GODEBUG environment variable that all Go programs recognize.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An environment variable that all Go programs recognize, you say? I had a sneaking suspicion that I could just perform a string search for this term, given all Go programs would need to look for this environment variable definition. This way, we could guess if a binary was written in Go.&lt;/p&gt;</description></item><item><title>Need a hand?</title><link>https://lavafroth.is-a.dev/post/do-you-really-need-a-copilot/</link><pubDate>Thu, 03 Apr 2025 15:56:04 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/do-you-really-need-a-copilot/</guid><description>&lt;h1 id="the-tides"&gt;The tides&lt;/h1&gt;
&lt;p&gt;Over the past few months, a sizable fraction of my developer peers have taken to AI tools. Beckoned from under a rock by the light of day, I was taken aback by this rising wave of &lt;em&gt;vibe coding&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;They claim AI tools to be phenomenal for frontend technologies like React and NextJS. The selling point? Context aware autocompletes and agent mode.&lt;/p&gt;
&lt;p&gt;Context aware autocompletes happen when the model watches your code so it can suggest autocompletes while you code.&lt;/p&gt;</description></item><item><title>In search of the smallest DNA complement function</title><link>https://lavafroth.is-a.dev/post/in-search-of-the-smallest-dna-compl/</link><pubDate>Fri, 14 Feb 2025 09:40:11 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/in-search-of-the-smallest-dna-compl/</guid><description>&lt;p&gt;For the past few weeks, I have been trying to come up with a fast and purely agebraic function to convert DNA bases to their
respective complements.&lt;/p&gt;
&lt;h2 id="problem-statement"&gt;Problem statement&lt;/h2&gt;
&lt;p&gt;Our goal is rather straightforward. We aim to create a mapping of the characters &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;t&lt;/code&gt;, &lt;code&gt;g&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;n&lt;/code&gt; to their respective complements.
We choose to keep our solution one step behind the classical reverse complement which reverses the string after the mapping.&lt;/p&gt;</description></item><item><title>Building an in-browser Manim clone</title><link>https://lavafroth.is-a.dev/post/project-mana-demo/</link><pubDate>Mon, 20 Jan 2025 19:41:51 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/project-mana-demo/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Note: The following interactive animation requires JavaScript to render. Please enable it if it isn&amp;rsquo;t already.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A live, in-browser demo of &lt;code&gt;PROJECT_MANA&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to look around: rotate, zoom or pan the 3D view.&lt;/p&gt;
&lt;p&gt;Frames for the outline animation will be recomputed on the fly! The hardcoded duration for the animation
is 5 seconds in this example.&lt;/p&gt;

&lt;div id="stage"&gt;
&lt;/div&gt;
&lt;style&gt;
canvas {
 background: transparent;
 border-radius: 1rem;
 margin-bottom: 1rem;
}
&lt;/style&gt;
&lt;script type="frag" id="evolve-vert"&gt;
varying vec2 vUv;

void main() {
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
 vUv = gl_Position.xy / gl_Position.w;
 vUv = (vUv + 1.0) * 0.5;
}
&lt;/script&gt;
&lt;script type="frag" id="evolve-frag"&gt;
varying vec2 vUv;

uniform sampler2D gbufferMask;
uniform sampler2D initBufferMask;

void main() {
 gl_FragColor = vec4(texture(initBufferMask, vUv));
}

&lt;/script&gt;
&lt;script type="frag" id="outline-frag"&gt;
 varying vec2 vUv;
 varying vec2 clipMeshCenter;
 varying vec2 glPos;

 uniform float time;
 uniform vec2 viewportSize;

 #define LINE_WEIGHT 2.0

 uniform sampler2D gbufferMask;

 void main() {
 float dx = (1.0 / viewportSize.x) * LINE_WEIGHT;
 float dy = (1.0 / viewportSize.y) * LINE_WEIGHT;

 vec2 uvCenter = vUv;
 vec2 uvRight = vec2(uvCenter.x + dx, uvCenter.y);
 vec2 uvLeft = vec2(uvCenter.x - dx, uvCenter.y);
 vec2 uvTop = vec2(uvCenter.x, uvCenter.y - dy);
 vec2 uvTopRight = vec2(uvCenter.x + dx, uvCenter.y - dy);
 vec2 uvDown = vec2(uvCenter.x, uvCenter.y + dy);
 vec2 uvDownLeft = vec2(uvCenter.x - dx, uvCenter.y + dy);

 float mCenter = texture(gbufferMask, uvCenter).r;
 float mTop = texture(gbufferMask, uvTop).r;
 float mRight = texture(gbufferMask, uvRight).r;
 float mTopRight = texture(gbufferMask, uvTopRight).r;
 float mLeft = texture(gbufferMask, uvLeft).r;
 float mDown = texture(gbufferMask, uvDown).r;
 float mDownLeft = texture(gbufferMask, uvDownLeft).r;

 float dT = abs(mCenter - mTop);
 float dR = abs(mCenter - mRight);
 float dTR = abs(mCenter - mTopRight);
 float dD = abs(mCenter - mDown);
 float dL = abs(mCenter - mLeft);
 float dDL = abs(mCenter - mDownLeft);

 float delta = 0.0;
 delta = max(delta, dT);
 delta = max(delta, dR);
 delta = max(delta, dTR);
 delta = max(delta, dD);
 delta = max(delta, dL);
 delta = max(delta, dDL);

 float threshold = 0.0;
 float isOutline = clamp((delta * 2.0) - threshold, 0.0, 1.0);

 vec4 outline = vec4(isOutline);
 gl_FragColor = outline;
 }
&lt;/script&gt;
&lt;script type="vert" id="outline-vert"&gt;
 varying vec2 vUv;
 uniform vec3 meshCenter;
 varying vec2 clipMeshCenter;
 uniform float time;
 varying vec2 glPos;

 void main() {
 clipMeshCenter = (projectionMatrix * modelViewMatrix * vec4(meshCenter, 1.0)).xy;
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
 vUv = gl_Position.xy / gl_Position.w;
 vUv = (vUv + 1.0) * 0.5;
 glPos = gl_Position.xy;
 }
&lt;/script&gt;
&lt;script type="importmap"&gt;
 {
 "imports": {
 "three": "https://cdn.jsdelivr.net/npm/three@0.172.0/build/three.module.js",
 "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.172.0/examples/jsm/"
 }
 }
&lt;/script&gt;

&lt;script type="module"&gt;
import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
let renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
let width = document.querySelector('#stage').offsetWidth;
let height = Math.round(9/16 * width);
renderer.setSize(width, height);
document.querySelector('#stage').appendChild(renderer.domElement);

function get(path) {
 return document.querySelector('#' + path).textContent;
};

const scene = new THREE.Scene();
const solidScene = new THREE.Scene();
const maskScene = new THREE.Scene();
const evolveScene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
const color = 0xFFFFFF;
let ambientLight = new THREE.AmbientLight(color, dark ? 1: 10);
solidScene.add(ambientLight);
let light = new THREE.PointLight(color, 200);
light.position.set(10, 10, 15);
solidScene.add(light);
camera.position.set(6,8,14);

let buffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})
let outlineBuffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})

const orbit = new OrbitControls(camera, renderer.domElement);
orbit.update();

const geometry = new THREE.TorusGeometry( 10, 3, 20, 100 );
geometry.translate(2, 2, 2);

const shadowMesh = new THREE.Mesh(geometry);
const uniforms = {
 gbufferMask: { value: buffer.texture },
 viewportSize: { value: new THREE.Vector2(width, height) },
}

const material = new THREE.ShaderMaterial({
 vertexShader: get('outline-vert'),
 fragmentShader: get('outline-frag'),
 uniforms,
 transparent: true,
});

let solidMeshMaterial = new THREE.MeshPhysicalMaterial({
 color: dark ? 0xaaeadb : 0xffffff,
 metalness: 0.8,
 clearcoat: 0.4,
 clearcoatRoughness: 0.1,
});

const solidMesh = new THREE.Mesh(
 geometry,
 solidMeshMaterial,
);

const mesh = new THREE.Mesh(
 geometry,
 material
);
solidScene.add(solidMesh);
scene.add(mesh);
maskScene.add(shadowMesh);


const evoUniforms = {
 initBufferMask: { value: null },
}

const evoMaterial = new THREE.ShaderMaterial({
 // this is a copy shader
 vertexShader: get('evolve-vert'),
 fragmentShader: get('evolve-frag'),
 uniforms: evoUniforms,
 transparent: true,
});

const evolveMesh = new THREE.Mesh(
 geometry,
 evoMaterial
);

evolveScene.add(evolveMesh)

function continuity(bitmap, width, height) {
 const visited = Array.from({length: height}, () =&gt; Array(width).fill(false));

 function valid(row, col) {
 return row &gt;= 0 &amp;&amp; row &lt; height &amp;&amp; col &gt;= 0 &amp;&amp; col &lt;= width
 }

 function valid_1(row, col) {
 return valid(row, col) ? 1 : 0
 }

 // for a point to be on the screen edge, it must have at least three invalid neighbors.
 // It must have at least six valid neighbors.
 function isSentinel(row, col) {
 return (
 valid_1(row - 1, col - 1) +
 valid_1(row - 1, col) +
 valid_1(row - 1, col + 1) +

 valid_1(row, col - 1) +
 valid_1(row, col + 1) +

 valid_1(row + 1, col - 1) +
 valid_1(row + 1, col) +
 valid_1(row + 1, col + 1)
 ) &lt; 6
 }

 var sentinels = [];
 var cyclic = [];

 function dfs_queue(rootRow, rootCol) {
 var stack = [];
 stack.push([rootRow, rootCol]);

 var steps = 0;

 for(; ; steps++) {
 let p = stack.pop();
 if (p === undefined) {
 return
 }
 let [row, col] = p
 if (isSentinel(row, col)) {
 sentinels.push([row, col]);
 return
 }

 if (steps &gt; 0 &amp;&amp; row == rootRow &amp;&amp; col == rootCol) {
 cyclic.push([row, col])
 return
 }

 if (!valid(row, col) || visited[row][col]) {
 return
 }

 // is this point switched off?
 visited[row][col] = true;
 var point = 4 * (row * width + col);
 if (bitmap[point] == 0 &amp;&amp; bitmap[point+1] == 0 &amp;&amp; bitmap[point+2] == 0) {
 continue;
 }

 stack.push([row + 1, col])
 stack.push([row, col + 1])
 stack.push([row - 1, col])
 stack.push([row, col - 1])

 }
 }

 for (let row = 0; row &lt; height; row++) {
 for(let col = 0; col &lt; width; col++) {
 if (visited[row][col]) {
 continue;
 }
 var point = 4 * (row * width + col);
 if (bitmap[point] == 1 &amp;&amp; bitmap[point+1] == 1 &amp;&amp; bitmap[point+2] == 1) {
 dfs_queue(row, col);
 }
 }
 }

 // Always prefer the sentinels to the cyclics
 // since walking back a line also ends up in a cycle.
 // Start waveforms from the sentinels (endpoints),
 // mark all the visited points. If there remain unvisited
 // points, those are legitimately parts of cyclic paths.
 return sentinels.concat(cyclic);
}

var durationInSeconds = 5;

// @param {Float32Array[]} points
// @param {Uint32Array} allThePixels
// Number all the points as we stumble along the outline.
// Akin to a wavefront. The pixels switched on (value = 1)
// touching the wavefront at time t will have a value t + 2
//
// This way we can quickly zero out all the pixels below a
// threshold when timing the animation.
function dijkstraNumber(points, buf) {
 var longestStrand = 2;
 points.forEach((point) =&gt; {
 longestStrand = Math.max(longestStrand, dijkstraPropagate(point, buf, 2))
 })
 return longestStrand
}

function dijkstraPropagate(point, buf, value) {
 var queue = [[point]];
 for (; ;) {
 let neighbors = queue.pop()
 if (neighbors === undefined) {
 return value
 }
 neighbors.forEach(([row, col]) =&gt; {
 let pos = 4 * (row * buffer.width + col);
 if (buf[pos] != 1) {
 return
 }

 buf[pos] = value;
 buf[pos+1] = value;
 buf[pos+2] = value;
 buf[pos+3] = value;
 value += 1

 // package all the neighboring points and
 // push them onto the stack
 queue.push([
 [row + 1, col],
 [row + 1, col + 1],
 [row, col + 1],
 [row - 1, col + 1],
 [row - 1, col],
 [row - 1, col - 1],
 [row, col - 1],
 [row + 1, col - 1]
 ])
 })
 }
}

const clock = new THREE.Clock();
var longestPixelStrand = 0;
var init = true;
const allThePixels = new Uint8Array(buffer.width * buffer.height * 4);
const dijkstraBuffer = new Uint32Array(buffer.width * buffer.height * 4);
function animate() {

 if (init) {
 renderer.setRenderTarget(buffer);
 renderer.render(maskScene, camera);

 renderer.setRenderTarget(outlineBuffer);
 renderer.render(scene, camera);

 renderer.readRenderTargetPixels(outlineBuffer, 0, 0, buffer.width, buffer.height, allThePixels);
 for (let i = 0; i &lt; allThePixels.length; i++) {
 dijkstraBuffer[i] = allThePixels[i] == 255 ? 1 : 0;
 }

 		let points = continuity(dijkstraBuffer, width, height)
 		longestPixelStrand = dijkstraNumber(points, dijkstraBuffer)

 let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
 points.forEach((point) =&gt; {
 let pos = 4 * (point[0] * buffer.width + point[1]);
 initBuffer[pos] = 255;
 initBuffer[pos+1] = 255;
 initBuffer[pos+2] = 255;
 initBuffer[pos+3] = 255;
 })

 let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
 ephemeralTex.needsUpdate = true;
 evoUniforms.initBufferMask.value = ephemeralTex;
 init = false;
 }
		let fractionAnimated = (clock.getElapsedTime() % durationInSeconds) / durationInSeconds;
		let pixelsAnimated = Math.round(longestPixelStrand * fractionAnimated);
 let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
 for (let row = 0; row &lt; height; row++) {
 for(let col=0; col&lt;width; col++) {
 var point = 4 * (row * width + col);
 if (dijkstraBuffer[point] &gt; 1 &amp;&amp; dijkstraBuffer[point] &lt; pixelsAnimated) {
 initBuffer[point] = dark ? 255: 22;
 initBuffer[point+1] = dark ? 255: 22;
 initBuffer[point+2] = dark ? 255 : 22;
 initBuffer[point+3] = 255;
 }
 }
 }
 let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
 ephemeralTex.needsUpdate = true;
 evoUniforms.initBufferMask.value = ephemeralTex;

 renderer.setRenderTarget(null);
 renderer.render(solidScene, camera);
 renderer.autoClear = false;
 renderer.clearDepth();
 renderer.render(evolveScene, camera);
 renderer.autoClear = true;
}

renderer.setAnimationLoop(animate);

orbit.addEventListener('change', function() {
 init = true;
})
&lt;/script&gt;</description></item><item><title>PicoCTF SansAlpha Writeup</title><link>https://lavafroth.is-a.dev/post/picoctf-sansalpha-writeup/</link><pubDate>Sun, 05 Jan 2025 11:55:52 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-sansalpha-writeup/</guid><description>&lt;p&gt;Hey everyone, since 2024 hasn&amp;rsquo;t seen a lot of posts on this blog, I plan to
start this year off by going back to the roots.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be focusing on posting more CTF writeups again! Today&amp;rsquo;s challenge is
&lt;em&gt;SansAlpha&lt;/em&gt; from PicoCTF. The challenge description states&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Multiverse is within your grasp! Unfortunately, the server that contains
the secrets of the multiverse is in a universe where keyboards only have numbers
and (most) symbols.&lt;/p&gt;</description></item><item><title>A Tale of a Frugal Home Server</title><link>https://lavafroth.is-a.dev/post/a-tale-of-a-frugal-home-server/</link><pubDate>Sat, 04 Jan 2025 10:04:37 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/a-tale-of-a-frugal-home-server/</guid><description>&lt;p&gt;Having run an on-premise server for the past two years, I think my setup has finally
matured enough to be worth talking about.&lt;/p&gt;
&lt;p&gt;At any point, you can check out the source code for the server&amp;rsquo;s infrastructure &lt;a href="https://github.com/lavafroth/dotfiles/tree/main/hosts/rahu"&gt;here&lt;/a&gt; for a concrete example.
For each service I talk about, I will also link the respective definitions in my config.&lt;/p&gt;
&lt;p&gt;My minimalist mindset has unsurprisingly aided the architecture of my server.
Throughout the rest of the post, you will come across the following broad strokes:&lt;/p&gt;</description></item><item><title>NixOS Secureboot Shenanigans</title><link>https://lavafroth.is-a.dev/post/nixos-secureboot-shenanigans/</link><pubDate>Fri, 20 Dec 2024 12:26:10 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/nixos-secureboot-shenanigans/</guid><description>&lt;h1 id="key-takeaways"&gt;Key takeaways&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;This issue only pertains to secureboot on NixOS using lanzaboote. Most Linux users have secureboot disabled. If you are paranoid like me and have enabled it, continue reading.&lt;/li&gt;
&lt;li&gt;Make sure to track the latest version of &lt;code&gt;lanzaboote&lt;/code&gt;. (&lt;a href="https://github.com/lavafroth/dotfiles/commit/4d64808ffbc135b5bf5a61df17ef02d7da8452b7"&gt;example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Set the PKI bundle location to the newer &lt;code&gt;sbctl&lt;/code&gt; default. (&lt;a href="https://github.com/lavafroth/dotfiles/commit/1fa71734bb3af83b8de9134e68f0153f49a18205"&gt;example&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="deprecated-overridescope"&gt;Deprecated &lt;code&gt;overrideScope'&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;For the past few months, I started noticing this new warning when rebuilding my system with &lt;code&gt;nixos-rebuild&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Wrapping up GSoC 2024</title><link>https://lavafroth.is-a.dev/post/the-gsoc-grand-finale/</link><pubDate>Sat, 24 Aug 2024 10:28:50 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/the-gsoc-grand-finale/</guid><description>&lt;h1 id="overview"&gt;Overview&lt;/h1&gt;
&lt;p&gt;Hello and welcome to the final GSoC post for 2024! My task was to formalize the SWHKD parser using context-free EBNF notation. This post is to serve as a birdseye view of what
I have developed over this summer.&lt;/p&gt;
&lt;h1 id="report"&gt;Report&lt;/h1&gt;
&lt;h2 id="architecting-the-parser"&gt;Architecting the parser&lt;/h2&gt;
&lt;p&gt;I started out with the scaffolding of the parser in an extended Backus-Naur form garmmar template
in a separate repository called &lt;a href="https://github.com/waycrate/sweet"&gt;SWEET&lt;/a&gt; using a Rust framework
called &lt;a href="https://pest.rs"&gt;pest.rs&lt;/a&gt;. Quite a lot of time was
spent in modelling the architecture of the syntax tree for our domain specific language.&lt;/p&gt;</description></item><item><title>Painlessly setting up ML tooling on NixOS</title><link>https://lavafroth.is-a.dev/post/cuda-on-nixos-without-sacrificing-ones-sanity/</link><pubDate>Sat, 10 Aug 2024 08:18:30 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/cuda-on-nixos-without-sacrificing-ones-sanity/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Note: Use the following method only if you wish to have the latest version of CUDA that is
not yet available in the nix-community cache, otherwise follow &lt;a href="https://nix-community.org/cache"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;TL;DR:&lt;/em&gt; Save &lt;a href="#the-flake"&gt;this flake&lt;/a&gt;, run &lt;code&gt;nix develop&lt;/code&gt; and &lt;a href="#setting-up-pytorch"&gt;setup PyTorch as described&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/CUDA"&gt;CUDA&lt;/a&gt; is a proprietary vendor lock-in for machine learning folks.
Training ML models is incredibly fast with CUDA as compared to CPUs due to the parallel
processing. So if you&amp;rsquo;re doing something serious, you have no other choice besides CUDA as of writing.
Although, OpenAI&amp;rsquo;s Triton and ZLUDA are worth keeping an eye on.&lt;/p&gt;</description></item><item><title>Amateur Blender Sculpture</title><link>https://lavafroth.is-a.dev/art/amateur-blender-sculpture/</link><pubDate>Sat, 03 Aug 2024 17:50:00 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/amateur-blender-sculpture/</guid><description>&lt;p&gt;This is my first time trying out sculpting in blender, so forgive me for the
quality of the sculpt. I&amp;rsquo;m still pretty much in the learning stage. Big thank
you to &lt;a href="https://www.pexels.com/@nichole-sebastian-1592975/"&gt;Nichole Sebastian&lt;/a&gt;
for the reference photo. Also apologies if the empty eye sockets gave you a
jumpscare.&lt;/p&gt;</description></item><item><title>How I Use SWHKD in My Workflow</title><link>https://lavafroth.is-a.dev/post/how-i-use-swhkd-in-my-workflow/</link><pubDate>Thu, 01 Aug 2024 17:17:31 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/how-i-use-swhkd-in-my-workflow/</guid><description>&lt;p&gt;SWHKD is the project that I have been working on for the past few months as a part of Google Summer of Code for this year.
Now that we are done with the development process, I want to talk about why I wanted to improve the project. Although
the easy answer is to get paid or to get a more production facing OSS development experience,
for me, the most important driving force is using it in my own workflow.&lt;/p&gt;</description></item><item><title>Polishing and Bugfix Week</title><link>https://lavafroth.is-a.dev/post/polishing-and-bugfix-week/</link><pubDate>Mon, 29 Jul 2024 13:46:41 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/polishing-and-bugfix-week/</guid><description>&lt;p&gt;Hello and welcome to the last instalment in the series where we build a parser
for a domain specific langauge in Rust. Please go through the previous articles
since this article assumes you are aware of such contextual details.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the bugfixes.&lt;/p&gt;
&lt;h1 id="eagerly-removing-unbinds"&gt;Eagerly removing unbinds&lt;/h1&gt;
&lt;p&gt;While going through the tests, I figured that
the prior parser eagerly parses unbinds and removes said keystroke combinations
from our binding set. Unlike the previous iteration, our iteration had unbinds
as a separate set which deferred the task of the removing the set intersection
to the upstream crate instead.&lt;/p&gt;</description></item><item><title>Humans Suck at Command Sanitization</title><link>https://lavafroth.is-a.dev/post/humans-suck-at-command-sanitization/</link><pubDate>Wed, 17 Jul 2024 07:55:34 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/humans-suck-at-command-sanitization/</guid><description>&lt;p&gt;Hello and welcome to the eighth instalment in the series where we build a parser
for a domain specific language in Rust. I’d highly recommend going through the
previous articles to make sense of what we’ll talk about today.&lt;/p&gt;
&lt;p&gt;Previously, we had built the scaffolding for modes to bind shortcuts to. Today,
we&amp;rsquo;ll create the mechanism to invoke commands in the contexts of the modes that
can be built.&lt;/p&gt;
&lt;p&gt;Now, SWHKD has a clever way to enter (and escape) mode contexts with inside commands
by chaining subcommands and mode instructions with double ampersands. Consider the following example:&lt;/p&gt;</description></item><item><title>Preventing Infinite Recursions From Eating Your Lunch</title><link>https://lavafroth.is-a.dev/post/preventing-infinite-recursions-from-eating-your-lunch/</link><pubDate>Thu, 04 Jul 2024 09:57:01 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/preventing-infinite-recursions-from-eating-your-lunch/</guid><description>&lt;p&gt;Hello and welcome to the eighth instalment in the series where we build a
parser for a domain specific language in Rust. I&amp;rsquo;d highly recommend
going through the previous articles to make sense of what we’ll talk about today.&lt;/p&gt;
&lt;p&gt;After a bit of back and forth with my mentor, we landed on moving the logic that imports
other config files into the parser crate itself. Config files can reference other modules
using import statements of the following form:&lt;/p&gt;</description></item><item><title>Test Driven Development - The Pinnacle of Engineering</title><link>https://lavafroth.is-a.dev/post/test-driven-development-the-pinnacle-of-engineering/</link><pubDate>Mon, 24 Jun 2024 08:45:49 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/test-driven-development-the-pinnacle-of-engineering/</guid><description>&lt;p&gt;Hello and welcome to the seventh instalment in the series where we build a
parser for a domain specific language in Rust. I would highly recommend you to
go through the previous articles to make sense of what we’ll talk about today.&lt;/p&gt;
&lt;h2 id="tying-loose-ends"&gt;Tying loose ends&lt;/h2&gt;
&lt;p&gt;Up until the last post, we had covered quite some ground, from building
elementary expressions to the penultimate levels of abstraction for macroscopic
expressions.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s begin today&amp;rsquo;s conversation by finishing off where we left off. For us to
be able to parse an entire config file, we must have one main rule. We combine
all of the primitives that we have built so far: comments, modes, bindings,
unbinds and imports into a blanket content expression.&lt;/p&gt;</description></item><item><title>Drowning</title><link>https://lavafroth.is-a.dev/art/drowning/</link><pubDate>Tue, 18 Jun 2024 09:30:00 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/drowning/</guid><description>&lt;p&gt;A cyborg head sinking in a pool of water. What more did you expect? &lt;a href="https://www.youtube.com/watch?v=lVbPXxq0xzg"&gt;Here&amp;rsquo;s a timelapse&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>This Error</title><link>https://lavafroth.is-a.dev/art/thiserror/</link><pubDate>Tue, 18 Jun 2024 09:30:00 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/thiserror/</guid><description>&lt;p&gt;My first hand drawn YouTube thumbnail, I&amp;rsquo;m might continue using
&lt;em&gt;lawyer ferris&lt;/em&gt; as my mascot both due to ferris being in the public domain
as well as the sheer memeworthiness of the debacle.&lt;/p&gt;</description></item><item><title>Modes, Unbinds and Other Ensembled Parser Patterns</title><link>https://lavafroth.is-a.dev/post/modes-unbinds-and-other-ensembled-parser-patterns/</link><pubDate>Mon, 10 Jun 2024 08:27:06 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/modes-unbinds-and-other-ensembled-parser-patterns/</guid><description>&lt;p&gt;Hello and welcome to the sixth instalment in this series where we build a parser
for a domain specific language from scratch. I would highly recommend you to go
through the previous articles to make sense of what we&amp;rsquo;ll talk about today.&lt;/p&gt;
&lt;p&gt;So far, we have built ranges, shorthands and bindings, starting all the way down
from primitives such as keys and modifiers. Continuing with the theme, we will
ensemble these patterns together along with some newer syntax to build modes.&lt;/p&gt;</description></item><item><title>I Solemnly Swear to Never Buy a Gaming Laptop Again</title><link>https://lavafroth.is-a.dev/post/i-solemnly-swear-to-never-buy-a-gaming-laptop-again/</link><pubDate>Fri, 07 Jun 2024 17:01:01 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/i-solemnly-swear-to-never-buy-a-gaming-laptop-again/</guid><description>&lt;p&gt;Around half a decade ago, I bought an Asus gaming laptop, one I&amp;rsquo;m currently using to write this article.
Although it came preinstalled with Windows, I never let it even boot and instead opted for linux. Bill Gates can cry a river.&lt;/p&gt;
&lt;p&gt;Despite switching distros multiple times, one sporadical issue my setup suffered from
was the wireless card dying after a few minutes of booting the box. The only solution to this was to reboot my computer, classic!&lt;/p&gt;</description></item><item><title>Modeling More Realistic Keybinds With Modifiers</title><link>https://lavafroth.is-a.dev/post/modeling-more-realistic-keybinds-with-modifiers/</link><pubDate>Wed, 05 Jun 2024 10:26:13 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/modeling-more-realistic-keybinds-with-modifiers/</guid><description>&lt;p&gt;Real world keybindings for shortcuts often involve more than just a simple keypress, especially outside the context of
a single application. The general distinction for these two types involves modifier keys. When I talk about a shortcut
bound to &lt;code&gt;super&lt;/code&gt; &lt;code&gt;v&lt;/code&gt;, chances are you automatically think of global bindings at the operating system or desktop environment
level. Today we&amp;rsquo;ll go through the process of writing the grammar for these bindings for swhkd.&lt;/p&gt;</description></item><item><title>Edge cases? You Shall Not Pass!</title><link>https://lavafroth.is-a.dev/post/edge-cases-you-shall-not-pass/</link><pubDate>Mon, 03 Jun 2024 08:18:19 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/edge-cases-you-shall-not-pass/</guid><description>&lt;p&gt;This post is a part of a series that explains the architecture of the config parser
I am building for swhkd as a part of Google Summer of Code. I highly recommend reading
through the previous posts as I&amp;rsquo;ll be referring to them from time to time.&lt;/p&gt;
&lt;p&gt;In the last post I talked about key attributes that can be used as prefix to denote
the timing of an event, on key press (&lt;code&gt;send&lt;/code&gt; / &lt;code&gt;~&lt;/code&gt;) or release (&lt;code&gt;on_release&lt;/code&gt; / &lt;code&gt;@&lt;/code&gt;). One nuanced
case we did not cover was the use of these attributes inside shorthands.&lt;/p&gt;</description></item><item><title>Timing is Key: A Tale of Keystrokes and Timings</title><link>https://lavafroth.is-a.dev/post/timing-is-key/</link><pubDate>Wed, 29 May 2024 21:18:22 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/timing-is-key/</guid><description>&lt;p&gt;Whether you&amp;rsquo;re playing a video game or competing in a constrained attack-defense CTF, your keystroke timings matter.
We at waycrate value your precision, to the extent that you can configure your keybindings to perform actions either
on a key&amp;rsquo;s press or a release.&lt;/p&gt;
&lt;p&gt;Hi, my name&amp;rsquo;s Himadri and this post is a part of a series explaining how
we (basically just me) are rewriting the config parser for swhkd using EBNF
grammar. I highly recommend reading the previous posts because I&amp;rsquo;ll be referring
to them from time to time. In the last post, we talked about regular keys
that form the foundation of bindings. However, we glossed over the &lt;code&gt;send&lt;/code&gt; and
&lt;code&gt;on_release&lt;/code&gt; expressions in the code.&lt;/p&gt;</description></item><item><title>Keep the Keys Clackin'</title><link>https://lavafroth.is-a.dev/post/keep-the-keys-clackin/</link><pubDate>Mon, 27 May 2024 08:59:29 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/keep-the-keys-clackin/</guid><description>&lt;p&gt;This is the second post in a series of posts I&amp;rsquo;m writing for Google Summer of Code.
Each post covers a separate topic.
While the previous posts might have given you an overview of ideas, this post will delve
into more technical details. I highly recommend reading the previous posts because I will
refer to them from time to time.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s begin with why we chose EBNF grammar in &lt;a href="https://pest.rs"&gt;pest.rs&lt;/a&gt; instead of regular expressions.&lt;/p&gt;</description></item><item><title>2 Afternoons, 2 Languages, 2 TUIs</title><link>https://lavafroth.is-a.dev/post/2-afternoons-2-languages-2-tuis/</link><pubDate>Thu, 23 May 2024 18:37:47 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/2-afternoons-2-languages-2-tuis/</guid><description>&lt;p&gt;Yesterday I created a tool in Golang to help me render my animations a little
faster. Although the alterior reason was to check my Golang proficiency, today
I rewrote it in Rust and I was blown away by the differences in the final
products.&lt;/p&gt;
&lt;p&gt;When I&amp;rsquo;m rendering animations for a YouTube video, the general development
iteration comprises me creating or modifying a file, switching to a different
terminal pane and manually issuing a &lt;em&gt;manim&lt;/em&gt; command for the respective file to
render and play the animation. My goal was to automate the last two processes,
switching terminal panes and manually issuing a command. The idea is to have a
tool running in the background that listens for filesystem events, like when a
file gets created or modified, and if the file happens to contain an animation,
renders it. On linux systems, it&amp;rsquo;s mostly a bunch of bindings to &lt;code&gt;inotify&lt;/code&gt; but I
have used platform agnostic libraries for both the languages.&lt;/p&gt;</description></item><item><title>A SWEET Little Parser</title><link>https://lavafroth.is-a.dev/post/a-sweet-little-config-parser/</link><pubDate>Fri, 17 May 2024 07:52:44 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/a-sweet-little-config-parser/</guid><description>&lt;p&gt;A few days ago, I had announced my project for this year&amp;rsquo;s Google Summer of Code. Today I&amp;rsquo;ll
be explanding upon that. I believe that to construct a good grammar, I should be able to understand
and explain it well. So here goes.&lt;/p&gt;
&lt;h2 id="general-idea"&gt;General Idea&lt;/h2&gt;
&lt;p&gt;SWHKD&amp;rsquo;s grammar parser, although similar to tools before it like sxhkd, has a more coherent
syntax. For starters, every binding declaration is one or more accelerators followed by a composite key.&lt;/p&gt;</description></item><item><title>Wayland Tools Rock!</title><link>https://lavafroth.is-a.dev/post/wayland-tools-rock/</link><pubDate>Fri, 17 May 2024 07:52:44 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/wayland-tools-rock/</guid><description>&lt;p&gt;Hey folks. Quite a few months have passed since I last posted here.
As you might have known from my earlier posts, I&amp;rsquo;ve been daily driving
Wayland instead of Xorg on my NixOS setup for quite some time now.&lt;/p&gt;
&lt;p&gt;One of the tools I stumbled upon while writing my voice automation abomination
was SWHKD (Simple Wayland HotKey Daemon). It&amp;rsquo;s a spiritual successor to sxhkd from the Xorg world
and in a sense better than the former because it works not only in wayland sessions but also
under X and TTY sessions!&lt;/p&gt;</description></item><item><title>Using an Android Phone as a webcam in NixOS</title><link>https://lavafroth.is-a.dev/post/android-phone-for-webcam-nixos/</link><pubDate>Sun, 10 Mar 2024 08:47:08 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/android-phone-for-webcam-nixos/</guid><description>&lt;p&gt;I recently had to attend an online meeting for a software development event.
While my PC did have a decent microphone, the built-in camera has been damaged to the extent that the best it can capture is this:&lt;/p&gt;
&lt;p&gt;&lt;img src="./pc-camera.avif" alt="A blurry image taken from my scuffed camera"&gt;&lt;/p&gt;
&lt;p&gt;No, it&amp;rsquo;s not a close-up of the moon, it&amp;rsquo;s the refraction caused by the scuffs to the lens plus other sciency stuff I&amp;rsquo;m not qualified enough to explain to you.&lt;/p&gt;</description></item><item><title>Throwing knives</title><link>https://lavafroth.is-a.dev/art/wip-animation/</link><pubDate>Fri, 19 Jan 2024 09:30:00 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/wip-animation/</guid><description>&lt;p&gt;An unfinished animation with a focus on anatomy. Thank you &lt;a href="https://www.pexels.com/@polina-tankilevitch/"&gt;Polina Tankilevitch&lt;/a&gt; for the &lt;a href="https://www.pexels.com/video/a-young-woman-showing-her-skill-in-dancing-5385879/"&gt;reference video&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Kringlecon 2023 Writeup</title><link>https://lavafroth.is-a.dev/post/kringlecon-2023-writeup/</link><pubDate>Wed, 10 Jan 2024 19:51:32 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/kringlecon-2023-writeup/</guid><description>&lt;p&gt;Happy new year everyone! As every year, I&amp;rsquo;ll begin this one with sharing my writeup for the 2023 Holiday Hack Challenge for Kringlecon.
I must warn you, I was unable to finish all the challenges due to other life events.&lt;/p&gt;
&lt;p&gt;With that out of the way, enjoy the writeup!&lt;/p&gt;
&lt;h1 id="christmas-island-orientation"&gt;Christmas Island: Orientation&lt;/h1&gt;
&lt;h2 id="cranberry-pi"&gt;Cranberry Pi&lt;/h2&gt;
&lt;p&gt;This is a sanity check question to kick the tires. The answer to it simply is
&lt;code&gt;answer&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Abstracting Structured Patterns in Concurrent Programming</title><link>https://lavafroth.is-a.dev/post/abstracting-structured-patterns-in-concurrent-programming/</link><pubDate>Wed, 06 Dec 2023 10:58:10 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/abstracting-structured-patterns-in-concurrent-programming/</guid><description>&lt;blockquote&gt;
&lt;p&gt;I hope this article provides a solid blueprint for building a concurrency management API.
If you have questions or feel that I have missed something, feel free to talk about it in this repository&amp;rsquo;s &lt;a href="https://github.com/lavafroth/lavafroth.github.io/issues"&gt;issue tracker&lt;/a&gt; or the &lt;a href="https://github.com/lavafroth/lavafroth.github.io/discussions"&gt;discussion board&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In recent months, I have come across multiple articles talking about the need
of structured concurrency in modern programming languages as a built-in. Notably, in the article &lt;a href="https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/"&gt;Notes on structured concurrency, or: Go statement considered harmful&lt;/a&gt;,
the author compares the &lt;code&gt;go&lt;/code&gt; statement used to spawn coroutines to &lt;code&gt;goto&lt;/code&gt; statements used
for jumping to other parts of code in early languages like COBOL.&lt;/p&gt;</description></item><item><title>Headache</title><link>https://lavafroth.is-a.dev/post/headache-reverse-engineering-amateursctf-2023/</link><pubDate>Thu, 07 Sep 2023 07:03:27 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/headache-reverse-engineering-amateursctf-2023/</guid><description>&lt;p&gt;This challenge involves reverse engineering a polymorphic binary, one that modifies its own instructions during runtime.&lt;/p&gt;
&lt;p&gt;Essentially, the binary checks if the current character equals a known value and &lt;em&gt;xor&lt;/em&gt; decrypts the next section where the
code jumps to. If the characters don&amp;rsquo;t match, the logic short-circuits and the program exits.&lt;/p&gt;
&lt;p&gt;This process of checking the character and decrypting the next branch continues like opening up a Matryoshka doll until
the last branch which returns instead of calling the decryption subroutine.&lt;/p&gt;</description></item><item><title>Compact XOR</title><link>https://lavafroth.is-a.dev/post/compact-xor-crypto-challenge-amateursctf-2023/</link><pubDate>Thu, 24 Aug 2023 18:05:59 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/compact-xor-crypto-challenge-amateursctf-2023/</guid><description>&lt;h1 id="description"&gt;Description&lt;/h1&gt;
&lt;p&gt;I found some hex in a file called fleg, but I’m not sure how it’s encoded. I’m pretty sure it’s some kind of xor…&lt;/p&gt;
&lt;h1 id="exploration"&gt;Exploration&lt;/h1&gt;
&lt;p&gt;We begin by creating a new rust project.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo new amateurs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd amateurs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo add hex
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo add itertools
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s decode the hexadecimal contents of the file using the following Rust code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;(), Box&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;dyn&lt;/span&gt; std::error::Error&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; bytes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; hex::decode(&lt;span style="color:#e6db74"&gt;&amp;#34;610c6115651072014317463d73127613732c73036102653a6217742b701c61086e1a651d742b69075f2f6c0d69075f2c690e681c5f673604650364023944&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; stream &lt;span style="color:#f92672"&gt;=&lt;/span&gt; String::from_utf8_lossy(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;bytes);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{:?}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, stream);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Ok(())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To execute the code, issue the following.&lt;/p&gt;</description></item><item><title>Volcano</title><link>https://lavafroth.is-a.dev/post/volcano-reverse-engineering-amateursctf-2023/</link><pubDate>Fri, 21 Jul 2023 18:29:59 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/volcano-reverse-engineering-amateursctf-2023/</guid><description>&lt;p&gt;This reversing challenge is very mathematical, focusing mainly on modulo congruences.
Like all challenges, there is some scary looking obfuscation for the fun which I&amp;rsquo;ll try my best to
explain. The challenge description says that it was &lt;em&gt;inspired by recent &amp;ldquo;traumatic&amp;rdquo; events&lt;/em&gt; but I&amp;rsquo;m oblivious to what that
reference meant.&lt;/p&gt;
&lt;h2 id="decompilation"&gt;Decompilation&lt;/h2&gt;
&lt;p&gt;We start off with downloading the binary and opening it in Ghidra.&lt;/p&gt;
&lt;p&gt;In the list of functions under the Symbol Tree, we can navigate to the &lt;code&gt;entry&lt;/code&gt; function which looks like:&lt;/p&gt;</description></item><item><title>Waiting an Eternity</title><link>https://lavafroth.is-a.dev/post/wait-an-eternity-web-challenge-amateursctf-2023/</link><pubDate>Wed, 19 Jul 2023 07:53:17 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/wait-an-eternity-web-challenge-amateursctf-2023/</guid><description>&lt;p&gt;This was a fairly straightforward and fun challenge that required a bit of common sense to solve.
We are given the URL &lt;a href="https://waiting-an-eternity.amt.rs"&gt;https://waiting-an-eternity.amt.rs&lt;/a&gt; to begin with.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s use &lt;code&gt;curl&lt;/code&gt; with its verbose flag to fetch this URL.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -v &lt;span style="color:#e6db74"&gt;&amp;#34;https://waiting-an-eternity.amt.rs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We get a response that tells us to wait an enternity.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; GET / HTTP/2
&amp;gt; Host: waiting-an-eternity.amt.rs
&amp;gt; User-Agent: curl/8.1.1
&amp;gt; Accept: */*
&amp;gt; 
&amp;lt; HTTP/2 200 
&amp;lt; content-type: text/html; charset=utf-8
&amp;lt; date: Tue, 18 Jul 2023 04:28:52 GMT
&amp;lt; refresh: 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; url=./secret-site?secretcode=5770011ff65738feaf0c1d009caffb035651bb8a7e16799a433a301c0756003a
&amp;lt; server: gunicorn
&amp;lt; content-length: 21
&amp;lt; 
* Connection #0 to host waiting-an-eternity.amt.rs left intact
just wait an eternity
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On closer inspection, the refresh header with the gigantic number sticks out like a sore thumb.&lt;/p&gt;</description></item><item><title>I Switched to NixOS</title><link>https://lavafroth.is-a.dev/post/i-switched-to-nixos/</link><pubDate>Sat, 08 Jul 2023 09:29:34 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/i-switched-to-nixos/</guid><description>&lt;p&gt;Hi. It&amp;rsquo;s been quite a while since I had last posted. I had been spending my time on some programming projects that withheld me from even participating in CTFs.
Tired of this workflow that I somehow spiraled into, I&amp;rsquo;m now seeking to learn new things in an attempt to break out of this workflow.&lt;/p&gt;
&lt;h1 id="the-end-of-an-overarching-journey"&gt;The End of an Overarching Journey&lt;/h1&gt;
&lt;p&gt;As any of my long time audience might be familiar with, I daily drove &lt;a href="https://archlinux.org"&gt;Arch Linux&lt;/a&gt;. A very flexible distribution, Arch allows beginners to get a good grasp of the
Linux way of doing things. It has an amazing package manager as well as &lt;a href="https://aur.archlinux.org"&gt;a user repository&lt;/a&gt; for extra software unavailable in the official repositories. It&amp;rsquo;s rather
easy to setup Arch for gaming, thanks to programs like &lt;a href="https://lutris.net/"&gt;Lutris&lt;/a&gt; and &lt;a href="https://usebottles.com/"&gt;Bottles&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Twosum</title><link>https://lavafroth.is-a.dev/post/picoctf-binary-exploitation-twosum/</link><pubDate>Mon, 10 Apr 2023 08:44:28 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-binary-exploitation-twosum/</guid><description>&lt;p&gt;This is a rather simple binary exploitation challenge. We are given the following source
code for the program running on the remote server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;addIntOvf&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; result, &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; a, &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; b) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a &lt;span style="color:#f92672"&gt;+&lt;/span&gt; b;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(a &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; result &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(a &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; result &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; num1, num2, sum;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FILE &lt;span style="color:#f92672"&gt;*&lt;/span&gt;flag;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; c;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;n1 &amp;gt; n1 + n2 OR n2 &amp;gt; n1 + n2 &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;What two positive numbers can make this possible: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;scanf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;num1) &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scanf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;num2)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;You entered %d and %d&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, num1, num2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; num1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; num2;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;addIntOvf&lt;/span&gt;(sum, num1, num2) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;No overflow&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;addIntOvf&lt;/span&gt;(sum, num1, num2) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;You have an integer overflow&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (num1 &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; num2 &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; flag &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fopen&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;flag.txt&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;r&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(flag &lt;span style="color:#f92672"&gt;==&lt;/span&gt; NULL){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;flag not found: please run this on the server&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; buf[&lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fgets&lt;/span&gt;(buf, &lt;span style="color:#ae81ff"&gt;59&lt;/span&gt;, flag);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;YOUR FLAG IS: %s&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, buf);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fflush&lt;/span&gt;(stdout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In order to trigger the program to disclose the flag, we need to supply two numbers that are greater that 0 and result in an integer overflow.
Since this is C and there is no integer overflow check, we can simply supply the maximum interger value for the first number and the value 1 for
the second. Adding them would cause the result to wrap around and become negative.&lt;/p&gt;</description></item><item><title>Java Code Analysis!?!</title><link>https://lavafroth.is-a.dev/post/picoctf-web-java-code-analysis/</link><pubDate>Sat, 18 Mar 2023 07:10:17 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-web-java-code-analysis/</guid><description>&lt;p&gt;To get started we are given the username &amp;ldquo;user&amp;rdquo; and password &amp;ldquo;user&amp;rdquo; to log into the BookShelf Pico web application.
We are also given the source code of the application.&lt;/p&gt;
&lt;p&gt;Taking a look at the &lt;code&gt;src/main/java/io/github/nandandesai/pico/security&lt;/code&gt; subdirectory of the project, we see that it uses JWT.&lt;/p&gt;
&lt;p&gt;Interestingly, the file &lt;code&gt;SecretGenerator.java&lt;/code&gt; in the aforementioned directory contains a weak hardcoded &lt;em&gt;&amp;ldquo;random&amp;rdquo;&lt;/em&gt; value 😱.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SecretGenerator&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; Logger logger &lt;span style="color:#f92672"&gt;=&lt;/span&gt; LoggerFactory.&lt;span style="color:#a6e22e"&gt;getLogger&lt;/span&gt;(SecretGenerator.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; String SERVER_SECRET_FILENAME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;server_secret.txt&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; UserDataPaths userDataPaths;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; String &lt;span style="color:#a6e22e"&gt;generateRandomString&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; len) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// not so random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;1234&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; String &lt;span style="color:#a6e22e"&gt;getServerSecret&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; String secret &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; String(FileOperation.&lt;span style="color:#a6e22e"&gt;readFile&lt;/span&gt;(userDataPaths.&lt;span style="color:#a6e22e"&gt;getCurrentJarPath&lt;/span&gt;(), SERVER_SECRET_FILENAME), Charset.&lt;span style="color:#a6e22e"&gt;defaultCharset&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger.&lt;span style="color:#a6e22e"&gt;info&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Server secret successfully read from the filesystem. Using the same for this runtime.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; secret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (IOException e){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger.&lt;span style="color:#a6e22e"&gt;info&lt;/span&gt;(SERVER_SECRET_FILENAME&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; file doesn&amp;#39;t exists or something went wrong in reading that file. Generating a new secret for the server.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; String newSecret &lt;span style="color:#f92672"&gt;=&lt;/span&gt; generateRandomString(32);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FileOperation.&lt;span style="color:#a6e22e"&gt;writeFile&lt;/span&gt;(userDataPaths.&lt;span style="color:#a6e22e"&gt;getCurrentJarPath&lt;/span&gt;(), SERVER_SECRET_FILENAME, newSecret.&lt;span style="color:#a6e22e"&gt;getBytes&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (IOException ex) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ex.&lt;span style="color:#a6e22e"&gt;printStackTrace&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger.&lt;span style="color:#a6e22e"&gt;info&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Newly generated secret is now written to the filesystem for persistence.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; newSecret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This string &amp;ldquo;1234&amp;rdquo; is used as the secret for the JSON web token.&lt;/p&gt;</description></item><item><title>Java Script Kiddie 2</title><link>https://lavafroth.is-a.dev/post/picoctf-web-java-script-kiddie-2/</link><pubDate>Fri, 03 Mar 2023 09:47:54 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-web-java-script-kiddie-2/</guid><description>&lt;h2 id="the-challenge"&gt;The challenge&lt;/h2&gt;
&lt;p&gt;This is a web challenge involving javascript, meaning most of the solution is
going to be client side. We are asked to visit the &lt;a href="http://jupiter.challenges.picoctf.org:42899/"&gt;challenge
page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From here, we can view the source code of the page.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;jquery-3.3.1.min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;bytes&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Array.&lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34; &amp;#34;&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; =&amp;gt; Number(&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;assemble_png&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;u_in&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LEN&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;00000000000000000000000000000000&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;shifter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;u_in&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;					&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;u_in&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LEN&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;++&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;					&lt;span style="color:#a6e22e"&gt;shifter&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Number(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;slice&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;),(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;					&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;j&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;j&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LEN&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;j&lt;/span&gt; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;						&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;[(&lt;span style="color:#a6e22e"&gt;j&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LEN&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;[(((&lt;span style="color:#a6e22e"&gt;j&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;shifter&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LEN&lt;/span&gt;) &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bytes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;					}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;					&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;slice&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				document.&lt;span style="color:#a6e22e"&gt;getElementById&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Area&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data:image/png;base64,&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;btoa&lt;/span&gt;(String.&lt;span style="color:#a6e22e"&gt;fromCharCode&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Uint8Array&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;/&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&amp;lt;&lt;span style="color:#f92672"&gt;center&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&amp;lt;&lt;span style="color:#f92672"&gt;form&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;action&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onsubmit&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;assemble_png(document.getElementById(&amp;#39;user_in&amp;#39;).value)&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;user_in&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;				&amp;lt;&lt;span style="color:#f92672"&gt;input&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Submit&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&amp;lt;/&lt;span style="color:#f92672"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&amp;lt;&lt;span style="color:#f92672"&gt;img&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Area&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&amp;lt;/&lt;span style="color:#f92672"&gt;center&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break it down. We are going to begin with the contents in the script
tags. First, the script fetches a blob of whitespace separated numbers into the
variable &lt;code&gt;bytes&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Some Assembly Required 3</title><link>https://lavafroth.is-a.dev/post/picoctf-web-some-assembly-required-3/</link><pubDate>Thu, 09 Feb 2023 16:39:08 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-web-some-assembly-required-3/</guid><description>&lt;p&gt;This is a web exploitation challenge from 2021. It&amp;rsquo;s pretty old but
has less solves as of writing this post. I figured, it&amp;rsquo;s worth talking
about.&lt;/p&gt;
&lt;p&gt;We are told to visit
&lt;a href="http://mercury.picoctf.net:60022/index.html"&gt;http://mercury.picoctf.net:60022/index.html&lt;/a&gt;
where we find a simple textbox prompting us to submit the flag.&lt;/p&gt;
&lt;p&gt;Looking at the page source by pressing &lt;code&gt;ctrl&lt;/code&gt; &lt;code&gt;u&lt;/code&gt;, we see that it is sourcing javascript code from &lt;code&gt;rTEuOmSfG3.js&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;rTEuOmSfG3.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While examining the javascript, we will notice that it is obfuscated and
packed. Put this through &lt;a href="https://lelinhtinh.github.io/de4js/"&gt;de4js&lt;/a&gt; to
prettify it.&lt;/p&gt;</description></item><item><title>Kringlecon 2022 Writeup</title><link>https://lavafroth.is-a.dev/post/kringlecon-2022-writeup/</link><pubDate>Mon, 09 Jan 2023 10:36:35 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/kringlecon-2022-writeup/</guid><description>&lt;p&gt;This writeup is rather haphazard as I jumped around from one place to another
solving different unrelated challenges. Although the writeup covers all the
challenges, it definitely is not sequential. Just wanted to point that out
before diving in.&lt;/p&gt;
&lt;h3 id="clone-with-a-difference"&gt;Clone with a Difference&lt;/h3&gt;
&lt;p&gt;This challenge wants us to clone a git repository. It&amp;rsquo;s using git with ssh for
cloning which doesn&amp;rsquo;t seem to work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone git@haugfactory.com:asnowball/aws_scripts.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can clone this the HTTPS way:&lt;/p&gt;</description></item><item><title>Pixelated</title><link>https://lavafroth.is-a.dev/post/picoctf-cryptography-pixelated/</link><pubDate>Tue, 22 Nov 2022 09:25:20 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-cryptography-pixelated/</guid><description>&lt;p&gt;This challenge gives use two images and asks us if we can make a flag out of them.
At first glance, both the images look like noise. Upon a quick web lookup of
&lt;a href="https://en.wikipedia.org/wiki/Visual_cryptography"&gt;visual cryptography&lt;/a&gt;, it appears
that these separate images, known as shares of the original image, can be overlayed
on each other to reconstruct the original image.&lt;/p&gt;
&lt;h2 id="exploration"&gt;Exploration&lt;/h2&gt;
&lt;p&gt;Now, I&amp;rsquo;m pretty sure that there are online services that will automatically solve these
but I decided to write some code to solve this locally. For the past week, I&amp;rsquo;ve been
learning the Rust programming language and this was the perfect excuse to test my knowledge.&lt;/p&gt;</description></item><item><title>Treebox</title><link>https://lavafroth.is-a.dev/post/google-ctf-2022-treebox/</link><pubDate>Fri, 19 Aug 2022 10:04:36 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/google-ctf-2022-treebox/</guid><description>&lt;p&gt;This challenge asks for python code as an input, converts it into an AST (abstract syntax tree) and if there aren&amp;rsquo;t any function calls or imports, executes the code.&lt;/p&gt;
&lt;p&gt;Our goal here is to avoid explicitly calling any functions yet reading the flag located at &lt;code&gt;flag&lt;/code&gt;. We also can&amp;rsquo;t import any modules explicitly.&lt;/p&gt;
&lt;p&gt;The source code provided with the challenge imports the &lt;code&gt;sys&lt;/code&gt; module giving us an opportunity to chain its functionalities.&lt;/p&gt;</description></item><item><title>I Saw a Little Elf</title><link>https://lavafroth.is-a.dev/post/r0-i-saw-a-little-elf/</link><pubDate>Fri, 19 Aug 2022 09:57:34 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/r0-i-saw-a-little-elf/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This challenge asks us to connect to an webpage with a base64 encoded message.
If we try to decode the message manually, the decoded message ends up either in a reversed ELF (Executable and Linkable Format) binary or more base64 to be decoded.&lt;/p&gt;
&lt;p&gt;Trying this multiple times, it becomes apparent that the challenge reverses an ELF binary, encodes it one or more times in base64 and sends it to us.
If we run the executable, we are given a string which we must send to the challenge endpoint through the &lt;code&gt;r&lt;/code&gt; HTTP GET query parameter. The real challenge is to do this within the few seconds before the server resets the challenge.&lt;/p&gt;</description></item><item><title>RingZer0 CTF Hash Me Reloaded</title><link>https://lavafroth.is-a.dev/post/r0-hash-me-reloaded/</link><pubDate>Fri, 19 Aug 2022 09:57:15 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/r0-hash-me-reloaded/</guid><description>&lt;p&gt;In this RingZer0 challenge, we are to visit the challenge url where we are
given 2 seconds to SHA512 hash the message represented by the binary provided
string. We must send the response with the request parameter &lt;code&gt;r&lt;/code&gt;. Let&amp;rsquo;s write
a go program to do that.&lt;/p&gt;
&lt;p&gt;First let&amp;rsquo;s declare the url as a constant.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;uri&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;http://challenges.ringzer0team.com:10014/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We fetch the challenge page and defer closing its body once the program ends.&lt;/p&gt;</description></item><item><title>Hash Me Please</title><link>https://lavafroth.is-a.dev/post/r0-hash-me-please/</link><pubDate>Fri, 19 Aug 2022 09:57:00 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/r0-hash-me-please/</guid><description>&lt;p&gt;In this RingZer0 challenge, we are asked to visit
&lt;a href="http://challenges.ringzer0team.com:10013/"&gt;http://challenges.ringzer0team.com:10013/&lt;/a&gt; and are given 2 seconds to hash the
provided message using the SHA512 algorithm. We must send the response as
&lt;a href="http://challenges.ringzer0team.com:10013/?r=response"&gt;http://challenges.ringzer0team.com:10013/?r=&lt;em&gt;response&lt;/em&gt;&lt;/a&gt;
and to do that, we&amp;rsquo;ll be using some Golang.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s declare the URI as a constant.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;uri&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;http://challenges.ringzer0team.com:10013/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We fetch the challenge page using the &lt;code&gt;Get&lt;/code&gt; function from the &lt;code&gt;http&lt;/code&gt; standard
library, checking for errors along the way.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;uri&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fatalln&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We defer closing the response body when the program ends.&lt;/p&gt;</description></item><item><title>Oh my God, they killed Kenny!</title><link>https://lavafroth.is-a.dev/post/oh-my-god-they-killed-kenny/</link><pubDate>Tue, 02 Aug 2022 09:26:51 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/oh-my-god-they-killed-kenny/</guid><description>&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Despite its infamy for profanity and dark, satiric humor, I&amp;rsquo;ve been a huge fan of South Park over the years. I&amp;rsquo;d like you to try out a random episode of &lt;a href="https://www.southparkstudios.com"&gt;South Park&lt;/a&gt;.
Before you walk away saying, &lt;a href="https://www.youtube.com/watch?v=RXS1sJm7QEA"&gt;&amp;ldquo;Screw you guys, I&amp;rsquo;m going home&amp;rdquo;&lt;/a&gt;, I&amp;rsquo;ll be sharing a little trick to watch a random episode without even launching the browser.&lt;/p&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nushell.sh"&gt;Nushell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mpv.io"&gt;mpv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;youtube-dl&lt;/code&gt; or &lt;a href="https://github.com/yt-dlp/yt-dlp"&gt;yt-dlp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="gone-scripting"&gt;Gone scripting&lt;/h1&gt;
&lt;p&gt;South Park&amp;rsquo;s official website has a route called &lt;code&gt;random-episode&lt;/code&gt; which redirects us to, well, a random episode.
The redirection, however, is done using javascript instead of regular HTTP status codes like 302.
This meant, one couldn&amp;rsquo;t simply run the following and expect to see a video.&lt;/p&gt;</description></item><item><title>Bash Jail 3</title><link>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail3/</link><pubDate>Sun, 24 Jul 2022 12:29:56 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail3/</guid><description>&lt;h1 id="the-challenge"&gt;The challenge&lt;/h1&gt;
&lt;p&gt;Logging into the box we are told that the flag is located at &lt;code&gt;/home/level3/flag.txt&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; check_space &lt;span style="color:#f92672"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $1 &lt;span style="color:#f92672"&gt;==&lt;/span&gt; *&lt;span style="color:#f92672"&gt;[&lt;/span&gt;bdksc&lt;span style="color:#f92672"&gt;]&lt;/span&gt;* &lt;span style="color:#f92672"&gt;]]&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; : 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Your input:&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; read input 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; check_space &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$input&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo -e &lt;span style="color:#e6db74"&gt;&amp;#39;\033[0;31mRestricted characters has been used\033[0m&amp;#39;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;$input&lt;span style="color:#e6db74"&gt;`&lt;/span&gt; &amp;amp;&amp;gt;/dev/null 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Command executed&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are also told that this prompt is launched using &lt;code&gt;./prompt.sh 2&amp;gt;/dev/null&lt;/code&gt;
which means we cannot exfiltrate the flag from &lt;code&gt;stderr&lt;/code&gt; since it is blocked.&lt;/p&gt;</description></item><item><title>Bash Jail 2</title><link>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail2/</link><pubDate>Sun, 24 Jul 2022 12:28:56 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail2/</guid><description>&lt;h1 id="the-challenge"&gt;The challenge&lt;/h1&gt;
&lt;p&gt;Logging into the box we are told that the flag is located at &lt;code&gt;/home/level2/flag.txt&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="challenge-bash-code"&gt;Challenge bash code&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; check_space &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; $1 &lt;span style="color:#f92672"&gt;==&lt;/span&gt; *&lt;span style="color:#f92672"&gt;[&lt;/span&gt;bdks&lt;span style="color:#e6db74"&gt;&amp;#39;;&amp;#39;&amp;#39;&amp;amp;&amp;#39;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;* &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; :
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Your input:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; read input
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; check_space &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$input&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo -e &lt;span style="color:#e6db74"&gt;&amp;#39;\033[0;31mRestricted characters has been used\033[0m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;echo Your command is: &lt;/span&gt;$input&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; eval $output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="inference"&gt;Inference&lt;/h1&gt;
&lt;p&gt;This time, the &lt;code&gt;check_space&lt;/code&gt; function returns a &lt;code&gt;1&lt;/code&gt; if there are any characters in the input
string among &lt;code&gt;b&lt;/code&gt;,&lt;code&gt;d&lt;/code&gt;,&lt;code&gt;k&lt;/code&gt;,&lt;code&gt;s&lt;/code&gt;, a semicolon, an ampersand and a whitespace. If the function does
return 1, we get a &amp;ldquo;restricted characters&amp;rdquo; message and no further processing happens.&lt;/p&gt;</description></item><item><title>Bash Jail 1</title><link>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail1/</link><pubDate>Sun, 24 Jul 2022 12:27:56 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/ringzer0ctf-bash-jail1/</guid><description>&lt;h1 id="the-challenge"&gt;The challenge&lt;/h1&gt;
&lt;p&gt;Upon SSHing into the box, we are told that the flag is located at &lt;code&gt;/home/level1/flag.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Challenge bash code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; :
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Your input:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; read input
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;$input&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="inference-and-experimenation"&gt;Inference and experimenation&lt;/h1&gt;
&lt;p&gt;The script is reading an input, executes it and then stores it in the
&lt;code&gt;output&lt;/code&gt; variable without ever displaying the output to the console.&lt;/p&gt;
&lt;p&gt;I tried a dummy command to see if I could see its &lt;code&gt;stderr&lt;/code&gt; since command
substitution (backticks) only capture the &lt;code&gt;stdout&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Hi, this is Himadri!</title><link>https://lavafroth.is-a.dev/about/</link><pubDate>Sat, 23 Jul 2022 19:11:10 +0530</pubDate><guid>https://lavafroth.is-a.dev/about/</guid><description>&lt;p&gt;I&amp;rsquo;m a self taught programmer and a digital artist. You might have arrived here
from &lt;a href="https://youtube.com/@lavafroth"&gt;my YouTube channel&lt;/a&gt; or my open source
work. I&amp;rsquo;m doing a bachelors in data science at IITM.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m decent with programming languages like C and Java, the latter of which was
taught in school. I&amp;rsquo;m &lt;em&gt;great&lt;/em&gt; at writing Golang and Rust. I render my YouTube
videos with Python and the manim framework, so I&amp;rsquo;d argue I&amp;rsquo;m competent at it
as well.&lt;/p&gt;</description></item><item><title>Truce</title><link>https://lavafroth.is-a.dev/art/tyler-joseph-portrait/</link><pubDate>Sat, 23 Jul 2022 19:07:32 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/tyler-joseph-portrait/</guid><description>&lt;p&gt;A painting of the lead vocalist of Twenty Øne Piløts, named
after one of my favorite songs from their album Vessel.&lt;/p&gt;</description></item><item><title>She's a Rebel</title><link>https://lavafroth.is-a.dev/art/shes-a-rebel/</link><pubDate>Sun, 17 Apr 2022 17:01:44 +0530</pubDate><guid>https://lavafroth.is-a.dev/art/shes-a-rebel/</guid><description>&lt;p&gt;Clearly the title was an afterthought.&lt;/p&gt;</description></item><item><title>Operation Oni, Operation Orchid</title><link>https://lavafroth.is-a.dev/post/picoctf-forensics-operation-oni-operation-orchid/</link><pubDate>Fri, 18 Mar 2022 07:10:17 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-forensics-operation-oni-operation-orchid/</guid><description>&lt;p&gt;In this post, we&amp;rsquo;ll walk through the Operation Oni and Operation Orchid challenges
from the PicoCTF competition held in March 2022. Both of these challenges involve
the use of tools from The Sleuth Kit suite. In order to follow along, I&amp;rsquo;d recommend
installing the suite of tools.&lt;/p&gt;
&lt;h1 id="operation-oni"&gt;Operation Oni&lt;/h1&gt;
&lt;p&gt;The challenge has an associated instance which we&amp;rsquo;ll need to log into using SSH using
the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -i key_file -p &lt;span style="color:#ae81ff"&gt;61948&lt;/span&gt; ctf-player@saturn.picoctf.net
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are provided with a compressed disk image &lt;code&gt;disk.img.gz&lt;/code&gt; which we&amp;rsquo;ll decompress with:&lt;/p&gt;</description></item><item><title>JAuth</title><link>https://lavafroth.is-a.dev/post/picoctf-web-challenge-jauth/</link><pubDate>Tue, 22 Feb 2022 14:49:34 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-web-challenge-jauth/</guid><description>&lt;p&gt;The challenge description states that most web application developers use third party components without testing their security.
It mentions some past affected companies, then asks us to identify and exploit the vulnerable component for the challenge at &lt;a href="http://saturn.picoctf.net:52025/"&gt;http://saturn.picoctf.net:52025/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The goal is to become an &lt;code&gt;admin&lt;/code&gt;.
We are provied with the username &lt;code&gt;test&lt;/code&gt; and the password &lt;code&gt;Test123!&lt;/code&gt; to look around.&lt;/p&gt;
&lt;p&gt;The challenge is a dummy bank portal. On login, we see the message:&lt;/p&gt;</description></item><item><title>Liberating 14GiB of disk space</title><link>https://lavafroth.is-a.dev/post/liberating-14gib-of-space/</link><pubDate>Mon, 21 Feb 2022 13:15:26 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/liberating-14gib-of-space/</guid><description>&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove all duplicates, including zero length files&lt;/li&gt;
&lt;li&gt;Fine tuning: Hand-pick and remove files deemed unnecessary&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since the mileage for second step might vary from person to person, I&amp;rsquo;ll elaborate on the first step.&lt;/p&gt;
&lt;p&gt;I chose &lt;a href="https://codeberg.org/jbruchon/jdupes"&gt;jdupes&lt;/a&gt; for deleting the duplicates because it&amp;rsquo;s open-source and is cross platform.&lt;/p&gt;
&lt;p&gt;For a given folder we would run the following to wipe the duplicates:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jdupes --recurse --delete --no-prompt --zero&lt;span style="color:#f92672"&gt;-match&lt;/span&gt; .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will recursively delete all the duplicates except the source file without prompting for a confirmation.
It will also consider zero length files to be duplicates.&lt;/p&gt;</description></item><item><title>Notepad</title><link>https://lavafroth.is-a.dev/post/picoctf-web-challenge-notepad/</link><pubDate>Mon, 21 Feb 2022 09:24:30 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/picoctf-web-challenge-notepad/</guid><description>&lt;p&gt;At first glance the webapp looks like a stripped down version of Pastebin where we can post a text / code snippet.
After submitting the query, we are redirected to an html page containing the content of the post.&lt;/p&gt;
&lt;p&gt;The first thing I tried was triggering XSS (cross site scripting) with the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;&lt;span style="color:#a6e22e"&gt;alert&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The application source directory tree looks like the following:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;.
├── app.py
├── Dockerfile
├── flag.txt
├── static
└── templates
 ├── errors
 │ ├── bad_content.html
 │ └── long_content.html
 └── index.html
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s inspect the &lt;code&gt;app.py&lt;/code&gt; source.&lt;/p&gt;</description></item><item><title>Gadgeting in Python Jails</title><link>https://lavafroth.is-a.dev/post/gadgeting-in-python-jails/</link><pubDate>Thu, 09 Dec 2021 09:52:29 +0530</pubDate><guid>https://lavafroth.is-a.dev/post/gadgeting-in-python-jails/</guid><description>&lt;p&gt;We&amp;rsquo;ve all been there. That one CTF that wants to test your object oriented skills by confining you to a python jail.
Additionally some might even keep &lt;code&gt;builtins&lt;/code&gt; and &lt;code&gt;eval&lt;/code&gt; out of reach.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=SN6EVIG4c-0"&gt;Here&lt;/a&gt; is a cool video explanation by @pwnfunction on server side template
injection wherein he mentions a way to &amp;ldquo;gadget&amp;rdquo; our way out of Flask&amp;rsquo;s Jinja2 backend to get remote code execution.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the gist of it:&lt;/p&gt;</description></item><item><title/><link>https://lavafroth.is-a.dev/webapps/easy-ssh-tunnel/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lavafroth.is-a.dev/webapps/easy-ssh-tunnel/</guid><description>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
 &lt;meta charset="UTF-8" /&gt;
 &lt;title&gt;Easy SSH tunnel&lt;/title&gt;
 &lt;meta name="viewport" content="width=device-width,initial-scale=1" /&gt;
 &lt;meta name="description" content="" /&gt;
 &lt;style&gt;
 * { font-family: monospace; }
 body {
 --purple-dark: oklch(29.3% 0.136 325.661);
 --purple-light: lab(78.5378% 39.3533 -32.9615);
 	background: var(--purple-dark);
 }

 input {
 	outline: none;
 	color: #eee;
 	border: none;
 	background: var(--purple-dark);
 }

 .post-content &gt; div {
 	margin: 1rem auto;

 	label:not([for=direction]) {
 		padding: .5rem;
 		background: var(--purple-light);
 color: var(--purple-dark);
 	}
 }

 .grid {
 	display: grid;
 	grid-template-columns: repeat(3, minmax(0, 1fr));
 	grid-template-rows: repeat(3, minmax(0, 1fr));
 	 * { text-align: center; }

 	label[for=direction] {
 			user-select: none;
 			grid-row: span 2 / span 2;
 grid-column-start: 2;
 			color: #eee;
 			&amp;::after {
 				display: block;
 				content: '&lt;';
 			}
 		}

 		input[type="checkbox"] {
 opacity: 0;
 position: absolute;
 pointer-events: none;
 		}

 	&amp;:has(input:checked) {
 		label[for=direction]::after {
 			content: '&gt;';
 		}
 	}
 }

 #remotehost {
			grid-column: span 2 / span 2;
 }

 #command {
 		background: oklch(45.2% 0.211 324.591);
 		margin-top: 1rem;
 		padding-inline: 1rem;
 		color: #eee;
 }

 #copy {
 background: var(--purple-light);
 color: var(--purple-dark);
 padding-inline: 1rem;
 float: right;
 &amp;:active {
 background: oklch(45.2% 0.211 324.591);
 color: #eee;
 }
 }
 &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

 &lt;div class="grid"&gt;

 &lt;label for="localport"&gt;Local port&lt;/label&gt;
 &lt;label for="direction"&gt;Traffic direction&lt;/label&gt;
 &lt;label for="remoteport"&gt;Remote port&lt;/label&gt;

 &lt;input id="localport" type="number" min="0" max="65535" step=1 placeholder=8888&gt;
 &lt;input type=checkbox id="direction"&gt;
 &lt;input id="remoteport" type="number" min="0" max="65535" step=1 placeholder=8888&gt;
 &lt;label for="remotehost"&gt;Connect to&lt;/label&gt;
 &lt;input id="remotehost" type="text" placeholder="user@remote.host"&gt;
 &lt;/div&gt;

 &lt;div id='command'&gt;&lt;/div&gt;
 &lt;button id='copy'&gt;⧉ Copy&lt;/button&gt;
 &lt;/div&gt;
&lt;/body&gt;

&lt;script&gt;
 const localPort = document.querySelector('#localport');
 const remotePort = document.querySelector('#remoteport');
 const remoteHost = document.querySelector('#remotehost');
 const direction = document.querySelector('#direction');
 const command = document.querySelector('#command');
 const copy = document.querySelector('#copy');
 const valueOrPlaceholder = (v) =&gt; v.value || v.placeholder;

 function updateCommand() {
 const directionString = direction.checked ? "R" : "L";
 command.textContent = `ssh -f -N -${directionString} ${valueOrPlaceholder(localPort)}:localhost:${valueOrPlaceholder(remotePort)} ${valueOrPlaceholder(remoteHost)}`;
 }

 function validatePort(e) {
 const code = e.keyCode;
 if (code &lt; 48 || code &gt; 57 || e.target.value * 10 + (code - 48) &gt; 65535) {
 e.preventDefault()
 }
 }

 for (const variable of [localPort, direction, remoteHost, remotePort]) {
 variable.addEventListener('input', updateCommand);
 }

 for (const portVariable of [localPort, remotePort]) {
 portVariable.addEventListener('keypress', validatePort);
 }

 copy.addEventListener('click', (_) =&gt; navigator.clipboard.writeText(command.textContent))


 updateCommand()
 &lt;/script&gt;
&lt;/html&gt;</description></item><item><title/><link>https://lavafroth.is-a.dev/webapps/project_mana/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://lavafroth.is-a.dev/webapps/project_mana/</guid><description>&lt;div id="stage"&gt;
&lt;/div&gt;
&lt;style&gt;
canvas {
 background: transparent;
 border-radius: 1rem;
 margin-bottom: 1rem;
}
&lt;/style&gt;
&lt;script type="frag" id="evolve-vert"&gt;
varying vec2 vUv;

void main() {
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
 vUv = gl_Position.xy / gl_Position.w;
 vUv = (vUv + 1.0) * 0.5;
}
&lt;/script&gt;
&lt;script type="frag" id="evolve-frag"&gt;
varying vec2 vUv;

uniform sampler2D gbufferMask;
uniform sampler2D initBufferMask;

void main() {
 gl_FragColor = vec4(texture(initBufferMask, vUv));
}

&lt;/script&gt;
&lt;script type="frag" id="outline-frag"&gt;
 varying vec2 vUv;
 varying vec2 clipMeshCenter;
 varying vec2 glPos;

 uniform float time;
 uniform vec2 viewportSize;

 #define LINE_WEIGHT 2.0

 uniform sampler2D gbufferMask;

 void main() {
 float dx = (1.0 / viewportSize.x) * LINE_WEIGHT;
 float dy = (1.0 / viewportSize.y) * LINE_WEIGHT;

 vec2 uvCenter = vUv;
 vec2 uvRight = vec2(uvCenter.x + dx, uvCenter.y);
 vec2 uvLeft = vec2(uvCenter.x - dx, uvCenter.y);
 vec2 uvTop = vec2(uvCenter.x, uvCenter.y - dy);
 vec2 uvTopRight = vec2(uvCenter.x + dx, uvCenter.y - dy);
 vec2 uvDown = vec2(uvCenter.x, uvCenter.y + dy);
 vec2 uvDownLeft = vec2(uvCenter.x - dx, uvCenter.y + dy);

 float mCenter = texture(gbufferMask, uvCenter).r;
 float mTop = texture(gbufferMask, uvTop).r;
 float mRight = texture(gbufferMask, uvRight).r;
 float mTopRight = texture(gbufferMask, uvTopRight).r;
 float mLeft = texture(gbufferMask, uvLeft).r;
 float mDown = texture(gbufferMask, uvDown).r;
 float mDownLeft = texture(gbufferMask, uvDownLeft).r;

 float dT = abs(mCenter - mTop);
 float dR = abs(mCenter - mRight);
 float dTR = abs(mCenter - mTopRight);
 float dD = abs(mCenter - mDown);
 float dL = abs(mCenter - mLeft);
 float dDL = abs(mCenter - mDownLeft);

 float delta = 0.0;
 delta = max(delta, dT);
 delta = max(delta, dR);
 delta = max(delta, dTR);
 delta = max(delta, dD);
 delta = max(delta, dL);
 delta = max(delta, dDL);

 float threshold = 0.0;
 float isOutline = clamp((delta * 2.0) - threshold, 0.0, 1.0);

 vec4 outline = vec4(isOutline);
 gl_FragColor = outline;
 }
&lt;/script&gt;
&lt;script type="vert" id="outline-vert"&gt;
 varying vec2 vUv;
 uniform vec3 meshCenter;
 varying vec2 clipMeshCenter;
 uniform float time;
 varying vec2 glPos;

 void main() {
 clipMeshCenter = (projectionMatrix * modelViewMatrix * vec4(meshCenter, 1.0)).xy;
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
 vUv = gl_Position.xy / gl_Position.w;
 vUv = (vUv + 1.0) * 0.5;
 glPos = gl_Position.xy;
 }
&lt;/script&gt;
&lt;script type="importmap"&gt;
 {
 "imports": {
 "three": "https://cdn.jsdelivr.net/npm/three@0.172.0/build/three.module.js",
 "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.172.0/examples/jsm/"
 }
 }
&lt;/script&gt;

&lt;script type="module"&gt;
import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
let renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
let width = document.querySelector('#stage').offsetWidth;
let height = Math.round(9/16 * width);
renderer.setSize(width, height);
document.querySelector('#stage').appendChild(renderer.domElement);

function get(path) {
 return document.querySelector('#' + path).textContent;
};

const scene = new THREE.Scene();
const solidScene = new THREE.Scene();
const maskScene = new THREE.Scene();
const evolveScene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
const color = 0xFFFFFF;
let ambientLight = new THREE.AmbientLight(color, dark ? 1: 10);
solidScene.add(ambientLight);
let light = new THREE.PointLight(color, 200);
light.position.set(10, 10, 15);
solidScene.add(light);
camera.position.set(6,8,14);

let buffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})
let outlineBuffer = new THREE.WebGLRenderTarget(width, height, {format: THREE.RGBAFormat})

const orbit = new OrbitControls(camera, renderer.domElement);
orbit.update();

const geometry = new THREE.TorusGeometry( 10, 3, 20, 100 );
geometry.translate(2, 2, 2);

const shadowMesh = new THREE.Mesh(geometry);
const uniforms = {
 gbufferMask: { value: buffer.texture },
 viewportSize: { value: new THREE.Vector2(width, height) },
}

const material = new THREE.ShaderMaterial({
 vertexShader: get('outline-vert'),
 fragmentShader: get('outline-frag'),
 uniforms,
 transparent: true,
});

let solidMeshMaterial = new THREE.MeshPhysicalMaterial({
 color: dark ? 0xaaeadb : 0xffffff,
 metalness: 0.8,
 clearcoat: 0.4,
 clearcoatRoughness: 0.1,
});

const solidMesh = new THREE.Mesh(
 geometry,
 solidMeshMaterial,
);

const mesh = new THREE.Mesh(
 geometry,
 material
);
solidScene.add(solidMesh);
scene.add(mesh);
maskScene.add(shadowMesh);


const evoUniforms = {
 initBufferMask: { value: null },
}

const evoMaterial = new THREE.ShaderMaterial({
 // this is a copy shader
 vertexShader: get('evolve-vert'),
 fragmentShader: get('evolve-frag'),
 uniforms: evoUniforms,
 transparent: true,
});

const evolveMesh = new THREE.Mesh(
 geometry,
 evoMaterial
);

evolveScene.add(evolveMesh)

function continuity(bitmap, width, height) {
 const visited = Array.from({length: height}, () =&gt; Array(width).fill(false));

 function valid(row, col) {
 return row &gt;= 0 &amp;&amp; row &lt; height &amp;&amp; col &gt;= 0 &amp;&amp; col &lt;= width
 }

 function valid_1(row, col) {
 return valid(row, col) ? 1 : 0
 }

 // for a point to be on the screen edge, it must have at least three invalid neighbors.
 // It must have at least six valid neighbors.
 function isSentinel(row, col) {
 return (
 valid_1(row - 1, col - 1) +
 valid_1(row - 1, col) +
 valid_1(row - 1, col + 1) +

 valid_1(row, col - 1) +
 valid_1(row, col + 1) +

 valid_1(row + 1, col - 1) +
 valid_1(row + 1, col) +
 valid_1(row + 1, col + 1)
 ) &lt; 6
 }

 var sentinels = [];
 var cyclic = [];

 function dfs_queue(rootRow, rootCol) {
 var stack = [];
 stack.push([rootRow, rootCol]);

 var steps = 0;

 for(; ; steps++) {
 let p = stack.pop();
 if (p === undefined) {
 return
 }
 let [row, col] = p
 if (isSentinel(row, col)) {
 sentinels.push([row, col]);
 return
 }

 if (steps &gt; 0 &amp;&amp; row == rootRow &amp;&amp; col == rootCol) {
 cyclic.push([row, col])
 return
 }

 if (!valid(row, col) || visited[row][col]) {
 return
 }

 // is this point switched off?
 visited[row][col] = true;
 var point = 4 * (row * width + col);
 if (bitmap[point] == 0 &amp;&amp; bitmap[point+1] == 0 &amp;&amp; bitmap[point+2] == 0) {
 continue;
 }

 stack.push([row + 1, col])
 stack.push([row, col + 1])
 stack.push([row - 1, col])
 stack.push([row, col - 1])

 }
 }

 for (let row = 0; row &lt; height; row++) {
 for(let col = 0; col &lt; width; col++) {
 if (visited[row][col]) {
 continue;
 }
 var point = 4 * (row * width + col);
 if (bitmap[point] == 1 &amp;&amp; bitmap[point+1] == 1 &amp;&amp; bitmap[point+2] == 1) {
 dfs_queue(row, col);
 }
 }
 }

 // Always prefer the sentinels to the cyclics
 // since walking back a line also ends up in a cycle.
 // Start waveforms from the sentinels (endpoints),
 // mark all the visited points. If there remain unvisited
 // points, those are legitimately parts of cyclic paths.
 return sentinels.concat(cyclic);
}

var durationInSeconds = 5;

// @param {Float32Array[]} points
// @param {Uint32Array} allThePixels
// Number all the points as we stumble along the outline.
// Akin to a wavefront. The pixels switched on (value = 1)
// touching the wavefront at time t will have a value t + 2
//
// This way we can quickly zero out all the pixels below a
// threshold when timing the animation.
function dijkstraNumber(points, buf) {
 var longestStrand = 2;
 points.forEach((point) =&gt; {
 longestStrand = Math.max(longestStrand, dijkstraPropagate(point, buf, 2))
 })
 return longestStrand
}

function dijkstraPropagate(point, buf, value) {
 var queue = [[point]];
 for (; ;) {
 let neighbors = queue.pop()
 if (neighbors === undefined) {
 return value
 }
 neighbors.forEach(([row, col]) =&gt; {
 let pos = 4 * (row * buffer.width + col);
 if (buf[pos] != 1) {
 return
 }

 buf[pos] = value;
 buf[pos+1] = value;
 buf[pos+2] = value;
 buf[pos+3] = value;
 value += 1

 // package all the neighboring points and
 // push them onto the stack
 queue.push([
 [row + 1, col],
 [row + 1, col + 1],
 [row, col + 1],
 [row - 1, col + 1],
 [row - 1, col],
 [row - 1, col - 1],
 [row, col - 1],
 [row + 1, col - 1]
 ])
 })
 }
}

const clock = new THREE.Clock();
var longestPixelStrand = 0;
var init = true;
const allThePixels = new Uint8Array(buffer.width * buffer.height * 4);
const dijkstraBuffer = new Uint32Array(buffer.width * buffer.height * 4);
function animate() {

 if (init) {
 renderer.setRenderTarget(buffer);
 renderer.render(maskScene, camera);

 renderer.setRenderTarget(outlineBuffer);
 renderer.render(scene, camera);

 renderer.readRenderTargetPixels(outlineBuffer, 0, 0, buffer.width, buffer.height, allThePixels);
 for (let i = 0; i &lt; allThePixels.length; i++) {
 dijkstraBuffer[i] = allThePixels[i] == 255 ? 1 : 0;
 }

 		let points = continuity(dijkstraBuffer, width, height)
 		longestPixelStrand = dijkstraNumber(points, dijkstraBuffer)

 let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
 points.forEach((point) =&gt; {
 let pos = 4 * (point[0] * buffer.width + point[1]);
 initBuffer[pos] = 255;
 initBuffer[pos+1] = 255;
 initBuffer[pos+2] = 255;
 initBuffer[pos+3] = 255;
 })

 let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
 ephemeralTex.needsUpdate = true;
 evoUniforms.initBufferMask.value = ephemeralTex;
 init = false;
 }
		let fractionAnimated = (clock.getElapsedTime() % durationInSeconds) / durationInSeconds;
		let pixelsAnimated = Math.round(longestPixelStrand * fractionAnimated);
 let initBuffer = new Uint8Array(buffer.width * buffer.height * 4);
 for (let row = 0; row &lt; height; row++) {
 for(let col=0; col&lt;width; col++) {
 var point = 4 * (row * width + col);
 if (dijkstraBuffer[point] &gt; 1 &amp;&amp; dijkstraBuffer[point] &lt; pixelsAnimated) {
 initBuffer[point] = dark ? 255: 22;
 initBuffer[point+1] = dark ? 255: 22;
 initBuffer[point+2] = dark ? 255 : 22;
 initBuffer[point+3] = 255;
 }
 }
 }
 let ephemeralTex = new THREE.DataTexture(initBuffer, width, height);
 ephemeralTex.needsUpdate = true;
 evoUniforms.initBufferMask.value = ephemeralTex;

 renderer.setRenderTarget(null);
 renderer.render(solidScene, camera);
 renderer.autoClear = false;
 renderer.clearDepth();
 renderer.render(evolveScene, camera);
 renderer.autoClear = true;
}

renderer.setAnimationLoop(animate);

orbit.addEventListener('change', function() {
 init = true;
})
&lt;/script&gt;</description></item></channel></rss>