Flash XSS 漏洞詳解 根治的好辦法


本文的目的是深層次的分析Flash的ExternalInterface的XSS漏洞,並提出比較有效的解決方案。
 
首先,我們看看什么情況下,會出現XSS。
 
第一種情況:
 
     把flashvars傳入的參數(或者其他能被別人控制的方式)當ExternalInterface.call的第一個參數
 
package
{
        import flash.display.Sprite;
        import flash.external.ExternalInterface;
       
        public class XSSTest extends Sprite
       {
               public function XSSTest()
              {
                      var jsFunction:String = loaderInfo.parameters.jsFunction;
                      var param:String = "abc";
                     ExternalInterface.call(jsFunction, param);
              }
       }
}

 

     注意,這里通過flashvars傳遞了一個參數,是js的函數。這種方式比較常見,swf可以做成通用,放到不同的業務中使用,每次只需要傳入對應的js函數即可。但是,這里就存在漏洞了。
     在瀏覽器中,構造 url: XSSTest.swf?jsFunction=alert(/XSS/),訪問swf,並以get參數的形式傳入flashvars,結果,造成了
 

 

 
甚至更狠一點, jsFunction=function(){alert(1);alert(2);}。。。
 
當然,這么惡作劇alert一下,貌似對小白用戶沒什么損失,但如果在function內調用這個域名的CGI,就能帶來很大的驚喜了~~~因為這里能獲取到對應的cookie,時間有限,具體攻擊的方式,這里不多說。
 
 



第二種情況:
 
     把flashvars傳入的參數(或者其他能被別人控制的方式)當ExternalInterface.call的第二和第三個參數
 
    這次,我們使用這段代碼:  
   public function XSSTest()
   {
          var param:String = loaderInfo.parameters.param;
          ExternalInterface.call("console.log", param);
   }
     這個方式也許沒有這么簡單進行XSS,但對於黑客來說,還是有辦法的。
 
 
在IE8下調試模式下,我們可以看到ExternalInterface的代碼:
 
正常情況下,Flash player會生成這樣的代碼:
 
try { __flash__toXML(console.log("good" )) ; } catch (e) { "<undefined/>"; }
 
對比自己寫的as代碼和生成的這段js代碼,可以猜測,Flash player是以一種簡單的拼接字符串的方式實現的。
 
稍稍做個小把戲,結果就可以注入代碼執行了。
 
 
是不是很神奇?怎么做到的呢?為什么url稍稍變化可以達到這樣呢。我們看看現在的 js代碼:
 
try { __flash__toXML(console.log("\\" ));alert(/XSS/);}catch(e){} //")) ; } catch (e) { "<undefined/>"; }
 
正好跟原來的雙引號對上了,結果,最后的catch也被替換了。。。也就是說,黑客可以寫自己的函數了,想怎么執行都可以了。。。
至於為什么這里雙引號對上了,可以簡單猜測flash遇到字符串中有雙引號的時候,只是簡單的以  \"  方式打印成js代碼,但如果用戶再惡意拼一個\,就負負得正了。。。
 
(這里__flash__toXML的代碼並不是關鍵點了,所以將在文章最后再列出)
 
 
第三種情況:
 
     沒有對swf Object的id沒有過濾
 
     頁面加載Flash,我們需要設定Object或者embed的id,否則ExternalInterface會失效。而這個地方,也會被黑客利用。
 
     我們看看實際執行的代碼:
 
try { document.getElementById("XSSTest" ).SetReturnValue(__flash__toXML(alert( null)) ); } catch (e) { document.getElementById("XSSTest" ).SetReturnValue("<undefined/>"); }     
 
     看到這里,應該發現跟上邊說的第二種情況很類似,黑客可以通過修改了Object id,惡意閉合雙引號,達到目的。
 
 
 
 
接下來,簡潔的總結一下怎么防XSS。
 
對於第一和第三種情況,我們應該對字符進行過濾,例如用以下的兩個函數:
              
               public static function checkJsFunctionValid(functionName:String):Boolean
              {
                      var reg:RegExp = /^[a-zA-Z0-9_\.]+$/;
                      return reg.test(functionName);
              }          
    
               public static function checkObjectIdValid():Boolean
              {
                      if (ExternalInterface.available)
                     {
                            var objectId:String = ExternalInterface.objectID;
                            if (!objectId || (objectId == objectId.replace(/[^0-9a-zA-Z_]/g , "")))
                                   return true;
                            else
                                   return false;
                     }
                      return true;
              }
 

 

對於第二種情況,我們應該盡量避免這樣跟js傳遞數據,但如果實在無法避免。可以用這樣的方式轉義字符串:
 
str.replace( /[\"\\]/g , function(d:String, b:*, c:*){ return '\\' + d.charCodeAt(0).toString(8); });
 
簡單解釋一下,這里把雙引號和反斜杠這樣比較敏感的字符,替換為轉義表示。再輸出成js代碼時,正好又還原回去了。
 
例如:
\\\"\\\"})));}catch(e){alert(/xss/);}//     變成了     \134\42\134\42})));}catch(e){alert(/xss/);}//
 
 
 
 
 
附帶額外的在IE8 開發工具中抓獲到的代碼:
 
function __flash__arrayToXML(obj) {
         var s = "<array>" ;
         for (var i=0; i<obj.length; i++) {
                s += "<property id=\"" + i + "\">" + __flash__toXML(obj[i]) + "</property>";
        }
         return s+"</array>" ;
}
function __flash__argumentsToXML(obj,index) {
         var s = "<arguments>" ;
         for (var i=index; i<obj.length; i++) {
                s += __flash__toXML(obj[i]);
        }
         return s+"</arguments>" ;
}
function __flash__objectToXML(obj) {
         var s = "<object>" ;
         for (var prop in obj) {
                s += "<property id=\"" + prop + "\">" + __flash__toXML(obj[prop]) + "</property>" ;
        }
         return s+"</object>" ;
}
function __flash__escapeXML(s) {
         return s.replace(/&/g, "&amp;" ).replace(/</g, "&lt;").replace(/>/g, "&gt;" ).replace(/"/g, "&quot; ").replace(/'/g, "&apos;");
}
function __flash__toXML(value) {
   var type = typeof(value);
         if (type == "string" ) {
                 return "<string>" + __flash__escapeXML(value) + "</string>";
        } else if (type == "undefined") {
        return "<undefined/>" ;
        } else if (type == "number") {
        return "<number>" + value + "</number>";
        } else if (value == null) {
        return "<null/>" ;
        } else if (type == "boolean") {
        return value ? "<true/>" : "<false/>";
        } else if (value instanceof Date) {
        return "<date>" + value.getTime() + "</date>";
   } else if (value instanceof Array) {
       return __flash__arrayToXML(value);
   } else if (type == "object") {
       return __flash__objectToXML(value);
   } else {
            return "<null/>" ; //???
        }
}
function __flash__addCallback(instance, name) {
  instance[name] = function () {
    return eval(instance.CallFunction("<invoke name=\"" +name+"\" returntype=\"javascript\">" + __flash__argumentsToXML(arguments,0) + "</invoke>" ));
  }
}
function __flash__removeCallback(instance, name) {
  instance[name] = null;
}
  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM