WEBVERSE

Loading...

Remit
masterFree

Remit

Remit is an accounts-payable platform where suppliers send invoices and a finance team reviews them. A supplier's bank details were quietly changed and a payment went to the wrong account. Trace how someone reached the review console.

xxeblind-xxeout-of-bandooxmlphp-filterwaf-bypassmass-assignmentbroken-access-controlphp-object-injectioninsecure-deserializationrce
phpapachemysqlnginxlibxml

The Scenario

Remit runs accounts payable for a few dozen companies out of Columbus. Suppliers send invoices in, a finance team approves them in a separate review console, and payments go out. Last month an outsider rewrote a supplier's bank details on a live invoice and the payment went to the wrong account, and none of the staff with console logins can explain how anyone else got in. You have a plain supplier account. Find the path to the console.

Lab Intel

Synopsis

Remit spans a public supplier site, an AP review console on its own subdomain, and a shared internal report library baked into both. The supplier invoice ingest parses an XLSX-embedded UBL part with libxml external entities on, giving a blind XXE. It is deliberately hard: no reflection (blind), a byte-level DOCTYPE denylist bypassed by UTF-16, exfil only out-of-band to the player's VPN IP, and php://filter needed to read PHP source. The source read yields the mass-assignable role field, the custom console role value remit_reviewer, and the report POP gadget. Set role=remit_reviewer, log into the console, and feed a serialized gadget to the console's saved-view cookie (unserialize) to get RCE as the review service account.

Architecture

A master-difficulty PHP/Apache/MySQL lab on four containers (nginx gateway, a public supplier site, an AP review console, and mysql). The chain is a blind out-of-band XXE, mass assignment, and PHP object-injection RCE. The invoice upload accepts an .xlsx workbook and parses its embedded remit/ubl.xml part with DOMDocument using LIBXML_NOENT | LIBXML_DTDLOAD and no LIBXML_NONET, so an external DTD loads and entities substitute. The ingest is blind (it stores the invoice number and total and echoes nothing) and runs a byte-level preg_match denylist on <!DOCTYPE / <!ENTITY, which is bypassed by encoding the part UTF-16. Lab egress is dropped except back to the player's VPN IP, so exfiltration is out-of-band to a listener the player runs on their own tun0 address, using an external DTD with parameter entities and php://filter/convert.base64-encode to read PHP source. The prize is the shared /opt/remit/lib/Report/Archive.php gadget (an arbitrary file write in __destruct) plus the web account handler, whose mass-assignment allowlist mistakenly includes role. The attacker self-registers a supplier, sets role=remit_reviewer via POST /account (the console rejects the guessable admin and reviewer values, so the XXE source read is required to learn the real value), and logs into review.remit.local. There the review queue restores a saved-view cookie with unserialize(base64_decode(...)), so a serialized Remit\Report\Archive deserializes and its __destruct writes a PHP webshell into the review webroot. The RCE runs as the non-root avery user (its own group, never group root) and reads the flag from /home/avery/flag.txt. There is no INTO OUTFILE / FILE grant; the object-injection write is the only path to RCE.

Who It's For

Advanced testers comfortable chaining a blind out-of-band vulnerability with backend exploitation. You should understand XXE (including parameter entities and external DTDs), how to run an OOB listener over the VPN, the php://filter stream wrapper, OOXML internals, mass assignment, and PHP object-injection / POP chains. This is a top-tier lab that stacks several of those at once.

Skills You'll Practice

  • Blind out-of-band XXE with external DTDs and parameter entities
  • Bypassing a byte-level XML denylist with UTF-16 encoding
  • Reading source through php://filter/convert.base64-encode
  • Mass assignment to escalate an account role
  • Building a PHP object-injection (POP) chain and triggering it through an unserialize sink

What You'll Gain

  • Why external entity loading plus any parsed user XML is a full-read primitive
  • How blind XXE becomes exfiltration when the only egress is your own listener
  • Recognising that an object-injection gadget in shared code is the real prize of a source read
  • Turning an unserialize of a client-controlled token into RCE with a POP gadget

Ready to hack Remit?

This lab is free. Sign up and start hacking.