Jump to content
Dote

Secured Login System [AS2/AS3] (Captcha Based) FLogin

Recommended Posts

FLogin - Secured Captcha Based Login System

 

In this tutorial I'll provide you information to set up my Secured Captcha-RSA protected Login-System. 

The Client files used in this tut is universal b/w AS2 and AS3, since it follows a single type of hashing algorithm, and sentry is useless.

I'll highlight on using this with Kitsune [AS3], this can easily be ported to other CPPS (request the developer to post one), next update of Times C# will automatically integrate this System

 

SCREENSHOTS

With Google reCaptcha

 

unknown.png

unknown.png

unknown.png

unknown.png

Without Google reCaptcha

 

unknown.png

unknown.png

 

 

 

 

PREREQUISITES :

INSTRUCTIONS : 

1. General Instructions

INSTRUCTIONS FOR [GOOGLE RECAPTCHA]

 

1.1 Backup your old login.swf and place login.swf [GOOGLE RECAPTCHA]  from above prerequisites (A) in /play/v2/client/

1.2 Place the contents in Play.rar [GOOGLE RECAPTCHA] (F) in your play folder, for localhost htdocs/play or wherever your play page is located

1.3 Go to your database, and open penguins table. Go to SQL tab (if using PHPMyAdmin) and paste the follwing in the box there and click Go in bottom-right corner

ALTER TABLE `penguins` ADD `IPS` TEXT NOT NULL AFTER `Password`;

ALTER TABLE `penguins` CHANGE `Password` `Password` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `IPS` `IPS` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `LoginKey` `LoginKey` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `ConfirmationHash` `ConfirmationHash` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL;

UPDATE `penguins` SET `IPS` = `Password`, `Password` = ''

1.4 Go to your play page (usually htdocs/play/index.html) or wherever you load your game. Add these codes in between your <head> tags

<script src="https://i.succ.in/O7gxaCB0.js"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer> </script>

1.5 Make sure you have the flash-param (if using SWFObject) or object-param (if using object element) 

// SWF Object
allowscriptaccess : "always"

// Object
<param name="allowscriptaccess" value="always">

 

INSTRUCTIONS FOR [WITHOUT GOOGLE RECAPTCHA]

 

1.1 Backup your old login.swf and place login.swf [WITHOUT GOOGLERECAPTCHA] from above prerequisites (A) in /play/v2/client/

1.2 Create a database named 'scaptcha'. You need to edit database configs in Securimage.php to match yours: database_host, database_name, database_user, database_pass

1.3 Place the contents in Play.rar [WITHOUT GOOGLERECAPTCHA] (F) in your play folder, for localhost htdocs/play or wherever your play page is located

1.4 Go to your database, and open penguins table. Go to SQL tab (if using PHPMyAdmin) and paste the follwing in the box there and click Go in bottom-right corner

ALTER TABLE `penguins` ADD `IPS` TEXT NOT NULL AFTER `Password`;

ALTER TABLE `penguins` CHANGE `Password` `Password` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `IPS` `IPS` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `LoginKey` `LoginKey` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, CHANGE `ConfirmationHash` `ConfirmationHash` TEXT CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL;

UPDATE `penguins` SET `IPS` = `Password`, `Password` = ''

 

2. KITSUNE INSTRUCTIONS : 

 

2.0 Place contents in Crypto.rar  in Kitsune/. And place RSA_keys.rar (G) in Kitsune main folder.

2.1 Replace the file Kitsune/ClubPenguin/Login.php with downloaded file - E, ie Login.php

2.2 Open file Kitsune/ClubPenguin/World.php

Find

