Making custom firmware for Grandstream phones

    Our company finally decided to switch to ip telephony, and we purchased Grandstream ip phones of different models, among them were the GXP2130 and GXP2160. Everything would be fine, but the BLF keys on these phones, in the case of a free line, glow terribly bright green, very annoying. Below I will tell you how I solved this problem.

    Vulnerability Search

    To get started, I ran the phone firmware through binwalk and looked in the hex editor. The result was upset, the firmware was encrypted, so you need to go the other way, try to get root access to the phone itself. For almost two weeks, I searched for a vulnerability in filtering fields in the web interface. In a couple of places I found the opportunity to pass my parameters to the syslogd and udhcpd commands. In the case of syslog, this did not provide interest, but in the case of udhcpd, it was possible to specify the -s parameter, which referred to a script that was run to configure the interface. Any command could be executed here, BUT it was impossible to specify its parameters, it was always executed with the defconfig parameter. Using this vulnerability, it was not possible to do anything worthwhile. Therefore, the search for vulnerability continued. And I found her! I will not tell you exactly where, because
    Further, I suggested that the manufacturer probably should have left the possibility of getting the root for itself, for example, for debugging, and it got useful to look for it. I analyzed all the scripts executed via the web, I didn’t find anything similar there. Then he began the analysis of the shell, which is available via ssh.

    Shell analysis

    When we connect to the phone via ssh, we get to the gs_config shell, from which a small list of commands processed by the shell itself is available. I suggested that there may be service commands not described in help. For this, I launched
    strings gs_config
    to look at the lines inside the binary, and saw an oddly curious:
    fw_setenv console yes
    I quickly typed these commands in the shell, and I saw the following:
    Grandstream GXP2130 Command Shell Copyright 2014
    GXP2130> gssu
    Challenge: fb72f22fc5e233ae
    The command requires a password generated on the basis of a certain challenge
    Without hesitation, load gs_config in the IDA and look for the gssu line there,

    then XFER go to the sub_94BC function, we

    see that after the gssu command is entered, the sub_B254 function is called, and depending on its result, it executes or the / bin / sh command is not executed

    Go to this function and press F5, to switch from the assembler to C ++ pseudo-code
    Having run a glance through the code you can see that challenge is generated first, and the resulting challenge is put into the variable s
    printf("Challenge: %s\n", s);
    then the input from the user response is accepted and the generation of Response itself begins.

    This line pulls out the admin password from nvram, by which you log in to the web face and ssh.
    Next, it is put into the v13 variable.
    Then the contents of the variable v1, which is the parameter of our sub_B254 function, are analyzed. Its value indicates that for which team we are testing Response, there should be three such teams, but I found only two: gssu and console
    In the case of gssu, we get the string % s: sfTXrhCA2010:% s in the variable v14
    Next, through sprintf we get the final line of the form Callenge: sfTXrhCA2010: Password and put it in the variable v27
    Then we count md5 from this line.

    Next is the do ... while loop for 8 iterations in which we go through half of the md5 sum, and translate it into a hex line. Then we compare it with the entered Response.
    The algorithm is quite simple, here is the implementation of keygen on python:
    import hashlib
    import sys
    secret=':sfTXrhCA2010:'                         # /sin/sh
    #secret=':a50ba3e905c0627eb0a204d82880fb46:'    # console
    print result

    Work with firmware

    Well, we have learned how to permanently get root on the phone, now it's time to learn how it unpacks the firmware.
    A quick look at the executable scripts on the phone finds the script / sbin / provision, which is actually responsible for flashing the phone.
    It can be seen from it that the firmware is unpacked into separate files with the prov_pipe_unpack command , and then, individual sections of the firmware are decrypted with the prov_pipedec command. In fact, this is the same binary. To find out all its features, I also threw it into the IDA, where I found the teams we are interested in, these are:
    I will not dwell on how this was searched in the IDA, I’ll just say that to facilitate reverse engineering, gdbserver was downloaded to the phone, and I ran these commands in the debugger.
    Now about these commands in more detail:
    prov_unpack - unpacks the firmware into separate files, starts like this:
    prov_unpack  gxp1400fw.bin
    The decompression result will be in the current directory.
    prov_dec - decrypts individual firmware files
    prov_dec nokey gxp1400prog.bin gxp1400prog.bin
    The first parameter is the firmware key, for factory firmware it is nokey, however there may be oem phones with their own keys.
    The second parameter is the file to be decrypted.
    The third parameter is, according to the manufacturer’s intention, this is the section corresponding to the image in the phone’s flash, the program compares the version from the file and the already flashed version, and if they are equal, it does nothing. We specify the encrypted firmware file again as the second parameter, then everything goes smoothly. At the output, we get the decrypted file gxp1400prog.bin
    prov_enc - encrypts the image back, but it needs the image in a special format.
    The image format is more detailed:
    An image consists of a header and actually useful data, for example, the squashfs file system.
    Below is an example image header gxp2130prog.bin The

    header occupies the first 0x5C bytes, here is its format:
    struct header
        DWORD signature;
        DWORD version;
        DWORD size_max;
        DWORD size;
        WORD image_id;
        WORD checksum;
        WORD ts_year;
        WORD ts_month_day;
        WORD ts_time;
        WORD oem_id;
        DWORD FW_V_Mask;
        WORD supported_bits1;
        WORD supported_bits2;
        WORD supported_bits3;
        WORD supported_bits4;
        WORD HW_id;

    The purpose of not all fields is clear to me, but it doesn’t matter, consider the main ones:
    version is the version, if we want our firmware to flash on the phone, its version must be higher than the current
    size - the size of the useful data in the firmware, this parameter uses prov_enc and encryption
    checksum - the checksum of the firmware, it is used when decrypting the firmware, if it does not match, the firmware will not be updated, it will be generated later. The remaining header fields should be left as in the original, unless it is possible to correct the date.
    Next, the header reaches zeros to a size of 0x5C. The
    useful data goes from offset 0x200, the space between the header and useful data is clogged with units ...
    This is how the decrypted firmware looks and in this form it is written to the flash, along with the header.
    The prov_enc utility works with a different format. At the input, it should have a file where useful data first goes, and right after them (i.e. at the end of the file), a header, size 0x5C. prov_enc reads from the header the size of the payload, encrypts them, and then encrypts the header itself. Only the first 32 bytes are always encrypted in the header, the remaining bytes are not encrypted. In order to assemble the encrypted file back into the prov_pack utility firmware, it must be converted to the first format, i.e. move the already encrypted header to the beginning of the file, and put the encrypted body of the firmware at offset 0x200.
    It starts prov_enc like this:
    prov_enc nokey gxp1400prog.bin gxp1400prog.bin
    Everything here is similar to prov_dec.
    prov_pack - collects all encrypted firmware files into a single firmware ready for firmware on the phone
    prov_pack nokey gxp1400fw.bin gxp1400boot.bin gxp1400recovey.bin gxp1400core.bin gxp1400base.bin gxp1400prog.bin

    At the output, we have the gxp1400fw.bin file ready for firmware.
    It is more convenient to work with these utilities in the qemu virtual machine than in the phone itself.

    Patch green LED

    Now let's move on to the fact for which everything was started, turning off the green LED on the BLF keys.
    The gs_gui process is responsible for the GUI in the phone, it lies in / app / gui / and uses the library of libraries from / app / gui / lib
    We make grep by the word LED in the folder / app / gui and find the library
    Merge it to your computer and ship to the IDA, since all the functions there have human names.
    We find a function with an interesting name turnOnMKPLED, another writeToFile function is called from it (LEDCOLOR, int, bool)
    Below is its piece:

    As you can see, the files / proc / sys / dev / led / * are used to work with LEDs.
    I tried to write data to these files through echo, and found that the prog_green and prog_red files are responsible for the BLF (MKP) keys.
    Accordingly, to prohibit the green LED from being lit, you just need to prohibit writing to the prog_green file. I did it simply, in the hex editor I changed one letter in the green table.

    Now the patched needs to be poured back into the phone. Let's create custom firmware for this.
    The / app directory is contained in the gxp2130prog.bin image. Unpack the firmware and decrypt this image. Next, in the hex editor, we trim everything to an offset of 0x200 and get a squashfs image.
    To work with squashfs, you need a set of squashfs-tools utilities.
    Version 4.0 from the Centos distribution was not able to unpack it, so we had to build version 4.2 from the sources.
    Unpack the command
    ./unsquashfs gxp2130prog.bin
    The contents will be in the squashfs-root directory.
    Next, change the file to ours and pack it back
    ./mksquashfs  squashfs-root new.bin -comp xz -all-root -noappend -always-use-fragments
    Now we need to prepare a headline. To get started, take the header from the original gxp2130prog.bin and copy it to the end of our squashfs image. Now you need to fix the version and size in it. Size is the size of the squashfs image itself, without a caption. Now you need to calculate the checksum. Here is the C code for its generation (for python, a similar code for some reason worked 200 times slower, but it started in qemu with arm emulation)
    void main(int argc, char *argv[])
            FILE *f;
            int summ=0;
            int word;
            char buff[32];
            int i;
            f = fopen(argv[1],"rb");
                    while(fread(buff,32,1,f) != 0)
                                    word = buff[i];
                                    word |= buff[i+1]<<8;
                                    summ += word;
                                    summ &= 0xFFFF;

    Next, encrypt the file, rearrange the header and assemble the firmware, as described in the previous section. Well, flashing it.
    If you want to return the original firmware, then on the phone you need to enter the command from the root
    nvram set force_upgrade=1
    And the phone will flash any firmware. regardless of version.

    If interested, I can also write a detailed article on the provisioning of Grandstream phones (there are nuances that were not written anywhere) and the closed web-api

    upd: there was a secret for generating recponse on the GXV3140:

    Also popular now: