[re] Snake (10)

Description

Short info about task:

  • Name: Snake
  • Difficulty: easy
  • Score: 10

Flag should be in the format: HTB{username:password}

Overview

We have got the python script that is waiting for some username and password:

$ python snake.py

___________.__               _________              __
\__    ___/|  |__   ____    /   _____/ ____ _____  |  | __ ____
 |    |   |  |  \_/ __ \   \_____  \ /    \__  \ |  |/ // __ \
 |    |   |   Y  \  ___/   /        \   |  \/ __ \|    <\  ___/
 |____|   |___|  /\___  > /_______  /___|  (____  /__|_ \___  >
               \/     \/          \/     \/     \/     \/    \/


The Snake Created by 3XPL017
Your number is 809
Authentication required

Enter your username
QWE
Wrong username try harder

Let’s look into code to get more info about how to get username and password.

Bypass code

We can see partially obfuscated variables, that are later xored with some values, let’s try to get username.

Username

Username is combined from random dropped variables that are of course hex-encoded letters (i.e db = '\x6e').

user_input = raw_input('Enter your username\n')
if user_input == slither:
    pass

else:
    print 'Wrong username try harder'
    exit()

To get correct username we should know the slither value, we can easly get it by printing before verification check.

I’ve decided to ‘patch’ above code into:

user_input = slither
print "====> Username: {}".format(user_input)
if user_input == slither:
    pass
> python snake.py

___________.__               _________              __
\__    ___/|  |__   ____    /   _____/ ____ _____  |  | __ ____
  |    |   |  |  \_/ __ \   \_____  \ /    \__  \ |  |/ // __ \
  |    |   |   Y  \  ___/   /        \   |  \/ __ \|    <\  ___/
  |____|   |___|  /\___  > /_______  /___|  (____  /__|_ \___  >
                \/     \/          \/     \/     \/     \/    \/


The Snake Created by 3XPL017
Your number is 351
Authentication required

====> Username: anaconda
Enter your password
PASSWORD
Wrong password try harder

Yay, so the username is anaconda, now we need to guess password.

Password

pass_input = raw_input('Enter your password\n')
for passes in pass_input:
    for char in chars:
        if passes == str(chr(char)):
            print 'Good Job'
            break
        else:
            print 'Wrong password try harder'
            exit(0)
    break

This part is much harder, because every input which starts with 'u' is accepted (we can realize it by checking runtime to which letter input is compared). This is probably “programming mistake”, now we should guess what author wanted to achieve - what kind of verification.

We can also get all chars array ([117, 100, 118, 118, 114, 106, 119, 97, 36, 36, 126, 114, 115, 125, 42, 115, 125, 42, 107, 42, 126, 124, 121, 118, 118]) and print this as string: "udvvrjwa$$~rs}*s}*k*~|yvv", but this is not our password :/

Making a step back, by ignoring loop where chars are incremented by 0xA we can read that chars is just a message: "this is a troll", so the real password is somewhere else.

We can also notice unused variable password: password = [0x69, 0x74, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x65, 0x61, 0x73, 0x79], but this is just another rabbit hole:

>>> password = [0x69, 0x74, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x65, 0x61, 0x73, 0x79]
>>> s = ""
>>> for c in password:
...   s += chr(c)
...
>>> s
'its not that easy'

With some additional prints we can notice that output and lock = 5 variable are the same:

==> chains: this is a troll
==> keys: password!!
==> auth: keep trying
Your number is 774
Lock 5
==> chars1: udvvrjwa$$
==> chars2: udvvrjwa$$~rs}*s}*k*~|yvv

My simplified code which gives above result:

#!/usr/bin/python2.7
import random 

def print_array(name, chars):
    r = ""
    for c in chars:
        r += chr(c)
    print "==> {}: {}".format(name, r)

chains = [0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x6f, 0x6c, 0x6c]
keys = [0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x21, 0x21]
auth = [0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x72, 0x79, 0x69, 0x6e, 0x67]

print_array("chains", chains)
print_array("keys", keys)
print_array("auth", auth)

chars = []

lock_pick = random.randint(0, 0x3e8)
lock = lock_pick * 2
lock = lock + 10
lock = lock / 2
lock = lock - lock_pick

print 'Your number is ' + str(lock_pick)
print 'Lock ' + str(lock)

for key in keys:
    keys_encrypt = lock ^ key
    chars.append(keys_encrypt)

print_array("chars1", chars)
for chain in chains:
    chains_encrypt = chain + 0xA
    chars.append(chains_encrypt)
print_array("chars2", chars)


pass_input = raw_input('Enter your password\n')
for passes in pass_input:
    print chars
    for char in chars:
        if passes == str(chr(char)):
            print 'Good Job'
            break
        else:
            print 'Wrong password try harder'
            exit(0)
    break

After we delete ’troll-part’ of password we can check the flag : HTB{anaconda:udvvrjwa$$} we will get a success.

Summary

After we will make some understanding of all strings, variables and code we can get the flag, but to be clear: this is more like try every possible flag challenge, than challenge which will allow you to learn something.

It’s just internet troll - but not very funny.