syaffers / cyber-talents-qualifiers-2017-write-ups

Write up for the CyberTalents 2017 qualifications round

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CyberTalents CTF 2017 Write-ups

This is my second time doing CTFs so I thought I'd join the crowd and do a write-up.

I'm using Ubuntu 16.04 for most of these terminal commands. I assume that the commands are pre-installed on the system so I don't have to keep putting in sudo apt-get install <x> everywhere. Time represented in the write-ups are arbitrary. Since this was a take-home qualification round, a lot of time was spent out with my family or doing something else. I took lots of break.

Sections:

This is Sparta

(Easy, 50 pts, Web Security)

The very first problem showed a website. Pretty innocent, if you ask me:

This is Sparta?

So, there is a username and password box and a hint: "Easier than Ableton" which didn't provide much info. Trying admin and admin123 returned wrong user or password. Obviously, with these types of challenge, the first place to check is the source.

Sparta source page

Oh look: a nicely mangled JS block. I took the script out into my editor and prettified it:

var _0xae5b = [
  "\x76\x61\x6C\x75\x65",
  "\x75\x73\x65\x72",
  "\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64",
  "\x70\x61\x73\x73",
  "\x43\x79\x62\x65\x72\x2d\x54\x61\x6c\x65\x6e\x74",
  "\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x43\x6F\x6E\x67\x72\x61\x74\x7A\x20\x0A\x0A",
  "\x77\x72\x6F\x6E\x67\x20\x50\x61\x73\x73\x77\x6F\x72\x64"
];

function check() {
    var _0xeb80x2 = document[_0xae5b[2]](_0xae5b[1])[_0xae5b[0]];
    var _0xeb80x3 = document[_0xae5b[2]](_0xae5b[3])[_0xae5b[0]];
    if (_0xeb80x2 == _0xae5b[4] && _0xeb80x3 == _0xae5b[4]) {
        alert(_0xae5b[5]);
    } else {
        alert(_0xae5b[6]);
    }
}

By the looks of it, these cryptic hexadecimal values are just strings actually. So let's take a closer look at it in the console:

> _0xae5b[0]
"value"
> _0xae5b[1]
"user"
> _0xae5b[2]
"getElementById"
> _0xae5b[3]
"pass"
> _0xae5b[4]
"Cyber-Talent"
> _0xae5b[5]
"                      Congratz

"
> _0xae5b[6]
"wrong password"

Okay, clearly it's checking the values of the username and password fields. If we substitute the string values into the actual code, we should get the following:

function check() {
    var _0xeb80x2 = document["getElementById"]("user")["value"];
    var _0xeb80x3 = document["getElementById"]("pass")["value"];
    if (_0xeb80x2 == "Cyber-Talent" && _0xeb80x3 == "Cyber-Talent") {
        alert(_0xae5b[5]);
    } else {
        alert("wrong password");
    }
}

According to the if statement, both of the fields need to equal Cyber-Talent. Okay, let's put that in and we get our flag.

This is Sparta?

{J4V4_Scr1Pt_1S_Aw3s0me} indeed.

Search in Trash

(Easy, 50 pts, Digital Forensics)

We are given some file called search-trash, no extension. So let's file it:

$ file search-trash
search-trash: Windows Recycle Bin INFO2 file (Win2k - WinXP)

Okay, so it's an INFO2 recycle bin file. I'm not too sure what it is but after briefly looking at the file contents using less, there were several parts which are clearly strings but the remainder were mainly binary values. Let's see if the flag is directly visible in the file using strings:

$ strings search-trash
...
:\ext.txt
:\fa.txt
:\fi.txt
:\FLag{Fat_32_DF_2}.txt
:\fr.txt
:\fur.txt
:\fy.txt
...

Yup, it's there, Ctrl-C, Ctrl-V and done.

Pure Luck

(Basic, 25 pts, Malware Reverse Engineering)

