⭐Nanocombattants
404CTF{fi3r_n4n0comb4ttant}
Catégorie: Reverse Difficulté: hard Flag: -
Challenge
Description
Entrez dans l'arène
Le CHAUSSURE, cette fameuse entité pionnière dans le domaine du sport de combat a ouvert un tournoi pour tous les chat-diateurs qui souhaiteraient se mesurer au reste du monde. Il est l'heure d'aller se confronter dans l'arène, à un détail près... Une vérification est opérée à l'entrée, mais impossible de vous souvenir du mot de passe ! Retrouvez-le.
Format de flag : 404CTF{mot-de-passe}
Décompilation du binaire
Pour des questions de simplicité, j'ai commenté le code et zappé 2-3 lignes inutiles pour les explications.
Commençons déjà par le main
, on voit qu'un mot de passe est demandé et que sa taille doit faire 19 caractères, sans quoi on ne rentre pas dans la fonction verify
.
int main() {
// ... des trucs inutiles
puts("=================================================================================");
puts("Bienvenue dans l\'arène ! Pour rentrer, saisissez le mot de passe du CHAUSSURE : ");
puts("_____ _____");
puts("tez de me suivre, pleutre!");
puts(" ||| ||| ");
puts(" ||| ~Qu\'est-ce que tu peux faire contre le 404 CROU~ ||| ");
puts(" ||| ~on a la méthode et les XOR qui rendent fou~ ||| ");
puts(" ||| ||| ");
puts(
"(___) ≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈ (___)"
);
puts("");
__printf_chk(0x1," >>> ");
__isoc99_scanf("%255s",&password);
size = strlen(password);
if (size == 19) {
verify(&password);
code = 0x0;
} else {
perdu();
code = -1;
}
return code;
}
Passons maintenant à la fonction verify
mais on ne va pas s'amuser à tout décortiquer. Il suffit de voir que la boucle est parcourue 19 fois (si entre temps, on n'a pas terminé dans la fonction perdu
) et qu'à chaque fois, l'adresse de notre password est incrémentée. Autrement dit, on vérifie très probablement notre mot de passe caractère par caractère.
Il faut juste retenir qu'à l'intérieur de la fonction joue_avec_les_regs
, il y a un appel de ptrace
avec le paramètre PTRACE_GETREGS
.
void verify(char *password) {
uint pid_00;
uint _pid;
__pid_t pid;
uint uVar1;
undefined *__dest;
undefined *__dest_00;
int password_address;
long in_FS_OFFSET;
uint status;
__dest = (undefined *)mmap((void *)0x0,(ulong)DAT_00104d60,0x7,0x21,-0x1,0x0);
memcpy(__dest,&DAT_00104ec0,(ulong)DAT_00104d60);
__dest_00 = (undefined *)mmap((void *)0x0,(ulong)DAT_00104cd0,0x7,0x22,-0x1,0x0);
memcpy(__dest_00,&DAT_00104ce0,(ulong)DAT_00104cd0);
pid_00 = fork();
if ((int)pid_00 < 0x0) {
__printf_chk(0x1,"ERREUR : TRAVAIL TERMINÉ");
exit(0x1);
}
if (pid_00 != 0x0) {
_pid = fork();
if ((int)_pid < 0x0) {
__printf_chk(0x1,"ERREUR : TRAVAIL TERMINÉ");
exit(0x1);
}
if (_pid != 0x0) {
// Adresse du premier char de notre
password_address = &password;
do {
pid = waitpid(pid_00, &status, 0);
if (pid == -1) goto quit;
if ((status & 0x7f) == 0) goto quit;
if (((char)status == '\x7f') && ((status >> 8 & 0xff) == 5)) {
fonction_osef(_pid,(long)__dest_00, password_address,(long)__dest);
ptrace(PTRACE_CONT, (ulong)pid_00,0, 0);
// Fait de la magie noire avec les ptrace
result = joue_avec_les_regs(pid_00,(long)__dest, password_address);
// Passe au caractères suivant
password_address++;
if (uVar1 == -1) {
perdu();
goto perdu;
}
ptrace(PTRACE_CONT, pid_00, 0, 0);
}
} while ((int)password != 19);
success();
quit:
return;
}
perdu:
FUN_001022f5(__dest_00);
}
arrete_de_me_suivre(__dest, password);
}
Résolution
En lançant avec la commande strace
et un mot de passe de 19 caractères, on voit 3 appels à la fonction ptrace
avec PTRACE_GETREGS
.
$ strace ./nanocombattant 105 ⨯
execve("./nanocombattant", ["./nanocombattant"], 0x7ffe5ffce1c0 /* 56 vars */) = 0
[...]
write(1, "Bienvenue dans l'ar\303\250ne ! Pour r"..., 83Bienvenue dans l'arène ! Pour rentrer, saisissez le mot de passe du CHAUSSURE :
read(0, "0123456789abcdef012\n", 1024) = 20
[...]
ptrace(PTRACE_GETREGS, 16017, NULL, 0x7ffcf1e885a0) = 0
ptrace(PTRACE_SETREGS, 16017, NULL, 0x7ffcf1e885a0) = 0
ptrace(PTRACE_CONT, 16017, NULL, 0) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=16017, si_uid=1000, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
wait4(16017, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP}], 0, NULL) = 16017
ptrace(PTRACE_GETREGS, 16017, NULL, 0x7ffcf1e885a0) = 0
ptrace(PTRACE_SETREGS, 16017, NULL, 0x7ffcf1e885a0) = 0
ptrace(PTRACE_CONT, 16017, NULL, 0) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=16017, si_uid=1000, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
ptrace(PTRACE_CONT, 16016, NULL, 0) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=16016, si_uid=1000, si_status=SIGTRAP, si_utime=0, si_stime=0} ---
wait4(16016, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGTRAP}], 0, NULL) = 16016
ptrace(PTRACE_GETREGS, 16016, NULL, 0x7ffcf1e885b0) = 0
write(1, "Crois-tu pouvoir me fumister si "..., 47Crois-tu pouvoir me fumister si facilement ?!?
) = 47
lseek(0, -1, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(105) = ?
+++ exited with 105 +++
En fait, chaque tour de boucle dans la fonction verify
déclenche 3 appels PTRACE_GETREGS
. On peut donc tester tous les caractères pour savoir lequel nous fait passer au tour de boucle suivant en comptant le nombre d'appels à ptrace
.
import subprocess
import string
CHARSET = string.ascii_letters + string.digits + string.punctuation
PASSWORD_SIZE = 19
password = ''
while len(password) != PASSWORD_SIZE:
found = False
print(f'Password: {password}')
for c in CHARSET:
try:
# Lance le binaire avec strace et complète la suite du mot de passe avec des "a"
data = subprocess.run(['strace', './nanocombattant'], input=f'{password}{c}'.ljust(PASSWORD_SIZE, 'a'), capture_output=True, text=True, check=False)
# Compte le nombre d'appels à ptrace avec PTRACE_GETREGS
getregs_count = data.stderr.count('PTRACE_GETREGS')
# Le bon caractère aura plus de PTRACE_GETREGS que les autres
if getregs_count > (len(password) + 1) * 3 or data.returncode == 0:
password += c
found = True
break
except Exception as e:
exit(1)
if not found:
print('No char found')
exit(2)
print('Flag: 404CTF{' + password + '}')
Dernière mise à jour
Cet article vous a-t-il été utile ?