Bouton bleu

"Troy! Troy!"

Troy looked up from his keyboard with a frown as his coworker Cassie skidded to a halt, panting for breath. "Yes?"

"How soon can you get that new client converted?" Cassie asked. "We're at DEFCON 1 in ops. We need to be running yesterday!"

Troy's frown only deepened. "I told you, I've barely had a chance to peek at their old system."

The client was hoping to convert sometime in the next month—usually no big deal, as they'd just have to schedule a date, write a handful of database conversion scripts, and swing the domains to a fresh instance of their own booking software. It was that middle step that Troy hadn't gotten to. With no go-live date picked, working on new features seemed a higher priority.

Cassie had been spouting doom-and-gloom predictions all month: the client's in-house solution read like mid-1990s code despite being written in 2013. She'd been convinced it was a house of cards ready to collapse at any minute. Apparently, she'd been right.

"Okay, slow down. Where's the fire?" It wasn't that Troy didn't believe her per se, but when he'd skimmed the database, he hadn't seen anything spectacularly bad. Even if the client was down, their data could be converted easily. It wasn't his responsibility to maintain their old system, just to get them to the new one. "Is this a data problem?"

"They're getting hundreds of new bookings for phantom clients at the top of every hour," Cassie replied. "At this rate, we're not sure we'll be able to separate the garbage from the good bookings even if you had a conversion script done right now." Her eyes pleaded for him to have such a script on hand, but he shook his head, dashing her hopes.

"Maybe I can stop it," Troy said. "I'm sure it's a backdoor in the code somewhere we can have them disable. Let me have a look."

"You do that. I'm going to check on their backup situation."

As Cassie ran off again, Troy closed his Solitare game and settled in to read the code. At first, he didn't see anything drastically worse than he was expecting.

PHP code, of course, he thought. There's an init script: login stuff, session stuff ... holy crap that's a lot of class includes. Haven't they ever heard of an autoloader? If it's in one of those, I'll never find it. Keep pressing on ... header? No, that just calls ob_start(). Footer? Christ on a cracker, they get all the way to the footer before they check if the user's logged in? Yeah, right there—if the user's logged out, it clears the buffer and redirects instead of outputting. That's inefficient.

Troy got himself a fresh cup of coffee and sat back, looking at the folder again. Let's see, let's see ... login ... search bookings ... scripts? Scripts.php seems like a great place to hide a vulnerability. Or it could even be a Trojan some script kiddie uploaded years ago. Let's see what we've got.

He opened the folder, took one look at the file, then shouted for Cassie.


<?php
    define('cnPermissionRequired', 'Administration');

    require_once('some_init_file.php'); // validates session and permissions and such
    include_once('Header.php'); // displays header and calls ob_start();

    $arrDisciplines = [
        13  => [1012, 1208], 14  => [2060, 2350],
        17  => [14869, 15925], 52  => [803, 598],
        127 => [6624, 4547], 122 => [5728, 2998],
    ];

    $sqlAdd = "INSERT INTO aResultTable
                   SET EventID = (SELECT EventID FROM aEventTable ORDER BY RAND() LIMIT 1),
                       PersonID = (SELECT PersonID FROM somePersonView ORDER BY RAND() LIMIT 1),
                       ResultPersonFirstName = (SELECT FirstName FROM __RandomValues WHERE FirstName IS NOT NULL ORDER BY RAND() LIMIT 1),
                       ResultPersonLastName = (SELECT LastName FROM __RandomValues WHERE LastName IS NOT NULL ORDER BY RAND() LIMIT 1),
                       ResultPersonGender = 'M',
                       ResultPersonYearOfBirth = (SELECT Year FROM __RandomValues WHERE Year IS NOT NULL ORDER BY RAND() LIMIT 1),
                       CountryFirstCode = 'GER',
                       ResultClubName = (SELECT ClubName FROM aClubTable ORDER BY RAND() LIMIT 1),
                       AgeGroupID = 1,
                       DisciplineID = :DisciplineID,
                       ResultRound = (SELECT Round FROM __RandomValues WHERE Round IS NOT NULL ORDER BY RAND() LIMIT 1),
                       ResultRoundNumber = 1,
                       ResultRank = (SELECT Rank FROM __RandomValues WHERE Rank IS NOT NULL ORDER BY RAND() LIMIT 1),
                       ResultPerformance = :ResultPerformance,
                       ResultCreated = NOW(),
                       ResultCreatedBy = 1;";
    $qryAdd = $objConnection->prepare($sqlAdd);

    foreach ($arrDisciplines as $DisciplineID => $Values) {
        set_time_limit(60);

        $iNumOfResults = rand(30, 150);

        for ($iIndex = 0; $iIndex < $iNumOfResults; $iIndex++) {
            $qryAdd->bindValue(':DisciplineID', $DisciplineID);
            $qryAdd->bindValue(':ResultPerformance', rand(min($Values), max($Values)));

            $qryAdd->execute();
            $qryAdd->closeCursor();
        }
    }

    // ... some more code

?>
<?php

    include_once('Footer.php'); // displays the footer, calls ob_get_clean(); and flushes buffer, if user is not logged in
?>

"Holy hell," breathed Cassie. "It's worse than I feared."

"Tell them to take the site down for maintenance and delete this file," Troy said. "Google must've found it."

"No kidding." She straightened, rolling her shoulders. "Good work."

Troy smiled to himself as she left. On the bright side, that conversion script's half done already, he thought. Meaning I've got plenty of time to finish this game.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!