Hey everyone,
I frequently see people struggling with disableing the domain locking, logging services, and client checks embedded into Club Penguin client files to prevent the files being used outside the http://clubpenguin.com domain.
I have written a tool in Python to help aid the process of disabling these functions, whilst maintaining the crossdomain policies within the files which actually act as a security feature.
import zlib
import logging
import argparse
from os.path import isfile, isdir, splitext, join, exists, dirname
from os import walk, makedirs
from glob import glob
if __name__ == "__main__":
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
parser = argparse.ArgumentParser(description="Club Penguin client tool")
parser.add_argument("path", type=str, nargs="*", help="Path(s) to files or directories")
parser.add_argument("-d", "--domain", type=str, help="The domain to use (ex. clubpenguin.com)")
parser.add_argument("-r", "--recursive", action='store_true', help="Enables recursive mode")
parser.add_argument("-o", "--output", type=str, help="Output directory")
parser.add_argument("-D", "--dump", action='store_true', help="Dump decompressed FWS data")
arguments = parser.parse_args()
logger.info("This program can overwrite files on the disk, make sure to make backups before" +
" running this script or make sure the output flag is enabled!")
for path in arguments.path:
if isfile(path):
logger.info("Found \"%s\"", path)
paths = [path]
elif isdir(path):
if not arguments.recursive:
logger.warning("\"%s\" is a directory but recursion is disabled! skipping...", path)
continue
else:
paths = [y for x in walk(path) for y in glob(join(x[0], '*.swf'))]
for file in paths:
logger.info("Found \"%s\"", file)
filename, file_extension = splitext(file)
if file_extension != ".swf":
logger.warning("\"%s\" is not an SWF file! skipping...", path)
continue
logger.info("Opening \"%s\"", file)
raw = open(file, "rb")
signature = raw.read(3)
logger.info("Reading file signature for \"%s\"", file)
if signature != b"CWS":
logger.warning("File has invalid signature, file sig should be 0x43 0x57 0x43(\"CWS\")")
continue
header = raw.read(5)
data = raw.read()
logger.info("Read %d bytes from \"%s\"", data.__sizeof__(), file)
logger.info("Decompressing")
decompressed_data = zlib.decompress(data)
original_data = decompressed_data
if b'\x00clubpenguin\x00boot\x00BootLoader' in decompressed_data:
logger.info("Found a BootLoader, cracking client checks...")
s = b'\x00logEvents\x00boot\x00BootL0ader'
decompressed_data = decompressed_data.replace(b'\x00logEvents\x00boot\x00BootLoader', s)
s = b'clubpenguin.c0m'
decompressed_data = decompressed_data.replace(b'clubpenguin.com', s)
if b'clubpenguin.com' in decompressed_data:
logger.info("Found clubpenguin.com")
s = bytes(arguments.domain.encode())
decompressed_data = decompressed_data.replace(b'clubpenguin.com', s)
if b'disney.com' in decompressed_data:
logger.info("Found disney.com")
s = bytes(arguments.domain.encode())
decompressed_data = decompressed_data.replace(b'disney.com', s)
if decompressed_data == original_data:
logger.warning("No changes were made to the data!")
continue
logger.info("Re-compressing data and appending file signature and header")
compressed = signature + header + zlib.compress(decompressed_data)
if arguments.output:
file = join(arguments.output, file)
if not exists(dirname(file)):
makedirs(dirname(file), exist_ok=True)
if arguments.dump:
dump = open(file + ".fws", "w")
dump.write(str(decompressed_data))
dump.close()
logger.info("Copied dump to \"%s\"", file + ".fws")
logger.info("Writing data to \"%s\"", file)
output = open(file, "wb")
output.write(compressed)
output.close()
raw.close()
logger.info("Finished. Goodbye!")
If you know Python a little then you will probably be able to understand how the script works rather quickly, as it is rather simple. It decompresses the SWF files to expose some of the actionscript constants within the file (no, this script does not make use of SWF disassemblers like RABCasm which makes my script very lightweight and very fast)
The script has a few flags, some of which are just useful, and some of which you'll probably want to use every single time you run it.
$ py iceburg.py -h
usage: iceburg.py [-h] [-d DOMAIN] [-r] [-o OUTPUT] [-D] [path [path ...]]
Club Penguin client tool
positional arguments:
path Path(s) to files or directories
optional arguments:
-h, --help show this help message and exit
-d DOMAIN, --domain DOMAIN
The domain to use (ex. clubpenguin.com)
-r, --recursive Enables recursive mode
-o OUTPUT, --output OUTPUT
Output directory
-D, --dump Dump decompressed FWS data
Domain (required) tells the script which internet domain to replace security locks and logging services with.
Recursive mode will make the script search through every directory below the path you specify for SWF files to check.
Output changes the output directory, assuming the output directory is empty no files will be overwritten.
Dump will make extra files containing the decompressed version of the data, useful for debugging purposes (for example if the script is making changes which break the SWF).
Example usage on real Club Penguin media!
/play/v2/client
/play/v2/games/
Script download attached :~)
Prerequisites
Python 3
:)
client-tool.zip