➜ ~

Playing Hacks and Stuffs!


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

Sunshine CTF 2023

image

Hi, I participated in this CTF with a friend of mine and it was a fun one :P

In this writeup I’ll give to the solution to the challenges I solved

Challenges Solved:

Cryptography

Reversing

Scripting

Web

Pwn

Cryptography (1/1)

BeepBoop Cryptography

image

After downloading the attached file on checking it’s content gave this image

If you notice it you’ll see that there are only just two words having multiple occurrence

So what I did was to replace beep to 0 and boop to 1 then convert to string

Here’s the script I wrote to do that:

#!/usr/bin/python3

fp = open('BeepBoop').read().split()
cnt = ''

for i in range(len(fp)):
    if fp[i] == 'beep':
        fp[i] = '0'
    else:
        fp[i] = '1'
    
for i in range(0, len(fp), 8):
    cnt += chr(int(''.join(fp[i:i+8]), 2))

print(f"Decoded: {cnt[1::]}")

But after running it the result wasn’t the flag? image

Ok at least it has the flag format sun{^.*} so I assumed this to be some sort of cipher and on using cyberchef I got it to be rot13

So here’s the final script to get the flag: solve

#!/usr/bin/python3
import codecs

fp = open('BeepBoop').read().split()
cnt = ''

for i in range(len(fp)):
    if fp[i] == 'beep':
        fp[i] = '0'
    else:
        fp[i] = '1'
    
for i in range(0, len(fp), 8):
    cnt += chr(int(''.join(fp[i:i+8]), 2))

flag = codecs.decode(cnt[1::], 'rot13')
print(f"Flag: {flag}")

Running it gives the flag image

Flag: sun{exterminate-exterminate-exterminate}

Reversing (1/2)

Dill

image

After downloading the attached file on checking the file type shows this image

So that’s a python compiled binary whose version is 3.8

We can decompile it using uncompyle6

Doing that I got this decompiled python code image

class Dill:
    prefix = 'sun{'
    suffix = '}'
    o = [5, 1, 3, 4, 7, 2, 6, 0]

    def __init__(self) -> None:
        self.encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls'

    def validate(self, value: str) -> bool:
        return value.startswith(Dill.prefix) and value.endswith(Dill.suffix) or False
        value = value[len(Dill.prefix):-len(Dill.suffix)]
        if len(value) != 32:
            return False
        c = [value[i:i + 4] for i in range(0, len(value), 4)]
        value = ''.join([c[i] for i in Dill.o])
        if value != self.encrypted:
            return False
        return True

Looking at this we can see that it defines a class object called Dill and some variables such as prefix, suffix, o are created, the encrypted flag is also given

The validate function of this program does this:

Here’s my solve script which just basically maps the encrypted value to it’s right index position: solve

#!/usr/bin/python3

encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls'
mapping = [5, 1, 3, 4, 7, 2, 6, 0]
prefix = "sun{"
suffix = "}"

enc = [encrypted[i:i+4] for i in range(0, len(encrypted), 4)]
r = [0]*8

for idx, value in enumerate(mapping):
    r[value] = enc[idx]

r = ''.join(r)
flag = prefix + r + suffix

print(f"Flag: {flag}")

Running it I got the flag image

To confirm it’s the right flag we can pass it into the Dill.validate() function image

Running it return True which means that’s the right value image

Flag: sun{ZGlsbGxpa2V0aGVwaWNrbGVnZXRpdD8K}

Scripting (2/4)

DDR

image

We are given a remote instance to connect to

After connecting to the remote instance via netcat it showed this image

Welcome to DIGITAL DANCE ROBOTS!

       -- INSTRUCTIONS --       
 Use the WASD keys to input the 
 arrow that shows up on screen. 
 If you beat the high score of  
     255, you win a FLAG!     

   -- Press ENTER To Start --

After pressing the ENTER key it showed this image

From reading it you can just get what we’re to do

The goal is that we need to send the received arrow corresponding character key which are W,A,S,D. We’re to repeat this process 255 times

First when I tried it I spent some time before I got it to work because the way it was doing I/O was weird to me but I eventually got it work

So my solution is simple and it involves basically grabbing the arrows, then iterate through every arrow in the arrows and map it to a hashtable (dictionary) containing it’s corresponding key value

Then I send the concatenated result to the server for evaluation

Here’s the result from running it: image

