Alex Schapiro 关于

破碎的OTP和开放端点如何将一个约会App变成跟踪者的乐园

私人消息、护照信息、性取向等在Cerca Dating App中暴露无遗

2025年4月21日

初创公司需要认真对待安全问题

时间线 & 负责任披露:在发现这些漏洞后,我于 2025年2月23日 通过电子邮件联系了 Cerca 团队。第二天(2月24日),我们进行了一次富有成效的视频通话,讨论了这些漏洞、潜在的缓解措施和后续步骤。在我们的谈话中,Cerca 团队承认了这些问题的严重性,对负责任的披露表示感谢,并向我保证他们将迅速解决这些漏洞并通知受影响的用户。

从那时起,我多次联系(在 3月5日3月13日),寻求关于修复和用户通知计划的更新。不幸的是,截至今天的发布日期(2025年4月21日),我得到的都是无线电静默。据我所知,尽管他们早些时候向我保证过,但 Cerca 尚未公开承认此事或告知用户有关此漏洞的信息。他们在通话后也没有跟进我,并忽略了我所有的后续邮件。

但是,我能够独立确认,本博客文章中详细描述的漏洞此后已得到修复,这使我能够负责任地发布这些发现。

很少有人知道如何制作安全的App——而抢占市场的冲动使消费者面临风险。我的一些朋友说他们收到了来自一个名为Cerca的新约会App的短信。 显然,约会App需要大量的个人信息,因此我想确保我朋友的数据在使用该App之前是安全的。

短信告诉用户下载Cerca

我下载了该App并启动了 Charles Proxy(使用iPhone App)来拦截网络请求,看看这个App在底层做了什么。

首先,让我们登录。 他们只使用基于OTP的登录(只是将代码发送到您的电话号码),所以我去检查了触发一次性密码的响应。 BOOM – OTP直接在响应中,这意味着只需使用电话号码即可访问任何人的帐户。

显示OTP响应的一次性密码漏洞的屏幕截图

但是,我现在需要找到一种方法来确定谁拥有帐户——我不想只是猜测电话号码。 因此,我转到 api.cercadating.com 端点并使用 directory fuzzer 枚举路径,希望找到相关的端点。 如果没有相关的App标头,我无法访问站点的任何部分:

API所需的App版本信息

所以我使用 Gobuster 传递了该标头,令我(半)惊讶的是,由于找到了提供 openapi.json/docs 端点,所有端点都被暴露了!

找到的来自Gobuster的/docs端点的屏幕截图

我启动了 Burp Suite 并使用 match-and-replace 工具始终传递该app-version标头,以及我从Charles proxy提取的 bearer token。 这里变得更有趣了。

显示所有暴露的API端点的屏幕截图

一些不受保护的端点似乎只影响业务逻辑——例如这个我可以用来强制两个人相互匹配的端点:

用于强制用户匹配的匹配端点的屏幕截图

但是其他的,比如获取用户资料端点 (user/{user_id}),似乎更有趣。 此端点采用有效的用户ID并返回各种个人信息(包括用于完全帐户接管的电话号码,这要归功于OTP漏洞)。 我编写了一个快速的 Python 脚本来找出有效的用户ID,然后 BANG – 我进去了。 我可以枚举所有用户; 响应格式如下所示:

{"status":"success","message":"string","results":0,"data":{"first_name":"string","last_name":"string","gender":"MALE","interested_genders":["MALE"],"city":"string","latitude":0,"longitude":0,"university_email":"user@example.com","university_email_verified":false,"industry":"string","profession":"string","date_of_birth":"2025-02-21","height":0,"university_id":0,"university_name":"string","profile_completed":false,"national_id_verified":false,"mobile_verified":false,"email_verified":false,"premium":false,"premium_expiry":"2025-02-21T21:31:06.213Z","active":true,"paused":false,"onboarded":false,"profile_type":"PROFESHIONAL","mobile_number":"string","email":"user@example.com","user_type":["user"],"user_id":0,"remaining_searches":0,"profile_images":[],"university":{"id":0,"name":"string"},"score":[],"match_preferences":[],"user_prompts":[],"mutual_contact_previews":[],"mutual_contact_preview_data":[],"mutual_contact_count":0,"created_at":"2025-02-21T21:31:06.213Z","updated_at":"2025-02-21T21:31:06.213Z","zodiac_info":{},"distance_km":0,"final_score":0,"age":0},"meta":{}}

现在,我不仅可以找出链接到帐户的所有有效电话号码(然后可以使用OTP错误配置来接管),而且所有这些PII都在那里,无需OTP登录! 但情况变得更糟—— national_id_verified 字段似乎特别令人担忧。 果然,他们也将您的护照或ID信息存储在系统中,如下所示:

返回个人数据的用户资料(ID)端点的屏幕截图

{"status":"success","message":"string","results":0,"data":{"verification_type":"PASSPORT","document_number":"string","front_side_url":"string","back_side_url":"string","selfie_url":"string","status":"pending","id":0,"user_id":0},"meta":{}}

这仅对登录用户可用,但由于我可以作为任何用户登录,因此如果他们已提交,我可以查看任何人的ID信息(同样,我没有这样做)。 我不仅可以看到 任何人与潜在约会对象的私人消息,我还可能能够看到他们的 护照信息! 我运行了一个快速脚本,以查看我可以获取多少用户信息,有多少人注册为耶鲁大学学生(我假设更多人是耶鲁大学学生,也许只是没有填写他们的大学),以及有多少用户输入了他们的ID信息。 该脚本基本上只是计算了它看到了多少个有效用户; 如果在连续1,000个ID之后没有找到,则停止。 因此,可能还有更多(Cerca本身声称第一周有1万个用户),但我能够找到6,117个用户,其中207个已将他们的ID信息输入,而19个声称是耶鲁大学学生。

显示用户数量、耶鲁大学学生和具有ID信息的用户数量的脚本输出的屏幕截图 Cerca的Instagram帖子声称第一周有1万用户的屏幕截图

这是一个疯狂的泄漏!! 我可以访问 性偏好、亲密消息以及来自(根据Cerca自己的说法)成千上万毫无戒心的用户的各种PII。 Cerca在其隐私政策中说“我们使用加密和其他行业标准措施来保护您的数据”,但这显然具有误导性。 这对用户安全和隐私构成重大风险。 考虑到我只是一个随便看看的大学生,完全有可能存在其他关键漏洞(尽管完全帐户接管设置了一个很高的标准)。

此漏洞的后果是对隐私的完全侵犯,并可能造成非常有害的现实后果。 人们需要学习如何制作安全的App,而不是在他们的App不安全时声称它们是安全的。 尤其是对于约会App! 您不能指望所有用户都像我在本文中所做的那样进行检查。 谁知道在我发现之前有多少人已经可以访问所有这些数据? 有人可能已经下载了一个完整的6,000多个用户的个人信息和亲密聊天的数据库,准备利用它。 如果有恶意的人掌握了这些信息,可能会导致身份盗用、跟踪、勒索——你能想到的都有可能。 这些类型的漏洞非常可怕,它们可能会在一夜之间毁掉生活。 人们需要优先保护用户数据,而不仅仅是发布他们认为可以传播开来的App。 我本意不是为了找到这个漏洞来写这篇博客文章,但由于Cerca自我们的通话以来没有回复我的任何邮件,也没有警告任何用户,我认为发布这篇文章是公平的。 不想 pwn 任何人,只是想要一个更安全的互联网!

Alex Schapiro

Building and breaking stuff!