Skip to main content
XsiSec.com
HomeReposBlogProjectsPortfolio
© 2026 XsiSec.com
Security rules |security.txt
Updated 2026-08-22 · v1.0.0+2026-08-22.8585a7b · 8585a7b
← Back to overview
Security article

HTB Academy broken API:s challange #3

HTB Academy broken API:s challange #3: Work through a simple REST‑style API that exposes table/row paths and supports CRUD over HTTP. Use curl + jq to read, create, update, and delete entries — and finish by renaming a city to flag and retrieving the flag. • HTB Academy • crud, curl

2022-09-292 tags
Tags

🎯 Objective

Work through a simple REST‑style API that exposes table/row paths and supports CRUD over HTTP. Use curl + jq to read, create, update, and delete entries — and finish by renaming a city to flag and retrieving the flag.


🧩 Quick refresher — APIs & CRUD

Some APIs map database entities directly into URL paths, e.g. /<table>/<row-id>, and rely on HTTP methods to choose the operation.

Example endpoint pattern

text
/api.php/<table>/<row>

CRUD mapping

OperationMethodWhat it does
CreatePOSTAdds a new row
ReadGETFetches rows
UpdatePUTReplaces a row (full update)
(Partial)PATCHModifies fields (partial update)
DeleteDELETERemoves a row

Not all APIs behave exactly the same (authZ rules, PATCH vs PUT semantics, etc.). Many require cookies or an Authorization header (e.g., JWT) to write.


🔎 Read

Fetch a single city by key (nice‑print with jq):

bash
curl -s http://<SERVER_IP>:<PORT>/api.php/city/london | jq
# [
#   { "city_name": "London", "country_name": "(UK)" }
# ]

Prefix search (returns all matches):

bash
curl -s http://<SERVER_IP>:<PORT>/api.php/city/le | jq
# [
#   { "city_name": "Leeds", "country_name": "(UK)" },
#   { "city_name": "Dudley", "country_name": "(UK)" },
#   { "city_name": "Leicester", "country_name": "(UK)" },
#   ...
# ]

List all entries (empty tail element after city/):

bash
curl -s http://<SERVER_IP>:<PORT>/api.php/city/ | jq
# [ { "city_name": "London", ... }, ... ]

➕ Create

POST JSON and set content type:

bash
curl -X POST http://<SERVER_IP>:<PORT>/api.php/city/   -d '{"city_name":"HTB_City","country_name":"HTB"}'   -H 'Content-Type: application/json'

Verify:

bash
curl -s http://<SERVER_IP>:<PORT>/api.php/city/HTB_City | jq
# [
#   { "city_name": "HTB_City", "country_name": "HTB" }
# ]

✏️ Update

Full replacement with PUT (specify target row in the URL):

bash
curl -X PUT http://<SERVER_IP>:<PORT>/api.php/city/london   -d '{"city_name":"New_HTB_City","country_name":"HTB"}'   -H 'Content-Type: application/json'

Confirm old vs new:

bash
curl -s http://<SERVER_IP>:<PORT>/api.php/city/london | jq
curl -s http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City | jq
# [ { "city_name": "New_HTB_City", "country_name": "HTB" } ]

Some APIs “upsert” on PUT (create if missing). In this lab, updating a non‑existing key returns empty/no-op — try it and observe.


🗑️ Delete

Delete by key:

bash
curl -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City
curl -s http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City | jq   # => []

Tip – include status code in output

bash
curl -sw '%{http_code}
' -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/boston
curl -sw '%{http_code}
' -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/london
curl -sw '%{http_code}
' -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/baltimore

Screenshots (my run): update-to-flag delete-cities count-cities


🏁 Challenge — rename to flag and find it

  1. Update a city’s name to flag (I used “Boston”):
    bash
    curl -X PUT http://134.209.21.176:32526/api.php/city/boston      -d '{"city_name":"flag","country_name":"HTB"}'      -H 'Content-Type: application/json'
  2. Delete a couple cities (optional cleanup):
    bash
    curl -sw '%{http_code}
    ' -X DELETE http://134.209.21.176:32526/api.php/city/boston
    curl -sw '%{http_code}
    ' -X DELETE http://134.209.21.176:32526/api.php/city/london
    curl -sw '%{http_code}
    ' -X DELETE http://134.209.21.176:32526/api.php/city/baltimore
  3. Search for flag:
    bash
    curl -s http://134.209.21.176:32526/api.php/city/flag | jq
    flag-found

🧪 Troubleshooting

  • Empty array? Key doesn’t exist; check path/case; try listing all and grep.
  • 415/400 errors? Wrong/missing Content-Type: application/json or malformed JSON.
  • 401/403? API requires auth (cookie/JWT). Re-run with -H 'Authorization: Bearer <jwt>' or -b 'session=<cookie>'.
  • PUT vs PATCH: Server may accept only one; use OPTIONS or check docs.
  • Order of fields: JSON is unordered — server shouldn’t care.

📎 Handy one‑liners

bash
# Pretty-print JSON and fail on HTTP errors
curl -fsS http://host/api.php/city/ | jq

# Show only HTTP code
curl -o /dev/null -s -w '%{http_code}
' http://host/api.php/city/london

# Create, then immediately read back
curl -sS -X POST http://host/api.php/city/   -H 'Content-Type: application/json'   -d '{"city_name":"TMP","country_name":"XX"}'   && curl -sS http://host/api.php/city/TMP | jq

✅ Result

I exercised the full CRUD path with curl + jq, verified status codes, and completed the task by renaming a city to flag and retrieving it.

Comments

Comments

Loading comments…

Navigate

In this post

  1. 01🎯 Objective
  2. 02🧩 Quick refresher — APIs & CRUD
  3. 03🔎 Read
  4. 04➕ Create
  5. 05✏️ Update
  6. 06🗑️ Delete
  7. 07🏁 Challenge — rename to flag and find it
  8. 08🧪 Troubleshooting
  9. 09📎 Handy one‑liners
  10. 10✅ Result
Search
Explore

Popular tags

Browse all 30 tags