The challenges can be found at https://imaginaryctf.org/ArchivedChallenges
Misc
Pickle
Description
This pickle seems to be hiding the flag…
Attachments https://imaginaryctf.org/f/1O5dx
Solution
Pickle can be decompiled with Fickling
output from fickling
❯ python -m fickling out.pickle
from __main__ import FlagPrinter
_var0 = FlagPrinter()
_var0.__setstate__({'flag': [105, 99, 116, 102, 123, 99, 117, 99, 117, 109, 98, 101, 114, 115, 95, 111, 114, 95, 112, 105, 99, 107, 108, 101, 115, 63, 125], 'fake': 'jctf{c0uld_th1s_b3_th3_fl4g?}'})
result = _var0
flag is a list with ascii
values now, we can get the flag from the below python code
flag = [105, 99 ,116,102,123,99 ,117,99 ,117,109,98 ,101,114,115,95 ,111,114,95 ,112,105,99 ,107,108,101,115,63 ,125]
for i in flag:
print(chr(i),end="")
Flag: ictf{cucumbers_or_pickles?}
TARp
Description
It helps to have a rain tarp when there’s bad weather.
Attachments https://imaginaryctf.org/f/tiUxj#server.py
Solution
it is a service that can unTAR files and read the extracted files
i found that symlinks can be used to read or write files outside the extracted folder
https://github.com/cwgem/python-safe-tar-extract
then created a tar that points to the flag.txt
supplying the tar file we get the file
Flag: ictf{is_it_still_a_zip_slip_if_we_use_a_tar_file?}
Forensics
Lost flag
Description
Quasar said he lost his flag but it seems to me like it’s in this file
Attachments https://imaginaryctf.org/f/fOweJ#flag.zip
Solution
running strings on the ./flag/.DS_store
we get the flag
❯ strings -e b -n 6 .DS_Store
flag.jpg
!ictf{mac_is_better_than_templeos}
-e b
specifies the 16-bit bigendian encoding(man page)
there are also online parsers for .DS_store
files
Flag: ictf{mac_is_better_than_templeos}
Age
Description
Some things, despite appearing quite new, are actually rather old. Did you know python has been around for more than 30 years?
Attachments https://imaginaryctf.org/f/T2F1K#outer.py
Solution
.pyc
is the compiled bytecode,the internal representation of a Python program in the CPython interpreter
I was not able to find a pyc
decompiler or parser for 3.10 so I dug in the source code and found the structure of pyc files
#https://github.com/python/cpython/blob/7e7a570818a41df6e97e25989d92449c7cc40aad/Lib/importlib/_bootstrap_external.py#L683
def _code_to_timestamp_pyc(code, mtime=0, source_size=0):
"Produce the data for a timestamp-based pyc."
data = bytearray(MAGIC_NUMBER)
data.extend(_pack_uint32(0))
data.extend(_pack_uint32(mtime))
data.extend(_pack_uint32(source_size))
data.extend(marshal.dumps(code))
return data
we have starting 16 bytes containg
MAGIC_NUMBER
0
unix_timestamp
source_size
and the rest is marshal
dump of the code
we can disassemble the the pyc with the following code
import dis
import marshal
import struct
with open('./outer/gen.cpython-310.pyc','rb') as f:
a = f.read(16)
b = f.read()
header = struct.unpack('<4sLLL',a)
print(header)
code = marshal.loads(b)
print(dis.disassemble(code))
output:
(b'o\r\r\n', 0, 667044855, 449)
3 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('ZipFile',))
4 IMPORT_NAME 0 (zipfile)
6 IMPORT_FROM 1 (ZipFile)
8 STORE_NAME 1 (ZipFile)
10 POP_TOP
4 12 LOAD_CONST 0 (0)
14 LOAD_CONST 2 (('sha256',))
16 IMPORT_NAME 2 (hashlib)
18 IMPORT_FROM 3 (sha256)
20 STORE_NAME 3 (sha256)
22 POP_TOP
5 24 LOAD_CONST 0 (0)
26 LOAD_CONST 3 (('time',))
28 IMPORT_NAME 4 (time)
30 IMPORT_FROM 4 (time)
32 STORE_NAME 4 (time)
34 POP_TOP
6 36 LOAD_CONST 0 (0)
38 LOAD_CONST 4 (('system',))
40 IMPORT_NAME 5 (os)
42 IMPORT_FROM 6 (system)
44 STORE_NAME 6 (system)
46 POP_TOP
8 48 LOAD_NAME 7 (int)
50 LOAD_NAME 4 (time)
52 CALL_FUNCTION 0
54 CALL_FUNCTION 1
56 STORE_NAME 8 (unixtime)
9 58 LOAD_NAME 3 (sha256)
60 LOAD_NAME 9 (str)
62 LOAD_NAME 8 (unixtime)
64 CALL_FUNCTION 1
66 LOAD_METHOD 10 (encode)
68 CALL_METHOD 0
70 CALL_FUNCTION 1
72 LOAD_METHOD 11 (hexdigest)
74 CALL_METHOD 0
76 STORE_NAME 12 (password)
10 78 LOAD_NAME 13 (print)
80 LOAD_CONST 5 ('Writing with time')
82 LOAD_NAME 8 (unixtime)
84 LOAD_CONST 6 ('and password')
86 LOAD_NAME 12 (password)
88 CALL_FUNCTION 4
90 POP_TOP
11 92 LOAD_NAME 6 (system)
94 LOAD_CONST 7 ('zip --password ')
96 LOAD_NAME 12 (password)
98 FORMAT_VALUE 0
100 LOAD_CONST 8 (' inner.zip flag.txt')
102 BUILD_STRING 3
104 CALL_FUNCTION 1
106 POP_TOP
13 108 LOAD_NAME 1 (ZipFile)
110 LOAD_CONST 9 ('outer.zip')
112 LOAD_CONST 10 ('w')
114 LOAD_CONST 11 (('mode',))
116 CALL_FUNCTION_KW 2
118 SETUP_WITH 24 (to 168)
120 STORE_NAME 14 (outer_zip)
14 122 LOAD_NAME 14 (outer_zip)
124 LOAD_METHOD 15 (write)
126 LOAD_CONST 12 ('inner.zip')
128 CALL_METHOD 1
130 POP_TOP
15 132 LOAD_NAME 14 (outer_zip)
134 LOAD_METHOD 15 (write)
136 LOAD_NAME 16 (__file__)
138 LOAD_METHOD 17 (split)
140 LOAD_CONST 13 ('/')
142 CALL_METHOD 1
144 LOAD_CONST 14 (-1)
146 BINARY_SUBSCR
148 CALL_METHOD 1
150 POP_TOP
152 POP_BLOCK
13 154 LOAD_CONST 15 (None)
156 DUP_TOP
158 DUP_TOP
160 CALL_FUNCTION 3
162 POP_TOP
164 LOAD_CONST 15 (None)
166 RETURN_VALUE
>> 168 WITH_EXCEPT_START
170 POP_JUMP_IF_TRUE 87 (to 174)
172 RERAISE 1
>> 174 POP_TOP
176 POP_TOP
178 POP_TOP
180 POP_EXCEPT
182 POP_TOP
184 LOAD_CONST 15 (None)
186 RETURN_VALUE
None
from the disassembly we can see that the passwd for the innner.zip
is sha256(unix_timestamp)
i tried passwd around the the timestamp
and found the right timestamp as 667044877
.
decrypting with ba2fe6b52f7610a6ddc4ce405d302e0eb93223b3b0c4d833895fe3ae68f0c0fe
we get the flag
Flag: ictf{i've_traveled_here_from_decades_ago_to_deliver_you_this_flag}
Geoguessr Sucks
Description
I went on a crazy vacation to the middle of nowhere, and I took a real picture with real camera of this very important field. Where is it?
Flag format is ictf{lat_long}, both rounded up to 5 decimal places because this exact spot is very important to me. Example: ictf{1.23456_7.89101}
Attachments https://imaginaryctf.org/f/1OD91
Solution
Running exiftool on the png we get the GPS coordinate
GPS Latitude Ref : North
GPS Longitude Ref : West
GPS Position : 39 deg 8' 1.31", 98 deg 31' 42.01"
we can convert the DMS
to DD
with online convertors like this
Flag: ictf{39.13370_-98.52834}
Crypto
Rotating Secret Assembler
Description
Encrypt the flag as many times as you want! I’m making sure to never use the same public key twice, just to be safe.
Challenge file
#!/usr/bin/env python3
from Crypto.Util.number import *
class Rotator:
QUEUE_LENGTH = 10
def __init__(self):
self.e = 65537
self.m = bytes_to_long(open('flag.txt', 'rb').read())
self.queue = [getPrime(512) for i in range(self.QUEUE_LENGTH)]
def get_new_primes(self):
ret = self.queue[-2:]
self.queue.pop()
while(len(self.queue) < self.QUEUE_LENGTH):
self.queue = [getPrime(512)] + self.queue
return tuple(ret)
def enc_flag(self):
p, q = self.get_new_primes()
n = p*q
print(f"Public key: {(n, self.e)}")
print(f"Your encrypted flag: {pow(self.m, self.e, n)}")
rot = Rotator()
print('='*80)
print(open(__file__).read())
print('='*80)
while True:
inp = input("Would you like an encrypted flag (y/n)? ")
if 'y' in inp.lower():
rot.enc_flag()
print()
else:
break
print(long_to_bytes(pow(c1, d, n1)))
Solution
Between two consecutive encryption a prime is common so we can extract the the common prime by taking the GCD of the two consecutive n
once we get one prime it is easy to decrypt the RSA text
more info on RSA can be found on wikipedia
Flag: `ictf{why_would_I_throw_away_perfectly_good_primes?
Relatively Small Arguments
Description
Last challenge before the big CTF! Bog-standard RSA, but I made some of the numbers smaller, just for slightly faster calculation.
Attachments https://imaginaryctf.org/f/3z4Km#rsa.py
Solution
d
is very small compared to p,q
so it is vulnerable to Wiener’s RSA Attack
we use the method to get the pair of primes and then decrypt the flag
Flag: ictf{have_fun_at_ICTF_22!!!_559543c1}
xorrot
Description
Last challenge before the big CTF! Bog-standard RSA, but I made some of the numbers smaller, just for slightly faster calculation.
Attachments https://imaginaryctf.org/f/VTZOE#xorrot.py
Solution
it is a simple xor with changing key the following code decrypt it
ch=bytes.fromhex('970a17121d121d2b28181a19083b2f021d0d03030e1526370d091c2f360f392b1c0d3a340e1c263e070003061711013b32021d173a2b1c090f31351f06072b2b1c0d3a390f1b01072b3c0b09132d33030311')
pt = b''
def xor(i,ch):
key = i
tmp = b''
for c in ch:
key = c^key
tmp+= bytes([key])
return tmp
for i in range(1,256):
pt = xor(i,ch)
if b'ictf' in pt :
print(pt)
Flag: ictf{it_would_probably_help_if_the_key_affected_more_than_just_the_first_char_lol}
Web
Almost SSTI
Description
I heard that you can prevent SSTI by enforcing really strong restrictions on user input length, so I’ve done that! Surely my webserver is now completely impregnable from any bugs.
Attachments http://puzzler7.imaginaryctf.org:3002/
Solution
in the code, i saw debug=True
so i went to /console
path and it was unlocked so
we can do RCE and read the flag
Flag: ictf{oops_I_left_my_debugger_on_I_need_to_run_home_before_my_webserver_burns_down}
Unchained
Description
I heard that you can prevent SSTI by enforcing really strong restrictions on user input length, so I’ve done that! Surely my webserver is now completely impregnable from any bugs.
Attachments http://puzzler7.imaginaryctf.org:3006
Solution
since there are two web servers we can use HTTP Parameter Pollution Flask reads the first value while Django reads the last so the following link gives us the flag
/flag?user=admin&user=aaa
Flag: ictf{only_accessible_by_schroedinger's_admin}
escape quikmafs
a simple parsing chall solve script
import pwn
import re
server = pwn.remote('puzzler7.imaginaryctf.org', 4006)
for i in range(100):
a= server.recvuntil(b">>>")
prog = re.compile(b"\d* [\*,\-,\+,\^,\&,\|] \d*")
ans = prog.findall(a
hehe = eval(ans[0].decode())
print(f"{i} {ans} {hehe}")
server.sendline(str(hehe).encode())
print(server.recvall())
flag :ictf{congrats_you've_conquered_the_blackboard...for_now...}
BC jail
only /bc?
characters are allowed we get the file name by incrementally bruteforcing with echo
and printing it with /bin/cat
>>> /b??/?c?? ??????????????????????
/bin/zcat /bin/zcmp ictf{bre4k1ng_7he_j41l
>>> /b??/c?? ??????????????????????
_like_4_b0ss!}
>>>
flag: ictf{bre4k1ng_7he_j41l_like_4_b0ss!}
mixup
flag: ictf{un1c0de_m4g1c_nahsdfoasihdfasohdfoiashdfjkadshfljadsfhdsklahflkhjdafs}
Enormous
n is very large so we can brute force for valid m
for i in range(100):
t = c + i*n
t = int(pow(t,1/31))
print(t)
t = long_to_bytes(t)
if b'ictf' in t:
print(t)
flag: ictf{d0nt_f0rget_t0_pad_y0ur_pl@intexts!}
Personalized
bruteforcing for a seed with least value i found 4213973159
makeing e=1 so c==m
flag : ictf{just_f0r_y0uuuuuuuu}
aes
it was a simple bruteforce chall solve script
from Crypto.Cipher import AES
ans = b"\xd6\x19O\xbeA\xb0\x15\x87\x0e\xc7\xc4\xc1\xe9h\xd8\xe6\xc6\x95\x82\xaa#\x91\xdb2l\xfa\xf7\xe1C\xb8\x11\x04\x82p\xe5\x9e\xb1\x0c*\xcc[('\x0f\xcc\xa7W\xff"
keys =[]
data = open("""/usr/share/wordlists/rockyou.txt""", "rb").readlines()[:10000]
for key in data:
key = key.strip()
key = key.zfill(16)
if len(key) == 16:
cipher = AES.new(key, AES.MODE_ECB)
t = cipher.decrypt(ans)
if b"ictf" in t:
print(t)
flag: ictf{d0nt_us3_w3ak_k3ys!!!!}
same
same message was enc with two diff e we can recover m using Extended Euclidean algorithm
sol.sage
from Crypto.Util.number import long_to_bytes as lb
n = 88627598925887227793409704066287679810103408445903546693879278352563489802835708613718629728355698762251810901364530308365201192197988674078034209878433048946797619290221501750862580914894979204943093716650072734138749420932619469204815802746273252727013183568196402223549961607284086898768583604510696483111
c1 = 45254947860172381004009381991735702721210786277711531577381599020185600496787746985669891424940792336396574951744089759764874889285927022268694128526139687661305707984329995359802337446670063047702309778972385903473896687843125261988493615328641864610786785749566148338268077425756876069789788618208807001704
c2 = 16054811947596452078263236160429328686151351092304509270058479526590947874445940946506791900760052230887962479603369427120610506778471930164144528718052332194666418267005043709704814833963217926271924910466448499814399455203725279998913865531351070938872586642424346857094632491904168889134624707595846754719
e1 = 1337
e2 = 31337
extended = xgcd(e1, e2)
g = extended[0]
a = extended[1]
b = extended[2]
m = (((c1^a)%n) * ((c2^b)%n))%n
print(lb(m))
flag : ictf{n3ver_r3use_m0dul1}
Shady Penguins
trying xor
,and
on the images and
gave us the flag
we can xor with gmic
PS>.\gmic.exe ..\Penguins.png ..\Spy.png -blend and -o ../sol.png
flag: ictf{visual_crypto_is_neato}
Just Plane Crazy
the given image has gps coordinates and with flightradar24 we can obtain the flag
flag : ictf{FRA_SFO_777-322}
I like to MOV it, MOV it
the binary is checking the flag char by char so we can bruet force thr flag with the output
flag : ictf{mov_S1d3_ChanN3l_Att4ck}
Backtracking
i got the flag by simply pressing the back button
flag : ictf{it's_me_the_friend_you_made_along_the_way}
Fake Flag Database
the sheet with the original flag was locked :/ when i tried seaching for ictf{
in inspect window i found the flag :]
flag: ictf{nothing_is_hidden_nothing_is_safe}
Replacement
in the network logs i go the flag
flag: ictf{gr333333333333333n_flags_are_g00d_tho}
Zippy
since the passwd is long its hash will be used for the password
import pyzipper
import base64
passwd = base64.b64decode("ng3pV1YIws4l91Ai04m3IMVa2kg=")
with pyzipper.AESZipFile('chall.zip', 'r', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as extracted_zip:
extracted_zip.extractall(pwd=passwd)
flag : ictf{fastest_hash_cracking_gun_in_the_w3st}
phpsucks
alphabets and numbers are blocked but we can still execute commands as shown here
/cmd=$_=',:<-:["[*.<[)/@'^"_____/}=_@_/@@.";$_();
flag: ictf{th3r3_4r3_n0_l4ngu4g3_l1k3_Php}
What Next
flag : ictf{sn34ky_st4t1c_g3n3r4t10n}
nameless jail
Shine
each line has odd char which are uppercase or \x0e
and \x00
taking the position as hex we get the flag
import string
a = open('flag.txt','rb')
a = a.read()
a = a.replace(b"\n",b"")
print(len(a)/64)
data = [a[i:i+16] for i in range(0,len(a),16)]
print(data)
flag = ''
for i in data:
for k,j in enumerate(i):
t = bytes([j])
s =string.ascii_uppercase.encode()+b"\x0e\x00
if t in s:
flag+=hex(k)[-1]
print(flag)
print(bytes.fromhex(flag))
flag: ictf{writing_challenges_is_sort_of_like_writing_a_novel_i_guess}