Reverse engineering of the device firmware on the example of a flashing "rhino". Part 2


We present to your attention the second part of the article on reverse engineering of the Flashing Rhino device firmware based on the master class at the SMARTRHINO-2018 conference.

In the first part of the article, the device firmware was loaded into the IDA disassembler and a primary analysis of the device protocol commands was performed. Individual teams were tested on a running device.

In the second part, the analysis of the remaining firmware tasks will be performed.

Let me remind you, after analyzing the Bluetooth task regarding the LED control, it was decided to switch to LED-task, since the initial task is to create an application for controlling the LEDs, and this requires a detailed understanding of the firmware.

A firmware file is available for self-study.

All information is provided for educational purposes only.

Under the cat a lot of flashing rhino.

LED task


In short: a complete analysis of the task responsible for switching the LEDs. Analyze data types and global variables.

The LED task is represented by the x_leds_task function located at 0x08005A08 .

In addition to the strange lines "I've got a super power ..." in the main function of the LED task you can pay attention to the line "hue> max: change shine \ r \ n" .



At the same time we see a familiar situation - (WORD *) (v26 + 4). In the context menu of the v26 variable, select the “Convert to struct *” item, then specify the structure created earlier:



Taking into account that v5 = v26 , we repeat the operation “Convert to struct *” for the variable v5.

We continue to structure the code and data. Install the hex representation everywhere. Rename:


The code is improving. But some variables still hurt the eye, for example, the variable v23:





Apparently, v23 is an array of 4 bytes.
idx is the LED index; this index is added to the base address; thus, the call is conducted to the elements at the same displacements - arrays behave this way.

Assign the type of char v23[4] and rename it to leds_smth , the code becomes nicer:



You can also note that the result of the function x_queue_recv is returned in the variable v25:

 x_queue_recv(&v25, leds_queue, 1000); 

But it may not be clear how the necessary data is in the _led structure. The fact is that the variables v25 and _led are located next to each other in the stack - this can be understood by the fact that they are written on adjacent lines in the decompile. The location of the variables on the stack can be seen in a separate window, if you double-click on the variable:



They are probably a structure or the compiler has optimized. Thus, it can be argued that the data from the Bluetooth task is transferred to the LED task. To find out more precisely, I will perform a check on the device - for a zero LED via Bluetooth I will send the values 0x208 , 0x2D0 , 0x398 , 0x3E9 , which could be seen in the code:



The results of checking the value of hue (shade) on the device:


If you look at the code again, you can see that the value 0x398 can be logically associated with a value less than 0x167 (different values ​​are set for the element of the leds_smth array). Therefore, I will perform the following check: I first set the first LED to green (hue = 0x78, LED 010078FF20 command LED 010078FF20 ), while the other three LEDs continue to switch their colors.


Now I will LED 010398FFFF Bluetooth protocol LED 010398FFFF - after that, the first LED has switched to the general color switching mode.

Thus, the value of hue 0x398 resets the static color value, which means that the array leds_smth contains flags (0 or 1) of the busy LEDs:


Rename leds_smth to leds_busy .

Now the purpose of the next block of code should be clear:



The loop in lines 83-101 performs a smooth color mosaic with a color switching step of 5: v12 += 5 . If the LED is on a static color, then this LED does not participate in the mosaic. After the cycle there are lines of short-term switching on of all LEDs.

Rename:


The sub_80039FE function presumably performs a timeout (otherwise the LEDs did not switch smoothly, but instantly), let's call it x_sleep , and the variable v16 — led_timeout .

The purpose of the sub_8006934 function is not yet obvious, but it is used everywhere after setting the color on the LEDs - you can call it x_led_fix_color .

After these renames, it is easy to understand the sub_8006944 function (called in the “hue <= 0x167” branch):



Here, an additional check is simply performed to establish the color of the LED. Rename the sub_8006944 function to x_led_set_hsv_wrap (the _wrap suffix is ​​an explanation that this is a wrapper over another function) and install the following prototype for it:

 signed int __fastcall x_led_set_hsv_wrap(int led_control, signed int idx, int hue, char sat, char val) 

Let us return to the level above to the function x_leds_task. Looking again at the code, you may find that the “hue> 0x3E8” branch began to look like this:



That is, the value of hue greater than 0x3E8 should change the timeout of the colored mosaic. I will check by sending some values ​​to the device:


When exiting the main loop of the LED task, the sub_8003C44 function is used , which is also used in the sub_8005070 function:



Rename:


Further, in the LED task, the following branch cannot but draw attention to itself:



You can try the LED B816D8D90000FFFF command LED B816D8D90000FFFF . But if you remember that only 2 symbols are taken as the LED index, an attempt to achieve this code will be obviously unsuccessful. Let's leave this thread for later. I will rename the function sub_8004AE8 to x_mad_blinking , and it is also time to fix the signature of the function x_printf (last time I wrote down the wrong signature):

 void x_printf(const char *format, ...) 

The main cycle of the LED task is disassembled, but there is still code at the very beginning of the task.

Let's look at the code:



