Disclaimer: This article is a personal technical study note. Any operations described should only be performed in authorized environments. Users bear full responsibility for any misuse.
During the annual Alibaba Cloud security competition, the FakeJumpServer challenge was the one I spent the most time on: I had a hint and still did not solve it within the event window. In hindsight the whole challenge is built around a stacked-query injection whose entry point sits in the SSH authentication flow.
FakeJumpServer
Connecting over SSH and reading the banner shows that the endpoint only imitates an SSH service; the scenario itself is modelled on a bastion host of the kind found in real internal networks. The organizers published the background of the scenario.

Background article for this challenge: https://developer.aliyun.com/article/1161775
The root cause is that the credentials submitted to the fake SSH service are interpolated into SQL statements without parameterization, and the driver accepts stacked statements. Whether a given input actually reaches the query can be decided by timing: an input that instructs the backend to wait for several seconds (pg_sleep-style) produces a measurable delay, which is enough to confirm the injection point. Here the injection point is the password field.

What makes the challenge worth recording is the escalation from SQL injection to command execution. Three PostgreSQL capabilities are relevant. pg_read_file and pg_ls_dir read arbitrary files from the database host, and COPY ... FROM PROGRAM executes an operating-system command as the account running the database process. All three are restricted to superusers precisely because they cross the boundary between the database and the host, so an application that connects as a superuser turns any injection into host compromise. In this challenge the escalation was completed by writing a small script to a temporary path and executing it through the same mechanism; the concrete statement sequence and the Python driver that fed it over the SSH flow are omitted here, since they are a ready-to-run recipe against a real database product.
The more useful conclusion for defenders is that every step of that chain depends on the application’s database role. Parameterizing the credential lookup, or connecting as a role without superuser rights, would have reduced a complete compromise to a failed login.

JTools
This challenge was reproduced after the competition; only three solutions were submitted during the event. The bundled source deserializes a data field, so the task reduces to reaching that deserialization with controlled bytes.

The stack is Apache Fury, a comparatively new serialization framework with few public analyses of its own, plus a utility library named feilong that is pulled in transitively.
feilong bundles Hutool, and cn.hutool.core.convert.impl.BeanConverter#convertInternal converts a Map into an instance of a target bean class without verifying that the Map content actually matches the declared type. MapProxy builds a dynamic proxy whose invoke triggers that conversion, which yields a reachable path from the deserialized Map to a bean conversion.


The remaining piece is a long-known JDK-side gadget rather than anything specific to this framework: TemplatesImpl carrying attacker-supplied bytecodes, combined with a PriorityQueue whose entries are ordered by an attacker-chosen property name, so that comparing two entries calls into the templates object. feilong’s PropertyComparator performs exactly that property lookup, and the deserializer rebuilds the proxy objects that sit in the queue.
The full exp builder is omitted here. What is worth keeping is the shape of the chain: a deserialization entry point, a component that silently converts untyped maps into typed beans, and a JDK gadget at the end. Each of the three is defensible on its own; only the combination is exploitable.
https://xz.aliyun.com/news/17029
Offens1ve
A similar scenario has appeared on HackTheBox before; the variant here changes the entry point. The environment is a small Active Directory lab whose addresses fall in a documentation range, so the hosts file is configured as follows.
1 | 192.0.2.20 oa.offensive.local |
The setup consists of an ADFS single sign-on in front of an internal monitoring system, an ADFS host whose configuration database can be exported from one of the monitoring pages, and a domain controller that answers LDAP queries.


The single sign-on endpoint is reached through a WS-Federation sign-in request on the STS host (https://sts.offensive.local/adfs/ls/?wtrealm=...&wa=wsignin1.0), and the monitoring application is published on port 8080.
https://monitor.offensive.local:8080/Monitor/ADFS01

On the domain controller a plain LDAP query returns several user objects, which shows that the directory can be enumerated without credentials.
1 |
|

ADFS signs its tokens with material derived from the DKM (Distributed Key Manager) key, which is stored in the domain configuration partition and protected by the ADFS service account. Exporting the ADFS configuration database is therefore equivalent to obtaining the root of trust for every token the federation issues, and the natural next step is to attempt key recovery and offline token forging. That is the point of the challenge, and it is also why an over-broad export capability deserves to be treated as a critical finding in its own right: it hands over the root of trust rather than a single query.
A practical note on the defensive side: the ADFS configuration container should be readable only by the federated service account and tier-0 administrators, the export function of an internal monitoring tool should not run with domain-wide read rights, and anonymous LDAP binds should be disabled on every domain controller.
防御启示
- Parameterize at the boundary: build credential lookups with prepared statements, and rely on a driver/protocol mode that does not accept stacked statements. This alone removes the entire FakeJumpServer chain, because the injection point disappears rather than being filtered.
- Keep database roles least-privileged: the application account must not be a superuser or a member of
pg_execute_server_program. Features such asCOPY ... FROM PROGRAM,pg_read_fileandpg_ls_dirare superuser-only by design; alert on their use in the audit log (pgaudit,log_statement) as they are never routine in an application workload. - Treat deserialization as a boundary: do not deserialize untrusted bytes, and when a framework must be used, enable its class-registration/whitelist mode. Watch for the specific pattern seen here — a component that converts untyped maps into typed beans is a generic bridge from data to gadget invocation, not a framework-specific bug.
- Harden identity infrastructure first: the DKM material behind ADFS token signing is a tier-0 secret. Restrict read access to the ADFS configuration container, keep its export capability out of ordinary monitoring tools, rotate keys with documented procedures, and disable anonymous LDAP binds so that directory enumeration requires credentials.
- Separate management planes: monitoring and administration interfaces should not sit on the same network segment as user-facing applications, should require administrator-tier authentication, and should log every action with the authenticated identity that performed it.