Again, we were presented with an unidentified file pure-luck.out. Upon file-ing it, it's seems to be an executable, but is 32-bit ELF, which can't be run on my 64-bit linux. No worries. Let's decompile it using IDA Pro. On viewing the pseudocode for main, I found some involved code which I couldn't be bothered to pursue.

Back to the original file for some clues. Somehow, I decided to do a binwalk on the file and found a nice signature appended at the end:

$ binwalk pure-luck.out
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux)
273112        0x42AD8         Copyright string: "Copyright (C) 1996-2016 the UPX Team. All Rights Reserved. $"

Nice, some clue now: WTH is a UPX compression though? No matter, straight to Google and found a repo to decompress the file. Now, it seems like I've still got an executable, so binwalk again to see if there are more data:

$ binwalk pure-luck.out
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux)
481360        0x75850         Unix path: /proc/sys/vm/overcommit_memory
487717        0x77125         Unix path: /proc/sys/kernel/rtsig-max
488794        0x7755A         Unix path: /sysdeps/unix/sysv/linux/getcwd.c
490775        0x77D17         Unix path: /proc/sys/kernel/osrelease
496388        0x79304         Unix path: /usr/lib/locale/locale-archive
553468        0x871FC         ELF, 32-bit LSB no machine, (SYSV)
557098        0x8802A         Unix path: /sysdeps/unix/sysv/linux/dl-origin.c

Okay, some more things going on here. I could've extracted the data but let's just see if IDA Pro shows something new. On decompiling the main, I saw a bunch of variables which have static values:

...
v10 = 102;
v11 = 108;
v12 = 97;
v13 = 103;
v14 = 123;
v15 = 85;
v16 = 80;
v17 = 88;
v18 = 95;
v19 = 105;
v20 = 115;
v21 = 95;
v22 = 115;
v23 = 111;
v24 = 95;
v25 = 101;
v26 = 97;
v27 = 97;
v28 = 97;
v29 = 97;
v30 = 115;
v31 = 121;
v32 = 121;
v33 = 125;
...

Hey, this looks like ASCII! Let's use python to just convert them one by one:

>>> a = [102, 108, 97, 103, 123, 85, 80, 88, 95, 105, 115, 95, 115, 111, 95, 101, 97, 97, 97, 97, 115, 121, 121, 125]
>>> print("".join(map(chr, a)))
flag{UPX_is_so_eaaaasyy}

And done!

Post Number

(Medium, 100 pts, Open Source Cyber Intelligence)

I enjoyed this one, it was an internet-based goose-chase with real-world implications. Anyway, we are asked to find a post number (super misleading word, if you ask me) based on an image. The image didn't ring any bells but the description did ask to find the original author of the image. I know just the place: TinEye. This service allows you to find the first instance of an image on the net.

By uploading the sample image and sorting by oldest first returned a Flickr site with image by ahmedmahmoudphotography. So let's copy the post number in the URL, 5410375058, and paste it into the answer box. Nope, not it. Damn. Okay, could it be the image id number/tags assigned in the HTML? No, the values change when I scroll onto different photos.

After a few hours trying other challenges, I gave the description a fresh look and noticed that it mentions an address. At this point it clicked that the problem mentioned looking for a physical house address of the guy so I browse the album to find this photo. And after putting in 11371 in the box, I finally got the flag.

This has real-world implications as I've mentioned before: malicious people can find your original address if you're not careful through this method. Suffice to say this challenge creeped me out.

Message in a Bottle

(Easy, 50 pts, Digital Forensics)

I hated myself for this one, simply because it was a prime example of me not seeing the forest for the trees. Let's start. We have a PNG image:

The rabbit hole

$ file message-in-bottle.png
message-in-bottle.png: PNG image data, 350 x 144, 8-bit/color RGB, non-interlaced

Okay, that's fine, let's see if there is hidden information:

