➜ ~

Playing Hacks and Stuffs!


Project maintained by h4ckyou Hosted on GitHub Pages — Theme by mattgraham

Cyberlympics 2023 Prequalification

img

Hi! In this writeup I’ll give the solution to all the binary exploitation challenges. Maybe if I have time and I’m online I’ll try put the other challenge I solved

Have fun reading!

Binary Exploitation

Flag Bank [1st Blood 🩸]

image

I first connected to the remote instance but at the moment it is down image

So let’s work with the binary image

Seems we can purchase the flag is our amount is $20000 but currently our amount is $10000

We can also purchase a test flag which worth $3000

Let us do that image

Ok it works but now what’s our current account balance image

Nice it deducted $3000 from our balance which is expected

At this point we can try purchase a negative value of flag image

Ok that seems to work now we can try to buy the real flag image

We get content of flag not found why?

If you run ltrace you will see it trys to open up the flag image

So we can create a fake flag file and run the program again image

That works!

If you want to know why that works you can decompile the binary in ghidra and view the functions used by the program

I won’t go through the whole source but I’ll show you where the vuln lies image

We can see that it will subtract the multiplication of the number of flag to be bought and the fake flag price with our current balance

Since it doesn’t check if the number of flag to be bought is negative therefore the whole arithmetic will be changed to:

currentBalance = currentBalance + nFlag * fakeFlagPrice

With that our balance would be increased therefore bypassing this check making us get the real flag image

Simple

image

During the ctf I didn’t manage to solve this for some silly reason anyways this is an upsolve.

After downloading the binary I checked the file type and protections enabled on the binary image

We are working with a x64 binary which is dynamically linked and not stripped.

The only protection not enabled is Stack Canary.

Opening the binary in ghidra for decompilation shows this available functions

image

Let us view the main function image

undefined8 main(void)

{
  code *shellcode;
  
  setup();
  shellcode = (code *)mmap((void *)0x0,0x1000,7,0x22,-1,0);
  printf("%s","Easy! Fire it up: ");
  fgets((char *)shellcode,23,stdin);
  (*shellcode)();
  return 0;
}

It first calls the setup function which does some buffering and nothing much image

But the main function is pretty small and nothing much going on

Here’s what it does:

Basically all this binary does is to receive our input then run it as shellcode

But the catch here is that it has to be a 23 bytes shellcode

Well we could just google x64 23 bytes shellcode doing that would lead here

If we try that it won’t work image image

The shellcode looks good but the thing is here:

push 59
pop rax

That part can be optimized using this assembly instruction

mov al, 59

Basically instead of push 59 to the stack and putting it in the rax register we can just directly put it in the lower byte register of the rax register

Also this shellcode is basically called execve which requires three arguments /bin/bash, 0, 0

Modifying the shellcode to that worked image

Here’s my solve script

O Wise Traveler [1st Blood 🩸]

image

After downloading the binary I checked the file type and the protection enabled on it image

We can see that this is a x64 binary which is dynamically linked and not stripepd

The only protection enabled on the binary is NX (No-Execute) and PIE (Position Independent Executable)

So basically when NX is enabled this means that the stack is not executeable meaning we won’t be able to execute shellcode that’s placed on the stack

While when PIE is enabled that means when ever we run the binary it will get loaded into random addresses

So on each execution the binary memory address would change

To understand what the binary does I loaded and decompiled it in ghidra

Here’s the main function image image

undefined8 main(void)

