Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion incl/levels/uploadGJLevel.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
VALUES (:levelName, :gameVersion, :binaryVersion, :userName, :levelDesc, :levelVersion, :levelLength, :audioTrack, :auto, :password, :original, :twoPlayer, :songID, :objects, :coins, :requestedStars, :extraString, :levelString, :levelInfo, :secret, :uploadDate, :userID, :id, :uploadDate, :unlisted, :hostname, :ldm, :wt, :wt2, :unlisted2, :settingsString)");


if($levelString != "" AND $levelName != ""){
if($levelString != "" AND $levelName != "" AND (new LevelParser($levelString))->validate()){
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
$querye=$db->prepare("SELECT levelID FROM levels WHERE levelName = :levelName AND userID = :userID");
$querye->execute([':levelName' => $levelName, ':userID' => $userID]);
$levelID = $querye->fetchColumn();
Expand Down
79 changes: 79 additions & 0 deletions incl/lib/LevelParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

class LevelParser {
public $data = [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh i don't see the point of making this a single member class. this data member is basically useless anyways (unless you were planning on extending the functionality of this class?). just make validate static and pass the level string as its param

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes in the future functionality could be expanded on validating other parts of the level request. However for now I have implemented your suggestions


public function __construct($levelString)
{
$this->data = self::map($levelString, ':');
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
}

/**
* Convert GD's weird "keyed" array strings to dictionary. Works with out-of-order keys.
*
* @param $list string
* @param $separator string
* @return array
*/
public static function map($list, $separator) {
$bits = explode($separator, $list);
$array = [];
for ($i = 1; $i < count($bits); $i += 2) {
$array[$bits[$i - 1]] = $bits[$i];
}
return $array;
}

/**
* Flatten dictionary to single delimited string
*
* @param $dict array|object
* @param $separator string
* @return string
*/
public static function demap($dict, $separator) {
$string = '';
foreach ($dict as $key => $value) {
$string[] .= "${separator}${key}${separator}${value}";
}
return $string;
}

/**
* @param $string string
* @return false|string
*/
public static function base64_urlencode($string) {
return base64_encode(strtr($string, '+/', '-_'));
}

/**
* @param $string string
* @return false|string
*/
public static function base64_urldecode($string) {
return base64_decode(strtr($string, '-_', '+/'), true);
}

/**
* Validates level data for various things (currently only ACE exploit)
*
* @return bool
*/
public function validate() {
// Ocular Miracle ain't happening
$max_level_size = 50 * 1024 * 1024; // 50 MB Max uncompressed size

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep in mind that any level over this size could bypass the check entirely. it wouldn't be impossible to add padding in ways that inflate the size of the level while keeping it working on desktop platforms...

$objs = explode(';', zlib_decode(self::base64_urldecode($this->data[4]), $max_level_size));
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
// Skip level header
for ($i = 1; $i < 2; $i++) {
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
$obj = self::map($objs[$i], ',');
// Check for pickup trigger exploit
if ($obj[1] == 1817) {
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
$id = intval($obj[80]);

@qimiko qimiko May 27, 2023

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would recommend checking the color id property of color triggers as well, but it's probably not necessary. mostly just crashes

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What index is color id?

if ($id > 1099 || $id < 0) return false;
Comment thread
0x1DEA marked this conversation as resolved.
Outdated
}
}

return true;
}
}