Recently I found new interesting wargame/ctf site 247ctf. So let's try solving some of the challenges.
The More The Merrier
This is the first and easiest of reversing challenges offered. Description:
One byte is great. But what if you need more? Can you find the flag hidden in this binary?
After not getting great results with Ghidra, I went along with IDA. Decompiling program in IDA we get:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
puts("Nothing to see here..");
return 0LL;
}
So nothing much, after running strings on binary without success. I opened program in hex editor:
and we get our flag. All in all, I feel it was more of steganography challenge then reversing but still a good introduction for beginners.
The Secret Lock
Can you reverse the secret combination to open the lock and recover the flag?
Now, this was an unexpected challenge! After extracting zip file we get html file .. It took me some time to find it since I was really expecting elf file.
Opening it in the browser we get a huge combination lock, 40 slots each having a range between 0 and 500. That's 40^500 combinations in total!
If we look at html source we can find huge if condition:
if (Object.keys(flag).length == 40 && ((flag[37] - flag[37]) * flag[15] == 0) && ((flag[3] + flag[31]) ^ (flag[29] + flag[8]) == 234) && ((flag[32] - flag[12]) * flag[9] == -2332) && ((flag[24] - flag[27] + flag[13]) ^ flag[6] == 114) && ((flag[38] - flag[15]) * flag[33] == 800) && ((flag[34] - flag[21]) * flag[26] == 98) && ((flag[29] + flag[0]) ^ (flag[8] + flag[38]) == 248) && ((flag[21] * flag[18]) ^ (flag[7] - flag[15]) == 2694) && ((flag[28] * flag[23]) ^ (flag[19] - flag[5]) == -9813) && ((flag[34] + flag[30]) ^ (flag[37] + flag[6]) == 72) && ((flag[23] - flag[22]) * flag[12] == 4950) && ((flag[9] * flag[28]) ^ (flag[20] - flag[11]) == 5143) && ((flag[2] * flag[22]) ^ (flag[37] - flag[0]) == 2759) && ((flag[26] - flag[12]) * flag[3] == -3350) && ((flag[35] * flag[0]) ^ (flag[23] - flag[21]) == 2698) && ((flag[20] + flag[31]) ^ (flag[5] + flag[10]) == 22) && ((flag[31] * flag[19]) ^ (flag[1] - flag[2]) == -2655) && ((flag[38] - flag[14]) * flag[18] == 55) && ((flag[29] - flag[19] + flag[10]) ^ flag[2] == 93) && ((flag[13] - flag[25] + flag[30]) ^ flag[29] == 13) && ((flag[35] + flag[33]) ^ (flag[26] + flag[21]) == 249) && ((flag[17] + flag[24]) ^ (flag[34] + flag[1]) == 253) && .....
Obviously, a job for SAT solver. I decided to go with Z3. After parsing if and splitting its conditions we can run our script and get our flag.
Python code here.
Interestingly biggest issue I had was with getting results from Z3 model so in the end I just manually modified printed output in notepad and gotten the flag.
Text Editor Jail
We didn't have time to setup and test a proper jail, so this text editor will have to do for now. Can you break free?
In vim/nvim we can run shell commands by using :! <command>
syntax.
Solution:
:! ls
:! ./run_for_flag
An Impossible Number
Can you think of a number which at the same time is one more than itself?
Let's look at provided c source:
#include <stdio.h>
int main() {
int impossible_number;
FILE *flag;
char c;
if (scanf("%d", &impossible_number)) {
if (impossible_number > 0 && impossible_number > (impossible_number + 1)) {
flag = fopen("flag.txt","r");
while((c = getc(flag)) != EOF) {
printf("%c",c);
}
}
}
return 0;
}
Max signed 32 int is 2 147 483 647. Adding 1 to it will turn it negative and we can pass our check.
nc <url> <port>
2147483647
Sensitive Server Memory
The webserver for this challenge is storing sensitive data in memory. Can you read it? Did anybody patch since 2014?
After reading the description it is obviously a reference to one of the more famous vulnerabilities, heartbleed. Using available POC on a given server is enough to solve this challenge.
python .\heartbleed.py <url_without_tcp://> -p <port>
The Flag Lottery
Can you guess the secret number to win the lottery? The prize is a flag!
Challenge source:
import SocketServer, threading, random, time
class ThreadedLotteryServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
class LotteryHandler(SocketServer.BaseRequestHandler):
def handle(self):
secret = random.Random()
secret.seed(int(time.time()))
winning_choice = str(secret.random())
self.request.sendall("Can you guess the number to win the flag lottery?\n")
your_choice = self.request.recv(1024).strip()
if winning_choice == your_choice:
self.request.sendall("Congratulations you won the lottery! Have a flag!\n")
self.request.sendall("%s\n" % open('flag.txt').readline().rstrip())
else:
self.request.sendall("Nope! The winning number was %s, better luck next time!\n" % winning_choice)
return
if __name__ == '__main__':
SocketServer.TCPServer.allow_reuse_address = True
server = ThreadedLotteryServer(("0.0.0.0", 5000), LotteryHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()
The problem lies in incorrectly initialized random. We can simply initialize it in the same way (with a small added time delay of 1 sec) and get the correct solution.
Note: The solution requires python 2. On python 3 precision of random differs a bit and won't produce the same results.
All provided challenges and solutions can be found on github repo.
Comments
comments powered by Disqus