Server Side Request Forgery (SSRF)

This is a blog post summarising a few notes I’ve gathered around the internet, with the purpose of cementing them in my mind rather than adding anything new or attempting to broadcast them to a wider crowd. If you find it useful, that’s great, but it’s nothing original and its been pulled from several sources noted at the end of the post.

The post is organised in several categories: general attacks against URL parsers and implementations, and specific attacks against parsers in specific languages, with the intention of highlighting differences in interpretation of URL strings.

Discrepancies between parsers and HTTP libraries

Python

This is interesting. These three URL parsing libs in python all interpret this individual URL differently:

import urllib
import urllib2
import requests

url = "http://1.1.1.1 &@2.2.2.2# @3.3.3.3/"

urllib.urlopen(url) # 3.3.3.3
urllib2.urlopen(url) # 1.1.1.1
requests.get(url) # 2.2.2.2

urlparse(url) # ParseResult(scheme='http', netloc='1.1.1.1 &@2.2.2.2', path='', params='', query='', fragment=' @3.3.3.3/')

PHP

In versions earlier than a few years ago, there was an inconsistency between parse_url and readfile which could lead to URL parser bypass with URLs with several colons: http://127.0.0.1:11211:80/ or a URL with extraneous characters http://google.com#@evil.com/. In both examples parse_url and readfile interpreted the URL differently. In the second case, readfile interprets the host as evil.com, whereas parse_url interprets the host as google.com.

This may affect other programming languages.

Curl

Curl is in widespread usage, and there are curl bindings in every language under the sun. Discrepancies between language URL parsers and Curl could lead to SSRF. Consider the following URL, as interpreted by PHP:

# php
<?php print_r(parse_url("http://foo@127.0.0.1 @google.com/"));
Array
(
[scheme] => http
[host] => google.com
[user] => foo@127.0.0.1
[path] => /
)

And as interpreted by cURL:

curl 'http://foo@127.0.0.1 @google.com/'
curl: (7) Failed to connect to 127.0.0.1 @google.com port 80: Connection refused

As you can see it is attempting to retrieve 127.0.0.1. This example uses PHP, but these discrepancies have been identified in other languages as well and more are bound to exist. Example vulnerabilities have been found in WordPress, VBulletin and MyBB utilising this technique.

Node

Path traversal bypasses are possible with special unicode character U+FF2E. This happens because node’s internal unicode parser interprets this multibyte character as two separate bytes, and then proceeds to discard a part, leaving \x2E, a dot.

Similar results can be observed by injecting U+FF0D and U+FFOA, which results in a newline, allowing newline injection.

General Bugs

In linux, hostname resolution is generally done with gethostbyname. As per RFC1035 it supports escaping of values with \DDD notation. This may allow for additional parser confusion.

# echo or\\097nge.tw
or\097nge.tw
# nslookup or\\097nge.tw

Non-authoritative answer:
Name: orange.tw
Address: ...

Interestingly enough, gethostbyname will remove all backslashes that are not followed by a digit.

echo or\\an\\g\\e.tw
or\an\g\e.tw
# nslookup or\\an\\g\\e.tw
Server: 192.168.20.1
Address: 192.168.20.1#53

Non-authoritative answer:
Name: orange.tw
Address: ...

gethostbyname will also pass input to getaddrinfo at times, which means that it will ignore invalid input as long as it is preceded by a valid address. Examples below:

>>> import socket
>>> socket.gethostbyname("127.0.0.1 foo")
'127.0.0.1'
>>> socket.gethostbyname("127.0.0.1\r\nfoo")
'127.0.0.1'

The ability to add invalid trailing content can lead to an attacker that can perform HTTP header smuggling attacks in the event they can inject encoded new lines, such as 127.0.0.1\r\nfoo.com\r\nAuthorization: blah

Additionally, an attacker can smuggle other protocols (such as SMTP) thanks to TLS’ SNI.

IDNA

Internationalizing Domain Names in Applications (IDNA) is a standard or a set of standards that allow for characters not in the ascii set to be used in domain names. There are two diferring standards, IDNA2003 and IDNA2008, which are difficult to transition between for client implementations, which lead the unciode consortium to release UTS46.

Different HTTP libraries and URL parsing libraries implement different versions of this standard, as well as implementing the standard in different ways. This can be useful to avoid blacklists of disallowed hosts. An example of this is an inconsistency in PHP’s gethostbynamel function and curl’s resolver: PHP’s gethostbynamel fails when provided with a domain with a special character, which can lead to bypasses. cURL will then retrieve the URL and resolve it successfully.

Values synonymous to localhost

Beside the obvious examples, the following URLs will all attempt to retrieve localhost.

wget http://0/
wget http://127.0.0.2/
http://2130706433/
wget http://[::1]/
wget http://SERVER_IP/
wget http://127.1/

DNS

Several mechanisms for bypassing SSRF protections through DNS shenanigans exist. I will cover these on a high level below:

Host that resolves to a malicious IP

DNS records may point to an internal IP address (such as 10.0.0.2 or 127.0.0.1). This frequently works because developers check whether the ip matches an address range but accept arbitrary DNS names regardless of what they resolve to.

Time of check, time of use vulnerabilities (TOCTOU)

A TOCTOU vulnerability can occur if the target application implements host whitelisting or host blacklisting. Imagine the following pseudo-code:

download_url = request.get('target')
target_host = urlparse.urlparse(download_url).netloc
target_ip = socket.gethostbyname(target_host) #resolve

if target_ip in blacklist:
raise Exception('fail.')
else:
retrieve_with_curl(download_url)

