P2P平台暴雷后,该怎么确认自己的待回款资金?

本文不探讨P2P行业现状,只是借此介绍下接口渗透以及中间人攻击原理

前几年我试着把一些资金投到P2P产品里,想要赚取比余额宝多一些的利润,当时选择的某个大平台下的一个P2P子品牌。到了去年这个时候,监管政策不断收紧,隐隐约约觉得原先的平台不稳妥,但同时又觉得P2P产品的合理性是有的,监管层面应该会给头部的大平台留一些生存空间。自以为是的把资金切换到了头部的并且都已上市的平台上,好在我还懂不能把鸡蛋都放一个篮子里的道理:没把所有的钱都投到P2P,还把投入到P2P的钱分散到了两个平台上。

不过平台暴雷这个事情还是被我赶上了↓↓↓

我惊愕平台单方面擅自修改了用户协议,但愤怒归愤怒,我需要快速理解现在的还款逻辑,方便我收集证据、维权:

比如你在去年3月1日投标给这个网贷平台,期限一年,本金1万。按理能在今年的2月29日收到本金一万,以及利息若干。P2P平台实际运作时,为了分摊风险,不会把你的钱只贷款给一个人,它会把你的钱打散,分到多个散标之中,放款给借款人。

该平台修改协议后,只有当你投的散标回款后你才可能收到回款,而且这些散标不是一年前的,有可能是半年前的,有可能最近才发,而且整个回款周期最长要三年。

惨,我为了图那点利息,资金被平台占用四年之久,而且连一年的利息都不给你了,只还本金。

打开APP看到的惨状:

不考虑之后跟平台怎么撕逼维权这些事,面对这么多散标,并且已经无法再相信平台的情况,有几个问题亟待解决:

  • 如何确认被拆成这么多散标后,我的资金总额是完整的?
  • 每天都可能有散标回款,少则一分,多则几十、几百,如何进行对账?

接口嗅探

我的想法是从接口层面拿结构化的数据出来,累加之后判断散标总额是否正确,之后每次回款都进行对账,不能遗漏一笔标的。

上面的方法是建立于该平台APP未进行双向证书验证(大部分APP都没有该防护),并且接口设计上未做严格的数字签名,能够被外部用户手动构造合法请求的基础之上。动手试试看吧。

我这边使用的mitmproxy,也可以使用charles,完成iOS的网络配置后,我试着打开APP做一些操作。

mitmweb

接口请求在mitmweb上齐刷刷的出现的,果然,这类APP并没有双向证书校验,通讯报文一目了然,我也很快定位到我需要的一个接口上:/api/v2/v3/user/repay/list/BIDDING

接口分析

先分析下这个接口的query params,比较简单:

  • holdStatus: 散标持有状态吧,0应该表示为回款的
  • page&size: 分页参数,page从1开始递增

再来看下请求头参数,这里就有一大堆了:

  • Device-Mac: 猜测是手机硬件信息,但是应该可以随便传
  • Cookie:这个没什么可说的,原样复制就行
  • vendorId: 估计是表示手机类型,苹果或者安卓
  • ScreenRes: 不清楚
  • Channel: 不清楚,但不重要
  • Keys-Identity:看着很重要的一个值,但是固定不变,抄就行了
  • Authorization: 最担心的一个参数,不过居然也是固定不变,障碍扫清了
  • safeimeiidfa: 不清楚什么含义,但是值是一致的,也是固定不变的,抄

再来看下响应报文,看看怎么提取我要的数据:

