Enumerating IPs in X-Forwarded-Headers to bypass 403 restrictions
As of late, I have been pentesting more and more applications that use some sort of mechanism to prevent unauthorized access to directories based on client IP addresses. In many cases, this has proven to be a weak method of protection if implemented incorrectly.
There have been instances where spoofing IP addresses to web application servers has allowed for full administration access - without any other form of authentication.
Take a look at what happened to StackOverflow for example: http://blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html
One such implementation that I commonly see used in protecting WordPress administration panels is to whitelist IP addresses in HTAccess files on Apache servers. Take for example the following
.htaccess file located in the
#Hosting Environment IPs SetEnvIF X-FORWARDED-FOR "18.104.22.168" AllowIP SetEnvIF X-FORWARDED-FOR "10.0.2.11" AllowIP #Personal Computer SetEnvIF X-FORWARDED-FOR "22.214.171.124" AllowIP
Now, depending on your web servers configuration, the above configuration could be completely useless. Why? Due to the way
X-Forwarded-For headers are handled.
Due to proxies that may lie between your request and the actual web server hosting the content, the
X-Forwarded-For header passed down to the final host being contacted, will usually contain an ordered list of IP addresses. As explained in this blog post, the
X-Forwarded-For header will look something like this:
X-Forwarded-For: A, B, C
Where A will represent the client’s IP address, B will represent an intermediate proxy and C will represent another intermediate proxy.
Note: there is no limit to the number of proxies in between the server and the request, the
X-Forwarded-For could have an unlimited list of IPs in a comma delimited format.
As mentioned by the above linked blog post: the leftmost IP address is the easiest to forge. For the application server to determine the most trustworthy IP address, it must traverse the chain of proxies in reverse. Not every framework or application does this and from experience, the chances of being able to spoof IP addresses through the
X-Forwarded-For header are high.
In fact, by adding a large list of IPs, where the IP addresses are the same but comma delimited in the
X-Forwarded-For header, have yielded better results for me when bypassing IP restrictions in administration panels.
Going back to the HTAccess file I mentioned earlier in this blog post, a simple bypass that allowed for the bypassing of IP restrictions was demonstrated through this GET request:
GET /wp-admin/ HTTP/1.1 Host: vulnsite.com X-Forwarded-For: 126.96.36.199, 188.8.131.52, 184.108.40.206, 220.127.116.11 Cookie: ilikecookies=yes
By appending this header, full access to the wp-admin directory was achieved.
However, this brought me to another problem. If this wasn’t a white box test, I probably wouldn’t have tried to gain access to the
/wp-admin/ directory with the
X-Forwarded-For header set to such a specific IP address.
Since there was no such tool that enumerates access to a 403 forbidden resource by enumerating IP addresses in the
X-Forwarded-For header at the time of writing this, I created one.
This tool can be found here: http://github.com/infosec-au/enumXFF
All you need to do is run the tool from the terminal giving the following input:
- Page to attempt (
- The content length of the response that you get when you are denied
- The IP range you wish the tool to attempt through
The tool will then simply request the given page with a range of IP addresses and then determine whether or not access to the said page is still forbidden.
python3 enumXFF.py -t http://sketchysite.com/admin -badcl 234 -r 192.168.0.0-192.168.255.255
The above command will attempt to request
http://sketchysite.com/admin with all IP addresses in the range
192.168.0.0-192.168.255.255. The python script takes advantage of asynchronous HTTP requests via the
requests-futures module and hence should be fairly quick. Note, this tool only functions on Python3.
In addition to this, the enumXFF project on Github also contains a script called
generateIPs.py. This will simply generate a list of comma delimited IP addresses that can be input directly to Burp's Intruder. If the tool isn't the best way for you, Burp's Intruder is a reliable option to fall back on.
To generate the IPs for the range 192.168.0.0-192.168.255.255, you would need to use the following command:
python3 generateIPs.py -r 192.168.0.0-192.168.255.255 -o burp_xff_ips.txt
Perhaps in the future if time allows, I might port my script over to a simple Burp extension.
If you find this blog post useful, feel free to give the project a star on Github or tweet/follow me @infosec_au :)