Skip to content

Collection of binary explotation and reverse engineering walkthroughs on i386 systems.

Notifications You must be signed in to change notification settings

AndreIglesias/Rainfall

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rainfall

Welcome to Rainfall, a collection of walkthroughs and solutions for binary exploitation challenges and deep dives into reverse engineering on i386 systems. In this project, we explore various techniques such as buffer overflows, shellcode injection, format string exploits for printf, heap and stack injection, Global Offset Table (GOT) hijacking, and more.

Overview

Rainfall consists of 10 binaries that have been carefully selected and analyzed. Each binary presents unique challenges, allowing you to explore different aspects of binary exploitation and reverse engineering.

Features

  • Binary Exploitation: Learn how to exploit vulnerabilities in binary executables.
  • Reverse Engineering: Deep dive into understanding the inner workings of compiled programs.
  • Multiple Techniques: Explore a variety of techniques including buffer overflows, shellcode injection, format string exploits, and more.
  • Practical Examples: Real-world scenarios to apply your knowledge and skills.

Walkthroughs

Each binary comes with a detailed walkthrough and solution, guiding you through the process of analyzing, reverse engineering, and exploiting the vulnerabilities present in the executable.

Getting Started

To get started with Rainfall, clone this repository and navigate to the specific level you want to analyze. Each level directory contains the following:

  • binary: The executable binary file.
  • flag: The obtained flag.
  • source.c: Optional, contains the original C source code if available.
  • README.md: Detailed walkthrough and solution for the binary.
  • resources: The tools used to exploit the binary.

Requirements

To follow along with the walkthroughs, you'll need:

  • Basic understanding of C programming language.
  • Familiarity with Linux environments.
  • Knowledge of assembly language (x86/i386 architecture).

Levels summary

Level 0Objective: Binary analysis to find out which number we have to input (423).
README.md
int main(int argc, const char **argv, const char **envp)
{
    if (atoi(argv[1]) != 423)
    {
        // Write "No !" to stderr
        fwrite("No !\n", sizeof(char), 5, stderr);
    } 
    else 
    {
        // Execute /bin/sh
        char *shell_cmd = strdup("/bin/sh");
        setresgid(getegid(), getegid(), getegid());
        setresuid(geteuid(), geteuid(), geteuid());
        execv(shell_cmd, argv);
    }
    return (0);
}
Level 1Objective: Buffer overflow on gets function, to overwritte the EIP reg to point to the run function.
README.md Walkthrough_pwntools.md
void run() {
    FILE *stdout_ptr = stdout;

    // Print the message "Good... Wait what?\n" to the standard output
    fwrite("Good... Wait what?\n", sizeof(char), 17, stdout_ptr);

    // Execute the "/bin/sh" shell command
    system("/bin/sh");
}

int main(int argc, const char **argv, const char **envp)
{
    char buffer[76]; // Buffer to hold user input
    
    gets(buffer); // Reading input from the user

    return 0;
}
Level 2Objective: Buffer overflow on gets function, to inject shellcode into the Heap, and execute it overwriting the EIP reg to point to the code on the Heap.
README.md
int p()
{
  char buffer[64]; // ebp+0x4C - ebp+0xC
  int arg;
  int eax;
  int edx;

  fflush(stdout);   // Flush stdout buffer
  gets(buffer);     // Again, possible buffer overflow
  memcpy(eax, &buffer[80], 4);  // Copy EIP (return address) from buffer[80] to eax
  arg = &buffer[64];  // Set arg to point to the end of buffer
  memcpy(arg, eax, 4);  // Copy 4 bytes from eax to arg
  memcpy(eax, arg, 4);  // Copy 4 bytes from arg to eax
  if ( (eax & 0xB0000000) == 0xB0000000 )
  {
    printf("(%p)\n", arg);
    exit(1);
  }
  puts(buffer);
  return (strdup(buffer));
}
int main(int argc, const char **argv, const char **envp)
{
  return (p());
}
Level 3Objective: Format string attack on printf function, to inject the number 64 into the global variable m.
README.md
int m = 0;

int v() {
    int result;         // EAX
    char buffer[520];   // [esp+10h] [ebp-208h] BYREF
    fgets(buffer, 512, stdin);  // Read up to 512 characters from stdin
    printf(buffer); // Print the buffer
    result = m;
    if (m == 64) {  // @
        fwrite("Wait what?!\n", 1, 12, stdout);
        return (system("/bin/sh"));
    }
    return (result);
}
int main() {
    return (v());
}
Level 4Objective: Format string attack on printf function, to inject the number 16930116 into the global variable m.
README.md
int m = 0;

