记一次绕过会员权限-爬空软件190万条数据的过程

开始

上周末在驾校练车中,收到朋友的消息,说最近发现一个宝藏软件,仔细了解之后发现,他口中的宝藏软件也就是一个有色的漫画软件,不过作为资深二次元的他,在他认为是宝藏软件的应该有点东西。我安装之后发现果然是个”好东西“,噗哈哈,资源多,无广告。

然后重点来了,这个软件是要会员的,他的需求是能不能让我破解一下,啊这!我又不是搞移动端软件开发的,咋破解!不过我还是对这个软件分析了一波,分析之后让我深深的鄙视了一番这个软件的作者。

分析

首先打开软件,发现这个软件主体就是漫画展示,除此之外也没别的。然后第二个tab是漫画的分类,有全部、校园、真人…在全部中,向上滑动可加载更多的漫画,我试着滑了几波,发现漫画还挺多,也不见底。于是惯例打开抓包软件试试能不能从这找到突破口,通过抓包发现,这个软件的数据传输没有做任何的防护加密,SSL没有,对数据内容的加密也没有,就是赤裸裸的展示着,响应回来的就是json数据。

全部漫画分页接口

请求地址:http://******.***/e/extend/api/index.php?type=cartoon&paged=true&size={}&page={}&filter=id
后来通过分析得知,虽然这个接口地址发出的是post请求,但是复制到浏览器地址栏回车也能得到数据,后端没做严格的判断,而且size参数也没限制,我改成100回车,也会返回100条漫画信息,真垃圾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
{
"code": 200,
"msg": "操作成功",
"content": {
"pageNum": 0,
"pageSize": 10,
"size": 10,
"startRow": 0,
"endRow": 0,
"firstPage": 1,
"prePage": 0,
"nextPage": 2,
"isFirstPage": true,
"isLastPage": false,
"hasPreviousPage": false,
"hasNextPage": true,
"navigatePages": 6,
"orderBy": null,
"total": 984,
"pages": 99,
"lastPage": 99,
"navigatepageNums": [],
"list": [
{
"id": 1826,
"name": "好友的私生活",
"author": "刑作家&经文旗",
"description": "太阳与闵皓是一起长大的好朋友,两人之间无话不谈,连A片也会一起分享。却不知从何开始,他们之间出现了不能与对方分享的秘密…",
"keywords": "好友的私生活",
"type": {
"name": "cartoon",
"value": "漫畫"
},
"categoryId": 1,
"category": ".都市",
"status": {
"name": "serialized",
"value": "連載中"
},
"freeFlag": false,
"onSale": {
"name": "on_sale",
"value": "銷售中"
},
"coverUrl": "https://******.com/*****/*****/cover-bf70b36b7a572f570b491040842b4231.jpg",
"extensionUrl": "https://******.com/*****/*****/extCover-8868cc5b9f37495ad5fdd6ef58bcb006.jpg",
"lastChapter": 38491,
"chapterCount": 7,
"wordCount": null,
"readCount": 310097,
"likeCount": 1,
"chapterPoints": 58,
"recommend": false,
"competitive": true,
"tags":"**,**,**",
"score": 9.86,
"vipFree": true,
"exclusive": false,
"fresh": true,
"discount": null,
"freeInTime": false,
"h": true
}
]
},
"options": []
}

通过这个接口响应的数据还真能得到不少的信息,漫画果然多一共984本,果然多…然后list数组中展示每一本漫画的详情(名称、作者、简介、封面图、标签、评分等等)。

接着我又点开了一本,发现响应的数据更全,该本漫画的目录都返回过来了。

漫画章节接口

