OWASP Seasides CTF 2019

After getting bored from TAMU CTF, I thought of giving Seasides CTF 2019, a try.
A big shoutout to Team AlCapwn for creating these awesome challenges. Although It was a 24 hr CTF Competition, but the challenges were really fun to solve and I learnt a lot from them as well.

This Post contains writeups to the following challs::

Web

The Vice

Crypto

Simple Encryption

Reversing

NonDBG
Break This

TheVice

Task Description :

Browsing to the specified URL, I got some values regarding a device with a uuid = c4ca4238a0b923820dcc509a6f75849b

It looked like a md5hash so I tried to crack it in the first place..
And to my surprise ..

So I made a simple brute-forcer in python which created md5 hashes of 1,2……49,50
And used a simple curl command to output the result of browser.

import hashlib
import os

m = hashlib.md5()

for i in range(0,50):
  m = hashlib.md5()
  m.update(str(i))
  res = m.hexdigest()
  print '{} => {}'.format(i,res)
  os.system('curl http://35.200.147.161:32338/device/'+res)

And Our Flag was waiting for us at the ‘45th’

SimpleEncryption

Task Description :

Download File

This Challenge was as easy as its Title says. XD

Its just a simple Single-byte XOR cipher which you could remember if you’ve solved cryptopals SET-1 challenges. For this I pulled out my XOR key brute-forcer.

def get_english_score(input_bytes):
    """Compares each input byte to a character frequency 
    chart and returns the score of a message based on the
    relative frequency the characters occur in the English
    language
    """

    character_frequencies = {
        'a': .08167, 'b': .01492, 'c': .02782, 'd': .04253,
        'e': .12702, 'f': .02228, 'g': .02015, 'h': .06094,
        'i': .06094, 'j': .00153, 'k': .00772, 'l': .04025,
        'm': .02406, 'n': .06749, 'o': .07507, 'p': .01929,
        'q': .00095, 'r': .05987, 's': .06327, 't': .09056,
        'u': .02758, 'v': .00978, 'w': .02360, 'x': .00150,
        'y': .01974, 'z': .00074, ' ': .13000
    }
    return sum([character_frequencies.get(chr(byte), 0) for byte in input_bytes.lower()])


def single_char_xor(input_bytes, char_value):
    """Returns the result of each byte being XOR'd with a single value.
    """
    output_bytes = b''
    for byte in input_bytes:
        output_bytes += bytes([byte ^ char_value])
    return output_bytes


def main():
    hexstring = '143c32733a207332732027322736733a3d732436202736213d731a3d373a3273243a273b73303c3220273f3a3d3620732027213627303b3a3d3473323f3c3d3473273b3673122132313a323d730036327d731a2720733f3c3d34733b3a20273c212a733220733273033c212726342636203673303c3f3c3d2a7323213a3c2173273c73626a6562733a207336253a37363d27733a3d733a272073232136203621253637736264273b7e30363d2726212a73303b2621303b362073323d3773273b367332213632b1d3ca207327213c233a30323f7320233a303673233f323d2732273a3c3d207d73143c32733a2073323f203c73383d3c243d73353c21733a272073313632303b36207f7321323d343a3d347335213c3e73233c23263f3221732027213627303b3620733227731132343273323d377303323f3c3f363e73273c73273b3c2036733a3d733f323a377e3132303873353a203b3a3d3473253a3f3f32343620732026303b7332207312343c3d37327d73073b3673313632303b3620733a3d73273b367335322173003c26273b73323d3773273b3673353221273b362027731d3c21273b7332213673273b3673313620277d731d3c21273b73143c3273262026323f3f2a7327363d372073273c73313673123d39263d327c05323432273c217332213632737b373630363d277f7330213c2437363773313632303b36207a73323d377310323d373c3f3a3e7c113234327b2536212a7330213c2437363773313632303b36207a7f73323d377312203b24363e7c1221323e313c3f737b35263d73303f36323d7f7323213627272a73313632303b36207a7d73203632286404630c62660c1160646460010c641b131d0c631d602e203a37362073143c32733a20733273303c322027323f732136343a3c3d733f3c3032273637733a3d73273b36732436202736213d7323322127733c35731a3d373a327335323e3c2620733220733e3c202773233c23263f322173273c26213a202773373620273a3d32273a3c3d7d73113c263d37363773312a73122132313a323d73003632733c3d733c3d3673203a373673323d37733f323d37733c3d73273b36733c273b362173203a3736207f73273b3a20732136343a3c3d73313c3220272073606373323d37733e3c213673313632303b36207d'
    ciphertext = bytes.fromhex(hexstring)
    potential_messages = []
    for key_value in range(256):
        message = single_char_xor(ciphertext, key_value)
        score = get_english_score(message)
        data = {
            'message': message,
            'score': score,
            'key': key_value
            }
        potential_messages.append(data)
    best_score = sorted(potential_messages, key=lambda x: x['score'], reverse=True)[0]
    for item in best_score:
        print("{}: {}".format(item.title(), best_score[item]))