In line 49, it is likely that the availability of LEDs is checked and, in the event of an error, the function sub_8004BBC is called, which turns off interrupts and starts an infinite loop that uses the string "../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_gpio.c". Most likely, this is assert or a similar function.

Rename:


The purpose of the sub_8006968 function will become clear if you look closely at the device when it is turned on:


All four LEDs together turn on first red, then green, then blue. After that, they are set according to colors: 0-red, 1-green, 2-blue, 3-violet. And only then begin to switch mosaic.

Since the mosaic starts in the main task cycle, it is logical that lines 58-61 before the main cycle are responsible for the short-term switching on of different colors on the LEDs, and lines 52-56 - for installing red-green-blue on all the LEDs at once. Let's rename the sub_8006968 function to x_led_all_set_rgb (RGB is purely based on the arguments passed).

Oddities in LED Task


Briefly: definition of the functionality of a code containing strange strings. Forming a password for the device.

Now let's move on to the very beginning of the x_leds_task function:



"Eraze" , "gen" , "flash" , "reset" - why is this all ???

Let's try to figure it out.

Let sub_80066BC be x_leds_task_init .

Let's look at sub_8006B38:



Pure memset, agree?



Let's go back to x_leds_task. Something is wrong with the v24 variable type:



IDA made a little mistake with the type, but a comment with a stack mark helps us. Between the v24 and v25 variables, 12 bytes (0x44 - 0x38). Therefore, we rename v24 to buf and assign the type unsigned __int8 buf[12] (Ida will warn you that the new data type is more than the old one - we agree).

Further. Sub_8004CE4 function:



Rename a1 to buf , v1 to _buf .

Sub_8006B26 function:



Did you recognize her?

And if no makeup?

Of course, memcpy . Rename.

Then the purpose of the sub_8004CE4 function is to get some data at 0x08007C00 . By the way, this address lies in the range of addresses of the flash memory of the microcontroller (and the firmware, in particular). Rename sub_8004CE4 to x_read_data_0x08007C00 .

Function x_leds_task, line 36:

 if ( (unsigned int)buf[0] - 65 > 0x19 ) 

Let's change the data display (R key on 65, H key on 0x19):

 if ( (unsigned int)buf[0] - 'A' > 25 ) 

After some thought, you can understand that this is such a test of the AZ Latin range.

Next, using the hints in the form of format strings, we rename:


The sub_8003C66 function doesn’t do anything remarkable - it only increases a global variable - rename sub_8003C66 to x_smth_inc .

The x_erase function does not actually take any arguments - this can be seen in the disassembler:



Inside x_erase, the familiar address 0x08007C00 is used and three unknown functions are accessed:



Skimming through these three functions, we see that they are accessing addresses in the range 0x40022000 - 0x400223FF . The microcontroller documentation clearly states that this is the “FLASH Interface” range. That is, the function x_erase erases a piece of flash memory - great!

