一次微信活动刷票的絮叨

0x00 起因

近日一好友推来一链接,说是自家猫咪们正在参加一个评选活动,需要投票支持。举手之劳,再加上两只小猫也是从他们小不点的时候看着长大的,甚是讨人喜欢。没咋细看,投完票之后回到首页看到的是这样的

活动首页图

关注点当时只在小猫上了,随手翻了翻,发现这些参赛的喵们,人气多是单数票,俺们的小喵已经有近200左右的支持,那到底处于什么样的位置呢?

真心的讲,并没注意看官方的活动界面,只是满脑子琢磨的是这个商家有点意思呀,让大家只投票,不按照排行来,这样很好的避免了最新加入的选手,猛然一下子看到自己距离第一名的落差从而放弃参赛,这样应该更会让选手产生自己有很大优势的错觉呢?

我想的是必须要很明确的知道,当前小喵虽然200票左右,看起来很有优势,那么,以这个票数具体处于整个比赛的第几梯队?考虑目前一些大众票选活动多数都是10次活动8次骗,是否要抱有巨大期望? 避免期望越大失望越大。

落定心思后,直接开干,先来解决心中的疑惑。事后才注意到,人家活动方是提供了排行榜的,是自己没注意到,哈哈,快被自己蠢哭了。
活动首页图-排行榜

0x01 白鹤亮翅

祭法宝,开撸。由于东西并不复杂,又是基于公开协议的,就非常简单的抓到了数据包。

经过分析数据包,找到参与报名人数用户信息分页加载的接口。

由于脚本会交给朋友使用,所以,就简单使用nodejs来进行实现。

1.获取当前活动的参与报名人数

代码片段-获取基础数据信息

- 构建请求路径
- 发送数据请求,获取响应结果
- 从响应结果中找到需要的数据

2.获取当前的参与列表

拿到报名人数后,按照用户分页数据接口每次返回10条进行简单的分页计算,获取到可以加载的总页数,然后使用循环来完成每页数据的加载。为了不给活动官方服务器造成压力,就慢慢的来获取。

代码片段-获取用户列表信息

- 构建请求路径
- 构建请求参数
- 发送数据请求,获取响应结果
- 解析结果,转换为自己的逻辑对象

加载完数据后,然后对缓存的数据按照票数进行排序处理。

代码片段-获取数据并排序

最后对排序的结果进行输出,就可得到想要的榜单了。

代码片段-输出排行榜

非常简单的就拿到了需要的排名表,可以看到虽然靠前,但还是不算太高的。

有编程基础的朋友可能一眼就看明白怎么回事了,其实这里面涉及的东西也是不少的。

本来想着通过另一个角度,来展开说说的。原计划是想从外挂的原理、辅助工具的角度开始说,然后到这个事儿其中涉及到的http协议知识中间人攻击重放攻击等等。其实也就名字听起来感觉陌生,实际上在编程工作中大家都有用到,只是场景不同罢了,理解起来都是一回事儿。

由于这里主要想说的是后面的事儿,这里就不过多啰嗦了,喜欢hacker的朋友可以一起多聊聊。

0x02 野马分鬃

接下来主要介绍的,也可能是大家关心的了 —— 刷票。

其实初衷并不是来刷票影响比赛的,虽然也知道这种小成本比赛多数都是内定的。

但是,听到朋友说正在研究怎么刷票的时候,我心里就跳了一下,既然已经在路上了,也不介意多看一下,于是就抓了下投票的数据包。

当看到数据包构成的时候,当时心里已经有数儿了。刷票,没有一点积累的是做不到了。

为什么这么说呢? 做过微信或者其他平台的第三方服务开发的同学,应该都接触过一个词是:openId,我举个简单的栗子

应用服务商A在微信平台注册一个应用,获取到安全码:ABC
应用服务商B在微信平台注册一个应用,获取到安全码:XYZ

同一个用户(OPQ)在分别关注了两个服务商的公众号后,可以获得不同的openId,这个openId就是用户在服务商A或者B系统中唯一的用户ID

1
2
OPQ  -> 关注服务商A -> ABC + OPQ = abc123opq
OPQ -> 关注服务商B -> XYZ + OPQ = xyz456opq

当然具体的算法逻辑比这个要复杂的多。

投票的接口中使用了当前微信用户的openId,所以,想要刷票,那就得有非常多的微信账户来作为基础。

正当我准备放弃的时候,扫了一下前面加载分页数据的响应数据包,看到了这个

报文片段-服务端响应数据

当前参与者的列表中,竟然将参与者的openId给暴露出来了,更不可思议的竟然还有手机号(前面在做数据对象转换的时候,只关注票数了,还真没注意到)。

心中默默为这个接口的开发者写一个大大的服字。

盘算着所有参与者应该都只会支持自家的小喵,每人每天可投3票,那么这些openId每个应该还有再投2票的机会,既然如此,嘿嘿嘿嘿……

后面就简单了,就按照前面的套路,回补前面的获取用户列表逻辑,构建投票数据包,然后发送数据包。

假设后台有设计投票流水查看功能,那么还不能让投票显得太过紧凑。所以,就通过代码实现一个10分钟投20票的功能。这20票通过随机时间,散布在十分钟之内,这样使看起来的数据还算合理一些。

日志片段-输出执行结果

0x03 后记

最后,想聊一聊的就是关于web系统的安全性,大部分开发者在开发中并不会过多关注这个。毕竟,也都受大环境影响,业务的开发工作量那么多,能实现要求的业务功能就已经很不错了,哪里还会想这么多。

其实,我觉得这多数取决于开发者的意识,毕竟,更多同学感觉这些更应该是测试人员的事儿。

当然,最主要的原因就是:价值问题。每天上线的项目、产品不计其数,被攻击的又有多少?

虽然外界原因有很多,但是,作为一个开发人员,我们也应该顺带的多想想。业务功能是实现了,工作任务也交差了,这样就完事儿了么?尤其是基于前后端分离开发的模式。

就拿前面刷票的这个问题来说,涉及到的公司暂且不提,也算上市公司,技术能力应该是不弱的。

代码上来讲,项目里面可能也是DTOEntity满天飞,我一直不提倡一个实体走天下,要针对不同的View场景给出不同的数据,并且,要屏蔽掉敏感数据。关于这点,有时候想想我就乐,以前大家还通过各种手段来猜解表字段,现在有的接口中都直接把表字段给列出来了。

设计上来讲,能基于构建请求参数来完成攻击的,多数都是滥用、校验等级低等。他们的设计可能是这种style:

- 用户为某条数据进行投票 toupiao(用户ID, 投票目标数据ID); 
- 前端调用toupiao接口,携带当前用户ID和目标ID;

这样很容易造成越权漏洞,最起码,敏感数据放session中那也是相对安全的!

0x04 后后记

本以为一直在第二待着就舒舒服服的了,结果那帮孙子刷的真够狠的,1000多票的距离往开拉,玩的太猛了。

由于参与人数增长缓慢,这种偷票方式会很快导致动力不足,所以,需要更多的openId

猜想,活动官方管理后台应该会有投票记录来方便商家统计使用。

于是,找到提供活动的官方平台,注册账户。嚯,做的挺齐全的还。

找到当前活动的模板,然后创建一个Test活动,果不其然,活动的管理后台有每个参与者的投票列表,而且列表的数据中也包含有openId

只是,后台整体的健壮性比活动模块要好的多,一些敏感数据也被保护的很好,简单尝试了一些注入和越权,结果都不太理想,由于时间不多,就没有过度深入。等以后有时间了,一定好好办了她。

平台图片-活动模板