Domanda Linguaggi prototype-based

0xbro

Super Moderatore
24 Febbraio 2017
4,471
180
3,833
1,825
Ciao a tutti,
in questi giorni stavo approfondendo le classi di vulnerabilità legate a prototipi e concetti simili, e vorrei approfondire maggiormente la questione.
In genere quando si parla di prototype pollution, che sia essa lato client o lato server, si fa sempre riferimento a JavaScript.

Oggi mi sono imbattuto però in una vulnerabilità simile, denominata Class Pollution, però su Python (che di fatto non ha prototipi ma comunque deriva le classi dai builtins).

Bene, arrivo al punto: oltre a JavaScript e Python, conoscete altri linguaggi che si appoggiano a prototipi o chi per essi?
 
Secondo wikipedia c'è una bella lista di linguaggi prototype-based. Però andando a vedere caso per caso a parte ECMAScript (JavaScript, ActionScript, TypeScript...) tutti gli altri sono linguaggi obsoleti, poco diffusi oppure che supportano solo in parte e in modo diverso i prototipi come R, LUA e Perl che usano un modulo specifico per il prototyping. Non ho abbastanza esperienza con quei linguaggi per poter dire se la classe di vulnerabilità si estende a tutti gli oggetti o solo a quelli creati tramite quel modulo.
 
  • Grazie
Reazioni: 0xbro
Ok perfetto, grazie. Online c'è poca roba quando si parla di classi simili ma al di fuori di JavaScript, infatti volevo prendere questa ricerca - fatta su Python - e provare ad esterndarla su altri linguaggi. Dalla lista ho notato che Ruby potrebbe essere un buon candidato, forse anche Lua... però mi sorge un altro dubbio.

Che tu sappia, dell'elenco sopra c'è qualche linguaggio usato per scrivere codice di back-end, oppure è tutta roba morta e sepolta?
So che Lua viene usato per i moduli di Nmap e Ruby è usato per Metasploit e altre applicazioni, però non ho idea se possano/vengano impiegati per fare backend :boh:
 
In quella lista come linguaggi backend ancora in uso ci sono Perl e Ruby. Cercando meglio in rete ho capito che in Perl intendono tutt'altro per prototipo e non mi sembra vulnerabile. Mentre per Ruby on Rails non ho trovato da nessuna parte il supporto ai prototipi come dice wikipedia, però cercandolo unito a "pollution" escono fuori dei risultati CVE di librerie JS portate su Ruby. LUA potrebbe essere usato in un backend ma non è affatto comune, l'ho visto fare a qualche videogame che lo usava anche nel client e qualche altro sistema che lo usava insieme a C++ per delle parti di logica ad alto livello.

Insomma mi sembra di capire che JS sia l'unico ad aver progettato i prototipi così male, cercando CVE simili per altri linguaggi non viene fuori nulla quindi immagino dipenda solo dall'uso scorretto dello sviluppatore e non un problema intrinseco al linguaggio, perché di questo si tratta: non è normale fare merge tra due oggetti e ritrovarsi con una RCE, anche se a noi piace :asd:
 
Ok perfetto, grazie. Online c'è poca roba quando si parla di classi simili ma al di fuori di JavaScript, infatti volevo prendere questa ricerca - fatta su Python - e provare ad esterndarla su altri linguaggi.
Ho dato solo uno sguardo veloce al tuo link e non ho ancora ben capito in che modo quella roba, almeno per come funziona in python, sia da considerarsi un bug (una fragilità del linguaggio) piuttosto che una feature. Più tardi vedo di leggermelo tutto e con più calma. Comunque, se il tuo obiettivo è quello di replicare quel comportamento in altri linguaggi, direi che potrebbero interessarti i linguaggi che supportano la reflection. Qui ne trovi una lista: https://en.wikipedia.org/wiki/List_of_reflective_programming_languages_and_platforms
 
