Skip to main content
  1. Write-ups/
  2. CTF Write-ups/

API Attack Skill Assessment

·548 words·3 mins·

We have a web application that should be secured than v0 and v1, and the question said that Submit the contents of the flag at '/flag.txt'. After seeing that the flag is in /flag.txt, I thought about searching for SSRF to LFI. This enable me to know the local files paths and get the flag.

  • First I signed in using the credentials provided htbpentester@hackthebox.com:HTBPentester as a customer.
  • Then I want to check the roles to see what I actions I can do using these roles, so navigating to /api/v2/roles/current-user:
{
  "roles": [
    "Suppliers_Get",
    "Suppliers_GetAll"
  ]
}
  • I went to supplier section to check what actions I can do and data I can get.

  • Sending a request to /api/v2/suppliers we will get some PII: Screenshot 2026-06-26 020014

  • Some emails and security questions and such, save it and continue.

  • Still searching for endpoint enable me to upload a file to know the path of the files, I found /api/v2/suppliers/current-user/cv endpoint and tried to upload a shell.php file but give me unauthorized.

  • I really tried all endpoints that accept files to test but all of them give me unauthorized and need suppliers’ roles, so I got back to the data I have to find how can I use them, can I get a supplier email from them ??!

  • The data provide email and security question, if you scrolled the file you find that some of them has a security question so I used AI to filter me the emails that have a security question What is your favorite color? and this give me some emails.

  • If you noticed the authentication section you will find an endpoint that resets passwords using security questions –> /api/v2/authentication/suppliers/passwords/resets/security-question-answers and requires three parameters:

{
  "SupplierEmail": "string",
  "SecurityQuestionAnswer": "string",
  "NewPassword": "string"
}
  • That’s greatttt. let’s brute forcing this.

  • Using the filtered emails and created wordlist with the all possible colors I used burp intruder with cluster bomb atatack which try all emails with all colors and typed true in Grep Match option to match the correct true credentials. Screenshot 2026-06-26 020014

  • We get a valid one, the next step is to sign in –> new JWT –> Authenticate.

  • After doing this, I checked the available roles for this user:

{
  "errorMessage": "User does not have any roles assigned"
}

That’s Interesting!!!

  • What ever, let’s check the endpoints which accept uploading files again, and I find this one in supplier too –> POST /api/v2/suppliers/current-user/cv and requires no roles too: Screenshot 2026-06-26 020014

  • I uploaded a random pdf file and done it’s successfully uploaded: Screenshot 2026-06-26 020014

  • If we sent a request to GET /api/v2/suppliers/current-user we will find our file is saved. Screenshot 2026-06-26 020014

  • Now I want to update the file path to point to our flag, so I made a request to PATCH /api/v2/suppliers/current-user/cv and typed ../../../../flag.txt. NOTE: I used path traversal because I don’t know the exact path of the flag so we want to make sure that the flag will save in our cv data successfully.

  • These parameters are required to update the file:

{
  "SecurityQuestion": "string",
  "SecurityQuestionAnswer": "string",
  "ProfessionalCVPDFFileURI":
  file:///app/wwwroot/SupplierCVs/../../../flag.txt,
  "PhoneNumber": "string",
  "Password": "string"
}

The others we can answer some with any random data, and done it’s updated. image

  • Our Flag is finally here, we just want to view it.
  • Sending a request to GET /api/v2/suppliers/current-user/cv we will get out flag base64 encoded: image

JUST DECODE IT AND DONNEEEEE ;)

db1M
Author
db1M
Mohanad Edrees — penetration testing enthusiast and CTF player.

Related

GraphQL Skill Assessment

·883 words·5 mins
First explored the request and decided to see if the GraphiQL endpoint is enabled. I started my test by sending an introspection query to know the relations between objects and queries.

File Inclusion Skill Assessment

·761 words·4 mins
The challenge description: Assess the web application and use a variety of techniques to gain remote code execution and find a flag in the / root directory of the file system. Submit the contents of the flag as your answer.

File Upload Skill Assessment

·560 words·3 mins
Inspecting the webapp and the functionalities found a submit flag functionality. I read the JS and the HTML codes to see the validation of the frontend, noticed that only images extensions are allowed. I uploaded a .png file and see the request in the burp. it was a GET request and it supposed to be a POST request in uploading a file, so I inspect the code to see the right place to upload files and found in the JS file the correct is to send to the endpoint: contact/upload.php I asked AI to create me a curl request to send a POST request to this endpoint. I fuzzed to see the allowed extension and the allowed content type too and found that svg and images/svg+xml are allowed. So I thought about upload a svg file with XXE payload to craft gain the source code of the backend to know the logic, blacklist, and the whitelist of the uploading function. curl -s -X POST "154.57.164.61:30775/contact/upload.php" \ -F "uploadFile=@/dev/stdin;filename=shell.svg;type=image/svg+xml" \ <<< '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/contact/upload.php">]> <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500"> <text y="20">&xxe;</text> </svg>' The parameters of the payload got from the HTML code inspection: <input name="uploadFile" id="uploadFile" type="file" class="custom-file-input" id="inputGroupFile02" onchange="checkFile(this)" accept=".jpg,.jpeg,.png"> We got the source code encoded, after decoded, we knew the logic: <?php require_once('./common-functions.php'); // uploaded files directory $target_dir = "./user_feedback_submissions/"; // rename before storing $fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]); $target_file = $target_dir . $fileName; // get content headers $contentType = $_FILES['uploadFile']['type']; $MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']); // blacklist test if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) { echo "Extension not allowed"; die(); } // whitelist test if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) { echo "Only images are allowed"; die(); } // type test foreach (array($contentType, $MIMEtype) as $type) { if (!preg_match('/image\/[a-z]{2,3}g/', $type)) { echo "Only images are allowed"; die(); } } // size test if ($_FILES["uploadFile"]["size"] > 500000) { echo "File too large"; die(); } if (move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $target_file)) { displayHTMLImage($target_file); } else { echo "File failed to upload"; } i want to get the flag then The code tells us the following: 1. Blacklist: blocks .php .phps .phtml 2. Whitelist: filename must end in [a-z]{2,3}g → jpg, png, svg, jpeg 3. Content-Type: must match image/[a-z]{2,3}g → image/jpg, image/png, image/svg, image/jpeg 4. MIME: same regex check on actual file content 5. Upload dir: ./user_feedback_submissions/ 6. The file is stored in the database with a unique schema --> DATE_Filename I noticed that the .phar extension not in the blacklist so I can use in uploading a shell which can allow execution of a PHP code. The vulnerability of the whitelist is that it only checks the end of the filename extension has a g so using jpg with double-extension technique will bypass the filters. To ensure the attack implemented successfully, we should put a jpg signature to bypass the MIME check –> \xff\xd8\xff\ ![[Pasted image 20260607022513.png]] So the full request will be: ![[Pasted image 20260607022051.png]] The shell is uploaded and now we want to visit it, after some struggling with the data I reached the true path: http://154.57.164.61:30775/contact/user_feedback_submissions/260606_shell.phar.jpg?cmd=ls The flag isn’t in flag.txt as I tried before catch it with the same way I get the source code, so I knew that I needed an RCE. I search about flag in the entire system: find / -name 'flag*' -o -name '*flag*' -o -name '*.flag' 2>/dev/null The flag in flag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt , just read it.