saiger.dev

CTF Writeup - GlacierCTF25 - GlacierProfile

Published: 2025-11-23 − Reading Time: 3.5 minutes

Glacier Profiler

https://github.com/LosFuzzys/GlacierCTF2025_writeups/tree/main/web/glacier-profile

TLDR;: You can access the SPX UI by leaking the password through phpinfo(). Furthermore, you can side channel the password by counting the call counters in the SPX UI.

Overall Overview

When visiting the site you can see a list of profile pictures. At the bottom of the page you have form, where you can enter a code to gain access to the admin section.

When investigating the Dockerfile we can see that a PHP module is loaded, called php-spx

Dockerfile

[...]
RUN cd /tmp && git clone https://github.com/NoiseByNorthwest/php-spx.git && cd /tmp/php-spx && phpize && ./configure && make && make install
[...]

php-spx is a profiling tool for investigating function calls and their performance. There is also a spx.ini file provided:

extension=spx.so
spx.http_enabled=1
spx.http_key=DOCKERFILE_OVERWRITES_THIS_KEY
spx.http_profiling_enabled=1
spx.http_profiling_auto_start=1
spx.http_ip_whitelist="*"

However, the http_key is overwritten in the Dockerfile with a random string:

[...]
RUN sed -i -e "s/DOCKERFILE_OVERWRITES_THIS_KEY/`tr -dc A-Za-z0-9 </dev/urandom | head -c 16`/g" $PHP_INI_DIR/conf.d/spx.ini
[...]

Exploit Stage 1 - Accessing Profiling Tool

In the first step, you need to gain access to the SPX UI. When investigating the functions.php, we have a handleDbg, giving us informations about the PHP configuration.

function handleDbg() {
  $action = filter_input(INPUT_POST, "action", FILTER_DEFAULT); 
  if($action !== "dbg") return;
  phpinfo();
}

So you can extract the spx key with the following request:

$ curl http://localhost:1337 -X POST -d 'action=dbg' | grep spx.http_key

So given the spx.http_key, we can now access the Web UI. Regarding the php-sdx README, you can access it with:

http://<DOMAIN>/?SPX_KEY=<KEY>&SPX_UI_URI=/

If you scroll down, you can see that for each web request you get a profiling report if you click on it. Also note, that you have Recorded calls for each web request indicating how many function were called.

Note: Note that the Recorded Calls only records function calls defined by you in the PHP script. Internal functions like e.g. strlen are not counted!

Exploit Stage 2 - Leak Password

Let's investigate the check_password a bit:

function check_password($pw, $hp) {
  if(strlen($pw) != strlen($hp)) return false;
  for($i = 0; $i < strlen($pw); $i++) {
    if(!check_char($pw[$i], $hp[$i]))
      return false;
  }
  return true;
}

function check_char($a, $b) {
  return $a == $b;
}

First, let's try to login with an empty password. You'll then see in the SPX UI a request to POST / and recorded calls: 8

So, we get stuck at this code line here:

if(strlen($pw) != strlen($hp)) return false;

Leak Password Length

Note: Given the Dockerfile you can see, that the password length has to be 32, but I'll explain it for the sake of completeness

RUN tr -dc A-Za-z0-9 </dev/urandom | head -c 32 > /rcon.pw

In order to determine the password length, you can perform logins with arbitrary passwords of different lengths. Whenever we pass this check, we will enter the for loop, and call check_char, which increases our counter.

Password Length   Recorded Calls
1                 8
2                 8
3                 8
[...]             8
31                8
32                9

Ok, so as the recorded call for a password with length 32 is 9, we must have reached check_char, so we know our password length.

Leak Password

The idea is similar to leaking the password length. You start with the password a0000000000000000000000000000000 and measure the recorded calls. If it is 9, you progress with the next character e.g. b0000000000000000000000000000000. If it is 10, then you know you have found the character, as you went in to check_char again. So you proceed with the next character ba000000000000000000000000000000 and repeat the step above. If the recorded call gets to 11, you have found the next character and continue with the next one until you leaked the password.

After you leaked the password, you can login in and retrieve the flag.

Implementation

You can see the implementation here