int p(int buffer) {
    return (printf(buffer));
}
int n()
{
  int eax;      // EAX
  char v1[520]; // [esp+10h] [ebp-208h] BYREF

  fgets(v1, 512, stdin);
  p(v1);
  eax = m;
  if ( m == 16930116 )
    return system("/bin/cat /home/user/level5/.pass");
  return eax;
}
int main() {
    int eax;

    n();
    return (eax);
}
Level 5Objective: Format string attack on printf function, to hijack the Global Offset Table replacing there the exit address for the o() function address to redirect the code execution.
README.md
int n()
{
  char v4[520]; // [esp+10h] [ebp-208h] BYREF

  fgets(v4, 512, stdin);
  printf(v4);
  exit(1);
}
int o()
{
  system("/bin/sh");
  _exit(1);
}
int main() {
    return (n());
}
Level 6Objective: Buffer overflow on strcpy function, to overwrite the EIP (which was going to execute m()) to make it execute n() instead.
README.md
int n()
{
  return (system("/bin/cat /home/user/level7/.pass"));
}
int m()
{
  return (puts("Nope"));
}
int main(int argc, const char **argv, const char **envp)
{
  int (**v4)(void); // [esp+18h] [ebp-8h]
  int v5; // [esp+1Ch] [ebp-4h]

  v5 = malloc(64);
  v4 = (int (**)(void))malloc(4);
  *v4 = m;
  strcpy(v5, argv[1]);
  return ((*v4)());
}
Level 7Objective: Buffer overflow on the 2 strcpy functions, to hijack the Global Offset Table replacing there the puts address for the m() function address to redirect the code execution and print the .pass content.
README.md
char *c = NULL;

int m()
{
  int eax;

  eax = time(0);
  return printf("%s - %d\n", c, eax);
}
int main(int argc, const char **argv, const char **envp)
{
  int eax; // eax
  _DWORD *v5; // [esp+18h] [ebp-8h]    argv[2]
  _DWORD *v6; // [esp+1Ch] [ebp-4h]    argv[1]

  v6 = (_DWORD *)malloc(8);
  *v6 = 1;
  v6[1] = malloc(8);
  v5 = (_DWORD *)malloc(8);
  *v5 = 2;
  v5[1] = malloc(8);
  strcpy(v6[1], argv[1]); // Vulnerable for buffer overflow
  strcpy(v5[1], argv[2]); // Vulnerable for buffer overflow
  eax = fopen("/home/user/level8/.pass", "r");
  fgets(&c, 68, eax); // 68 is the lenght of the flag from .pass
  // c has now the flag
  puts("~~"); // Call m() instead of puts()
  return 0;
}
Level 8Objective: play with the options of the program to write at the 32th byte of the auth global variable.
README.md
char *auth = NULL;
char *service = NULL;

int main(int argc, const char **argv, const char **envp)
{
  char *input;

  while ( 1 )
  {
    printf("%p, %p \n", auth, service);
    if ( !fgets(input, 128, stdin) )
      break;
    if ( !memcmp(input, "auth ", 5u) )
    {
      auth = malloc(4);
      *auth = 0;
      if ( strlen(input + 5) <= 30 )
        strcpy(auth, input + 5);
    }
    if ( !memcmp(input, "reset", 5u) )
      free(auth);
    if ( !memcmp(input, "service", 6u) )
      service = strdup(input + 7);
    if ( !memcmp(input, "login", 5u) )
    {
      if ( auth[32] )
        system("/bin/sh");
      else
        fwrite("Password:\n", 1, 10, stdout);
    }
  }
  return 0;
}
Level 9Objective: Buffer overflow on c++ object with memcpy to overwrite the vtable of a second object on the heap.
README.md
class N {
public:
    char buffer[100];
    int value;

    N(int value) : value(value) {}

    void setAnnotation(char* annotation) {
        int len = strlen(annotation);

        std::memcpy(this->buffer, annotation, len);
    }

    int operator+(const N &right) const {
        return (this->value + right.value);
    }

    int operator-(const N &right) const {
        return (this->value - right.value);
    }
};

int main(int argc, char* argv[]) {
    if (argc < 2) {
        exit(1);
    }

    N* instance1 = new N(5);
    N* instance2 = new N(6);

    instance1->setAnnotation(argv[1]);

    return (*instance2 + *instance1);
}

Disclaimer

Rainfall is intended for educational purposes only. By using this project, you agree that the authors are not responsible for any misuse or illegal activities.

License

This project is licensed under the GNU General Public License - see the LICENSE file for details.