Skip to content

libmagic asi nie je uplne najlepsi napad #1365

@sammko

Description

@sammko

#1332

Pastujem zo slacku:

Pretože libmagic nemá najlepšiu reputaciu, ale aj keby mal, tak je to presne tá jedna vec, ktorá môže byť deravá. Parsovanie vstupov je asi najčastejší attack vector. A to na to stačí aby ten software parsoval jeden konkrétny formát, nie libmagic, ktorý je určený na to, aby pochopil všetky formáty na svete. Okrem toho je to native libka v C. Neexistuje ani šanca, že tam nie sú desiatky RCE. (samozrejme závisí odkiaľ je ten libmagic a kto ho ako kompiluje, kompilátory dnes vedia generovať kadečo, aby sa zabránilo takýmto exploitom, no samozrejme to nie je riešenie)
Takže niekto tam uploadne exploit, payload sa execne v procese toho uwsgi s prístupom do databazy a tak ďalej a tak podobne. Riešení niekoľko: púšťať libmagic v nejakom sandboxe. Toto znie komplikovane, ale verím, že sú na presne takéto účely hotové riešenia. Sandboxy sú ale deravé a my riešime iný (ľahší) problém ako "dostanem súbor, chcem zistiť čo to je", konkrétne chceme zistiť či je súbor PDF alebo nejaký docx alebo xlsx ako pozerám. PDF je trivialne, ma asi 4B magic number na začiatku. docx a xlsx su zipka, takže buď to vie akceptovať ľubovoľné zip (tiež magic number) alebo sa dá pozrieť či sa v tom zipku nachádza súbor so správnym názvom (toto robí aj libmagic pre tieto formaty), nazvy suborov v zip nie su komprimovane. Dokopy asi 10 riadkov pythonu, ktorý bude bezpečnejší, asi rádovo rýchlejší a máme o dependency menej.

A teda ak nie RCE, tak aspoň DOS, čo tiež nie je nejaké super, keď nám niekto bude zhadzovať uwsgi. Chápem, že možno nie sme nejaký top target, ale ten prínos libmagic-u v tejto situácii je zaporny

Proof of concept:

def is_zip(buf):
    return buf.startswith(b'PK\3\4')

def is_pdf(buf):
    return buf.startswith(b'%PDF-')

def is_rtf(buf):
    return buf.startswith(b'{\\rtf1')

def is_msooxml(buf, T):
    if not is_zip(buf):
        return False

    have_ct = False
    offset = 0
    while True:
        try:
            x = buf.index(b'PK\3\4', offset, 2048)
        except ValueError:
            return False
        
        fname = buf[x+30:x+45]
        if fname == b'[Content_Types]':
            have_ct = True
        if fname.startswith(T) and have_ct:
            return True
        offset = x+1
    return False

def is_docx(buf):
    return is_msooxml(buf, b'word')

def is_xlsx(buf):
    return is_msooxml(buf, b'xl')

Taketo nieco spolu s kontrolou pripony ma takmer nulovu sancu nefungovat, ak teda niekto aktivne nevymysla subory, ktore prejdu a nie su platne. Tomu samozrejme ale nepomoze ani libmagic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions