ReverseEngineering – Telegram
ReverseEngineering
1.24K subscribers
40 photos
10 videos
55 files
666 links
Download Telegram
It's official! Dragon CTF 2020 has started on time!

https://ctf.dragonsector.pl

@reverseengine
1
1😭1
این بخش داخل ریورس بدافزار آنپکینگ تحلیل فانکشن‌ های ++C و Lib ها استفاده میشه

Struct داخل struct

مثال C:

struct B {
int x;
int y;
};


struct A {
int id;
struct B pos;
double score;
};


داخل حافظه اینجوری دیده میشه:

offset 0 id (int)
offset 4 pos.x (int)
offset 8
pos.y (int)

offset 12 padding
(برای align کردن double)

offset 16 score (double)


در اسمبلی:

mov eax, DWORD PTR [rdi] ; id
mov eax, DWORD PTR [rdi + 4] ; pos.x
mov eax, DWORD PTR [rdi + 8] ; pos.y
movsd xmm0, [rdi + 16] ; score


نکته‌

اگر دیدید چند فیلد int پشت‌سر همن تقریبا همیشه یک struct است نه آرایه

اگر یک offset یهو زیاد شد مثل 16 یعنی وجود double / pointer / align



This section is used in reverse malware unpacking analysis of C++ functions and Libs

Struct inside struct

Example C:

struct B {
int x;
int y;
};


struct A {
int id;
struct B pos;
double score;
};


In memory it looks like this:

offset 0 id (int)

offset 4 pos.x (int)

offset 8
pos.y (int)

offset 12 padding
(to align double)

offset 16 score (double)


In assembly:

mov eax, DWORD PTR [rdi] ; id
mov eax, DWORD PTR [rdi + 4] ; pos.x
mov eax, DWORD PTR [rdi + 8] ; pos.y
movsd xmm0, [rdi + 16] ; score


Tip

If you see multiple int fields in a row, it's almost always a struct, not an array

If an offset suddenly increases, like 16, it means there's a double / pointer / align

@reverseengine
1
بخش سیزدهم بافر اورفلو


مفهوم ret2libc و چطور از توابع libc برای هدایت اجرا استفاده میکنیم

چرا وقتی استک مال ما قابل اجرا نیست NX یا آدرس‌ ها جا به‌ جا میشن به جای گذاشتن شل‌ک د روی استک میریم سراغ توابع آماده libc مثل puts یا printf یا در حالت خطرناک system


وقتی نمیتونم کد بذاریم و اجراش کنیم NX یا آدرس‌ها متغیرن ASLR میایم از کتابخونه سیستم استفاده میکنیم به جای اینکه RIP رو بندازیم روی شل‌ کد خودمون RIP رو میندازیم روی آدرس تابعی تو libc که قبلا هم خود برنامه ازش استفاده کرده puts و آرگومان مناسب رو هم جوری کنار میذاریم که puts یه رشته دلخواه رو چاپ کنه
این ایده دو مرحله اصلی داره

1 نشت آدرس libc یا یک آدرس ثابت پیدا میکنیم

2 از اون آدرس استفاده میکنیم تا آدرس توابع libc را محاسبه کنیم و بعد RIP رو به آن تابع ببریم


Code C:

file9_demo.c

#include <stdio.h>
#include <string.h>

char secret[] = "this is a libc string for demo";

void vuln(char *s) {
    char buf[32];
    strcpy(buf, s);   /* unsafe but  دمو */
    puts("returned from vuln");
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("usage %s input\n", argv[0]);
        return 1;
    }
    vuln(argv[1]);
    puts("program finished");
    return 0;
}


ret2libc
یعنی استفاده از توابع آماده libc به جای اجرای شل‌ کد روی استک

معمولا دو قدم لازمه:
نشت آدرس و بعد پر کردن استک جوری که وقتی برگشت تابع libc اجرا بشه با آرگومان مناسب

امنیت: NX و ASLR و PIE ترکیبی هستند که کار رو پیچیده میکنن


Part 13 Buffer Overflow


The concept of ret2libc and how we use libc functions to direct execution

Why when our stack is not executable NX or addresses are moved instead of putting shellcode on the stack we go to ready libc functions like puts or printf or in dangerous mode system