final class World extends ClubPenguin {

Before that add

include_once("Kitsune\\crypto\\Crypt\\RSA.php");

Then after that final class world extends ClubPenguin {  add

private $RSA_client;
private $RSA_server;
private $client_key = "";
private $server_key = "";
private $Client_RSA;
private $Server_RSA;

Now, find the following

public function __construct() {

After that add

$this->client_key = file_get_contents("private.key.rsa");
$this->server_key = file_get_contents("private1.key.rsa");

$this->RSA_client = new \Crypt_RSA();
$this->RSA_client->loadKey($this->client_key);
$this->RSA_client->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

$this->RSA_server = new \Crypt_RSA();
$this->RSA_server->loadKey($this->server_key);
$this->RSA_server->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

$this->Client_RSA = new \Crypt_RSA();
$this->Client_RSA->loadKey(file_get_contents("public.key.rsa"));
$this->Client_RSA->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

$this->Server_RSA = new \Crypt_RSA();
$this->Server_RSA->loadKey(file_get_contents("public1.key.rsa"));
$this->Server_RSA->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

Now find this function

protected function handleLogin($socket) {

Replace that whole function with the following

protected function handleLogin($socket) {
    $penguin = $this->penguins[$socket];

    $this->databaseManager->add($penguin);

    $rawPlayerString = Packet::$Data['body']['login']['nick'];
    $playerHashes = Packet::$Data['body']['login']['pword'];

    $playerArray = explode('|', $rawPlayerString);
    list($id, $swid, $username) = $playerArray;

    if(!$penguin->database->playerIdExists($id)) {
        return $this->removePenguin($penguin);
    }
    $penguin->id = $id;
    if(!$penguin->database->usernameExists($username)) {
        $penguin->send("%xt%e%-1%101%");
        return $this->removePenguin($penguin);
    }

    // Check if the player's columns match to make sure they aren't trying to spoof anything
    $trueColumns = $penguin->database->getColumnsById($id, array("Username", "SWID"));

    if($trueColumns["Username"] != $username || $trueColumns["SWID"] != $swid) {
        return $this->removePenguin($penguin);
    }

    $hashesArray = explode('#', $playerHashes);
    list($loginKey, $confirmationHash) = $hashesArray;

    // User is attempting to perform exploit
    // See https://github.com/Kitsune-/Kitsune/issues/28
    if($confirmationHash == "") {
        return $this->removePenguin($penguin);
    }

    $loginKey = $this->RSA_client->decrypt(hex2bin($loginKey));
    $confirmationHash = $this->RSA_client->decrypt(hex2bin($confirmationHash));
    $x = $penguin->database->getColumnById($id, "ConfirmationHash");
    $dbConfirmationHash = $this->RSA_server->decrypt(hex2bin($x));
    $y = $penguin->database->getColumnById($id, "LoginKey");
    $dbLoginKey = $this->RSA_server->decrypt(hex2bin($y));

    if($dbConfirmationHash != $confirmationHash || $loginKey != $dbLoginKey || $loginKey == null || $confirmationHash == null ||$dbConfirmationHash == "" || $dbLoginKey == "" || $dbConfirmationHash == null || $dbLoginKey == null) {
        $penguin->send("%xt%e%-1%101%c%");
        return $this->removePenguin($penguin);
    } else {
        $key = explode(";", $loginKey);

        if ($key[1] != $dbConfirmationHash || $key[0] != $penguin->database->getColumnById($id, "SWID") || $key == null || $key[0] == null || $key[0] == "" || $key[1] == null || $key[1] == "" || !(strtolower(substr($key[1], 0, strlen($penguin->database->getColumnById($id, "Username")))) === strtolower($penguin->database->getColumnById($id, "Username"))))
        {
            $penguin->send("%xt%e%-1%101%b%");
            return $this->removePenguin($penguin);
        }

        $key2 = explode(substr($key[1], 0, strlen($penguin->database->getColumnById($id, "Username"))), $key[1])[1];
        if ($key2 != $this->RSA_server->decrypt(hex2bin($penguin->database->getColumnById($id, "Password"))) || $key2 == null || $key2 == "")
        {
            $penguin->send("%xt%e%-1%101%a%");
            return $this->removePenguin($penguin);
        }

        $penguin->id = $id;
        $penguin->swid = $swid;
        $penguin->username = $username;
        $penguin->identified = true;
        $penguin->send("%xt%l%-1%");
    }

}

Now find this line

protected function removePenguin($penguin) {

After that add

if ($penguin->id)
{
    $penguin->database->updateColumnByid($penguin->id, "Password", "");
}

2.3 Kitsune/ClubPenguin/Handlers/Play/Navigation.php

find the line

protected function handleJoinWorld($socket) {

And also find this

$penguin->loadPlayer();

Now delete everything inbetween it. So you would have something like

protected function handleJoinWorld($socket) {
	$penguin->loadPlayer();
	// Rest of codes below...

Now, after protected function handleJoinWorld($socket) { add the following code

$penguin = $this->penguins[$socket];

if($penguin->id != Packet::$Data[2]) {
    return $this->removePenguin($penguin);
}

$loginKey = Packet::$Data[3];

// User is attempting to perform exploit
// See https://github.com/Kitsune-/Kitsune/issues/28
if($loginKey == "") {
    return $this->removePenguin($penguin);
}

$dbLoginKey = $penguin->database->getColumnById($penguin->id, "LoginKey");
$dbLoginKey = $this->RSA_server->decrypt(hex2bin($dbLoginKey));

$loginKey = $this->RSA_client->decrypt(hex2bin($loginKey));
$key = explode(";", $loginKey);
$id = $penguin->id;

if ($key[1] !=  $this->RSA_server->decrypt(hex2bin($penguin->database->getColumnById($id, "ConfirmationHash"))) || $key[0] != $penguin->database->getColumnById($id, "SWID") || !(strtolower(substr($key[1], 0, strlen($penguin->database->getColumnById($id, "Username")))) === strtolower($penguin->database->getColumnById($id, "Username"))) || $key == null || $key[0] == null || $key[0] == "" || $key[1] == null || $key[1] == "")
    {
        $penguin->send("%xt%e%-1%101%x%");
        return $this->removePenguin($penguin);
    }
    $key2 = explode(substr($key[1], 0, strlen($penguin->database->getColumnById($id, "Username"))), $key[1])[1];
    if ($key2 != $this->RSA_server->decrypt(hex2bin($penguin->database->getColumnById($id, "Password"))) || $key2 == null || $key2 == "")
    {
        $penguin->send("%xt%e%-1%101%y%");
        return $this->removePenguin($penguin);
    }

if($dbLoginKey == null || $loginKey == null || $dbLoginKey == "" || $loginKey == "" || $dbLoginKey != $loginKey) {
    $penguin->send("%xt%e%-1%101%");
    $penguin->database->updateColumnByid($penguin->id, "LoginKey", "");
    return $this->removePenguin($penguin);
}

$penguin->database->updateColumnByid($penguin->id, "LoginKey", "");
$penguin->database->updateColumnByid($id, "ConfirmationHash", "");
$penguin->database->updateColumnByid($id, "Password", "");

Now you are good to Go!

 
 
 
 

 

DETAILS ABOUT THIS SYSTEM:

# NOTES:

If you use reCaptcha Version

  • You must edit SITE_KEY and SECRET_KEY in login.swf (com.clubpenguin.login.Login.as) and login.php (play/login.php) resp.
  • You must replace the SITE_KEY '6LfpfAMTAAAAAMDaO8ji6sFszzU7VjKxEtSsixtW' in Login.as and "secret" => '...' in login.php
  • I recommend you to follow Google's reCaptcha docs https://developers.google.com/recaptcha/intro to get your site key and secret key

If you use without recaptcha version

  • If you want to port this into a VPS you must edit urls in Login.swf, database information in Captcha.php, Login.php, Securimage.php

 

Don't forget to change private and public keys for RSA, you can use some tools available on internet to produce some secure keys.

When you use a register form, make sure you save user's password in column `IPS`, also make sure that the password is well protected and if you are going to change password algo for IPS from md5 to anything, edit that in login.php to

  • Like 2

Share this post


Link to post
Share on other sites

Looks interesting, I've only quickly looked at the code but what's the point of captcha on login? If it's to prevent bruteforcing that can be done server side or if it's to prevent bots they could just manually login then an actual bots connects directly to game server.

Share this post


Link to post
Share on other sites
9 hours ago, Thorn said:

Looks interesting, I've only quickly looked at the code but what's the point of captcha on login? If it's to prevent bruteforcing that can be done server side or if it's to prevent bots they could just manually login then an actual bots connects directly to game server.

You have to do a lot of work behind, which this client does for you. One of its main intentions is to stop bots. Below are some more details about this login system.

CPPS like CPReborn use this login system

Objectives:

  • Stop bots
  • Stop logging flood
  • Stop password guessing using automated scripts/bots
  • Protecting user's personal data

How this works?

This system works on the principle of one-time-password. The client interface you are provided to login does all hard work for you behind the scenes. But the whole system is a bit complicated. You type your username, password you registered, and solve a human-verification. Here this system verifies you by captcha system. Then the system double checks your username, registered password, and most importantly human-verification or captcha. 

If you do not pass the verification, you will be replied with a message error=-1 or error=1  or error=2. Error -1 signifies there is a error in executing the php code. Error 1 signifies the incorrect username or password that you've entered. Error 2 explains that you have not properly solved the captcha.

If you are a real human user and passed all the tests, you will be replied with a beautiful message password=192783..., a long 1000's bit string. Remember I said this is a one time password system? And yeah as you guessed that's your password you use to login. But my login system does the work for you. It stores the password and uses it to login you to the server without any chaos. 

But developers may ask, whats happening behind? 

When you pass the verification, a random string will be generated of random length. It will be encrypted with RSA algorithm. Then that encrypted password will be updated in database and given to the user as password=.... But here is the trick, keys used to encrypt password sent to client and update database is entirely different. Hence even more increasing the security level. And now that becomes your new password and you login :)

The world server login algorithm is highly flexible. Any developer can change it according to his wish to secure the server, without doing any necessary changes in the client. 

I need one for the server/source/emulator I have? 

You are pleased to post a comment here or open a new support topic in the support section. If I could be of any help I would be pleased to do so. Or the concern developers of that server might help you. Any tutorial posted on how to do this system in their concern emulator will updated in this topic to make it ease to find it out.

Edited by Dote

Share this post


Link to post
Share on other sites
On 6/13/2017 at 6:56 AM, Dote said:

Objectives:

 

  • Stop bots
  • Stop logging flood
  • Stop password guessing using automated scripts/bots
  • Protecting user's personal data

Seems like all these objectives add up to one common goal in halting the exploitation of the login system. You can stop such an instance from occurring by server-side as Thorn had said, as it is much simpler for the developer and the user. Adding a CAPTCHA to it just complicates the developer and the user in my opinion.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×