<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title> Blog Posts on Monish Kumar&#39;s Blog</title>
        <link>https://itsmonish.pages.dev/blog/</link>
        <description>Recent content in  Blog Posts on Monish Kumar&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Fri, 20 Mar 2026 10:41:25 +0530</lastBuildDate><atom:link href="https://itsmonish.pages.dev/blog/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>JITFP - PicoCTF 2026 Challenge Writeup</title>
        <link>https://itsmonish.pages.dev/blog/jitfp-picoctf-2026/</link>
        <pubDate>Fri, 20 Mar 2026 10:41:25 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/jitfp-picoctf-2026/</guid>
        <description>&lt;p&gt;I took part in PicoCTF 2026, that happened between 9th March 2026 to 19th March 2026. I ranked at 570 globally and got a total score of 11,000 out of the maximum 14,500.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;picoctf2026-result&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t plan on writing writeups for this one. But there was one challenge that was so good, that I had to write about it.&lt;/p&gt;
&lt;h1 id=&#34;jitfp&#34;&gt;JITFP
&lt;/h1&gt;&lt;h2 id=&#34;challenge-description&#34;&gt;Challenge Description
&lt;/h2&gt;&lt;p&gt;If we can crack the password checker on this remote host, we will be able to infiltrate deeper into this criminal organization. The catch is it only functions properly on the host on which we found it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;challenge-description&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;The challenge is now moved to picoCTF gym, you can access it &lt;a class=&#34;link&#34; href=&#34;https://play.picoctf.org/practice/challenge/737?category=3&amp;amp;page=1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;here&lt;/a&gt;. In the live CTF, this challenge was for 500 points, which was the highest among all the other challenges (except for other 500 point challenge, of course).&lt;/p&gt;
&lt;h2 id=&#34;solution&#34;&gt;Solution
&lt;/h2&gt;&lt;p&gt;The description doesn&amp;rsquo;t tell us much or doesn&amp;rsquo;t give us any files to actually reverse engineer binaries. However, there is an instance associated with the challenge. Launching it gives you credentials for a SSH login on a remote host.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;logging-in&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Logging in, we are greeted with, uh well, a message. And put in a directory with only one binary. Assuming this is the target binary, I started to poke it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;first-runs&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;The binary takes an arguement input and does something with it. There is a loading bar like output that takes some time to complete before printing the incorrect message. I wanted to time it, so I used &lt;code&gt;time&lt;/code&gt; utility. It takes around 33 seconds to run once. This brings a thought, is this a timing side channel attack scenario? Perhaps if I give a correct character at a position it would speed up or slow down? I tried it but it still ran the same 33 seconds.&lt;/p&gt;
&lt;p&gt;So to move further, we have to pop open and look under the hood. Since the binary was in a SSH instance, I used &lt;code&gt;scp&lt;/code&gt; to make a local copy and then used it to analyze it locally. I loaded it in Binary Ninja and the main function looked like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;binja-main&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;We have a peculiar looking if statement, where if the condition is false, it calls another function, prints out &amp;ldquo;Incorrect&amp;rdquo; and then exits. The other flow is what we should be doing to get the correct message. We still need to know what the function &lt;code&gt;sub_401932&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;sub-function&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So basically, it prints out asterisk, sleeps and prints a new line and exits. Not that interesting except this is where the sleep happens.&lt;/p&gt;
&lt;p&gt;Going back to the if condition, it didn&amp;rsquo;t make much sense to me, so I decided to get a second opinion from IDA (yes I have multiple decompilers, they are all awesome in their own way).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;ida-main&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Now that is better. So we have one memory array that index into another memory array which has function pointers because whatever is in there is used to call a function, so the most sensible conclusion is that they are function pointers. So let&amp;rsquo;s take a look at the memory arrays. At the first layer memory array, that index into the function pointer array we have:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;4020-array&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So we have a bunch of indices indeed. Looking at the second array, the one which should have function pointers, we have:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;4120-array-ida&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;That don&amp;rsquo;t look like much. So I took a look at the same address in Binary Ninja:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/10.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;4120-array-binja&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;There is nothing here. So I started to look around for other things.&lt;/p&gt;
&lt;p&gt;Another important detail I found was that in the binary there are a lot of functions like these:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;check-funcs&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;There were functions like these for all the lowercase and uppercase alphabets, digits, underscore and curly braces. So these functions basically check if the given character is the one there are validating or not. So it is logical to assume that, these are the functions that would be present and called from the function pointer array.&lt;/p&gt;
&lt;p&gt;However that is empty. Or is it just uninitialized? Perhaps the function pointers are populated at runtime only? And then the title suddenly made sense, &amp;ldquo;Just In Time Function Pointers&amp;rdquo;, JITFP. So I just have to dump whatever that memory is in runtime and get the functions pointers. Easy, right?. Or that&amp;rsquo;s what I thought. But no, I can&amp;rsquo;t even run the thing in my local machine&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;failed-local-run&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Now, this shouldn&amp;rsquo;t really come as a surprise, because the description of challenge literally tells that we can only run it on the remote machine. So I have to rely on any debugging tools on the remote machine.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/13.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;no-tools-on-remote&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So, in terms of tools, there was none. Hence, the question became &amp;ldquo;how can I dump a memory of a live running process?&amp;rdquo;. Yes, the procfs at &lt;code&gt;/proc&lt;/code&gt;. Every process&amp;rsquo;s has it&amp;rsquo;s mapping of memory at &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/maps&lt;/code&gt; and actual memory contents at &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/mem&lt;/code&gt;. So I opened two SSH shells into the remote machine with tmux and saw if I could access the procfs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/14.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;procfs-map&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;I could. Now we just have to dump the memory contents into a file and analyze it. Turns out the file system is read-only. Great. So I thought of redirecting the memory to &lt;code&gt;xxd&lt;/code&gt; utility, copying the hex dump and then analyzing it. But before I could go further I wanted to check what kind of protection this binary has. Thankfully, &lt;code&gt;checksec&lt;/code&gt; was installed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/15.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;checksec-output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;We got PIE, Position Independent Executable. But this shouldn&amp;rsquo;t really be a problem as PIE just randomizes the base address of the virtual memory. If we had ASLR, then it would have been hell, because the entire layout of the virtual memory would be randomized, but thankfully no.&lt;/p&gt;
&lt;p&gt;Now all I had to do was put together a bunch of commands that would take the &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/maps&lt;/code&gt; as input and dump the required memory. Since the memory we are interested is in &lt;code&gt;0x4120&lt;/code&gt;, the content would be present at base + &lt;code&gt;0x4120&lt;/code&gt;. We only need to read 264 bytes of information because that&amp;rsquo;s how long the function pointer array was. Put these together, I had this script to dump stuff:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./ad7e550b aaaa &amp;amp; pid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$!;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;base&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat /proc/$pid/maps | grep -m1 &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r--p 00000000&amp;#34;&lt;/span&gt; | cut -d&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; -f1&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;target&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;printf &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%d&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16#&lt;/span&gt;$base &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;x4120&lt;span style=&#34;color:#66d9ef&#34;&gt;)))&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dd &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/proc/$pid/mem iflag&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;skip_bytes,count_bytes skip&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$target count&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;264&lt;/span&gt; 2&amp;gt;/dev/null | xxd -p;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gave an output like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/16.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;memory-dump&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;If I dumped the memory immediately, it is still uninititialized. So I had to add some sleep command to get some actual output. Right now it doesn&amp;rsquo;t look like much, but these do actually look like function pointers from a binary. I put together a small python script to parse this and print me the function offsets.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;dumped hex&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ptrs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [int&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_bytes(data[i : i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;], &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;little&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, len(data), &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;x&lt;span style=&#34;color:#f92672&#34;&gt;....&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;offsets &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [p &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; base &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; ptrs]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i, o &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; enumerate(offsets):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;idx &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;: offset 0x&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;o&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;x&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This prints out the offsets that are given by the addresses in the hexdump.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/17.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;function-offsets&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;And good enough, these offsets correspond to actual function offsets in the binary. So the only thing now to do is index this offsets using the first memory array and see in which order, which functions are being called. I extended the previous script a little to get that part done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;dumped hex&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;func_map &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;} &lt;span style=&#34;color:#75715e&#34;&gt;# A dictionary mapping which function offset correspond to which character&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dword_4020 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#75715e&#34;&gt;# The indices from the first memory array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ptrs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [int&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_bytes(data[i : i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;], &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;little&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, len(data), &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;x&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;offsets &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [p &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; base &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; ptrs]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flag &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; idx &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; dword_4020:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flag&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(func_map[offsets[idx]])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(flag))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Executing this gave:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/18.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;wrong-output-1&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Now that does not look like a flag to me. I checked my scripts and other things, everything seemed to be right. So I decided to do the same process one more time. And it gave me:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/19.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;wrong-output-2&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;An entirely different output. But why? I did everything the same, except for one thing. I adjusted the sleep duration in bash from 5 to 2, thinking I need to read the memory early. At this point I had no idea what to do and I almost gave up. It wasn&amp;rsquo;t until later it struck me, &amp;ldquo;&lt;strong&gt;what if the entirety of the function pointer array changes every second?&lt;/strong&gt;&amp;rdquo;. That would mean that only one memory address at a point of time would be the right one, the rest are just fillers.&lt;/p&gt;
&lt;p&gt;I modified the bash script I used to extract the memory from &lt;code&gt;procfs&lt;/code&gt; as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./ad7e550b aaaa &amp;amp; pid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$!; i&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;1; cat /proc/$pid/maps; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; $i -lt &lt;span style=&#34;color:#ae81ff&#34;&gt;35&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  base&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat /proc/$pid/maps | grep -m1 &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r--p 00000000&amp;#34;&lt;/span&gt; | cut -d&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; -f1&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  target&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;printf &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%d&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16#&lt;/span&gt;$base &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;x4120&lt;span style=&#34;color:#66d9ef&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  dd &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/proc/$pid/mem iflag&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;skip_bytes,count_bytes skip&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$target count&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;264&lt;/span&gt; 2&amp;gt;/dev/null | xxd -p
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  i&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$((&lt;/span&gt;$i&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sleep &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This dumped the memory address 33 times. I put 35 just to be sure, in case. So I put all those hex dumps in a text file and modified my python script to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;func_map &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;} &lt;span style=&#34;color:#75715e&#34;&gt;# A dictionary mapping which function offset correspond to which character&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dword_4020 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;] &lt;span style=&#34;color:#75715e&#34;&gt;# The indices from the first memory array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;parse_dump&lt;/span&gt;(line):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hex_str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; line&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    data &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bytes&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fromhex(hex_str)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [int&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_bytes(data[i : i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;], &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;little&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, len(data), &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lines &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dumps.txt&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; _ &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        l &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; _ &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(f&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;readline()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;strip(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lines&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(l))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;x&lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flag &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; second, line &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; enumerate(lines):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; second &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; len(dword_4020):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ptrs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parse_dump(line)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dword_4020[second]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ptr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ptrs[idx]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    offset &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ptr &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; base
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    char &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; func_map&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(offset, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;?&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flag&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(char)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;second &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;second&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;02d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;: idx=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;idx&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#x&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; offset=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;offset&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#x&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; char=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;char&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Flag: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(flag)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, there is a bit of a timing requirement to this challenge, as the first time I ran it, it was some random output. But when I ran it again, it worked. I got:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/jitfp/20.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;the-flag&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;I still didn&amp;rsquo;t get the &amp;lsquo;p&amp;rsquo; in &amp;lsquo;picoCTF&amp;rsquo; for some reason, but I could fill it, submit it and get the points. This was such a cool challenge, that I had to rethink everything again and again and again. And the part where the function pointer array getting randomized for every second execpt for the right index was absolutely devious. Loved it.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Barbwire: an eBPF based behavioral correlator</title>
        <link>https://itsmonish.pages.dev/blog/barbwire/</link>
        <pubDate>Sun, 08 Mar 2026 16:42:18 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/barbwire/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;After the eBPF learning phase I wrote about in the &lt;a class=&#34;link&#34; href=&#34;https://itsmonish.pages.dev/blog/learning-ebpf&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;last post&lt;/a&gt;, I wanted to build something that looked like a real security tool. Not because I thought I&amp;rsquo;d ship it, but because the programs I tried before where stupid. At some point you need a problem with actual constraints.&lt;/p&gt;
&lt;p&gt;Barbwire is that project. It&amp;rsquo;s a lightweight behavioral correlator for Linux. It watches for processes that open sensitive files and then make network connections within a short time window. This is classic exfil behavior and rough fingerprint for credential harvesting and so on. It&amp;rsquo;s not a production EDR. It&amp;rsquo;s an experiment. It&amp;rsquo;s on my &lt;a class=&#34;link&#34; href=&#34;https://github.com/ItsMonish/barbwire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Github&lt;/a&gt; if anyone&amp;rsquo;s interested.&lt;/p&gt;
&lt;h2 id=&#34;the-core-design-decision&#34;&gt;The core design decision
&lt;/h2&gt;&lt;p&gt;The first instinct when building something with eBPF is to put logic in the BPF side. It&amp;rsquo;s running in the kernel, it&amp;rsquo;s fast, why not do the correlation there?&lt;/p&gt;
&lt;p&gt;I thought about it and decided against it early. The BPF verifier gets unhappy with complex logic. It&amp;rsquo;s harder to debug, less portable across kernel versions, and for this use case, unnecessary. Userspace is fast enough. The design I landed on: BPF is a dumb data collector, userspace does the thinking.&lt;/p&gt;
&lt;p&gt;The BPF side attaches tracepoints to three syscalls. &lt;code&gt;sys_enter_openat&lt;/code&gt; for file opens, &lt;code&gt;sys_enter_connect&lt;/code&gt; for network connections, and &lt;code&gt;sys_enter_execve&lt;/code&gt; for process execution to track lineage. All three write into a single shared ring buffer with an event type field. Userspace reads and routes.&lt;/p&gt;
&lt;p&gt;I used a ring buffer over a perf event array deliberately. Perf event arrays are per-CPU, so you have to poll multiple buffers and deal with out-of-order events. A ring buffer is a single shared buffer, better throughput, simpler consumer. So obviously I wanted the path of least resistance.&lt;/p&gt;
&lt;h2 id=&#34;how-correlation-and-scoring-works&#34;&gt;How correlation and scoring works
&lt;/h2&gt;&lt;p&gt;On the userspace side, three in-memory maps hold state. Recent file opens per PID, process lineage from exec events, and a deduplication map to avoid alerting on the same PID repeatedly. A cleanup goroutine evicts stale entries every 30 seconds.&lt;/p&gt;
&lt;p&gt;The correlation window is configurable. I have it set to 5 seconds. When a connect event comes in, Barbwire looks back at that PID&amp;rsquo;s recent file opens and checks if any fall within the window. If they do, it scores the pair.&lt;/p&gt;
&lt;p&gt;Scoring is based on file and connect pairs, not individual signals. A process connecting to the network is normal. Opening &lt;code&gt;/etc/passwd&lt;/code&gt; on its own is normal. Both within 5 seconds is suspicious. The score comes from which file category was accessed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;suspicious_files&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;category&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;credential access&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;base_score&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;patterns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/etc/passwd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/etc/shadow&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/.aws/credentials&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;category&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ssh key exfiltration&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;base_score&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;patterns&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.ssh/id_rsa&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.ssh/id_ed25519&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Process lineage modifies the score up or down. A shell spawning a process that reads credentials and connects out is more suspicious than the same behavior happening under systemd:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;suspicious_parents&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;comm&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;modifier&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;comm&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;modifier&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;legit_parents&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;comm&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;systemd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;modifier&lt;/span&gt;: -&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;comm&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dockerd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;modifier&lt;/span&gt;: -&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alert threshold and severity thresholds are decoupled. The threshold decides if the alert is an actual alert, severity is a classification of the alert. When a connect event comes in, Barbwire picks the highest scoring file match for that PID and emits one alert. One alert per connect event per process, no duplicates (hopefully).&lt;/p&gt;
&lt;p&gt;A fired alert looks like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;┌─ barbwire alert — PID 41890  ─────────────
│  command  : python
│  file     : /etc/passwd
│  connect  : 2404:6800:4007:821::200e:80
│  severity : HIGH
│  reasons  : credential access, suspicious parent: fish
│  parent   : fish (pid 13387)
│  gparent  : tmux: server (pid 9483)
└─────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;my-screwups-while-building-it&#34;&gt;My Screwups while building it
&lt;/h2&gt;&lt;p&gt;A few things broke in ways that were actually instructive. The first one was actually two bugs hiding behind each other, which made it genuinely annoying to debug.&lt;/p&gt;
&lt;p&gt;There was a constant mismatch between the C and Go sides. &lt;code&gt;EVENT_EXEC&lt;/code&gt;, the event type for &lt;code&gt;sys_enter_execve&lt;/code&gt; tracepoint, was 2 and &lt;code&gt;EVENT_CONNECT&lt;/code&gt;, the event type for &lt;code&gt;sys_enter_connect&lt;/code&gt;, was 3 in the BPF code, but I had them swapped in Go. At the same time, I had attached the exec tracepoint and called Close() on it immediately without a defer, so it detached right after attaching (Yeah this is embrassing).&lt;/p&gt;
&lt;p&gt;The symptom was simple, in terms of events I had none. Thing was execve events were being routed to the connect handler. Since those events carry no IP address family, they got dropped as invalid. Meanwhile connect events were being routed to the exec handler, which was already closed. Everything was failing quietly in a different place than where I was looking.&lt;/p&gt;
&lt;p&gt;I spent a while staring at the correlation logic before going back to basics and checking whether the tracepoints were even active. Once I caught the missing defer, connect events started showing up, but in the wrong handler. That&amp;rsquo;s when I found the swapped constants. Two separate bugs, one symptom, neither pointing at the other. Really annoying&lt;/p&gt;
&lt;p&gt;The most useful bug involved stale memory in the event struct. &lt;code&gt;bpf_ringbuf_reserve&lt;/code&gt; gives you a pointer to uninitialized memory. If you skip the &lt;code&gt;__builtin_memset&lt;/code&gt; before writing your fields, whatever was in memory from the previous event bleeds into the fields you didn&amp;rsquo;t set. I had filenames with garbage characters appended, leftovers from longer filenames earlier in the buffer. The fix is one line. But you have to know it&amp;rsquo;s needed.&lt;/p&gt;
&lt;h2 id=&#34;limitations-and-takeaways&#34;&gt;Limitations and takeaways
&lt;/h2&gt;&lt;p&gt;The biggest problem with Barbwire is the one that can&amp;rsquo;t be tuned away.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;curl&lt;/code&gt; reads &lt;code&gt;/etc/passwd&lt;/code&gt; on every invocation. Here is it making the call:
&lt;img src=&#34;https://itsmonish.pages.dev/images/barbwire/1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;curl opening passwd&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Not because it&amp;rsquo;s malicious, but because that&amp;rsquo;s how it works. I believe, basically any program using libc networking does this. Barbwire flags it every time. No matter how hard I tuned the knobs, I couldn&amp;rsquo;t get rid of such alerts. Then it hit me, single-process correlation without broader context has a ceiling.&lt;/p&gt;
&lt;p&gt;Evidently, production security tools handle this differently. Binary hash whitelisting instead of process name matching, which is spoofable. Process ancestry graphs that track behavioral chains across multiple processes. Behavioral baselines built up over time. A shell that reads &lt;code&gt;/etc/passwd&lt;/code&gt; and then forks a child that connects out would be caught by a graph-based correlator. Barbwire misses it because the file open and the connect are in different PIDs.&lt;/p&gt;
&lt;p&gt;Building this made that limitation concrete in a way I don&amp;rsquo;t think I&amp;rsquo;d have gotten from just reading about EDR design. Honestly, I never even thought of these before. You also understand why process ancestry graphs exist when your tool fails exactly where they&amp;rsquo;d succeed.&lt;/p&gt;
&lt;p&gt;The other thing I took away: the BPF to userspace boundary is unforgiving. Memory layout, pointer semantics, which helper functions apply where, what the verifier accepts. The documentation covers the what, but the why only shows up when things break.&lt;/p&gt;
&lt;p&gt;All things considered, I had fun with all this. That&amp;rsquo;s the whole point if you think about it.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>I wanted to poke at the Linux kernel. eBPF was the answer</title>
        <link>https://itsmonish.pages.dev/blog/learning-ebpf/</link>
        <pubDate>Sat, 07 Mar 2026 20:32:58 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/learning-ebpf/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;A while back I came across &lt;a class=&#34;link&#34; href=&#34;https://blog.cloudflare.com/how-cloudflare-auto-mitigated-world-record-3-8-tbps-ddos-attack/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Cloudflare&amp;rsquo;s writeup&lt;/a&gt; on how they mitigated a 3.8 Tbps DDoS attack, the largest ever disclosed publicly at the time. The post goes deep into how their systems detected and dropped attack traffic autonomously (quite interesting stuff). One part that caught my eye: they used XDP and eBPF to drop packets directly at the NIC level, before the kernel network stack even saw them. I had zero clue on what eBPF was back then. I thought that was interesting, bookmarked it, and then forgot about it (Yeah I do that a lot).&lt;/p&gt;
&lt;p&gt;When I eventually got around to it, I was looking to get into lower level Linux stuff anyway. How the kernel works, how you can poke at it from the outside. eBPF kept coming up. I also saw it framed as a safer alternative to writing kernel modules for certain use cases. That part made immediate sense to me, because writing kernel modules means one bad pointer dereference and your machine is just down. So I thought maybe I could look into it seriously.&lt;/p&gt;
&lt;h2 id=&#34;what-even-is-ebpf&#34;&gt;What even is eBPF
&lt;/h2&gt;&lt;p&gt;eBPF lets you run sandboxed programs inside the Linux kernel without modifying the kernel source or loading a module. Before your program runs, the kernel verifies it. No unbounded loops, no out-of-bounds memory access, every code path has to terminate. If it doesn&amp;rsquo;t pass, it doesn&amp;rsquo;t load. However it doesn&amp;rsquo;t mean it&amp;rsquo;s bullet-proof, I still found some bugs and weird things happen now and then. But it catches the most obvious ones.&lt;/p&gt;
&lt;p&gt;Evidently, it started as Berkeley Packet Filter, which was just a way to filter network packets efficiently. The &amp;ldquo;extended&amp;rdquo; part came later and expanded it well beyond that when people wanted better tracing options when debugging inside the kernel. These days it shows up in observability tools, performance profilers, security products, and a lot of infrastructure tooling that runs quietly in the background of production systems.&lt;/p&gt;
&lt;h2 id=&#34;how-i-learned-it&#34;&gt;How I learned it
&lt;/h2&gt;&lt;p&gt;I started reading &lt;a class=&#34;link&#34; href=&#34;https://www.oreilly.com/library/view/learning-ebpf/9781098135119/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Learning eBPF&lt;/a&gt; by Liz Rice and went through it cover to cover. It builds up a solid mental model of how everything fits together, the verifier, maps, ring buffers, CO-RE, without throwing you into kernel internals from page one. Great book, would recommend it if you&amp;rsquo;re starting out.&lt;/p&gt;
&lt;p&gt;After that I spent time staring and reading through examples at &lt;a class=&#34;link&#34; href=&#34;https://github.com/libbpf/libbpf-bootstrap&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;libbpf-bootstrap&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://github.com/cilium/ebpf/tree/main/examples&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;cilium/ebpf&lt;/a&gt;. That was just to see how things are done in real projects and scripts.&lt;/p&gt;
&lt;p&gt;Then I started writing stuff. A per-process file access counter. A pure C rough version of what would eventually become &lt;a class=&#34;link&#34; href=&#34;https://github.com/ItsMonish/barbwire&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Barbwire&lt;/a&gt;. None of it was useful or anything. It was just to get familiar with the toolchain, get yelled at by the verifier, figure out why and repeat.&lt;/p&gt;
&lt;h2 id=&#34;what-i-liked-about-ebpf&#34;&gt;What I liked about eBPF
&lt;/h2&gt;&lt;p&gt;A couple of things genuinely impressed me.&lt;/p&gt;
&lt;p&gt;The first was CO-RE, which stands for Compile Once, Run Everywhere. Despite my reservations on Java, I always thought the idea of Code once, run everywhere was pretty neat (Thanks to JVM). CO-RE brings the same thing to Linux kernel programming. It handles struct field offset relocations across kernel versions, so your program isn&amp;rsquo;t silently tied to the exact kernel it was compiled against. Without it, anything that walks kernel structs like &lt;code&gt;task_struct&lt;/code&gt; breaks the moment you run it on a different machine. With it, the same binary works across versions. It is remarkable as far as I can tell.&lt;/p&gt;
&lt;p&gt;The second was how dynamic the whole thing is. With kernel modules you write code, compile, load, and if something goes wrong you restart. With eBPF you attach programs to running systems and detach them without rebooting or disrupting anything. For observability and containers especially, that matters a lot. You can instrument a live system, see what you need, and pull the probe out. No downtime, no risk of leaving a bad module loaded.&lt;/p&gt;
&lt;h2 id=&#34;what-came-next&#34;&gt;What came next
&lt;/h2&gt;&lt;p&gt;After a few weeks of experiments I felt ready to build something with an actual purpose. That became Barbwire, a lightweight behavioral correlator that watches for processes opening sensitive files and then making network connections. I&amp;rsquo;ll write about that in the next post.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not a production security tool. It&amp;rsquo;s an experiment. But building it was a good reason to use eBPF for something real instead of just counting file accesses forever.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Honeyfused: An intrusion detection script using FUSE filesystem</title>
        <link>https://itsmonish.pages.dev/blog/honeyfused/</link>
        <pubDate>Tue, 26 Aug 2025 18:24:58 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/honeyfused/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;First things first, what is Honeyfused? It&amp;rsquo;s a fun little side project that I spent some time on. It is on my &lt;a class=&#34;link&#34; href=&#34;https://github.com/ItsMonish/honeyfused&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github repo&lt;/a&gt; if anyone&amp;rsquo;s interested.&lt;/p&gt;
&lt;p&gt;Honeyfused is in a sense an intrusion detection application using honey-files. The program emulates filesystem mounted on directories and presents files that are actually not part of the original filesystem. Since the program emulates folders that usually contains secrets and API keys, intruders and infostealers may try to access it and possibly exfil the files. Since we control the filesystem, we can generate alerts whenever the file is accessed or modified. That is the gist of it. In this article you&amp;rsquo;ll find why I did this, how I did this and things like that.&lt;/p&gt;
&lt;h2 id=&#34;how-did-i-get-here&#34;&gt;How did I get here?
&lt;/h2&gt;&lt;p&gt;I very recently learnt that you can implement virtual filesystems, that is, filesystem that are not actually present on the disk, using FUSE bindings. All one had to do is override the operating system filesystem function calls to &lt;strong&gt;mimic&lt;/strong&gt; an actual filesystem and you can literally pull show a filesystem out of nowhere.&lt;/p&gt;
&lt;p&gt;Armed with the knowledge, I thought &amp;ldquo;why can&amp;rsquo;t I just implement a decoy filesystem with decoy files and generate alerts whenever the files are accessed?&amp;rdquo;. But this is basically honey-files with extra steps, that is, the virtual filesystem. Then I thought a honey-file is always the same, always there in the same place, and it is usually something like &amp;ldquo;passwords.txt&amp;rdquo; or &amp;ldquo;keys.csv&amp;rdquo;. So what if I could dynamically generate new file contents in important places, places where applications usually keep their tokens, secrets and keys, places which are more believable and look like legit files to an attacker, places which an infostealer would automatically enumerate. And hence, I got started to find out.&lt;/p&gt;
&lt;h2 id=&#34;the-filesystem-implementation&#34;&gt;The filesystem implementation
&lt;/h2&gt;&lt;p&gt;There are a few libraries that provide libfuse bindings in Python. Namely &lt;a class=&#34;link&#34; href=&#34;https://github.com/fusepy/fusepy&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;fusepy&lt;/a&gt;, &lt;a class=&#34;link&#34; href=&#34;https://github.com/libfuse/python-fuse&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;python-fuse&lt;/a&gt;, &lt;a class=&#34;link&#34; href=&#34;https://github.com/mxmlnkn/mfusepy&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;mfusepy&lt;/a&gt; etc. I choose to work with &lt;code&gt;mfusepy&lt;/code&gt; as it is one of the still maintained one. I couldn&amp;rsquo;t find any documentation about the module. But lucky for me, they maintain a folder of &lt;a class=&#34;link&#34; href=&#34;https://github.com/mxmlnkn/mfusepy/tree/master/examples&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;example implementations&lt;/a&gt;. One of those aligns closely with what I want, a pure &lt;a class=&#34;link&#34; href=&#34;https://github.com/mxmlnkn/mfusepy/blob/master/examples/memory.py&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;in-memory virtual filesystem implementation&lt;/a&gt;. Even though it did not clearly specify what should be actually done, it did give me a gist of how to do it.&lt;/p&gt;
&lt;p&gt;However the example script didn&amp;rsquo;t quite work out-of-the-box for me. So I made some modifications and additions of my own to emulate an actual filesystem. At the end of it, I had a script to create a virtual filesystem that can contain and display files, create new files, delete existing ones, create a directoy and so on.&lt;/p&gt;
&lt;h2 id=&#34;what-to-generate&#34;&gt;What to generate?
&lt;/h2&gt;&lt;p&gt;Now that, I can mount an filesystem whereever I want, I need to generate files. Not just any files, files that appear to be legit, files that should be placed in places where popular applications places them. Then I spent some time researching in popular applications and their configurations. Nah, I&amp;rsquo;m kidding. I used ChatGPT to tell me how popular applications stored their configs and keys. At the end of it, I had a list of applications that seemed to be good candidates for the decoy files. I settled for Kubernetes&amp;rsquo;s kubeconfig, AWS&amp;rsquo;s credential and config file, Azure&amp;rsquo;s credentials.json, GCP&amp;rsquo;s default application config file, Terraform&amp;rsquo;s credential file, Docker&amp;rsquo;s config file and lastly Bitcoin&amp;rsquo;s config and wallet files. Mostly developer centric, I know.&lt;/p&gt;
&lt;p&gt;With an list of candidate application, I then again asked ChatGPT to give me a blueprint of configuration files and what kind of data should I use to fill it with. With that, I used Python&amp;rsquo;s &lt;code&gt;string.template&lt;/code&gt; to craft template of files and wrote generator functions to dynamically populate the templates. It took me some time to cover this aspect of the ground but it was fine.&lt;/p&gt;
&lt;h2 id=&#34;putting-things-together&#34;&gt;Putting things together
&lt;/h2&gt;&lt;p&gt;At this point, I have a script to mount a fake filesystem anywhere I want and generator functions that can give me realistic looking files that popular applications use. Only thing left is to put these things together. I used a YAML file (&lt;code&gt;config.yml&lt;/code&gt;) as the configuration file for user. With that user can specify which applications can the script emulate and present decoys for. Because we don&amp;rsquo;t want to present decoys for already present applications.&lt;/p&gt;
&lt;p&gt;It took going a little back and forth on things, until I finally managed to put it together. So now my script (actually scripts), managed to generate decoy files for applications provided in &lt;code&gt;config.yml&lt;/code&gt; and mount it in place I wanted.&lt;/p&gt;
&lt;h2 id=&#34;the-logging&#34;&gt;The logging
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ll admit, I haven&amp;rsquo;t done a lot of logging stuff in Python. Since &lt;code&gt;mfusepy&lt;/code&gt; already provided debug logs for the filesystem, I tried to implement logging that gives debug information for my script. So I read the documentation (yes I actually did that), and implemented it. And then I remembered that I had to log alerts as well, because that is the whole point of the application. But I didn&amp;rsquo;t want to throw all the log at once. So I seperated logs from &lt;code&gt;myfusepy&lt;/code&gt;, debug information from my script and the alerts I want to give out. Turns out it&amp;rsquo;s surprisingly easy than it sounds.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;It took me over an week to put all these together. I mean I didn&amp;rsquo;t spent all my time on this, but okay. So basically I got an Intrusion detection system out that puts fake files on your filesystem and then alerts you when someone accesses it. It is geared more towards developers who keep these kinda files in their storage. But at the end this is just a stupid experiment. So I hope no one uses it as an actual security measure. Still it was pretty fun convincing the system that a filesystem exists when it doesn&amp;rsquo;t and generate configuration files for various applications.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Run Run, Ember Run! - EchoCTF Writeup</title>
        <link>https://itsmonish.pages.dev/blog/echoctf-run-run-ember-run/</link>
        <pubDate>Sun, 22 Jun 2025 13:45:21 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/echoctf-run-run-ember-run/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;This writeup deals with the &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/challenge/9&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Run Run, Ember Run!&lt;/a&gt; challenge from &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;EchoCTF&lt;/a&gt;. This challenge focused on understanding the MBR boot record format and the bootstrap code it contains. A compressed gunzip archive &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/uploads/run_run_ember_run.gz&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;run_run_ember_run.img.gz&lt;/a&gt; contains challenge file.&lt;/p&gt;
&lt;p&gt;Challenge Statement:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;A small challenge with only four questions for you to answer.

An attacker gained access to one of the systems leaving behind the file attached to this challenge.

Not many details are known about the file but there are at least two ways to get what you want. Depending on the road you&amp;#39;ll take it can be as easy as walking in the park or hard as climbing the Himalayas.

You need to analyze the file and answer the following questions...

This is not a filesystem, even though you&amp;#39;ll find it on disks... and although its not executable you can still run it!!!

Q1: What is the first thing you&amp;#39;d see from running this image? (200 pts)
The first thing that would greet you is a flag. Give it for answer to this question.

Q2: What do you get from address 0? (300 pts)
What are the values of the first 16 bytes starting from address 0. Remove any spaces from your answer.

Q3: What is the address backdoored by the attacker? (400 pts)
The attacker seemed to have added a backdoor at a certain address. When you try to print this address you will get a flag. What is the backdoored address in hex (prefixed by 0x).

Q4: What is the flag from the backdoored address? (500 pts)
What is the flag being displayed from the backdoored address?
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;writeup&#34;&gt;Writeup
&lt;/h2&gt;&lt;p&gt;The question makes it look like the challenge has something to do with a binary reverse engineering analysis. But also there are points that suggest that it a disk? So first we need to understand what it is we are given.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;file utility output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;It says that the file is a DOS/MBR boot sector. So we can&amp;rsquo;t &amp;ldquo;run&amp;rdquo; a boot sector right? That&amp;rsquo;s what I thought and decided to take a look at the hex dump of the file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;hex dump&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all of the hex dump. So taking a preliminary look at it revealed nothing. I mean no hidden files, no corrupted data, nothing. But what caught my attention is how it is exactly 512 bytes. The boot partitions are indeed 512 bytes. So that suggested we really do have a MBR boot sector at our hands. Hence we should be able to look at the entries using something like &lt;code&gt;fdisk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;fdisk output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So we have something. The partition data that the sector holds. But this is of no use without the actual disk (except for now we know what kinda partitions the computer this record belonged to had). But that can&amp;rsquo;t be it. I was missing something. And then looking at the wikipedia page for &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Master_boot_record&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MBR boot record&lt;/a&gt; had the following table:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;mbr wikipedia&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Huh, so there IS something that I was missing. There is an entire 446 bytes of &amp;ldquo;Bootstrap code area&amp;rdquo;. That should have been obivous but it wasn&amp;rsquo;t for me. After all this code region is usually targetted by malicious softwares called &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Rootkit#bootkit&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Bootkits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;According to the wiki at &lt;a class=&#34;link&#34; href=&#34;https://wiki.osdev.org/MBR_%28x86%29&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OSDev&lt;/a&gt; (not explicitly mentioned, the example provided there indicated it), this code is of 16-bit x86 instruction set. So all I have to do is disassemble the image provided. Easy.&lt;/p&gt;
&lt;p&gt;But before going head first into assembly, recall that the challenge statement explicitly stated to &amp;ldquo;run&amp;rdquo; the image. Since we have a valid boot sector image, perhaps we can? Quick search on the internet showed &lt;a class=&#34;link&#34; href=&#34;https://www.qemu.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;QEMU&lt;/a&gt; is good for it. Using the command,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;qemu-system-i386 -drive format=raw,file=run_run_ember_run.img&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;we are able to boot into the boot sector on a virtualized machine console.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;first boot and answer&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Yeah we are indeed greeted with a message. Hence our answer for Q1. It looks like a prompt waiting for input.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;second answer&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Hitting enter on the prompt gave some interesting output. It displays the memory dump of the sector. Since our second question is literally what are the values of first 16 bytes of address 0, we have our answer for that as well.&lt;/p&gt;
&lt;p&gt;And for Q3, we apparently have a backdoor of sorts. I saw no other option than having to dive into the assembly code. We can dump the entire image using &lt;code&gt;objdump&lt;/code&gt;, I specifically used the following command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;objdump -D -b binary -mi386 -Maddr16,data16 -Mintel run_run_ember_run.img&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The options being:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-D : Disassemble the file
-b binary : target object is binary
-mi386 : target architecture is x86
-Maddr16,data16 : Disassembler options
    addr16 : Addresses are in 16-bit
    data16 : Data is in 16-bit
-Mintel : Use intel style assembly code
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;disassembly&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;There was a lot of disassembly. As a matter of fact, we have dumped the entire image file, but the actual code is only 446 bytes, which would mean the offset upto 1bd is the valid code.&lt;/p&gt;
&lt;p&gt;And now we read assembly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;backdoor&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Skimming through the assembly dump, the instruction at offset 86 caught my attention. A compare statement that compares the value at the register &lt;code&gt;bx&lt;/code&gt; with &lt;code&gt;0x3750&lt;/code&gt;. As far as I could tell &lt;code&gt;0x3750&lt;/code&gt; is nothing special. But the instruction checks for it, and then branches. So the question arises, what are the two branches?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;86:   81 fb 50 37             cmp    bx,0x3750
8a:   75 30                   jne    0xbc
8c:   b0 6d                   mov    al,0x6d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Assuming the likely case that &lt;code&gt;bx&lt;/code&gt; won&amp;rsquo;t be equal to 0x3750, it seems the code we want to execute is at &lt;code&gt;bc&lt;/code&gt; offset. That begs the next question, how could &lt;code&gt;bx&lt;/code&gt; be set to &lt;code&gt;0x3750&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;74:   e8 c5 00           call   0x13c    
77:   72 05              jb     0x7e     
79:   93                 xchg   bx, ax   
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A little above the offset of 86, at offset 74, we can see that a call is made to the address &lt;code&gt;0x13c&lt;/code&gt;. In x86 convention a call&amp;rsquo;s return value is usally found in &lt;code&gt;ax&lt;/code&gt; register (if it returns). Then instruction &lt;code&gt;xchg&lt;/code&gt; exchanges or swaps the value between &lt;code&gt;bx&lt;/code&gt; and &lt;code&gt;ax&lt;/code&gt; registers. So bx depends on whatever is returned from the instructions at &lt;code&gt;0x13c&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;13c:   e8 04 00                call   0x143
13f:   72 15                   jb     0x156
141:   88 c4                   mov    ah,al
143:   ac                      lods   al,BYTE PTR ds:[si]
144:   e8 10 00                call   0x157
147:   72 0d                   jb     0x156
149:   c0 e0 04                shl    al,0x4
14c:   88 c2                   mov    dl,al
14e:   ac                      lods   al,BYTE PTR ds:[si]
14f:   e8 05 00                call   0x157
152:   72 02                   jb     0x156
154:   08 d0                   or     al,dl
156:   c3                      ret
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be quite honest, I don&amp;rsquo;t exactly understand what&amp;rsquo;s going in here. The call to &lt;code&gt;0x13c&lt;/code&gt; in turn makes a call to &lt;code&gt;0x143&lt;/code&gt; which is 3 instruction away and kinda confusing and we are missing a ret instruction.&lt;/p&gt;
&lt;p&gt;But in a gist the instruction at &lt;code&gt;0x143&lt;/code&gt; loads a string, an input string to be specific, and calls to another &lt;code&gt;0x157&lt;/code&gt;. It does the same set of instruction twice, but the instruction &lt;code&gt;shl al, 0x4&lt;/code&gt; (shift the contents of al[lower 8 bits of ax] to the left by 4 places), suggests it just processes an input of 16 bits, 8 bits each time. And then the processed contents is stored at the register &lt;code&gt;ax&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So looking at &lt;code&gt;0x157&lt;/code&gt; we have:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                    ; turns strings to ascii. Like &amp;#39;0&amp;#39; -&amp;gt; 0
157:   2c 30                   sub    al,0x30    
                    ; jump to ret if subtraction is negative. For cases less than &amp;#39;0&amp;#39;
159:   72 0e                   jb     0x169      
                    ; compare al and 10(in decimal)
15b:   3c 0a                   cmp    al,0xa
                    ; compliment carry bit, not sure why
15d:   f5                      cmc
                    ; jump to ret(0x169) if al is above or equal to 10.
                    ; so al must have value between 0 and 9
15e:   73 09                   jae    0x169
160:   2c 31                   sub    al,0x31
162:   72 05                   jb     0x169
164:   04 0a                   add    al,0xa
166:   3c 10                   cmp    al,0x10
168:   f5                      cmc
169:   c3                      ret
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now just looking at the first few lines of the routine, we can see that the call converts the string input to number input. So if we want &lt;code&gt;0x3750&lt;/code&gt; as value of &lt;code&gt;ax&lt;/code&gt; register, we just need to input it?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_runrunemberrun/9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;backdoor&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Yes. We just had to put it there and the flag is given. Could&amp;rsquo;ve tried that first, nevertheless it was good. So we have our answer for Q4.&lt;/p&gt;
&lt;p&gt;We still need answer for Q3. Now this part, I think the question is kinda misleading. It asks for the &amp;ldquo;backdoored address&amp;rdquo;. Technically, the backdoor is at offset 86. MBR base address starts at 0x7c00, making the &amp;ldquo;backdoored address&amp;rdquo; as 0x7c86. But this is not it. The answer is the value we need to meet at this address. That is our answer for Q3. Or maybe I&amp;rsquo;m mistaken, but as far as I can tell, that is not an address.&lt;/p&gt;
&lt;h3 id=&#34;note&#34;&gt;Note
&lt;/h3&gt;&lt;p&gt;You can actually find the both of the flags in the hex dump from the beginning if you look for it (with some &amp;lsquo;pattern recognition&amp;rsquo;). I didn&amp;rsquo;t.But it&amp;rsquo;s just the flags.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;It did take me quite some time to figure out Q3, but nevertheless, the challenge was fun and unique. There ain&amp;rsquo;t many challenges that require you to boot a boot sector. So all things considered it was a solid.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>The Bin in the Dump - EchoCTF Writeup</title>
        <link>https://itsmonish.pages.dev/blog/echoctf-the-bin-in-the-dump/</link>
        <pubDate>Mon, 21 Apr 2025 22:27:44 +0530</pubDate>
        
        <guid>https://itsmonish.pages.dev/blog/echoctf-the-bin-in-the-dump/</guid>
        <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;This writeup deals with the &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/challenge/8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The bin in the dump&lt;/a&gt; challenge from &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;EchoCTF&lt;/a&gt;. This challenge focused on forensic examination of a filesystem image. A compressed gunzip archive &lt;a class=&#34;link&#34; href=&#34;https://echoctf.red/uploads/the_bin_in_the.dump.gz&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;the_bin_in_the.dump.gz&lt;/a&gt; contains the filesystem image to be examined.&lt;/p&gt;
&lt;p&gt;Challenge Statement:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;This forensics exercise is about demonstrating some basic tasks that are required in order to trace an attack on a compromised system.

The actual data are illustrative and no actual trojans or exploits exist in any of the files

You are given a filesystem dump and you are tasked to find and report the following details...
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;writeup&#34;&gt;Writeup
&lt;/h2&gt;&lt;p&gt;First things first. Before jumping into the questions we need to understand what kind of file system we are dealing with.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;file command output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So the output of file command tells us this is a linux-based EXT3 file system. Now we can try to list some contents of the file system using &lt;code&gt;fls&lt;/code&gt; from &lt;a class=&#34;link&#34; href=&#34;https://sleuthkit.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The Slueth Kit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;fls output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;It looks like we got a root file system at our hands.&lt;/p&gt;
&lt;p&gt;Now the questions. The questions are not answered in order as a single output command can give more than one.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Q3. What is the UUID of the filesystem? (100 pts)
Analyze the file and provide the UUID of the filesystem as an answer
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As from the file command, we already know the UUID. So that&amp;rsquo;s one.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Q1. What is the filesystem LABEL? (100 pts)
Analyze the file and provide the filesystem LABEL as an answer

Q2. What is the last folder this file system was mounted under? (100 pts)
Analyze the file and find what was the last folder this file system was mounted under. Provide the folder name verbatim as an answer.

Q4. When was the filesystem created? (100 pts)
Analyze the filesystem and report what was its creation date and time in UTC. The date format is: YYYY-MM-DD HH:MM:SS

Q5. When was the filesystem last write time? (100 pts)
Analyze the filesystem and report what was the last write date and time in UTC. The date format is: YYYY-MM-DD HH:MM:SS.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, all this information can be obtained in a number of ways. Here we use &lt;code&gt;debugfs&lt;/code&gt;. Using the tool with the &lt;code&gt;-R&lt;/code&gt; option allows us to execute a single &amp;ldquo;request&amp;rdquo; command. Otherwise the tool is interactive. It&amp;rsquo;s quite the loaded tool for debugging and examining EXT2/3/4 file systems.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;debugfs output&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Now we have a lot of information on the screen. We have our filesystem label as the volume name, Last mounted on folder name, file system creation date, last write date among them. Note that there is a matter of localization in the dates. The question specifies we need it in UTC, but I&amp;rsquo;m on IST, so I had to convert the time.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Q6. What is the file modified by the attacker? (150 pts)
Analyze the filesystem and report what was the full path of the filename that was modified by the attacker?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now it&amp;rsquo;s time to get a little creative. Assuming that the file system dump was created after the supposed &amp;ldquo;attack&amp;rdquo;, the file modified by attacker needs to have the most recent last modified date. Now I&amp;rsquo;m not familiar with ways to directly find out the most recently modified file directly.&lt;/p&gt;
&lt;p&gt;So I dumped all the files in to my local file system using a tool called &lt;code&gt;testdisk&lt;/code&gt; (&lt;a class=&#34;link&#34; href=&#34;https://www.cgsecurity.org/wiki/TestDisk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;about the tool&lt;/a&gt;). This can also be used to recover recently deleted files.&lt;/p&gt;
&lt;p&gt;The tool has a ncurses-like interface with nice hints. So it is pretty self-explanatory. One can also use &lt;code&gt;tsk_recover&lt;/code&gt; tool from TSK for the same purpose as well. But the problem is &lt;code&gt;tsk_recover&lt;/code&gt; does not preserve the modification date from the original file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;recovering&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Now you can use the basic &lt;code&gt;ls&lt;/code&gt; command in long list mode to find the most recently modified one manually. But we can use &lt;code&gt;find&lt;/code&gt; tool with some pipeline magic to do that for us. The following is the command I ended up going with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;find output -type f -exec stat --format &amp;#39;%Y |%y %n &amp;#39; &amp;#39;{}&amp;#39; \; | sort -nr | awk -F&amp;#39;|&amp;#39;&amp;#39; &amp;#39;{print $2}&amp;#39; | head -10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;recently modified&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;On top of the list we have our answer which seems to have modified in 2006. The others are from 2002. So that&amp;rsquo;s our answer.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Q7. What is the full path of the ETSCTF binary that got deleted? (250 pts)
Analyze the filesystem and report what was the full path of the ETSCTF file (the file may have extension)?
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now any file we delete is not actually wiped from the disk by default. Their inodes are declared as free and will be overwritten eventually. But until that happens the data is still present in the same location. Most data recovery programs use this.&lt;/p&gt;
&lt;p&gt;Parsing through all the inodes can be a tedious task. Lucky for us, TSK provides a tool &lt;code&gt;fls&lt;/code&gt; that is used to list all the files in a file system image. One can make it to return only deleted entries with the &lt;code&gt;-d&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;deleted entries&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Above is a snippet of the output. Note that we have a few entries with a non-zero number and others with a 0. As far as I know we can only recover the non-zero entries. The numbers are nothing but the inode numbers of that particular file or directory. So I think a zero means overwritten inode.&lt;/p&gt;
&lt;p&gt;Among the entries we have a peculiar directory &lt;code&gt;w0W_d1s_iz_a_hidden_f0ld3r&lt;/code&gt;. Yes it is a directory. Notice the &amp;rsquo;l&amp;rsquo;s, &amp;lsquo;r&amp;rsquo;s and &amp;rsquo;d&amp;rsquo;s? That&amp;rsquo;s what kind of data was stored in that inode. &amp;rsquo;d&amp;rsquo; means directory.&lt;/p&gt;
&lt;p&gt;But the question is for a file. So maybe it is in the directory? But if it was in the directory and if it was recoverable it should be in the dumped files right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;no file&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Nope. Not there. It&amp;rsquo;s an empty directory. But let&amp;rsquo;s not give up just yet. There is tool called &lt;code&gt;extundelete&lt;/code&gt;(&lt;a class=&#34;link&#34; href=&#34;https://extundelete.sourceforge.net/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;about the tool&lt;/a&gt;) that is specific for recovering data from EXT partitions. Using the &lt;code&gt;--restore-all&lt;/code&gt; option we can see 2 inodes are recovered.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;undelete&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Listing the files recovered, we have:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;recovered list&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;So there is our deleted file and the answer.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; Q8: What is the flag that would have been displayed by the ETSCTF binary? (300 pts)
Analyze the filesystem and restore the deleted ETSCTF file. If you only change the first 2 bytes you may be able to run this... If you run this you will be able to get a flag. Give it as an answer here verbatim
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/10.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;hex dump&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;By looking at the hex dump, we can say it is not a JPEG. But it doesn&amp;rsquo;t have any other known file signature as well. But the question says we have to modify the first 2 bytes. So what should we change to? The third and fourth bytes are &amp;lsquo;L&amp;rsquo; and &amp;lsquo;F&amp;rsquo; respectively. So it is safe to assume that this may be an ELF binary.&lt;/p&gt;
&lt;p&gt;Open the binary in a hex editor of choice and change the first two bytes to &amp;ldquo;7F 45&amp;rdquo; as the file signature for ELF is &amp;ldquo;7F 45 4C 46&amp;rdquo; according to &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/List_of_file_signatures&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Do that and slap on the executable bit on the binary with &lt;code&gt;chmod +x&lt;/code&gt;, we can execute it. Executing it yields:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://itsmonish.pages.dev/images/echoctf_thebininthedump/11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;flag&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;And that brings the challenge to an end. I wouldn&amp;rsquo;t say it as easy as the challenge description puts it. But if you&amp;rsquo;re strong with the fundamentals and know how to search for tools to do things, it is doable.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