请求地址:http://***.***/e/extend/api/index.php?type=cartoon&filter=directory&bookId={}
通过分析盲猜也知道,地址最后的bookId参数就是上一个接口里漫画的id。下面展示响应的部分json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"code": 200,
"msg": "操作成功",
"content": [
{
"id": 38485,
"title": "第1话-好兄弟之间没有秘密",
"bookId": 1826,
"sequence": 1,
"coverUrl": "https://******.com/*****/*****/cover-74db04a07d9cfc44612f795d5ded3554.jpg",
"freeFlag": true,
"points": 0,
"hasRead": true,
"hasPurchase": false,
"vipFree": false,
"freeInTime": false
}
],
"options": {}
}

返回的信息也挺全的,章节标题、封面图还有sequence排序字段。

紧接着就是每章节详情页了,这个要是没问题,那么这个软件的所有漫画数据就能被我掏空,噗哈哈。就这?由于大意直到后一天的异常让我知道没有那么简单。

由于这里所有的漫画第一章(漫画术语叫第一话?)都是免费试看的,之后的所有章节都是VIP才能看。于是我先点击这个免费的第一章,然后抓包分析,果然响应回来的数据包含了这张所有的漫画信息。

漫画章节详情接口

请求地址:http://***.***/e/extend/api/index.php?type=cartoon&filter=chapter&bookId={}&chapterId={}
参数中bookId是该本漫画的id,chapterId是上一个接口获取到的章节Id。响应的部分数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"code": 200,
"msg": "操作成功",
"content": {
"bookId": 1826,
"freeDay": null,
"vipFree": false,
"autoSubscribe": false,
"points": 0,
"orgPoints": 0,
"title": "第1话-好兄弟之间没有秘密",
"content": null,
"sequence": 1,
"imageList": [
{
"id": "1",
"bookId": 1826,
"chapterId": 38485,
"url": "https://******.com/*****/*****/img0-ef11ffe2cf3ef55b73069e16345567fd.png",
"sequence": "1",
"width": 720,
"height": 0,
"encodeName": "https://******.com/*****/*****/img0-ef11ffe2cf3ef55b73069e16345567fd.png"
}
],
"hasPurchase": false,
"freeFlag": true,
"id": 38485
},
"options": {}
}

可以看到imageList里url就是漫画的图片,encodeName和url一样,不知道啥意思,同样sequence是排序。

图片的伪装

于是复制了一张图片地址放到浏览器中打开发现不能够打开!WTF?咋回事啊,再次抓包发现软件中请求的图片非原地址,而是把url中图片的格式换成了html,然后返回的数据也不是图片,而是图片的base64编码,哈哈,这也难不倒我,只要没加密啥都好说。
漫画图片原地址:https://******.com/*****/*****/img0-ef11ffe2cf3ef55b73069e16345567fd.png
真实请求的地址:https://******.com/*****/*****/img0-ef11ffe2cf3ef55b73069e16345567fd.html
返回的数据:data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCA......

没啥难的,通过这三个接口完全可以把软件掏空。然后VScode打开,python脚本一把梭。我的思路是根据接口返回的数据取重要的建数据库,存表里,三张表完全够用,一张存漫画主题信息,一张存漫画的目录章节信息,最后一张存章节的详情信息,标准的一对多表结构。

于是代码脚本一顿梭哈,最后大功完成,在爬取得过程中发现了一点异常就是,每一本的漫画除了第一章有很多条详情数据外,之后的每一章都是3条数据,当时我还在心里安慰自己,可能这就是二次元画家的风格吧,每一章只更新一点点。然后也没多想,之后就开始运行脚本了,一切没啥大的异常只是刚开始爬取有一本章节接口返回的500,我在软件中测试该本漫画也是打不开的,吓一跳,以为触发警告,被防爬虫措施ban掉了呢。无关紧要,每次请求后多加了一条判断逻辑,返回非200就continue一下,继续爬取下一个。

就这样一上午就扒干净了该软件的“所有的”资料,之所以所有的加引号,那都是我自以为是的结果。且听我娓娓道来。