Apparently, the x_flash function writes to flash memory, having previously checked the length of the line to be written (by the way, the arguments a2 and a3 are superfluous here - let's help Ide):



And this all happens in the “lighting device” ???

Well, what about the x_gen function? After a quick glance and rename variables, it will look like this:



The sub_8006CB4 function looks like this:



And sub_8006D10 is like this:



Do not hold back the desire to search the Internet for these indecently beautiful constants: 0xABCD , 0x1234 , 0xE66D , 0xDEEC , 0x4C957F2D and 0x5851F42D . If the Internet is not yet completely banned, you will surely find these constants in the source code for a random function . No wonder the parent function is called x_gen.

This is also a very typical situation: call srand () before the loop, and call random () in the loop, so we rename it:


An inquisitive reader, looking at the sub_8005098 function, will be able to find out where the seed for the srand function comes from .

Thus, the x_gen function forms a random string of a specified size .

After the generated string is written to the flash memory, the device reboots:



It seems like some kind of strange reboot. But if we look at the list of tasks of this device, we will find among them “watchdogTask”. Obviously, in the presence of a “hung task”, the watchdog performs a reboot.

LED-task except MadBlinking mode can be considered analyzed.

Let's look through the lines, what other tasks are there in the system:



Having restored the links to the lines in the code, you can see the following picture:



First, there is a link to the string with the name task, then a link to the main function of the task. And they are used in the main function, where these tasks are launched:



Perform the missing renames:


Watchdog task


Task watchdog does nothing particularly interesting:



Rename:


UART task


Briefly: search for data and functions that have links from different functions. The definition of their destination.

A quick look at the UART task allows you to detect sending data to an unknown queue defined by the variable unk_200003EC :



Having restored references to this variable through a binary search, we will see that, in addition to x_uartRxTask, it is used in the main (there is a queue created, apparently) and in the yet unknown function sub_80051EC :



Rename:


See cross-references to x_recvMsg_uart_queue:


Let's first look at the sub_8005250 function:



Thinking, rename:


Now let's see where x_bluetooth_cmd is still used. All additional links are only from the Bluetooth task, it's time to return to it.

Back to bluetooth


Briefly: the final analysis of the Bluetooth task. Search for the possibility of authorization without a password.



If you look at the places where the sub_8006A84 function is used , and still not be lazy and look into its depths, then there will be no doubt - this is calloc . It is logical - to get the data to the buffer, you must first create this buffer.

Now sub_8006DBC . Let's look at it (the variables have already been renamed):



Recalling the functions of the standard C library for working with strings, we will see strstr (search for a substring) here and boldly rename it.

Go through the function code x_bluetooth_task - maybe something has changed here since the last visit . Along the way we name variables:


Immediately there is a function sub_80052E2 . By analogy with the functions that pull numbers from a Bluetooth command, it pulls out a string of the specified length — let's call it x_get_str .

We continue:


Finish with a quick rename:


Now examine the branches READ , WRIT , AUTP , SETP .

As the test showed on a working device, authorization is required for READ, WRIT, SETP commands. An authorization attempt with the AUTP command leads us to the x_check_password function to verify the password:



It turns out that the password length should be 8 characters and the password is compared (in the function sub_8006B08) with bytes at 0x08007C00 - where the generated string of random characters AZ is stored.

It turns out, without knowing the password, we can not log in to the device. Well, or almost can not ...

Notice where the auth_flag variable is used :



It turns out that it is used not only in the Bluetooth-task. But in the Sensor-task we just have not looked. We go there.

Sensor-task


In short: what does the touch button do?

In accordance with the best programming practices, the entire function of the Sensor Task fits into one IDA screen. And this is good news:



Lines-lines ...


Let's see how the device will behave if you press the touch button (did you realize that the rhino has a touch button on its leg?):



When pressed briefly, a small red LED lights up. With a long press, this LED turns on for a long time.

If we correlate it with the code, then we can make the assumption that the function sub_8000708 is a function of obtaining the current time. Then, if the difference between the current time and the beginning of the touch of the sensor is more than 1000 (1 second), then the LED turns on for 0xEA60 milliseconds (1 minute). But of greater interest is the variable auth_flag, which is set to 1 when the touch button is pressed for a long time, allowing an attacker to the administrator of the “lighting device” to access privileged functions.

Thus, after authorization “by button”, you can read the password stored in the device (READ command), write to the RAM (WRIT function) or set a new password (SETP).

Mad blinking


In short: can a strange code branch of “Mad Blinking” be performed?

Let's go back to the Bluetooth task and do some more renaming.


And here we see the number 0xB816D8D9 (it was found in the first part of the article in the Bluetooth task) as the LED index. This code will be executed if validation is performed:

 if ( sub_8005520(vip_str, vip_smth) == 0x46F70674 ) 

Rename sub_8005520 to x_vip_check and look at it:



Given that the first argument is a string (at least, the string is passed to this function), then by this code it turns out that the second argument is the length of this string (or the length that must be processed). Rename:


Let's look at the sub_8000254 function:



And now let's take a look at sub_8000148 . Here is its beginning:



This is only a third of the function ... Mmmm ... Delicious! An experienced digger can easily see here ...

What?
integer division operation.

How can you dig it out?
If you make an effort, then from the function sub_8000254 you can get to x_printf (through several other functions). In this place it is worth making an important note - usually all standard functions are fairly standard . This means that you can try to find in the open access at least some source code of the function being investigated, so that the study is more productive.

So, we take printf source code, then we look at vfprintf , comparing it with the code of the firmware under study. According to the source code, we enter the itoa function and conclude that the function sub_8000254 is the operator operator% (taking the remainder of the division), and this terrible long function is nothing but taking the whole part of the division (the operation div).

A legitimate question may arise - why? The fact is that the operations DIV, MOD may not be in a particular microcontroller, so the compiler instead of these operators substitutes the call of separate functions. By the way, here are some other mathematical functions .

Do not forget to perform renaming during digging.

Thus, the function x_vip_check , calculates ... And this will be your homework .

By the way, if you execute the right VIP team, you’ll get a “rhino at the disco”:



Summary of firmware


The device firmware is based on the real-time operating system FreeRTOS. The system has the following task:

  1. Bluetooth task . Handles commands that come in text form via Bluetooth.
  2. LED Task Controls color LEDs according to Bluetooth commands.
  3. Sensor-task Turns on a red LED, allows you to perform a brief authorization without a password on the device.
  4. UART-task . Allows you to interact with the Bluetooth-module on the internal UART-port (used to initialize Bluetooth).
  5. Watchdog-task . Tracks hang hangs.

The study did not take into account the ability to read data from the UART port (Tx / GND contacts).

Results


During the master class at the conference, only the main LED control functionality was disassembled. The most active participants were presented with their experimental "rhinos".

In my opinion, the “rhino” turned out to be a decent layout for a training course on reverse engineering and searching for vulnerabilities. A feature of the layout can be the ability to change the firmware as many times as necessary, for each course - its own firmware. Unlike parsing the executable file, the reverse firmware allows you to better understand:


Many thanks to all who read to the end!

Source: https://habr.com/ru/post/412561/


All Articles