A TOCTOU vulnerability allows for a bypass of the blacklist check if DNS resolution occurs twice: once for the check, and twice for the retrieval. An attacker-controlled DNS server could resolve the first time to a good address and the second time to a malicious IP.

Malicious redirect

A SSRF protection bypass may occur if an attacker creates a malicious site that redirects to an internal IP because the check is performed on the initial IP address and not the address the HTTP client gets redirected to. This frequently works for most HTTP clients as they tend to follow HTTP redirections by default. Here’s an example:

http://victim.org?url=http://127.0.0.1 -> FAIL, not allowed
http://victim.org?url=http://attacker.com/ -> 301 REDIRECT TO 127.0.0.1 -> 200 OK.

Final notes

The ability to inject \r\n in combination with either spaces or \t characters allows you to inject new headers into the request, which may allow other attacks. Imagine a request to `victim.com?url=yourserver.com/aa’ results in the following request:

GET /aa HTTP/1.1
Host: yourserver.com

 

A request that looks like victim.com?url=yourserver.com/aa%20HTTP/1.1%0Ainjected-header: true%0Ax-aa: could result in the following:

GET /aa HTTP/1.1
injected-header:true
x-aa: HTTP/1.1
Host: yourserver.com

This would allow you to attack a lot of plaintext protocols.

References:

https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf
https://www.hackerone.com/blog-How-To-Server-Side-Request-Forgery-SSRF https://github.com/cujanovic/SSRF-Testing/ https://web.archive.org/web/20130509011621/http://d0znpp.blogspot.com/

droopescan 1.33.7 released

Hi there,

A new version of droopescan has been released, which increases the version’s patch digit from 1.33.6 to 1.33.7. With this, droopescan officially reaches elite status. This is a minor increase to update fingerprint databases for all supported CMS’. No serious vulnerabilities have come out this round, although WordPress has patched a Cross Site Scripting vulnerability.

For those that are not aware, a large number of improvements have been implemented between 1.0 and 1.33.7, almost too many to count. A few highlights are:

  • Support for CMS type autodetection. This allows you to specify a list of URLs and dscan will automatically determine what CMS it is and perform the usual version and plugin enumeration. Performance is pretty great, and I’ve successfully version scanned several million hosts in three days time.
  • Allow for resuming of mass scans with the –resume flag.
  • Several performance improvements and tweaks.
  • Preliminary support for WordPress and Joomla.

droopescan development continues on, and exciting things for the future include tools that will make updating the fingerprint even easier (for me, as I am very lazy), support for other CMSs, and the release of version 2.0 which will tidy up JSON output.

Ta,
Pedro

Nueva version de droopescan (1.33.7)

¡Hola a todos!

Hoy estoy por sacar una versión nueva de droopescan, que debido a que es la versión que sigue a la 1.33.6, va a ser la versión 1.33.7. Este es un número muy particular para la comunidad de la seguridad informática y los nerds en general, y también es indicativo del mucho tiempo de desarrollo y el número de parches que he publicado para droopescan.

Esta nueva versión sale a raiz de que hay nuevas versiones en Drupal, WordPress, Joomla y SilverStripe. WordPress arregla algunos fallos de seguridad (XSS).

Para actualizar:

pip -U droopescan

en la línea de comandos.

Un abrazo grande,
Pedro

Compilado de enlaces navideño.

Hola a todos!

Feliz navidades y eso! Abajo un compilado de enlaces:

Un abrazo!
Pedro

Civilization V in Simplified Chinese

Hi All

I’ve been struggling lately in an attempt to get Civilization V working with simplified Chinese characters. As you probably know, Civilization V officially supports traditional Chinese characters, but simplified Chinese characters are not supported at all by firaxis.

The chinese people of course also want to play this game, and they have created a translation for it, but it is mostly inaccessible for people who don’t speak chinese.

After dealing with dodgy .exe files and all sorts of weird crap, I found an .exe which will actually install the translation pack here. You can skip all the sections which speak of a crack if you have a legitimate copy of the game. I’ve also made a mirror of the .exe I downloaded here.

Instructions are as follows:

  • Install Civilization V legitimately. Piracy is illegal and all pirates go to hell without exception.
  • Download and extract the zip file. Execute the .exe file within it (virustotal says it has no virus)
  • Locate your Config.ini file and replace the values at line 66 and 69 with zh_CN. An example location for the file could be D:\Programs\Steam\steamapps\common\Sid Meier’s Civilization V\Config.ini

Run civilization as normal. All text should now be in simplified chinese.

Thanks,
Pedro

Nueva versión de droopescan (1.33.0)

¡Hola a todos!

Hoy de mañana salió una nueva versión de droopescan. No tiene ninguna actualización respecto a nuevas versiones de CMS, pero trae consigo algunas mejoras que pueden ver en el CHANGELOG.

Entre ellas:

  • URLs interesantes para Joomla.
  • Mejor manejo para el Control + C (ahora funciona)
  • Mejor identificación para SilverStripe.
  • Notificaciones para los usuarios cuando se sigue una redirección.

Un abrazo grande,
Pedro

Actualización de seguridad para Drupal.

Hola a todos,

Salió una nueva versión de Drupal, que contiene parches de seguridad. Resuelve problemas de XSS, inyección SQL y CSRF, pero nada super grave que yo pueda ver.

Mas detalles acá: https://www.drupal.org/SA-CORE-2015-003

Pronto voy a sacar una versión nueva de droopescan.

Un abrazo,
Pedro