在我以为扒干净之后,就开始写下载脚本了,下载的逻辑就是,查询主表,获取各章节,根据各章节获取详情,然后转换真实下载地址,就是那个把后缀改成html的。然后根据返回的base64,再转换成图片,保存到本地中,当然保存到本地文件夹得建好,我的逻辑是每本漫画建一个文件夹,在这个文件夹里每个章节再建一个文件夹,最后再在章节文件夹里存具体的图片。

说时迟那是快,python脚本又一顿梭哈,第一版代码就下好了,我也没急着开始执行爬取。根据原图片地址报错页面得知,这些图片是存在阿里云OSS里的,我就开始了大量的查资料,“OSS有没有防爬取措施?”,“阿里云OSS有没有请求频率的限制?”等等,然而也没搜索啥有用的信息,我又结合我开通的阿里云OSS后台,研究了一番所有功能发现,没有这方面的功能,倒是有个根据referer限制防盗链的,为了防止意外,我也是在请求头中加入了referer,值为该漫画网站的域名。

于是我又改造了下脚本,在章节地方加入了多线程。该本漫画,多个章节内容同时下载。刚开始还有点害怕,创建章节目录的地方还sleep一下,防止跑的太快等待随机的0-2秒钟,后来干脆sleep也注释掉了,直接狂奔,阿里云不愧是阿里云,OSS的访问速度嗖嗖的,不排除对方买了cdn加速服务,一秒几十张图片的速度下载着。

本来是打算晚上执行的,但是没忍住就把脚本跑了起来,毕竟自己创造的“孩子”总要拉出来溜溜。之所以想在半夜执行,就是怕跑着跑着把他的下行流量跑光,阿里云给他发欠费短信就暴露了,噗哈哈(鸡贼),爬取的过程中我也粗略的计算了下,20多万张图片应该有上20G吧,加入多线程的原因也是怕被发现,所以赶紧加速下载起来。

下载中看着控制台疯狂刷的日志,还是对之前的那个问题疑问着,难道漫画界更新有不成文的规定吗,每章节只更新3张图片?

全部下载完之后,发现有进30多G,随意打开一个漫画文件夹中,看了一篇,还真他妈好看,故事情节很带劲,咳咳。但是看完第一章,第二章,第三章怎么和第二章不连贯啊,之后的几个章节也是。这时想起来了那个VIP功能,是不是之后的所有章节都3张图片是预览的,之所以后端返回三张,有可能不是vip的缘故,完,这下没辙了。这爬下来也没法看啊!

于是再次分析,发现这个几口域名的根路径在浏览器打开竟然,和软件一样的界面,啧啧啧,最低成本的软件了,网页打包的,和我之前的那个助聊软件如出一辙,可惜我那个现在打开闪退了,也没再维护,现在主推助聊小程序和网页版了。

跑题了,说回这个网页打包的漫画软件,怪不得在软件里,漫画详情页顶部也显示让下载客户端,早发现不对劲了。既然网页打包的,还使用接口进行交互数据,这差不多是个前后端分离的项目。

伪造身份欺骗后台拿数据

得!既然涉及到VIP权限,那就先注册个账号看看吧,底部不是说的注册账号绑定手机送阅读币吗,看这阅读币能够阅读两章付费内容的了,那我要看看。你这网站到底有没有隐藏的内容,别再纸老虎般糊弄人,也没有章节的剩下内容。然而注册账号后,发现根本就没有绑定手机号的地方,就没这功能,妈的,这个线索又断了。

浏览器F12打开开发者工具,研究了一番接口参数数据,发现,登录后请求cookie中携带的参数引起了我的注意。
登录携带的cookie参数:

bzsoxmlusername=1qw21qw;bzsoxmluserid=65793;bzsoxmlgroupid=1;bzsoxmlrnd=7RyMS9Mb3Pmsd23ouNtV;bzsoxmlauth=5ec94540370f10520e7673a4ecb97d5a;ticket=7RyMS9Mb3Pmsd23ouNtV

直白的看得出分别是用户名、用户id、分组id后面的不知道啥意思了。如果userid递增的话。这网站用户还真不少,我是第6万多名了。

