{ enessakircolak }

Return-Oriented_Programming

Aug 26, 2023
3 minutes

ROP

NOT AN EXPLOIT BLOG !!! :))

Just hanging with exploit and then I realize, I can use it for the programming maybe for more obfuscation.
It may not be the best way to programming but my purpose is make more secure programs against reverse engineering(cracking) and also inspect to different ideas at this field.

Let’s begin
As a programmer, when you need to use your local function, you actually use “call”. When that function end it execute “ret” instruction, it takes top of the stack to set EIP register. So if you can’t manage stack you are able to get access violation error because at the end, “EIP” register point to an address which it shouldn’t. Simply we will put our local function’s address to stack and then execute to “ret” instruction to visit our local function.

Look at the code and try to find out what is the problem ?

#include <iostream>
#include <windows.h>

void func2() {
    std::cout << "Rise from the ashes my child" << std::endl << std::endl;
 
}
void func1() {
    std::cout << "Fight fire with fire usually end up with ashes" << std::endl;
    __asm {
        push func2
        ret
    }
}
int main()
{

    __asm {
        push func1
        ret
    }

    std::cout << "Hello Ashes!!\n";

}

rop1.png


It looks like something is missing. “Hello Ashes!!” didn’t print. Because of couldn’t return to the main code flow.
Maybe we can find reason at func2. Let’s inspect from beginning.

rop3.png


Check the stack at the main. ropTry.13d1557 will using at the end of the main to return rest of code except main. So, that address is going to exit the program.


rop2.png


Image belong to func2… there is no “Hello Ashes!!” and it will go to exit after “ret” instruction.
Shortly we lost the main :))
At the regular flow, when we use “call”, our next instruction’s address pushing to stack. But we didn’t use “call”. We have to place main code’s next instruction’s address to stack.


int main()
{
    __asm {
        push label1
        push func1
        ret
    }
label1:
    std::cout << "Hello Ashes!!\n";
}


rop5.png


Actually problem solved. But there was no prolog and epilog. What if it is exist ?

Prolog-Epilog


rop4.png


This is an external program !!
Given areas included by compiler to manage stack dynamically. If there is prolog, when we use “ret” before the actual “ret” instruction, we left “ebp” in the stack, we must consider it.
Like previous example sometimes you won’t see it, but usually it will exist.



void func2() {
    std::cout << "Rise from the ashes my child" << std::endl << std::endl;
    __asm {
        pop ebp
        pop eax
        jmp eax
    }
}


Now we consider prolog.

Normally stack cleared by caller or callee according to calling convention. But we broke the rule, so we have to rearrange stack.
Also it applies to different situations such as calling a function with parameters. In that situation we can push parameter before function’s address and we should take out in order.


rop6.png


Giving “ebp” back to itself. Because we will never return the rest of that piece of code this is why we manually write an epilog before “actual epilog”. If you look carefully, after “jmp” instruction there is “pop” and “ret” which are never executed.





If you can direct “EIP”, congratulations you may exploit program. For example you reach the “EIP” with BOF and manipulate the stack, then you need to direct “EIP” to some functions belong to program for your benefits which are called “Gadgets”. Tricky part is finding gadgets and combine all of it in payload.

References

ired.team/offensive-security/rop-chaining-return-oriented-programming
exploit-db


~Those who wish to succeed must ask the right preliminary questions.