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

Register with a single letter user & single letter password. The resulting json:

{"username":"a","password":"a","db":"hitcon-ctf"}

Which is encoded with AES-CFB, in 16 byte blocks:

{"username":"a",
"password":"a","
db":"hitcon-ctf"
}

and set as the cookie with the IV.

XORing something to the cyphertext will xor the same to the related cleartext & fuck up the next block.

If we change the 3rd block from

db":"hitcon-ctf"

to

admin":true}

(by xoring the bytes 49-60 and cut off the rest of the cookie), we will have what is needed to become admin.

Our code was (Delphi):

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm3 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

uses IdTCPClient;

{$R *.dfm}

function EncodeURIComponent(const ASrc: string): UTF8String;
const
  HexMap: UTF8String = '0123456789ABCDEF';

      function IsSafeChar(ch: Integer): Boolean;
      begin
        if (ch >= 48) and (ch <= 57) then Result := True    // 0-9
        else if (ch >= 65) and (ch <= 90) then Result := True  // A-Z
        else if (ch >= 97) and (ch <= 122) then Result := True  // a-z
        else if (ch = 33) then Result := True // !
        else if (ch >= 39) and (ch <= 42) then Result := True // '()*
        else if (ch >= 45) and (ch <= 46) then Result := True // -.
        else if (ch = 95) then Result := True // _
        else if (ch = 126) then Result := True // ~
        else Result := False;
      end;
var
  I, J: Integer;
  ASrcUTF8: UTF8String;
begin
  Result := '';    {Do not Localize}

  ASrcUTF8 := UTF8Encode(ASrc);
    // UTF8Encode call not strictly necessary but
    // prevents implicit conversion warning

  I := 1; J := 1;
  SetLength(Result, Length(ASrcUTF8) * 3); // space to %xx encode every byte
  while I <= Length(ASrcUTF8) do
  begin
    if IsSafeChar(Ord(ASrcUTF8[I])) then
    begin
      Result[J] := ASrcUTF8[I];
      Inc(J);
    end
    else if ASrcUTF8[I] = ' ' then
    begin
      Result[J] := '+';
      Inc(J);
    end
    else
    begin
      Result[J] := '%';
      Result[J+1] := HexMap[(Ord(ASrcUTF8[I]) shr 4) + 1];
      Result[J+2] := HexMap[(Ord(ASrcUTF8[I]) and 15) + 1];
      Inc(J,3);
    end;
    Inc(I);
  end;

   SetLength(Result, J-1);
end;

function DoPost(user: string; pass: string): string;
var C: TIdTCPClient;
    s,s2: string;
    chunked: Boolean;
    ctL: Boolean;
    rem: integer;
    buff: Array of Byte;
    AData: string;
begin
rem := 0;
SetLength(buff,1024);
Result := '';
C := TIdTCPClient.Create;
C.Host := '52.69.244.164';
C.Port := 51913;
C.Connect;
AData := 'username=' + EncodeURIComponent(user) + '&password=' + EncodeURIComponent(pass);
C.IOHandler.WriteLn('POST / HTTP/1.1');
C.IOHandler.WriteLn('Host: 52.69.244.164:51913');
C.IOHandler.WriteLn('Content-Length: ' + IntToStr(Length(AData)));
C.IOHandler.WriteLn('Content-Type: application/x-www-form-urlencoded');
C.IOHandler.WriteLn('');
C.IOHandler.Write(AData);
s := 'a';
chunked := False;
ctl := False;
s2 := '';
while s <> '' do
   begin
   s := C.IOHandler.ReadLn;
   if copy(s,1,12) = 'Set-Cookie: ' then
      begin
      if pos(' auth=',s) > 0 then
         begin
         Result := copy(s,pos(' auth=',s) + 6);
         Result := copy(Result,1,pos(';',Result) - 1);
         end;
      end;
   if copy(s,1,16) = 'Content-Length: ' then
      begin
      ctL := True;
      rem := StrToInt(copy(s,17));
      end;
   if s = 'Transfer-Encoding: chunked' then
      chunked := True;
   s2 := s2 + s;
   end;
s := 'a';
s2 := '';
if not chunked then
   begin
   if ctl then
      begin
      s2 := C.IOHandler.ReadString(rem);
      end
      else
      begin
      while C.IOHandler.Readable(1000) do
         begin
         s := C.IOHandler.ReadLn;
         s2 := s2 + s;
         end;
      end;
   end
   else
   begin
   while True do
     begin
     s := C.IOHandler.ReadLn;
     if s = '0' then
        Break;
     rem := StrToInt('$' + s);
     s2 := s2 + C.IOHandler.ReadString(rem);
     s := C.IOHandler.ReadLn;
     end;
   end;