When we can't write code and execute it NX or addresses are variable ASLR we use the system library Instead of putting RIP on our shellcode we put RIP on the address of a function in libc that the program has already used puts and we leave the appropriate argument in such a way that puts prints a desired string

This idea has two main steps

1 We find a libc address leak or a fixed address

2 We use that address to calculate the address of libc functions and then put RIP to that function

Code C:

file9_demo.c

#include <stdio.h>
#include <string.h>

char secret[] = "this is a libc string for demo";

void vuln(char *s) {
    char buf[32];
    strcpy(buf, s);   /* unsafe but demo */
    puts("returned from vuln");
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("usage %s input\n", argv[0]);
        return 1;
    }
    vuln(argv[1]);
    puts("program finished");
    return 0;
}


ret2libc
i.e. using ready-made libc functions instead of executing shellcode on the stack

Usually two steps are required:
Leak the address and then fill the stack so that when the libc function returns it is executed with the appropriate arguments

Security: NX, ASLR and PIE are a combination that complicates the task

@reverseengine
👍2
پاکسازی ردپای پردازشی Process Artifacts


Process Artifact


هر برنامه‌ ای که اجرا میکنید حتی ساده‌ ترین اسکریپت یه سری اثر پردازشی از خودش میذاره:

چه ساعتی اجرا شد

چه Threadهایی ساخت

چه Handle هایی باز کرد

چه Module هایی لود شدن

چه مقدار RAM مصرف کرد

چه Parent داشت

چه Token استفاده کرد


این چیزاست که DFIR و EDR عاشقشن چون با همین‌ ها مسیر حمله رو از اول تا اخر reconstruct میکنن



مهم‌ ترین ردپاهای پردازشی که معمولا جا میمونه

Process Creation Info

مثل یک پرونده:
StartTime CommandLine ParentProcess، IntegrityLevel

Modules / DLLs

لیست DLL هایی که لود شدن معمولا کاملا مشخصه

Thread Artifacts

حتی اگه بعدا پاک بشه،اطلاعات Thread Creation یه مدت زنده میمونه


Handle Table

فایل‌ها pipeها registry ها mutex ها
هر کدوم روی handle اثر میذاره


Memory Layout

حتی اگر inject کرده باشید footprint بخش تزریقی تو Virtual Memory Table مشخصه

Token Artifacts

نوع توکن privilege هایی که borrow شده بودن Owner/Group




چطور باید کاهش شون بدیم

عمر پروسه رو کوتاه نگه داری

بیشتر artifact ها بر اساس زمان بالا بودن پروسه جمع میشن

تا جای ممکن کار های خارجی لود نکنید

هر DLL اضافه = یک trace


Thread نسازید مگر لازم باشه

Thread
کمتر یعنی footprint کمتر

Handle Hygiene

هرچیزی باز شد خیلی زود ببندید

خیلیا کدشون رو handle باز میذارن و همون میشه سند جرم

Memory Hygiene

Buffer
های بزرگ الکی نسازید

Memory section
های عجیب نذارید و حتما free کنید


رفتار Parent-Child منطقی باشه

اگه ParentSpoofing ناقص باشه artifact ش تو توکن و timestamps مشخصه

هرچی زنجیره عجیب‌ تر باشه
لو رفتن بیشتر میشه


Process Artifacts Cleanup


Process Artifact

Every program you run, even the simplest noscript, leaves behind a series of process traces:

What time it was run

What threads it created

What handles it opened

What modules were loaded

How much RAM it consumed

What parent it had

What token it used

This is what DFIR and EDR love because they reconstruct the attack path from start to finish

The most important process traces that are usually left

Process Creation Info

Like a file:

StartTime CommandLine ParentProcess, IntegrityLevel

Modules / DLLs

List of DLLs that were loaded is usually quite specific

Thread Artifacts

Even if it is later cleared, Thread Creation information remains alive for a while

Handle Table

Files, pipes, registries, mutexes

Each of which affects the handle

Memory Layout

Even if you have injected the footprint of the injected part In the Virtual Memory Table, it is specified

Token Artifacts

Type of borrowed privilege tokens Owner/Group

How to reduce them

Keep the process life short

Most artifacts are accumulated based on the time the process is up