if __name__ == '__main__':
    main()

The Key was 83
As Simple as it says… ;D

NonDBG

Task Description :

Download File

Now Unfortunately, I had to bootup my Windows for decompiling with IDA Pro :(

After debugging, I found that main() is calling two functions named as lencheck() and inpcheck().

Psuedocodes

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4;
  int v5; 
  char *v6; 
  int v7;
  int v8; 

  v8 = ptrace(0, 0LL, 1LL, 0LL);
  std::operator<<<std::char_traits<char>>(&std::cout, "Enter Input:\n");
  std::operator>><char,std::char_traits<char>>(&std::cin, &v4);
  v7 = lencheck(&v4);
  v6 = &v4;
  v5 = inpcheck(&v4, v7, v8);
  if ( v5 != 14 )
    std::operator<<<std::char_traits<char>>(&std::cout, "\n\nBetter Luck next time\n");
  return 0;
}

The lencheck function adds 10 to our input string’s length and returns it in v7 variable.

__int64 __fastcall lencheck(char *a1)
{
  return (unsigned int)strlen(a1) + 10;
}

When I moved on towards the inpcheck function, It took me a second to guess the input as it was in our plain sight. Also I saw that our Input String’s Length has to be 14 to output anything in our flag format which was sea{…}sides

__int64 __fastcall inpcheck(char *a1, int a2, unsigned int a3)
{
  unsigned int v3; 
  __int64 v4; 
  __int64 result; 
  char s1[7]; 
  char v7[7];
  int v8; 
  int v9; 
  int v10; 
  int i; 
  v3 = a3;
  v9 = 0;
  v8 = 0;
  i = a2 - 11;
  v10 = 0;
  v4 = std::operator<<<std::char_traits<char>>(&std::cout, "\nPtrace1: ");
  std::ostream::operator<<(v4, v3);
  if ( 10 * (v3 + 1) == 10 )
  {
    if ( a2 == 24 )
    {
      while ( i >= a2 - 17 && v10 <= 6 )
        s1[v10++] = a1[i--];
      if ( !strncmp(s1, "buGng$F", 7uLL) )
      {
        result = 0LL;
      }
      else
      {
        v9 = 7;
        for ( i = 0; i <= v10; ++i )
          v7[i] = a1[i];
        if ( !strncmp(s1, "AntI_D3", 7uLL) )
        {
          result = 0LL;
        }
        else
        {
          v8 = 7;
          printflag(a1);
          result = (unsigned int)(v9 + v8);
        }
      }
    }
    else
    {
      result = 0LL;
    }
  }
  else
  {
    printbad();
    result = 0LL;
  }
  return result;
}

I tried AntI_D3buGng$F and we got our flag as output.

BreakThis

Task Description :

Download File

This task was pretty simple when you’ve got the Hex Rays Decompiler Plugin. I didn’t try it the usual way.

Psuedocode (main)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3;  
  void *s;  
  size_t v5;  
  size_t v6;  
  char *v7; 
  char *v8; 
  char *s1; 
  if ( argc != 2 )
  {
    puts("please_find_me.exe <<key_goes_here>> ");
    exit(0);
  }
  v3 = strlen(argv[1]);
  s = malloc(v3 + 1);
  v5 = strlen(argv[1]);
  memset(s, 0, v5 + 1);
  v6 = strlen(argv[1]);
  strncpy((char *)s, argv[1], v6);
  v7 = (char *)base64(s);
  v8 = (char *)rot13(v7);
  s1 = (char *)uuencode(v8);
  if ( !strcmp(s1, flag_encoded) )
    puts("GG!, now put that flag on ye head and fly away!!");
  else
    puts("Nope, wrong flag ye got there m8, try again!");
  return 0;
}

I observed some conversions of our input into various encodings base64, rot13, and uuencode.
I knew about the first two, but wth is uuencode…

No Problem, Linux Man Page takes care of that for us.
uuencode and uudecode are used to transmit binary files over transmission mediums that do not support other than simple ASCII data. You can read more about it here.

It was also easy to find the flag_encoded variable which was declared.

I simply wrote a python script to reverse the encoding process. And FYI the begin & end line is necessary for using uuencode or else it’ll throw an error.

from codecs import decode,encode
import uu
enc_str = b'begin 666 <data>\n8<#))=7(R:VEO57$P36$Q;6Y*17EP:CT]\n \nend\n'
un_enc_str=decode(enc_str,'uu')
print "lev1 uudecode: {}".format(un_enc_str)
rot13_str = un_enc_str.encode('rot13')
print "lev2 rot13: {}".format(rot13_str)
base64_str = rot13_str.decode('base64')
print "lev3 base64: {}".format(base64_str)

Thanks for Reading this writeup..
Subscribe to my Newsletter for more updates regarding CTFs.
Also Feedback is always appreciated.
It won’t take much time though.

And Keep Escalating the Privileges Happy Hacking !!