C.Free;
//ShowMessage(Result);
//Result := s2;
end;

function DoGet(cookie: string): string;
var C: TIdTCPClient;
    s,s2: string;
    chunked: Boolean;
    ctL: Boolean;
    rem: integer;
    buff: Array of Byte;
  x: Integer;
    cck: string;
begin
cck := '';
while cookie <> '' do
   begin
   cck := cck + '%' + copy(cookie,1,2);
   cookie := copy(cookie,3);
   end;
rem := 0;
SetLength(buff,1024);
Result := '';
C := TIdTCPClient.Create;
C.Host := '52.69.244.164';
C.Port := 51913;
C.Connect;
C.IOHandler.WriteLn('GET / HTTP/1.1');
C.IOHandler.WriteLn('Host: 52.69.244.164:51913');
C.IOHandler.WriteLn('Cookie: auth=' + cck);
C.IOHandler.WriteLn('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8');
C.IOHandler.WriteLn('');
s := 'a';
chunked := False;
ctl := False;
s2 := '';
while s <> '' do
   begin
   s := C.IOHandler.ReadLn;
   if copy(s,1,16) = 'Content-Length: ' then
      begin
      ctL := True;
      rem := StrToInt(copy(s,17));
      end;
   if s = 'Transfer-Encoding: chunked' then
      chunked := True;
   s2 := s2 + s;
   end;
s := 'a';
s2 := '';
if not chunked then
   begin
   if ctl then
      begin
      s2 := C.IOHandler.ReadString(rem);
      end
      else
      begin
      while C.IOHandler.Readable(1000) do
         begin
         s := C.IOHandler.ReadLn;
         s2 := s2 + s;
         end;
      end;
   end
   else
   begin
   while True do
     begin
     s := C.IOHandler.ReadLn;
     if s = '0' then
        Break;
     rem := StrToInt('$' + s);
     s2 := s2 + C.IOHandler.ReadString(rem);
     s := C.IOHandler.ReadLn;
     end;
   end;
C.Free;
Result := s2;
end;

procedure TForm3.Button1Click(Sender: TObject);
var s1, s2, s3, s4: string;
    bs: array[1..16] of byte;
    x: Integer;
    ss: string;
    SL: TStringList;
    y: Integer;
    sss: string;
begin
s4 := DoPost('a','b');
Edit1.Text := s4;
s1 := '';
while s4 <> '' do
   begin
   if s4[1] = '%' then
      begin
      s1 := s1 + copy(s4,2,2);
      s4 := copy(s4,4);
      end
      else
      begin
      s1 := s1 + IntToHex(ord(s4[1]),2);
      s4 := copy(s4,2);
      end;
   end;
Edit2.Text := s1;
ShowMessage(DoGet(s1));
s2 := 'db":"hitcon-ctf"';
s3 := 'admin":true}    ';
for x := 48 to 63 do
    begin
    bs[x - 47] := StrToInt('$' + copy(s1,x * 2 + 1,2));
    end;
for x := 1 to 16 do
    bs[x] := bs[x] xor (ord(s2[x]) xor ord(s3[x]));
s4 := copy(s1,1,96);
for x := 1 to 12 do
    s4 := s4 + IntToHex(bs[x],2);
ShowMessage(doget(s4));
end;

end.

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

There was a message encrypted with something between RSA and Rabin encryption schemes.

n = p*q = 20313365319875646582924758840260496108941009482470626789052986536609343163264552626895564532307
e = 31415926535897932384
c = m^e % n = 19103602508342401901122269279664114182748999577286972038123073823905007006697188423804611222902

We factorized n with yafu.

p = 123722643358410276082662590855480232574295213977
q = 164184701914508585475304431352949988726937945291

e doesn’t have a modular inverse because it’s even, so first we RSA-decrypted with its “odd part”.

e = 32 * 981747704246810387

RSA-decrypting with e/32 gave that

m^32 % n = 6915497960347690034670190613920812775692185594434656280771177353647840352326299910999240632820

Decrypting it with Rabin 5 times in a row gave several possibilities for m % n.

m % n is one of:

