# Telegram之2fa研究随笔 > 写在前面:这篇只是整理了一下2fa二步验证的流程,即能相互交流也能免得遗忘,原本尝试了一下硬刚绕过,结果整理完整套逻辑后展示没想到除了`tgWebAuthToken`之外的其他绕过的方法,所以就暂时放弃了 ## 内存泄露验证 突发奇想想看tg如果被dump了内存,那么他的2fa会在内存里以什么形式存在呢? 然后我就找到了这个issues:https://github.com/telegramdesktop/tdesktop/issues/29653 上面的issues简单的说就是qt有内存泄露 我尝试直接用ce打开,搜索明文密码后发现确实有  但是这只是qt的临时缓存,如果删掉,或者点击submit之后密码就在内存清楚了,无法复用  ## 2fa流程 登录时的 2FA 云密码,流程在 Telegram/SourceFiles/intro/intro_password_check.cpp 里,让个ai来看看  ``` 如果你说的是登录时的 2FA 云密码,流程在 Telegram/SourceFiles/intro/intro_password_check.cpp 里: 1. 用户输入密码后,先算本地哈希并存到 _passwordHash const auto password = _pwdField->getLastText().toUtf8(); _passwordHash = Core::ComputeCloudPasswordHash(...) 见 intro_password_check.cpp:389 (Telegram/SourceFiles/intro/intro_password_check.cpp:389) 2. 先向服务器拉一次当前密码挑战数据 MTPaccount_GetPassword() 返回的 MTPaccount_Password 会被解析进 _passwordState 见 intro_password_check.cpp:185 (Telegram/SourceFiles/intro/intro_password_check.cpp:185) 3. Core::ParseCloudPasswordState() 把服务器数据拆成: - _passwordState.mtp.request.id - _passwordState.mtp.request.B - _passwordState.mtp.request.algo - 以及 hasPassword / hasRecovery / hint ... 见 core_cloud_password.cpp:303 (Telegram/SourceFiles/core/core_cloud_password.cpp:303) 和 core_cloud_password.h:40 (Telegram/SourceFiles/core/core_cloud_password.h:40) 4. 用 _passwordState.mtp.request + _passwordHash 生成 SRP 校验包 Core::ComputeCloudPasswordCheck(...) 它返回的东西是 CloudPasswordResult::result,类型是 MTPInputCheckPasswordSRP 见 core_cloud_password.h:65 (Telegram/SourceFiles/core/core_cloud_password.h:65) 和 core_cloud_password.cpp:248 (Telegram/SourceFiles/core/core_cloud_password.cpp:248) 5. 真正发给服务器的是 MTPauth_CheckPassword(check.result) 成功回调里拿到的是 const MTPauth_Authorization &result,这个 result 就是登录成功后的授权对象 见 intro_password_check.cpp:207 (Telegram/SourceFiles/intro/intro_password_check.cpp:207) 6. 这个 result 最终传到 Step::finish(result),再进入创建 session 的流程 见 intro_step.cpp:158 (Telegram/SourceFiles/intro/intro_step.cpp:158) 补一句变量对应关系: - 用户明文输入:_pwdField - 本地计算出的密码哈希:_passwordHash - 服务器下发的 SRP 挑战:_passwordState.mtp.request - 发请求时真正带出去的 payload:check.result - 服务器回来的登录结果:callback 里的 result(MTPauth_Authorization) 如果你说的不是登录 2FA,而是设置页里“校验当前云密码”的那条 Api::CloudPassword::check(),那条流程不返回授权对象,只是拿 MTPaccount_GetPasswordSettings(input.result) 做一次校验,成功后回调是无返回值。 ``` 整个submit的逻辑如下  ```c void PasswordCheckWidget::submit() { if (_sentRequest) { return; } if (_pwdField->isHidden()) { auto code = _codeField->getLastText().trimmed(); if (code.isEmpty()) { _codeField->showError(); return; } const auto send = crl::guard(this, [=] { _sentRequest = api().request(MTPauth_CheckRecoveryPassword( MTP_string(code) )).done([=](const MTPBool &result) { codeSubmitDone(code, result); }).fail([=](const MTP::Error &error) { codeSubmitFail(error); }).handleFloodErrors().send(); }); if (_passwordState.notEmptyPassport) { const auto confirmed = [=](Fn &&close) { send(); close(); }; Ui::show(Ui::MakeConfirmBox({ .text = tr::lng_cloud_password_passport_losing(), .confirmed = confirmed, .confirmText = tr::lng_continue(), })); } else { send(); } } else { hideError(); const auto password = _pwdField->getLastText().toUtf8(); _passwordHash = Core::ComputeCloudPasswordHash( _passwordState.mtp.request.algo, bytes::make_span(password)); checkPasswordHash(); } } ``` ## 梳理流程 整个2fa校验流程的本质梳理一下: 1. 登录在服务器那边校验成功之后发现有二次密码,下发2fa的请求,并且携带盐 2. 将盐和密码一起hash一下上云 3. 通过后下发session 基本没什么难点 而且虽然有泄露的问题,也只是内存没被释放,随时都有可能被覆盖,关键的是算hash居然还有服务器的下发的两个盐值,导致hash无法复用,所以这条路也堵死了。 除非直接拿到session,那么就可以直接绕过2fa,其实**2fa的本质是在下发session之前加一道密码** ## 最后捞一下 其实如果想要绕过2fa也不是没有办法,主要就是通过已经登录的tg点击在客户端发送 `z.t.me` 链接,受害者点击会打开 `telegram.org/#tgWebAuthToken=... `,这个`tgWebAuthToken`是tg的dc服务器下发的,一次认证密码,可以直接登录web版tg,下面这篇文章就是具体的思路: https://lyra.horse/blog/2024/05/stealing-your-telegram-account-in-10-seconds-flat/ 但可惜的是必须要有一个登录着的tg才能派生出来,有点局限性,而且只是绕过,不能拿到密码或者可以复现的地方,有点可惜。有时间在研究吧,先到这里了。之后有什么发现我就补充在留言里。