Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Alphanumeric Shifting Made Easy With Python

DZone's Guide to

Alphanumeric Shifting Made Easy With Python

Thanks to a couple of built-in functions, this dev was able to use Python to easily solve an otherwise rather complex puzzle. Read on to see how!

· Web Dev Zone ·
Free Resource

Bugsnag monitors application stability, so you can make data-driven decisions on whether you should be building new features, or fixing bugs. Learn more.

During my first CTF(Capture the Flag) competition I noticed that some challenges required a character shifting in order to discover the real value of a string. I spent a few minutes on those challenges trying to figure out a pattern that could be applied to “unmask” the given content.

Challenge 1

After gathering information about the given IP address, a file was found with the following content:

rxms{DqhqdeqpRxms}

It looks completely random, but given the fact that the flags had a pattern of: “flag{some-hash-here}”, we could try a character shifting:

rxms{DqhqdeqpRxms}
synt
tzou
uapv
vbqw
wcrx
xdsy
yetz
zfua
agvb
bhwc
cixd
djye
ekzf
flag{...} <- real content

We had to jump 12 positions in the alphabet to get the real content of the string.

Challenge 2

In order to connect to a service we had to solve a pass-phrase code:

AQW UJCNN PQV RCUU

At first glance, the pass-phrase code looks like a Caesar’s Cipher. Solving this challenge also required character shifting, but this time, backward:

AQW UJCNN PQV RCUU
ZPV TIBMM OPU QBTT
YOU SHALL NOT PASS <- backward content

Python to the Rescue

Python has the built-in functions ord() and chr() that can help us to accomplish this task:

class Alphanumeric(object):

    ALPHABET_LENGTH = 26

    def __init__(self, nrange=None):      
        self.current_letter = 'z'
        self.current_number = 0
        self.nrange = nrange

    def forward_letter(self, letter, positions):        
        if letter.islower():
            unicode_point = ord('a')
        else:
            unicode_point = ord('A')

        start = ord(letter) - unicode_point
        offset = ((start + positions) % self.ALPHABET_LENGTH) + unicode_point
        self.current_letter = chr(offset)
        return self.current_letter

    def backward_letter(self, letter, positions):        
        if letter.islower():
            unicode_point = ord('a')
        else:
            unicode_point = ord('A')

        start = ord(letter) - unicode_point
        offset = ((start - positions) % self.ALPHABET_LENGTH) + unicode_point
        self.current_letter = chr(offset)
        return self.current_letter

    def next_letter(self):        
        return self.forward_letter(self.current_letter, 1)

    def previous_letter(self):        
        return self.backward_letter(self.current_letter, 1)

    def forward_number(self, number, positions):        
        if not self.nrange:
            self.current_number = number + positions
            return self.current_number

        index = self.nrange.index(number)
        start = index + positions
        offset = (start % len(self.nrange))
        self.current_number = self.nrange[offset]
        return self.current_number

    def backward_number(self, number, positions):        
        if not self.nrange:
            return number - positions

        index = self.nrange.index(number)
        start = index - positions
        offset = (start % len(self.nrange))
        self.current_number = self.nrange[offset]
        return self.current_number

    def next_number(self):        
        return self.forward_number(self.current_number, 1)

    def previous_number(self):       
        return self.backward_number(self.current_number, 1)

    def forward_alphanumeric(self, alpha, positions, ignore_numbers=False, ignore_letters=False):        
        result = ""
        for char in alpha:
            if char.isdigit() and not ignore_numbers:
                char = str(self.forward_number(int(char), positions))

            if char.isalpha() and not ignore_letters:
                char = self.forward_letter(char, positions)

            result += char

        return result

    def backward_alphanumeric(self, alpha, positions, ignore_numbers=False, ignore_letters=False):        
        result = ""
        for char in alpha:
            if char.isdigit() and not ignore_numbers:
                char = str(self.backward_number(int(char), positions))

            if char.isalpha() and not ignore_letters:
                char = self.backward_letter(char, positions)

            result += char

        return result

Using the Alphanumeric class:

alpha = Alphanumeric()
print alpha.forward_alphanumeric('abc123', 1)
print alpha.backward_alphanumeric('abc123', 1)

// prints: bcd234
// prints: zab012

Note that you can define a range of numbers to perform a “cyclic loop” when shifting numbers:

alpha = Alphanumeric([1, 2, 3, 4 , 5])
print alpha.forward_alphanumeric('123', 6)

// prints: 234

A CLI Tool to the Rescue

Thinking about the next CTF’s challenges I wrote a small CLI tool called shift that makes it easier to shift alphanumeric characters.

echo "Dqhqdeqp Oazfqzf" | python shift.py -p 12 --backwards
// prints: "Reversed Content"

python shift.py abc123 -p 5
// prints: "fgh678"

You can see the documentation and the source code as well on GitHub.

Monitor application stability with Bugsnag to decide if your engineering team should be building new features on your roadmap or fixing bugs to stabilize your application.Try it free.

Topics:
python ,web dev ,python functions

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}