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

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

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.

Take a look at an Indigo.Design sample application to learn more about how apps are created with design to code software.

Topics:
python ,web dev ,python functions

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}