到这也始终没有突破口,于是猜想我的这个userid换成其他userid请求会有啥反应。于是携带上这个cookie,在postman中再次拿漫画章节详情试了试。关于id一般排在第一位的大多是管理员或者测试账号啥的,于是把bzsoxmluserid改成了1,找了个付费的章节,请求发现返回的真的不再是3条数据了,为了防止偏差,我把bzsoxmluserid改回我的id再次请求,发现返回3条数据,what?这就绕过了?额,的确就这样绕过了。

找到真相

于是我在浏览器中也是操作了一番,F12开发者工具Application-Storage-Cookies,直接修改bzsoxmluserid值为1。然后刷新网页,发现个人资料全变了,身份也真的是VIP,又测了其他userid发现,资料身份也会随着改变。真的是大的逻辑漏洞。

关于这个第一位id,之前绕过某网站后台也是通过这种方式绕过的(记一次攻破某网站拿到超级管理员权限的过程 )。所以对于这种递增的userid,排在前几位的不做处理,还是有点风险的,当前这不是主要的,其实也没关系。主要的是这网站逻辑的逻辑有巨大的漏洞,我猜想他们的逻辑是这样的:用户登录后,后端返回用户信息及其他参数给前端,前端放到cookie中,然后每次请求都携带着,后端获取到cookie信息后,只根据bzsoxmluserid查询数据库获取用户信息,来判断用户的权限角色等,如果该userid的账户是vip就返回所有的数据。对,肯定是这样的。

在发现userid是1可以返回所有数据后,立马改写了python脚本,加入了cookie参数,然后运行,看着控制台日志、数据库数据都正常的爬取中。

爬数据被抓

这时已经是晚上快21点了,脚本运行爬取了一会后看见控制台日志发现,不行了,从第二章之后又开始返回三条数据了,于是立马停掉脚本,postman再次请求发现已经不行了,卧槽这是咋回事,于是userid换成2、3、4,发现4是返回多条数据的,浏览器中发现是vip,并且还剩一个多月的时间。我猜想网站管理员肯定发现我爬取数据了,然后紧急把userid为1的账户撤掉了vip的角色。肯定是这样的。

接下来没有再运行脚本,我已经被监控了。然后对网站的各功能进行了检查,发现网站充值页面,写着客服在线时间是14点到凌晨12点,于是在第二天早晨又开始搞了,这次很顺利,userid用的4,这回数据量很大,上次半天就全部爬取完毕了,毕竟从第二章开始就只保存三条数据。这次他妈的984本漫画数据,爬取了整整一天,早晨6点40就开始搞起来了,中间出了一次差错,停了不到连分钟,又开始爬起来了,一直到晚上六点全部爬取脱库完毕。

数据披露

漫画主表数据一共984条无差异
漫画章节目录表38507条数据
漫画章节详情数据1903540条数据
网站数据量可真不小。

关于网站会员,我也试了一些账号,比如比我userid大和小的id,发现会员身份的账号还是不少的,可见一斑,这违法网站靠会员收入还是非常赚钱的。

总结

这是一次典型的相信前端数据不验证的逻辑漏洞问题。哪怕他根据bzsoxmluserid和bzsoxmlusername联合验证身份也不会让我那么轻易的绕过权限。之前就看过相关的新闻,“某男子利用抓包软件修改请求接口参数,进行薅羊毛,给公司造成巨大损失”等等,这些都是后端不做安全验证而相信前端传过来数据的后果。所以我在开发工作中,是不会相信前端传过来的任何重要数据的。总是会在后端进行验证一遍,以减少不必要的损失,而且在服务层也会限制接口的请求频次,放置恶意用户对数据的爬取。

总之挺垃圾的,没什么技术含量。绕过了vip权限,也没啥成就感,网站挺垃圾的,漏洞也很低级。


-------------本文结束感谢您的阅读-------------
感觉文章不错,就赏个吧!
0%