Do not load external tasks as much as possible

Each additional DLL = a trace

Do not create threads unless necessary

Fewer threads means less footprint

Handle Hygiene

Close everything that is opened very quickly

Many people leave their code handles open and that is evidence of the crime

Memory Hygiene

Do not create excessively large buffers

Do not leave strange memory sections and be sure to free them

Parent-Child behavior should be logical

If ParentSpoofing is incomplete, the artifact is specified in the token and timestamps

The stranger the chain, the more leakage

@reverseengine
1
اولین قدم توی باینری اکسپلویتیشن اینه که بفهمید برنامه چطوری و چرا کرش میکنه
کرش برای بقیه یه باگه ولی برای ما یه فرصت طلاییه با تحلیل کرش میفهمید یا جریان اجرای برنامه واقعا قابل‌ هک شدن هست یا نه


فهمیدن اینکه برنامه چطوری کرش میکنه Crash Analysis



قبل اینکه بریم سراغ ROP و شل‌ کد و این چیزا باید بدونیم برنامه دقیقا چرا کرش میکنه و چطوری میتونیم رفتار کرش رو کنترل کنیم اینجا همون جاییه که اکسپلویت واقعی شکل میگیره



کرش یعنی چی؟

کرش یعنی برنامه یه جایی دیگه نمیتونه ادامه بده و سیستم بهش میگه:
داداش جمع کن بریم گند زدی😂

ولی برای ما این بهترین نقطه‌ ی شروع حمله‌ ست
چون هر کرش یعنی یه چیزی از دست برنامه در رفته



چرا کرش برای ما مهمه؟

چون وقتی برنامه میمیره💀:

یا رجیسترها خراب شدن

یا جریان اجرای برنامه از کنترل خارج شده

یا یه آدرس عجیب شده مقصد اجرای کد


و اینا همون چیزایی هستن که قراره تبدیل بشن به:

کنترل PC Program Counter / RIP EIP

اجرای شل‌کد

یا ساخت ROP chain


پس اول باید بفهمیم کرش چه شکلیه و چی رو تغییر میده



چطور کرش رو تحلیل کنیم؟

معمولا با یکی از این ابزارها:

gdb لینوکس

gef / pwndbg / peda

WinDbg ویندوز

x64dbg ویندوز


ولی مهم‌ تر از ابزار چیزیه که باید دنبالش بگردیم:

چیزهایی که توی یه کرش باید چک کنیم :

آدرس کرش کجاست؟
مثلا SIGSEGV در آدرس 0x41414141 یعنی احتمالا برنامه با ورودی ما (A = 0x41) ترکیده😁


کدوم رجیسترها با ورودی ما پر شدن؟
مثل EIP/RIP، ESP/RSP، RBP و حتی رجیستر های کمکی مثل RDX و RAX


استک چه شکلی شده؟
آیا data ما روی استک ریخته؟
آیا اندازه ورود‌ی یادداشت شده؟


آیا برنامه قبل از کرش از مرز یه بافر رد شده؟
مثلاً یه رشته بلند باعث شده strcpy یا gets از کنترل خارج بشه؟


این اطلاعات همون چیزیه که بهمون میگه:

آیا قابل اکسپلویت هست؟

یا فقط تکون خورده ولی قابل سواستفاده نیست؟


چرا این مرحله مهمه؟

چون هیچ اکسپلویتی بدون تحلیل کرش نوشته نمیشه وقتی دقیق بفهمیم برنامه کجا و چطور ترکیده میتونی مرحله بعد بریم سراغ:

پیدا کردن Offset

کنترل EIP/RIP

ساخت Payload

و بعد ROP / شل‌کد




The first step in binary exploitation is to understand how the program crashes and why it crashes
A crash is a bug for others, but it's a golden opportunity for us. By analyzing the crash, you can understand whether the program's execution flow is really hackable or not

Understanding how the program crashes Crash Analysis

Before we go into ROP and shellcode and all that, we need to know exactly why the program crashes and how we can control the crash behavior. This is where the real exploit comes in.

What does a crash mean?

A crash means that the program can't continue anywhere else and the system tells it:
Bro, get out of here, you messed up😂

But for us, this is the best starting point for an attack

Because every crash means something is missing from the program