I ran it with pwntools debug mode because I was too lazy to fix the code then :(

So I had to fix it well and on running the updated one you should get this image

Here’s the solve script: solve

Flag: sun{d0_r0b0t5_kn0w_h0w_t0_d4nc3}

Web (2/2)

BeepBoop Blog

image

Going over to the web url shows this image

Viewing page source shows this image

After reading the javascript included I saw this image image

So basically going over to /posts should return the list of posts in json

But from the challenge description there’s a secret draft and we need to find it

I wasn’t the one who solved this but @Theory

Here’s his solve script: solve

#!/bin/bash

url_base="https://beepboop.web.2023.sunshinectf.games/post/"
i=0

while true; do
    url="$url_base$i"
    response=$(curl -skS -L "$url")
    hidden_value=$(echo "$response" | jq -r '.hidden')
    
    if [[ $hidden_value == "true" ]]; then
        echo "Found a response with hidden: true at $url"
        echo "$response"
        break
    else
        echo "No luck at $url"
        i=$((i + 1))
    fi
done

After running it we’ll get the hidden post which holds the flag image

So frustrating bash so slow well it’s using curl so I guess that’s the reason :)

Flag: sun{wh00ps_4ll_IDOR}

Hotdog Stand

image

Going over to the url shows this image

We have a login page, on checking /robots.txt reveals this image

The first two directories are invalid but the third one downloaded a file

The file type is a sqlite database image

The number of tables there are 4 image

The credential table looks interesting and on dumping it I got credential image

Username: hotdogstand
Password: slicedpicklesandonions

We can use this cred to login on the webapp image

This was also solved by @Theory

Cool that’s all for the web pretty easy

Flag: sun{5l1c3d_p1cKl35_4nd_0N10N2}

Pwn (2/5)

Array of Sunshine

image

After downloading the binary checking the file type shows this image

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

The protections enabled are Canary & NX

I ran the binary to get an overview of what it does image

Ok it asks for a fruit we want to eat then asks what we want to replace it with

This looks already like a write what where (arbitrary write) sort of challenge

To identify the vulnerability I decompiled it in Ghidra

Here’s the decompilation of the main function image

void main(void)

{
  printf_sym = sym_lookup("printf");
  scanf_sym = sym_lookup("scanf");
  logo();
  do {
    basket();
  } while( true );
}

Here’s the decompiled code image

void basket(void)

{
  long in_FS_OFFSET;
  int fruit;
  undefined *local_30;
  undefined *local_28;
  long local_20;
  undefined8 local_18;
  long canary;
  
  canary = *(long *)(in_FS_OFFSET + 0x28);
  printf("\nWhich fruit would you like to eat [0-3] >>> ");
  __isoc99_scanf("%i",&fruit);
  printf("Replace it with a new fruit.\n",(&fruits)[fruit]);
  printf("Type of new fruit >>>");
  __isoc99_scanf("%24s",&fruits + fruit);
  local_30 = &printf;
  local_28 = &scanf;
  local_20 = _printf;
  local_18 = _scanf;
  if ((printf_sym == _printf) && (printf_sym == _printf)) {
    if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
      __stack_chk_fail();
    }
    return;
  }
                    /* WARNING: Subroutine does not return */
  exit(-1);
}

The global variable fruits contains 4 values image

This is how it looks like in IDA, pretty neat right? image

There’s also a win function which would give us the flag image

So from this I was able to identify this:

But now what can we overwrite or what should we do?

Remember that after running the binary initially it exited instead of calling itself again due to the while loop why is that so?

To know the reason, we know that it can only happen if the got check returns false so let’s set a breakpoint there image image image image

From the result we can see that the resolved value from the lookup_symbols returned got of printf i.e printf@got which is in the rdx register while what it’s being compared with is the value stored in the address 0x404020 which is 0x6

So that’s the reason our it exited because the comparison is false

During the ctf I didn’t notice there was a more easier way to solve this but anyways here’s what I did

First I needed a way to control the value of printf_sym so I can overwrite it with 0x6 so that the comparison returns True and we hit the while loop

To calculate that I calculated the offset from the global variable fruits to printf_sym

I used gdb image image image

Cool we can see our replaced value which is 8 A’s in the 0th index of the fruits array.

Then at the offset 10 is the value of printf_sym

We can also just calculate that if we know the addresses of the two values image

Now that we have a relative offset to the printf_sym address we can overwrite it using the write what where primitive

Here’s the helper function for that image

At this point we know that the binary will keep looping

So what next?

The idea is that we want to call the win function right?

But that is only possible if any of the function to be called is replaced to the win function

So if we overwrite an address that’s supposed to be calling another address and that address is used in the basket function then we can overwrite it

At this point I realised I didn’t need to overwrite printf_sym because exit@plt will be called

And because exit@got ---> exit@plt that means we can overwrite exit@got to the win function

But we’ve already gotten our self in a while loop so I just decided to overwrite printf@got to the win function

Here’s my solve script: solve

Running it works image

If we run it in a debugger attached to it’s current process we will see that printf@got is going to call win image

We can run it remotely to get the flag image

Flag: sun{a_ray_of_sunshine_bouncing_around}