LastPass Android javascript 行为

逆向工程 安卓 javascript 安全
2021-06-18 01:29:46

几周前,我制作了一个概念验证应用程序,展示了 LastPass 在 Chrome 中填写凭据的方法是如何不安全的。对于那些感兴趣的源是在https://github.com/activems/clipcaster

本质上,他们将一段 javascript 复制到剪贴板并让用户执行它。这包括用户名和密码。在以前的版本中,它已明确填写为:

if (l_bte) {
    l_sfv(l_bte, decodeURIComponent(escape(atob('dXNlckBleGFtcGxlLmNvbQ=='))));
}
l_sfv(l_bpe, decodeURIComponent(escape(atob('cDRzc3cwcmQ='))));

'dXNlckBleGFtcGxlLmNvbQ=='是'user@example.com'的Base64,'cDRzc3cwcmQ='是'p4ssw0rd'

LastPass 最近更新了他们使用的 JavaScript。现在是(完整版在https://raw.githubusercontent.com/activems/clipcaster/master/lastpass_output_v2.js):

var l_x = function(t, l, m) {
    var o = [];
    var b = '';
    var p = document.location.href.replace(/https?:\/\//, '').substring(0, l);
    p = l_s('' + l_f(m) + p);
    for (z = 1; z <= 255; z++) {
        o[String.fromCharCode(z)] = z;
    }
    for (j = z = 0; z < t.length; z++) {
        b += String.fromCharCode(o[t.substr(z, 1)] ^ o[p.substr(j, 1)]);
        j = (j < p.length) ? j + 1 : 0;
    }
    return decodeURIComponent(escape(b));
};

....

var l_f=function(m){ 
    var t=new Date().getTime() /
        1000 | 0;
        while (t % 10 != m) {
            --t;
        }
        return t;
    };

....

if (l_bte) {

    l_sfv(l_bte, l_x(atob('W01cCVUlA1AVCkIDAk8AC1g='), 61, 0));
}
l_sfv(l_bpe, l_x(atob('QldeA0BREUAWDEMC'), 61, 0));

除了混淆之外,是否还有其他务实的理由来编写这样的 JavaScript?

1个回答

简而言之,没有。如果他们要以这种方式在 Javascript 中实现“加密”,那么除了更多的混淆之外,您无能为力。这是适用于这种情况下,100%,但是看到MataSano的文章在这里 使用JavaScript客户端认证的解释陷阱。

与文章中提到的类似,问题是无论JavaScript多么复杂,攻击者拦截并运行代码都是微不足道的。类似于中间人攻击可以拦截和替换客户端 JavaScrip,在这种情况下,问题是另一个应用程序通过 ClipboardManager 访问剪贴板非常容易。一个更好的最低限度的解决方案是让 LastPass 等人将保存的凭据存储在经过身份验证的 SQLite 数据库中。并在检测到可填充时使用应用程序中的代码。提取保存的凭据并直接输入它们,而不是使用剪贴板和运行 JavaScript。

问题的症结在于,攻击者使用当前 API 获取回调太容易了;让应用程序使用自己的数据库可以防止攻击者在没有 root 权限的情况下拦截身份验证工作流(这是完全不同的讨论)。这是在实现应用程序时没有从头开始考虑安全性的一个很好的例子。

顺便说一句,很酷的东西并报告错误。你能把一个易受攻击的应用程序版本放到 github 上吗(如果你还没有的话)?将来这将是一个很棒的演示。