{
    "d": {
        "repayments": [
            {
                "dateDesc": "2020年02月29日",
                "items": [
                    {
                        "amount": "0.01",
                        ...
                    },
                    {
                        "amount": "0.01",
                        ...
                    }
            },
            {
                "dateDesc": "2020年03月01日",
                "items": [
                    {
                        "amount": "0.01",
                       ...
                    }
                ],
            }
        ],
        "xplanContinuedGoodsNo": "",
        "xplanContinuedRate": "0"
    },
    "m": "",
    "r": 1
}
  • 回款数据都在d.repayments节点下
  • 回款记录按天来切割,并存放于..items节点下,是个数组
  • amount即为回款金额,是字符串类型

于是我快速写了个Python脚本来统计我的资金:

import requests

class WDWClient:
    headers = {
        "Device-Mac": "xxx",
        "Referer": "https://mobilegt.weidai.com.cn/api",
        "Cookie": "xxx",
        "User-Agent": "xxx",
        "vendorId": "apple",
        "ScreenRes": "2",
        "Channel": "AppStore",
        "Keys-Identity": "xxx",
        "Device-ID": "xxx",
        "Device-Version": "xxx",
        "Authorization": "xxx",
        "safeimei": "xxx",
        "idfa": "xxx",
        "Device": "1",
        "Trace-ID": "xxx",
    }

    def __init__(self):
        self.client = requests.Session()
        self.client.headers.update(self.headers)
        self.base_url = "https://mobilegt.weidai.com.cn"

    def get_bidding(self, hold_status=0, page=1, size=20):
        """获取散标"""
        return self.client.get(self.base_url + "/api/v2/v3/user/repay/list/BIDDING",
                              params={"holdStatus": hold_status, "page": page, "size": size}).json()

def get_repayments(res: dict) -> list:
    items = []
    repayments = res.get("d", {}).get("repayments", [])
    for day in repayments:
        if day.get("items", None):
            items.extend(day['items'])
    return items



wdw = WDWClient()
amount = 0
count = 0
page = 1
size = 20

while 1:
    res = wdw.get_bidding(hold_status=0, page=page, size=size)
    items = get_repayments(res)
    amount += sum([float(item["amount"]) for item in items])
    c = len(items)
    count += c
    if c < size:
        break
    page += 1

print(count)
print(amount)

到这里我就能统计出散标的资金总额了,暂时是没有差错,之后会再利用它的接口来实现对账能力,这是后话了。

整个接口渗透的过程非常简单,这其实也暴露出该平台技术人员在安全这块掉以轻心了:

  • 没有双向认证的https并不会提升什么安全性
  • 没有数字签名,用户侧很容易构造接口请求,又天然的绕开了前端校验
  • 没有控制接口调用频次

而对于“我”这个角色,不要去尝试越权、资金修改调用等,即便它有可能做好了完备的校验:狗咬你一口,你不需要咬回去

中间人攻击

到这里,我还是想尝试讲下中间人攻击的原理。

首先看一张https过程的图:

https

我把关键点罗列下:

  1. 服务端的证书分成公钥、私钥两部分,顾名思义,公钥是可以公开的,私钥不准别人看的
  2. client接入时,服务端会把公钥下发给client
  3. client会验证公钥的有效性,注意:只是验证,如果无效、非法呢?接受还是拒绝?这时由client来决定的,没有强制要求

中间人攻击形成的原理就是上面第三点,看下图:

在原有的请求链路加入一个中间人(Proxy),此时的通讯过程就会变成这样:

  • User向MITM建立请求,然后MITM向Service建立请求
  • Service的公钥不再直接给到User,而是MITM,MITM此时把Service公钥替换成自己证书的公钥,并把自己的公钥下发给User
  • User以为跟Service协商出了一个对称性密钥,实际却是跟MITM协商的,此时User跟MIMT通讯完成透明化
  • MITM也跟Service协商了对称性密钥,完成了通讯的透明化
  • User跟Serivce之间的通讯完全被劫持,MITM可以做任何事

要防范中间人攻击有几种做法:

  1. 双向认证
  2. 拒绝无效、非权威结构颁发的证书

对于第二点,思维严谨的同学可能会提出假设:如果MITM用的证书是可信的呢?如果能想到这一点,接受我由衷地称赞吧!👍👍👍

贴一张图,不多展开了