{
  char local_98 [143];
  char option;
  
  setup();
  memset(local_98,0,0x82);
  fwrite(&banner,1,0xbb,stdout);
  __isoc99_scanf("%c",&option);
  if (option == '1') {
    puts("Go back to where you came!");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  if (option == '2') {
    question();
    fwrite("Would you like to tell me more about pointers? (y/n): ",1,0x36,stdout);
    __isoc99_scanf("%c",&option);
    if (option == 'y') {
      question();
      fwrite("Anything else? (y/n): ",1,0x16,stdout);
      __isoc99_scanf("%c",&option);
      if (option != 'y') {
        if (option == 'n') {
          puts("Cheers mate!");
                    /* WARNING: Subroutine does not return */
          exit(0);
        }
        puts("It\'s a y/n question :)");
                    /* WARNING: Subroutine does not return */
        exit(0);
      }
      fwrite("Shoot: ",1,7,stdout);
      getchar();
      fgets(local_98,0x82,stdin);
      printf(local_98);
    }
    else {
      if (option != 'n') {
        puts("It\'s a y/n question :)");
                    /* WARNING: Subroutine does not return */
        exit(0);
      }
      getchar();
      fwrite("Hate to see you leave. Were the challenges fun? (y/n): ",1,0x37,stdout);
      __isoc99_scanf("%c",&option);
      if (option == 'y') {
        puts("Splendid!");
      }
      else if (option == 'n') {
        puts("There\'s nooo waaay!");
      }
      else {
        puts("It\'s a y/n question :)");
      }
    }
    return 0;
  }
  puts("It\'s either 1 or 2 :)");
                    /* WARNING: Subroutine does not return */
  exit(0);
}

I’ll work through each of what this binary does:

First it prints out some banner which is more of the option and receives our input option and if our option chosen is 1 it will exit image

Option 2 tends to perform more things image

So it will ask a question and if our answer is y it will call the question function

Here’s the decompiled function image

void question(void)

{
  char buffer [144];
  
  memset(buffer,0,0x82);
  fwrite("Educate me, what\'s so interesting about pointers: ",1,0x32,stdout);
  getchar();
  fgets(buffer,0x82,stdin);
  printf(buffer);
  return;
}

So basically what all this does is to receive our input and print it our back using printf

Back to the main function, we are asked the same question again if our answer is y it will call the question function again image

And after that it would ask Anything else if our answer if n it will exit the program

Else it receives our input and prints it out using printf

But if the initial question is n it would do this image

Nothing much going on there so I won’t look explain that

At this point the vulnerability is quite obvious and it’s occurs here:

void question(void)

{
  char buffer [144];
  
  memset(buffer,0,0x82);
  fwrite("Educate me, what\'s so interesting about pointers: ",1,0x32,stdout);
  getchar();
  fgets(buffer,0x82,stdin);
  printf(buffer);
  return;
}

And here:

fwrite("Shoot: ",1,7,stdout);
getchar();
fgets(local_98,0x82,stdin);
printf(local_98);

So the vulnerability here is Format String Vuln

And that happens because the binary uses printf to print out our input without using a format specifier

With that we can leak address off the stack and also exploit the binary

Here’s how my exploitation would go:

Another thing to know if where the offset of our input is on the stack

And we can easily calculate it using this image

At offset 6 is where our input is on the stack

We also need an offset where a binary address and libc address is on the stack so I made a simple fuzz script to get me that image image

Ok but before we move forward one thing to note is that we would need the libc for solving this but since it wasn’t provided I asked one of the mod if the docker instance is the same for all challenges and he said yes

So I got rce on one of the web box (Demon Slayer) then transferred the libc for it to my device and patched the binary using pwninit image

With that said the binary would be the same as the one in the remote instance

For the second chain I’ll perform a Global Offset Table (GOT) overwrite of printf@got to system@libc

Here’s my solve script image

Robin [1st Blood 🩸]

image

Doing the usual gives this image

Note that I already patched the binary with the remote libc file

So we’re working with a x64 binary which is dynamically linked and not stripped

The protection not enabled is PIE

Using ghidra I decompiled the binary here’s the main function image

undefined8 main(void)

{
  undefined8 buffer;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  code *idk;
  undefined8 *mmap_;
  
  setup();
  mmap_ = (undefined8 *)mmap((void *)0x999999000,0x20,3,0x21,-1,0);
  idk = notcalled;
  fwrite("Tell me, what\'s your strategy here: ",1,0x24,stdout);
  read(0,&buffer,0x20);
  printf("Riiiiight, %s",&buffer);
  *mmap_ = buffer;
  mmap_[1] = local_30;
  mmap_[2] = local_28;
  mmap_[3] = local_20;
  fwrite("This might actually come to fruition. Try fire it up: ",1,0x36,stdout);
  fgets((char *)&buffer,0x100,stdin);
  return 0;
}

The binary is fairly simple and here’s what it does:

So the buffer overflow is pretty obvious right? but the issue is because PIE is enabled this is going to look kinda hard

But there’s another issue with this binary

And it’s here:

printf("Riiiiight, %s",&buffer);

It uses printf to print our input and this time it uses a format specifier %s but the issue is this image

So when printf is used it would print our input till it meets a null byte this means that if we give it a specific amount of bytes let’s say we full the buffer with characters it would leak values image

In this case our buffer can hold up to 32 bytes of data so I did some trial and error and got the best leak to be at 31 bytes

With that said the leak turned out to be a binary section address and I just calculated the offset to the elf base address

From here since the second fgets gives us a buffer overflow we can calculate the offset to overwrite the instruction pointer image image

It is 56.

With this we can just perform a ROP technique called Ret2Libc

But initially I was trying to call execve() since it had some available gadgets but it didn’t work cause we need /bin/bash to be placed somewhere in the binary but that I couldn’t do

So I just went with Ret2Libc

Basically what that would do is this:

Here’s my solve solve image

Reverse Engineering

34sy-r3v

image

First I ran the binary to know what it does image

It asks for a pin

I used ltrace which is a library trace to know what’s happening image

It uses strcmp of our input with the flag

So I just ran the strings command and grep for the flag image

Flag: acdfCTF{5tr1ngs_b1n4ry_t0_g3t_fl4g}

CodeX

image

First thing I did was to run it to know what it does image

We can use ltrace image

Ok it uses strcmp but this time around we can’t use strings command cause it’s likely not hardcoded in the binary image

I opened it in gdb got the list of function available image

Since it uses strcmp therefore our input would be in the rdi register while the expected value would be in the rsi register

So I set a breakpoint before the strcmp call image image

Now when we run it we would get the flag in the rsi register image image

Flag: acdfCTF{Th3_p3rf3ct_r3c1p3_for_3t3rn1ty_l1f3}

And that’s all :P

I played with team sudoers and we got 3rd image