$ binwalk message-in-bottle.png
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 350 x 144, 8-bit/color RGB, non-interlaced
41            0x29            Zlib compressed data, best compression

Nice! A Zlib compressed data, this must be what we're searching for. Let's extract using binwalk -e message-in-bottle.png. Inside the folder is a file called 29. Interesting. Let's strings it... No good. What about less? Cryptic. Wow, okay.

After a while I thought of doing a hexdump:

0001b9e0: ffff ffff ffff ffff fffd ffff fdfb fdfa  ................
0001b9f0: fcfe fdfc fefd fcfc fcff fbee ffe7 c5da  ................
0001ba00: 915a e581 35ee 7e28 ee7e 28ee 7e28 ed7f  .Z..5.~(.~(.~(..
0001ba10: 2aed 7f2a ee7e 28ee 7e28 ed7f 26ed 7f26  *..*.~(.~(..&..&
0001ba20: ee7e 2aee 7e2a ee7e 28ee 7f26 ed7f 26ed  .~*.~*.~(..&..&.
0001ba30: 8025 f083 28e9 812a df7f 35e1 9a64 ffe7  .%..(..*..5..d..
0001ba40: d0ff fdf6 fdfe f9fc fffd ffff ffff ffff  ................
0001ba50: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001ba60: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001ba70: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001ba80: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001ba90: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001baa0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001bab0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001bac0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001bad0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001bae0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001baf0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
0001bb00: ffff ffff ffff fffe ffff fdff feff fbf2  ................
0001bb10: fff6 e1ee b88a db7e 31f1 8024 f17f 29ed  .......~1..$..).
0001bb20: 7d27 ed7f 28ed 7f26 ee7e 2af0 7e28 ee7e  }'..(..&.~*.~(.~
0001bb30: 28ee 7f26 ee7e 28ee 7e28 ee7e 28ee 7e28  (..&.~(.~(.~(.~(
0001bb40: f07d 2aee 7e2a ed7d 29ec 7e27 eb7e 23da  .}*.~*.}).~'.~#.
0001bb50: 8336 ebbf 92ff fae6 fffb f5fe ffff fbfd  .6..............

So I found that the values in 29 are 0xff inflated but had other values in a relatively ordered fashion. This 0xff values could be a white background on an image which contained the string itself. So I pulled up Python and using numpy and matplotlib, I reconstructed the image. I figured out proper dimensions for the image and blah blah blah and ultimately got the image below:

Are you kidding me?

After a moment pondering about self-reference (and my existence), I thought: well, that's not helpful. It's got these black lines of different intensity values which resembled the original image and didn't really provide any more information. Okay, let's see if we can't analyze the intensity for some data.

Time went waaay by. Like seriously, I wasted my time doing pointless numerical analysis on this image

Towards the deadline, I decided that I'm done with this, I'm just going to use basic tools to see if I can get anything. And obviously, after shooting myself in the face for so many times, this was indeed the right way to go. I tried steghide but it's only for JPG. stegsolve was just too weird to use. Desperately, I tried openstego and got the following error:

I finally have you!

After trying an older version (v0.6.1), I got the flag Flag{701_L@b$_CTF0}. Honestly, I've learned now that always try to try tools first before going off the deep-end and over-analyzing. Breadth-first, over depth-first.

Intercept

(Basic, 25 pts, General Information)

Oh this one pissed me the hell off. Like really, guys at CyberTalents, you couldn't allow for multiple answers for questions like these? 😤

So the description asks for a type of attack where there is a person intercepting data between two transacting parties. Obviously this is the notorious man-in-the-middle attack. So let's put that in the answer. RED, wrong!

What? How? Why not? So, I ended up trying (more than) a few, to no avail:

man_in_the_middle
maninthemiddle
MANINTHEMIDDLE
flag{man in the middle}
flag{man_in_the_middle}
flag{maninthemiddle}
flag{MANINTHEMIDDLE}
...

You get the idea. So I thought, meh it's 25 points, who cares. Towards the deadline, desperation ensues, I tried other possibilities like interception or sniffing but I just had a hunch. I took one last stab with man in the middle.

Maybe this is it, maybe I can get it. T minus 10 minutes.

Man In The Middle

RED. Nope... T minus 9 minutes.

elddim eht ni nam

RED. Yet again... T minus 8 minutes.

man+in+the+middle

RED. Crap. T minus 7 minutes. Man in the middle... MITM.

mitm

GREEN. My life was complete.

Get rid of them all

(Easy, 50 pts, Malware Reverse Engineering)

This one is really nice but maybe, due to a stroke of luck, I got this relatively quickly. So we're presented with a JAR file, which just spouts

$ java -jar get-rid-of-them.jar
No

Okay grumpy cat. Let's take you apart. I used this tool to decompile the JAR and got two files:

$ ls -l
-rw-rw-r-- 1 syafiq syafiq  834 Apr  7 21:04 Ctf.java
-rw-rw-r-- 1 syafiq syafiq  948 Apr  8 10:45 ooo.java

Nothing suspicious, let's look inside Ctf.java. There is a line of code which was particularly interesting:

# Ctf.java
...
static String flag = "&^&@|* Zm}&,);\\('))[\\[$`|_^#(x*]>&hZ)'$ $#(: [$3;&$t \\_']?&>,&i)!QG{`- ,% ~<`._@'::_\\_{}-|_[&{<`~$) ?'?(!$,.{>? @!^:#|R,?')`[,`;?!f_:$$<)Y}$:[|^?2)_h&><.:.-{&[|&A\\*;*)-($.>>(<^';#Q@?,,H\\`|)$ <):@(;}?-[~(&)>>*)(~)`$:[;>!.&%<!.>~ %J}*zX:(&:~:<0)*>(B(!?.#@A*<*{-,[Q@{%!~)~-~:@:#|![>)]?];H;$-<}>!@~)<<) \\_!|]#,&!,@>\\[]|J ]\\^[?>$|$?'|,#.)$l[^@X.~! \\;0-&,;,!['@[J*~#`AQ[*&%<,~]?~_^~(;}\\$>)[&@) (]}];;*^<)''@\\E[.@! B*.<-A-,:-#`-.}<-|)^Z@](?;H >-}.%.?}@<!())0] <&=@(<*$\\(("
...

Alright, this is interesting but seems like jumbled mess. Let's look further:

# Ctf.java
...
public static void main(String[] args) {
  if (args.length < 10) {
    System.out.println("No");
    return;
  }
  ooo o = new ooo();
  flag = o._2(flag, args);
  System.out.println(flag);
  System.out.println(o._1(flag));
}
...

Okay, the main prints No if we try to run it with fewer than 10 arguments. Otherwise it will print out the flag modified by this ooo object. Let's first run the JAR with ten zeros:

$ java -jar get-rid-of-them.jar 0 0 0 0 0 0 0 0 0 0
&^&@|* Zm}&,);\('))[\[$`|_^#(x*]>&hZ)'$ $#(: [$3;&$t \_']?&>,&i)!QG{`- ,% ~<`._@'::_\_{}-|_[&{<`~$) ?'?(!$,.{>? @!^:#|R,?')`[,`;?!f_:$$<)Y}$:[|^?2)_h&><.:.-{&[|&A\*;*)-($.>>(<^';#Q@?,,H\`|)$ <):@(;}?-[~(&)>>*)(~)`$:[;>!.&%<!.>~ %J}*zX:(&:~:<0)*>(B(!?.#@A*<*{-,[Q@{%!~)~-~:@:#|![>)]?];H;$-<}>!@~)<<) \_!|]#,&!,@>\[]|J ]\^[?>$|$?'|,#.)$l[^@X.~! \;0-&,;,!['@[J*~#`AQ[*&%<,~]?~_^~(;}\$>)[&@) (]}];;*^<)''@\E[.@! B*.<-A-,:-#`-.}<-|)^Z@](?;H >-}.%.?}@<!())0] <&=@(<*$\((
Wrong argsssss
&^&@|* Zm}&,);\('))[\[$`|_^#(x*]>&hZ)'$ $#(: [$3;&$t \_']?&>,&i)!QG{`- ,% ~<`._@'::_\_{}-|_[&{<`~$) ?'?(!$,.{>? @!^:#|R,?')`[,`;?!f_:$$<)Y}$:[|^?2)_h&><.:.-{&[|&A\*;*)-($.>>(<^';#Q@?,,H\`|)$ <):@(;}?-[~(&)>>*)(~)`$:[;>!.&%<!.>~ %J}*zX:(&:~:<0)*>(B(!?.#@A*<*{-,[Q@{%!~)~-~:@:#|![>)]?];H;$-<}>!@~)<<) \_!|]#,&!,@>\[]|J ]\^[?>$|$?'|,#.)$l[^@X.~! \;0-&,;,!['@[J*~#`AQ[*&%<,~]?~_^~(;}\$>)[&@) (]}];;*^<)''@\E[.@! B*.<-A-,:-#`-.}<-|)^Z@](?;H >-}.%.?}@<!())0] <&=@(<*$\((

Okay, looks like there's more to this. Let's look into that ooo.java file.

public String _1(String a) {
  try {
    return new String(Base64.getDecoder().decode(a));
  }
  catch (Exception e) {
    System.out.println("Wrong argsssss");
    System.out.println(a);
  }
  return "";
}

public String _2(String a, String[] b) {
  String temp = "";
  int i = 0;
  int j = 0;
  boolean bad = false;

  while (i != a.length()) {
    j = 0;
    bad = false;

    while (j != b.length) {
      if (a.charAt(i) == Integer.parseInt(b[j]))
        bad = true;
      j++;
    }

    if (!bad)
      temp = temp + a.charAt(i);
    i++;
  }
  return temp;
}

So the _1 function tries to decode a base 64 string which is the flag after some processing by the _2 function. I was too lazy to follow the _2 function thoroughly but based on the simplicity of the _2 function (since there are no values being edited, some some true/false flags), I thought of just removing all the unnecessary characters from the original flag and leaving the base 64 characters only:

>>> a = "&^&@|* Zm}&,);\\('))[\\[$`|_^#(x*]>&hZ)'$ $#(: [$3;&$t \\_']?&>,&i)!QG{`- ,% ~<`._@'::_\\_{}-|_[&{<`~$) ?'?(!$,.{>? @!^:#|R,?')`[,`;?!f_:$$<)Y}$:[|^?2)_h&><.:.-{&[|&A\\*;*)-($.>>(<^';#Q@?,,H\\`|)$ <):@(;}?-[~(&)>>*)(~)`$:[;>!.&%<!.>~ %J}*zX:(&:~:<0)*>(B(!?.#@A*<*{-,[Q@{%!~)~-~:@:#|![>)]?];H;$-<}>!@~)<<) \\_!|]#,&!,@>\\[]|J ]\\^[?>$|$?'|,#.)$l[^@X.~! \\;0-&,;,!['@[J*~#`AQ[*&%<,~]?~_^~(;}\\$>)[&@) (]}];;*^<)''@\\E[.@! B*.<-A-,:-#`-.}<-|)^Z@](?;H >-}.%.?}@<!())0] <&=@(<*$\\(("
>>> b64only = list(filter(lambda x: x in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", a))
>>> print(base64.b64decode("".join(b64only)))
flag{b@d_ch@@rs_@@@re_B@@@@d}

And that was it. I guess my brain found a shortcut as it was scanning through the _2 code, can't really say how.

About

Write up for the CyberTalents 2017 qualifications round