1170873348295885335059944818034561278496937179514286352764111586578035361653036340797178495797
6441791373850850303735177662669226594971314531797606586938114159564488393181507111460739720503 
13871573946024796279189581177591269513969694950673020202114872377044854770083045515434824811804 
19142491971579761247864814022225934830444072302956340436288874950031307801611516286098386036510 
4169292882246487226436372571580328445408188970955313275707356349635909107039805208587333631745 
8531407715482423717693263787976379790064568800203420573643404440830910301390203966050312684262 
11781957604393222865231495052284116318876440682267206215409582095778432861874348660845251848045 
16144072437629159356488386268680167663532820511515313513345630186973434056224747418308230900562

There was an assert in the encryption code that said the length of the flag is 50 (which means 400 bits), but these numbers were around 310 bits only.

We needed to find a multiple of n to add to m%n so that m will be 400 bits, and hex-decoding it gives ‘hitcon{…}’.

We had lower and upper limits because of the needed string’s beginning, we had to brute-force between those values and check if it only contains ascii characters and it ends with ‘}’.

It was too slow, but we could speed up the process by finding one possible multiplier such that it ends with ‘}’, and then try every 256th multipliers only (because those are the ones that start with ‘}’)

Here is the full python code that does the part after decrypting with RSA.

def modular_sqrt(a, p):
   if legendre_symbol(a, p) != 1:
       return 0
   elif a == 0:
       return 0
   elif p == 2:
       return p
   elif p % 4 == 3:
       return pow(a, (p + 1) / 4, p)
   s = p - 1
   e = 0
   while s % 2 == 0:
       s /= 2
       e += 1
   n = 2
   while legendre_symbol(n, p) != -1:
       n += 1
   x = pow(a, (s + 1) / 2, p)
   b = pow(a, s, p)
   g = pow(n, s, p)
   r = e
   while True:
       t = b
       m = 0
       for m in xrange(r):
           if t == 1:
               break
           t = pow(t, 2, p)
       if m == 0:
           return x
       gs = pow(g, 2 ** (r - m - 1), p)
       g = (gs * gs) % p
       x = (x * gs) % p
       b = (b * g) % p
       r = m

def legendre_symbol(a, p):
   ls = pow(a, (p - 1) / 2, p)
   return -1 if ls == p - 1 else ls

def mul_inv(a, b):
   b0 = b
   x0, x1 = 0, 1
   if b == 1: return 1
   while a > 1:
       q = a / b
       a, b = b, a%b
       x0, x1 = x1 - q * x0, x0
   if x1 < 0: x1 += b0
   return x1

def chinese_remainder(n, a, lena):
   p = i = prod = 1; sm = 0
   for i in range(lena): prod *= n[i]
   for i in range(lena):
       p = prod / n[i]
       sm += a[i] * mul_inv(p, n[i]) * p
   return sm % prod

e = 31415926535897932384L
c = 19103602508342401901122269279664114182748999577286972038123073823905007006697188423804611222902L
p = 123722643358410276082662590855480232574295213977L
q = 164184701914508585475304431352949988726937945291L
n = p*q
pl = 6915497960347690034670190613920812775692185594434656280771177353647840352326299910999240632820L

def f(g):
    for x in list(g):
        assert(x != 0)
        a, b = modular_sqrt(x,p), modular_sqrt(x,q)
        if a == 0 or b == 0:
            return
        yield chinese_remainder([p,q], [a, b], 2)
        yield chinese_remainder([p,q], [p-a, b], 2)
        yield chinese_remainder([p,q], [a, q-b], 2)
        yield chinese_remainder([p,q], [p-a, q-b], 2)

import binascii
def s(y):
    z = '{0:x}'.format(y)
    if len(z) % 2:
        z = '0'+z
    return binascii.unhexlify(z)
l0 = list(f(f(f(f(f([pl]))))))
st = int(binascii.hexlify('hitcon{'+43*'\0'),16)
end = int(binascii.hexlify('hitcon|'+43*'\0'),16)
for u in l0:
    print 'U', u
    assert(pow(u, e, n) == c)
    v = (st//n)*n+u
    while v < end:
        sv = s(v)
        if sv[-1] == '}':
            while v < end:
                for c2 in sv:
                    if ord(c2) > 127:
                        break
                else:
                    print sv
                v += 256*n
                sv = s(v)
        v += n

The flag was

hitcon{Congratz~~! Let's eat an apple pi <3.14159}

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}