Why is a crash important to us?

Because when a program dies💀:

Either the registers are corrupted

Or the program execution flow is out of control

Or a strange address is the destination of the code execution

And these are the things that are going to become:

Controlling PC Program Counter / RIP EIP

Executing shellcode

Or building ROP chain

So first we need to understand what the crash looks like and what it changes

How to analyze the crash?

Usually with one of these tools:

gdb Linux

gef / pwndbg / peda

WinDbg Windows

x64dbg Windows

But more important than the tool is what we need to look for:

Things to check in a crash:

Where is the crash address?
For example, SIGSEGV at address 0x41414141 means that the program probably exploded with our input (A = 0x41)😁

Which registers were filled with our input?
Like EIP/RIP, ESP/RSP, RBP and even auxiliary registers like RDX and RAX

What is the stack shape?

Did our data spill onto the stack?

Was the input size noted?

Did the program overflow a buffer before crashing?

For example, did a long string cause strcpy or gets to go out of control?

This information is what tells us:

Is it exploitable?

Or is it just shaken but not exploitable?

Why is this step important?

Because no exploit can be written without crash analysis. Once we know exactly where and how the program crashed, we can move on to the next step:

Finding Offset

EIP/RIP Control

Building Payload
4
And then ROP / Shellcode

@reverseengine
4
Struct داخل آرایه

C:

struct Item {
int a;
int b;
int c;
};


Item arr[100];
return arr[i].b;


آدرس:

arr + i * sizeof(Item)


اسمبلی:

mov eax, DWORD PTR [rdi + rsi*12 + 4]

چون sizeof (Item) = 12

offset b = 4


چطور تشخیص بدیم struct هست

وقتی:

ضربدر یک عدد غیر معمول دیدید 12 24 36

offset
های ثابت مثلا 4 8 16


یعنی struct array هست نه 2D array




Struct inside array

C:

struct Item {
int a;
int b;
int c;
};


Item arr[100];
return arr[i].b;


Address:

arr + i * sizeof(Item)


Assembly:

mov eax, DWORD PTR [rdi + rsi*12 + 4]

Because sizeof (Item) = 12

offset b = 4


How to tell if it is a struct

When:

You see an unusual number 12 24 36

Fixed offsets, such as 4 8 16

That means it is a struct array, not a 2D array

@reverseengine
1
مهم ترین بخش برای Exploit + ROP

کامپایلر دقیقا چه چیز هایی رو داخل استک ذخیره میکنه:

Saved RBP



Return Address



Local Variables



Padding / Alignment



Call-preserved registers



متغیر های محلی چجوری روی استک قرار میگیرن؟

کد C:

int func(int x) {
int a = 5;
int b = x + 3;
return a + b;
}


کامپایل با -O0:

push rbp
mov rbp, rsp
sub rsp, 16 ; allocate space for a, b

mov DWORD PTR [rbp-4], 5
mov eax, DWORD PTR [rbp+16] ; x
add eax, 3

mov DWORD PTR [rbp-8], eax
mov eax, DWORD PTR [rbp-4]
add eax, DWORD PTR [rbp-8]
leave
ret


نکته مهم برای اکسپلویت:

لوکال‌ها همیشه از آدرس‌ های:

rbp - 4

rbp - 8

rbp - 0x10


شروع میشن

این دقیقا جایی که بافر اورفلو اتفاق میوفته





Most important part for Exploit + ROP

What exactly does the compiler store on the stack:

Saved RBP


Return Address


Local Variables


Padding / Alignment


Call-preserved registers


How are local variables placed on the stack?

C code:

int func(int x) {
int a = 5;
int b = x + 3;
return a + b;
}


Compile with -O0:

push rbp
mov rbp, rsp
sub rsp, 16 ; allocate space for a, b

mov DWORD PTR [rbp-4], 5
mov eax, DWORD PTR [rbp+16] ; x
add eax, 3

mov DWORD PTR [rbp-8], eax
mov eax, DWORD PTR [rbp-4]
add eax, DWORD PTR [rbp-8]
leave
ret


Important note for exploit:

Locals always start at:

rbp - 4

rbp - 8

rbp - 0x10


This is exactly where the buffer overflow occurs

@reverseengine
1