non ho ancora ben capito in che modo quella roba, almeno per come funziona in python, sia da considerarsi un bug (una fragilità del linguaggio) piuttosto che una feature
Nono ma difatti non è un bug o una fragilità, ma appunto una feature che - se mal gestita - può aprire la strada ad altri bug (un po' come per i prototipi in JavaScript)

Un esempio un po' scemo ma che può rendere l'idea è questo
Python:
from os import popen

class Employee: pass # Creating an empty class

class HR(Employee): pass # Class inherits from Employee class

class Recruiter(HR): pass # Class inherits from HR class

class SystemAdmin(Employee): # Class inherits from Employee class
    def execute_command(self):
        command = self.custom_command if hasattr(self, 'custom_command') else 'echo Hello there'
        return f'[!] Executing: "{command}", output: "{popen(command).read().strip()}"'

def merge(src, dst):
    # Recursive merge function
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)


emp_info = {
    "__class__":{
        "__base__":{
            "__base__":{
                "custom_command": "whoami"
            }
        }
    }
}

recruiter_emp = Recruiter()
system_admin_emp = SystemAdmin()

print(system_admin_emp.execute_command())
merge(emp_info, recruiter_emp)
print(system_admin_emp.execute_command())

#> [!] Executing: "echo Hello there", output: "Hello there"
#> [!] Executing: "whoami", output: "abdulrah33m"

La execute_command in questo caso calza come un pugno nell'occhio per quella che è la logica di questo mini-programma, è messa giusto per avere un gadget all'interno del codice.

Il "problema" è che la merge è ricorsiva, copia anche le proprietà ereditate, e prende in input un presunto JSON inviato dal fronend (e quindi controllabile da users ed eventuali attaccanti). Se non vengono fatti i dovuti controlli, si potrebbe aprire lo scenario per cui con un input come quello di emp_info si riesce ad eseguire del codice arbitrario pur non controllando direttamente i parametri della popen (o di eventuali altri gadget)

In pratica facendo il merge di roba non trusted su un oggetto A posso finire ad alterare i valori di un oggetto B perchè entrambi sono stati derivati dalla stessa classe. Non ha lo stesso impatto di una prototype pollution (soprattutto perchè in Python i built-in object sono immutabili), però il concetto è molto simile e mi sembra abbia un senso.

Comunque, se il tuo obiettivo è quello di replicare quel comportamento in altri linguaggi, direi che potrebbero interessarti i linguaggi che supportano la reflection. Qui ne trovi una lista: https://en.wikipedia.org/wiki/List_of_reflective_programming_languages_and_platforms
Grazie mille, la userò di sicuro come riferimento
 
Okay, adesso ho afferrato il concetto. Quello che mi disturbava non era tanto la execute_command, che magari in alcuni contesti serve davvero, quanto la merge che mi sembrava messa lì solo per abilitare la pollution. Adesso ho capito meglio cosa fa la merge: l'idea è quella di creare il dump di un oggetto e poi ricaricarlo usando merge. È una di quelle cose che nella maggior parte dei linguaggi object-oriented non ha senso perché, tipicamente, lo scheletro dell'oggetto viene definito in una classe e le classi sono per loro natura statiche. In python (magari non è buona norma) però si può fare, perché le classi sono implementati come oggetti e tutti gli oggetti al di fuori dai built-in sono mutabili. Non ho ancora avuto il tempo di leggermi tutto l'articolo, però se ho capito bene basta fare
Python:
from dataclasses import dataclass

@dataclass(frozen=True)
class Employee: pass
per patchare il codice che hai postato e (tutti?) gli altri esempi postati da Abdulrah33m. Il problema di fondo comunque resta. Possiamo comunque scrivere una funzione che "infetta" un po' tutto quanto, perché questo problema in python è visto come una feature. Il problema vero è che la merge non è una funzione che è stata messa li solo per exploitare, ma è una funzione che fa qualcosa di potenzialmente utile (ma implementato con una fragilità).

Magari la reflection è quello che cerchi, però devi vedere se trovi un linguaggio che ti permette di usare la reflection per manipolare una classe... che non è una cosa proprio comunissima. Magari si può indagare se c'è qualche altro linguaggio "famoso" che implementa le classi come oggetti, però oltre a python a me vengono in mente solo linguaggi di nicchia.