Article directory
Written at the top: Be sure to task3.sct
link the file to the project first, the specific operation will be written later, and I explained the meaning of the sct file in the additional content.
It is finally possible to say goodbye to this practice. Can you add a sentence " the task load is relatively large, it is recommended to reduce the task load " in your experience ?
Experiment 3 FreeRTOS-MPU protection bypass
Experimental requirements
MPU presets:
a) Write C code to realize privilege escalation code and specified function lookup based on FreeRTOS-MPU v10.4
b) Use overflow vulnerability to realize system privilege escalation and Flag function printing in FreeRTOS MPU V10.4
Subtask 1
First of all, similar to the previous experiment, look at .c
the structure of the file.
main
Function:
① Define an unsigned integer variable id
, and assign it to the last 4 digits of the student number;
② Call prvSetupHardware()
, hardware initialization;
③ Call StartFreeRTOS(id, vTask3)
;
④ Use to for(;;)
keep the program from exiting.
vTask3
content, only for(;;)
.
The role of header files and lib files is similar to the previous experiment.
Therefore, all mission requirements are concentrated in StartFreeRTOS
it, and we need to conduct further reverse analysis.
Reverse analysis of StartFreeRTOS
IDA Pro opens and decompiles StartFreeRTOS
as follows:
As you can see, the function roughly has the following operations:
① byte_7714
Copy the content pointed to by the address to the task parameter xTask3Parameters
; ② Set the function pointer of
the task parameter to ; ③ val *= id; ④ Call , create a constrained task combined with the task parameter ; ⑤ Call to start the task sequence.xTask3Parameters
vTask3
xTaskCreateRetricted
xTask3Parameters
vTaskStartScheduler()
What we have to do is vTask3
call the privilege escalation function in , and then call the function that prints the flag.
The function to print the flag is easy to find. You can find it directly in the string window of IDA Pro. After finding it, double-click to open it, and then check the reference:
In short, we can find a vTaskRemove
function called , which is a parameterless function and can print flag.
To find the privilege escalation function, I didn't know how to find it at first.
Until I read the PPT explained in the experiment, I saw the following picture:
It can be seen that if a common task needs to use the kernel API, it cannot be used directly, but must be added MPU_
. This function is added, which includes the privilege escalation operation. Moreover, the privilege escalation operation should be to use SVC
interrupts.
Therefore, we might as well open a random function in IDA Pro to MPU_
reverse engineer and find the privilege escalation operation. I take as MPU_vTaskDelete
an example. The analysis in IDA Pro is as follows:
What I framed in the picture above is the privilege escalation operation. Task
It can be seen that it is to do some push operations to enter the function first, and then pass in the privilege escalation function R4
. If R4
it is not 0, it will jump to the privilege escalation function (if it is 0, then it is a privileged level). Then execute the kernel API normally xTaskDelete
.
Obviously, the privilege escalation function is xPortRaisePrivilege
, and it uses registers R4
to pass parameters, so it is also a parameterless function.
vTask3
Do not rush to fill in the address, because this address will change after the content is modified . Pretend to have found the address first, and vTask3
use the address to call these functions in , so that after finding it, you only need to modify it to the corresponding address. Add the code as follows:
Note that the addresses randomly filled in must not be all zeros or the same. It is better to fill in a bit similarly , otherwise the address will change after the second revision.
void (*pPrivilege)();
void (*pFunc)();
pPrivilege=(void(*)())0x00001051;
pFunc=(void(*)())0x000029AD;
pPrivilege();
pFunc();
After Rebuild, go to IDA Pro to find the function address. When using the address to call the function, you need to add one.
It should be noted that when the function address has been added correctly, but there is no
log.txt
link in itvTask3
, it is likely that there is a problem with the configuration of the project, such as the distribution of memory addresses. The problem is aslog.txt
follows:
At this time, you must import the one sent by the teacher
task3.srt
. The import method is as follows:
After the import is complete, rebuild the project and re-analyze the address. (I re-screened the picture after importing)
For the specific function of sct, see additional content 1 below .
Print Flag function name and address
Name: vTaskRemove
;
Address: 0x000005F4
.
The content is shown in the figure below:
Function name and address for privilege escalation
Name: xPortRaisePrivilege
;
Address: 0x00008EDC
.
The content is shown in the figure below:
filled code
Note that you need to add 1 to the address found above before calling. Fill in the code as shown below:
Screenshot of simulation run
The result of running and printing the flag is as follows:
log.txt is as follows:
The reason why I want to log.txt
take a screenshot is because in the previous analysis, we can see that xPortRaisePrivilege
the task of function escalation is R4
the register, and we simply called the function without R4
processing the register. But it worked.
If you check log.txt
, you will find that there is no obvious processing content for the R4 register. The last assignment is the pop of prvSVCHandler. I guess that it is possible to svc 2
escalate privileges only by executing interrupts. The specific situation is not yet clear, but it has no impact on this experiment.
Later, when I wrote the flag5 report, I realized that when R4 is 0, the task should be elevated; or, regardless of the value of R4, the task will be elevated.
Additional content 1: The role of sct files
The left side of the picture below is the sct file sent by the teacher, and the right side of the picture below is the sct file automatically generated by the software.
The sct file is also a memory-mapped layout file.
In general, when compiling, all the functions (as shown in the figure above .ANY (+R0)
) are sorted into the memory according to the function name, and there is no access control such as privileged access or user access.
After we set the MPU, the compilation will not be affected. Therefore, the unprivileged function may be placed in the area of the privileged function in the MPU specified by us. The unprivileged function is stored in the address range of the privileged function, and the MPU will not allow execution.
The sct file modified by the teacher rearranges the position of the API, puts the privileged function privileged_functions
in the first paragraph (that is, the position in the MPU that only allows privileged execution ER_IROM1
), and puts the others ER_IROM2
. And put the privileged data privileged_data
in the segment that only allows privileges to be read and written in the MPU RW_IRAM1
, and put the others RW_IRAM2
.
In this way, the memory map will be consistent with the MPU settings.
Subtask 2
Reverse analysis of StartFreeRTOS
The reverse analysis function address of this task is similar to the previous task, so I won’t repeat it here.
Print Flag function name and address
Name: vTaskDelayBackup
;
Address: 0x00001C7C
.
The content is as follows:
Function name and address for privilege escalation
Name: xPortRaisePrivilege
;
Address: 0x000086E2
.
The content is as follows:
Analysis process
The function of this file main
is super long, but very simple, as shown in the following figure:
For the time being, it can only be seen that the length of the last input string InputBuffer
is at most 0x63.
Then click StartFreeRTOS:
It just runs the constrained vTask3
tasks and does nothing else.
Since this is passed in by parameters, it cannot be opened directly, so return to the previous level first, and then click to open vTask3
the task:
vTask3
Called Function()
, according to experience, this is the problematic code.
Find buffers that are overflowing
Click to open Function
:
It was found that the parameters were passed in, and more importantly, the sum Function
appeared , and the value was assigned to , and the size was only 12, indicating that it might be an overflow buffer.length
InputBuffer
HelperBuffer
HelperBuffer
HelperBuffer
Show Function
the function in assembly as follows:
Explanation of PUSH and POP: The stack of the ARM architecture is a descending stack, from right to left when PUSH, and from left to right when POP.
After looking at the assembly, you will find that, strictly speaking, there is no overflow buffer at all. Function
Function, it first push
registers 4 registers, and then uses them at the very beginning of the function mov buffer, sp
to directly change the top pointer sp of the stack, and then immediately InputBuffer
fills them with the contents one by one buffer
(the variable name is parsed into in my IDA HelpBuffer
). At the end pop
, pop
isn't the content of the content just HelpBuffer[0]
, HelpBuffer[1]
, and HelpBuffer[2]
so on?
Overwriting the value of the register is Function
the purpose and buffer
the function of setting. It just wants to write to the stack and write to the register.
Since the purpose of every bit of this buffer is to write data to registers or stacks that should not be written , it has no part related to normal function at all. It’s like drawing with a piece of paper. The normal operation is drawn on the paper, but if you paint too much, it will overflow to the table. This piece of paper is called overflow buffer; and this code doesn’t even have paper.
In summary, I don't think this is a typical buffer overflow code. If you have to say that there is an overflow buffer, it is deliberately constructed HelpBuffer
, and the length of the overflow buffer is 0, which has no normal function .
Stack diagram
Click on the bp of Function to see the stack frame in IDA Pro.
That doesn't explain very well what's on the stack, though. I redraw the stack schematic.
The stack diagram before and after execution mov buffer, sp
is as follows:
LR (Function) needs to be overwritten with a privilege escalation function, and after calling the privilege escalation function, it is also necessary to call a function that prints the flag, so it is necessary to construct a stack structure that overflows the return address of the privilege escalation call. The fully constructed stack diagram will be drawn in the follow-up "Overflow Privilege Escalation" .
overflow privilege escalation
Overflow privilege escalation call xPortRaisePrivilege
, the address is 0x000086E2
. The first line of the function is PUSH {xRunningPrivileged,LR}
. If this sentence is executed, it will change the stack frame structure we have constructed, and it will also cause the return address to not be overwritten, so we need to skip this sentence and 0x000086E4
start from the address. Plus the base address is 1, so LR(Function)
it needs to be overwritten 0x000086E5
.
After successfully entering and executing the privilege escalation function, continue to execute the function of printing the flag. It is executed when returning POP {xRunningPrivileged,LR}
, so 8 HelpBuffer
bytes need to be constructed. The complete stack diagram is as follows:
Override the parsing process of the return address
The return value in Function
is overwritten with xPortRaisePrivilege
the address of the second line plus 1, that is 0x000086E5
, when it returns, it will xPortRaisePrivilege
start executing from the second line of code and complete the privilege escalation;
The return value in xPortRaisePrivilege
is overwritten as vTaskDelayBackup
the address plus 1, that is , so it will execute from the first line of code 0x00001C7C
when it returns , and complete the flag printing.vTaskDelayBackup
Screenshot of simulation run
Flowers are over! ! !