Skip to content

Commit ef1989f

Browse files
authored
Merge branch 'develop' into ts3-query-fix
2 parents 34f5625 + aab353d commit ef1989f

27 files changed

+593
-96
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ This file is a running track of new features and fixes to each version of the pa
33

44
This project follows [Semantic Versioning](http://semver.org) guidelines.
55

6+
## v1.2.2
7+
* **[security]** Fixes authentication bypass allowing a user to take control of specific server actions such as executing schedules, rotating database passwords, and viewing or deleting a backup.
8+
69
## v1.2.1
710
### Fixed
811
* Fixes URL-encoding of filenames when working in the filemanager to fix issues when moving, renaming, or deleting files.

app/Helpers/Utilities.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ public static function randomStringWithSpecialCharacters(int $length = 16): stri
4242
* @param string $minute
4343
* @param string $hour
4444
* @param string $dayOfMonth
45+
* @param string $month
4546
* @param string $dayOfWeek
4647
* @return \Carbon\Carbon
4748
*/
48-
public static function getScheduleNextRunDate(string $minute, string $hour, string $dayOfMonth, string $dayOfWeek)
49+
public static function getScheduleNextRunDate(string $minute, string $hour, string $dayOfMonth, string $month, string $dayOfWeek)
4950
{
5051
return Carbon::instance(CronExpression::factory(
51-
sprintf('%s %s %s * %s', $minute, $hour, $dayOfMonth, $dayOfWeek)
52+
sprintf('%s %s %s %s %s', $minute, $hour, $dayOfMonth, $month, $dayOfWeek)
5253
)->getNextRunDate());
5354
}
5455

app/Http/Controllers/Api/Client/Servers/ScheduleController.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public function store(StoreScheduleRequest $request, Server $server)
8484
'server_id' => $server->id,
8585
'name' => $request->input('name'),
8686
'cron_day_of_week' => $request->input('day_of_week'),
87+
'cron_month' => $request->input('month'),
8788
'cron_day_of_month' => $request->input('day_of_month'),
8889
'cron_hour' => $request->input('hour'),
8990
'cron_minute' => $request->input('minute'),
@@ -136,6 +137,7 @@ public function update(UpdateScheduleRequest $request, Server $server, Schedule
136137
$data = [
137138
'name' => $request->input('name'),
138139
'cron_day_of_week' => $request->input('day_of_week'),
140+
'cron_month' => $request->input('month'),
139141
'cron_day_of_month' => $request->input('day_of_month'),
140142
'cron_hour' => $request->input('hour'),
141143
'cron_minute' => $request->input('minute'),
@@ -211,6 +213,7 @@ protected function getNextRunAt(Request $request): Carbon
211213
$request->input('minute'),
212214
$request->input('hour'),
213215
$request->input('day_of_month'),
216+
$request->input('month'),
214217
$request->input('day_of_week')
215218
);
216219
} catch (Exception $exception) {

app/Http/Middleware/Api/Client/Server/AllocationBelongsToServer.php

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Middleware\Api\Client\Server;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Pterodactyl\Models\Task;
8+
use Pterodactyl\Models\User;
9+
use InvalidArgumentException;
10+
use Pterodactyl\Models\Server;
11+
use Pterodactyl\Models\Backup;
12+
use Pterodactyl\Models\Subuser;
13+
use Pterodactyl\Models\Schedule;
14+
use Pterodactyl\Models\Database;
15+
use Pterodactyl\Models\Allocation;
16+
use Illuminate\Database\Eloquent\Model;
17+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
18+
19+
class ResourceBelongsToServer
20+
{
21+
/**
22+
* Looks at the request parameters to determine if the given resource belongs
23+
* to the requested server. If not, a 404 error will be returned to the caller.
24+
*
25+
* This is critical to ensuring that all subsequent logic is using exactly the
26+
* server that is expected, and that we're not accessing a resource completely
27+
* unrelated to the server provided in the request.
28+
*
29+
* @param \Illuminate\Http\Request $request
30+
* @param \Closure $next
31+
* @return mixed
32+
*/
33+
public function handle(Request $request, Closure $next)
34+
{
35+
$params = $request->route()->parameters();
36+
if (is_null($params) || ! $params['server'] instanceof Server) {
37+
throw new InvalidArgumentException('This middleware cannot be used in a context that is missing a server in the parameters.');
38+
}
39+
40+
/** @var \Pterodactyl\Models\Server $server */
41+
$server = $request->route()->parameter('server');
42+
$exception = new NotFoundHttpException('The requested resource was not found for this server.');
43+
foreach ($params as $key => $model) {
44+
// Specifically skip the server, we're just trying to see if all of the
45+
// other resources are assigned to this server. Also skip anything that
46+
// is not currently a Model instance since those will just end up being
47+
// a 404 down the road.
48+
if ($key === 'server' || ! $model instanceof Model) {
49+
continue;
50+
}
51+
52+
switch (get_class($model)) {
53+
// All of these models use "server_id" as the field key for the server
54+
// they are assigned to, so the logic is identical for them all.
55+
case Allocation::class:
56+
case Backup::class:
57+
case Database::class:
58+
case Schedule::class:
59+
case Subuser::class:
60+
if ($model->server_id !== $server->id) {
61+
throw $exception;
62+
}
63+
break;
64+
// Regular users are a special case here as we need to make sure they're
65+
// currently assigned as a subuser on the server.
66+
case User::class:
67+
$subuser = $server->subusers()->where('user_id', $model->id)->first();
68+
if (is_null($subuser)) {
69+
throw $exception;
70+
}
71+
// This is a special case to avoid an additional query being triggered
72+
// in the underlying logic.
73+
$request->attributes->set('subuser', $subuser);
74+
break;
75+
// Tasks are special since they're (currently) the only item in the API
76+
// that requires something in addition to the server in order to be accessed.
77+
case Task::class:
78+
$schedule = $request->route()->parameter('schedule');
79+
if ($model->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) {
80+
throw $exception;
81+
}
82+
break;
83+
default:
84+
// Don't return a 404 here since we want to make sure no one relies
85+
// on this middleware in a context in which it will not work. Fail safe.
86+
throw new InvalidArgumentException('There is no handler configured for a resource of this type: ' . get_class($model));
87+
}
88+
}
89+
90+
return $next($request);
91+
}
92+
}

app/Http/Middleware/Api/Client/Server/SubuserBelongsToServer.php

Lines changed: 0 additions & 36 deletions
This file was deleted.

app/Models/Schedule.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* @property int $server_id
1313
* @property string $name
1414
* @property string $cron_day_of_week
15+
* @property string $cron_month
1516
* @property string $cron_day_of_month
1617
* @property string $cron_hour
1718
* @property string $cron_minute
@@ -58,6 +59,7 @@ class Schedule extends Model
5859
'server_id',
5960
'name',
6061
'cron_day_of_week',
62+
'cron_month',
6163
'cron_day_of_month',
6264
'cron_hour',
6365
'cron_minute',
@@ -93,6 +95,7 @@ class Schedule extends Model
9395
protected $attributes = [
9496
'name' => null,
9597
'cron_day_of_week' => '*',
98+
'cron_month' => '*',
9699
'cron_day_of_month' => '*',
97100
'cron_hour' => '*',
98101
'cron_minute' => '*',
@@ -107,6 +110,7 @@ class Schedule extends Model
107110
'server_id' => 'required|exists:servers,id',
108111
'name' => 'required|string|max:191',
109112
'cron_day_of_week' => 'required|string',
113+
'cron_month' => 'required|string',
110114
'cron_day_of_month' => 'required|string',
111115
'cron_hour' => 'required|string',
112116
'cron_minute' => 'required|string',
@@ -123,7 +127,7 @@ class Schedule extends Model
123127
*/
124128
public function getNextRunDate()
125129
{
126-
$formatted = sprintf('%s %s %s * %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_day_of_week);
130+
$formatted = sprintf('%s %s %s %s %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week);
127131

128132
return CarbonImmutable::createFromTimestamp(
129133
CronExpression::factory($formatted)->getNextRunDate()->getTimestamp()

app/Transformers/Api/Client/ScheduleTransformer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function transform(Schedule $model)
4040
'cron' => [
4141
'day_of_week' => $model->cron_day_of_week,
4242
'day_of_month' => $model->cron_day_of_month,
43+
'month' => $model->cron_month,
4344
'hour' => $model->cron_hour,
4445
'minute' => $model->cron_minute,
4546
],

database/factories/ModelFactory.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Pterodactyl\Models\Node;
88
use Faker\Generator as Faker;
99
use Pterodactyl\Models\ApiKey;
10+
use Pterodactyl\Models\Backup;
11+
use Pterodactyl\Models\Permission;
1012

1113
/** @var \Illuminate\Database\Eloquent\Factory $factory */
1214
/*
@@ -134,7 +136,9 @@
134136
});
135137

136138
$factory->define(Pterodactyl\Models\Subuser::class, function (Faker $faker) {
137-
return [];
139+
return [
140+
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT],
141+
];
138142
});
139143

140144
$factory->define(Pterodactyl\Models\Allocation::class, function (Faker $faker) {
@@ -161,7 +165,7 @@
161165
'database' => str_random(10),
162166
'username' => str_random(10),
163167
'remote' => '%',
164-
'password' => $password ?: bcrypt('test123'),
168+
'password' => $password ?: encrypt('test123'),
165169
'created_at' => Carbon::now()->toDateTimeString(),
166170
'updated_at' => Carbon::now()->toDateTimeString(),
167171
];
@@ -196,3 +200,12 @@
196200
'updated_at' => Carbon::now()->toDateTimeString(),
197201
];
198202
});
203+
204+
$factory->define(Pterodactyl\Models\Backup::class, function (Faker $faker) {
205+
return [
206+
'uuid' => Uuid::uuid4()->toString(),
207+
'is_successful' => true,
208+
'name' => $faker->sentence,
209+
'disk' => Backup::ADAPTER_WINGS,
210+
];
211+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddCronMonth extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::table('schedules', function (Blueprint $table) {
17+
$table->string('cron_month')->after('cron_day_of_week');
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*
24+
* @return void
25+
*/
26+
public function down()
27+
{
28+
Schema::table('schedules', function (Blueprint $table) {
29+
$table->dropColumn('cron_month');
30+
});
31+
}
32+
}

0 commit comments

Comments
 (0)