Batch statement injection

CQL groups several write operations into one atomic unit with BEGIN BATCH ... APPLY BATCH. Where user input is concatenated into a statement that is, or can be made into, a batch, an attacker smuggles extra INSERT, UPDATE, or DELETE writes alongside the one the application intended.

Why batches matter

Most Cassandra drivers reject multiple semicolon-separated statements in a single execute, so the classic SQL stacked-query trick (; DROP ...) usually fails. A batch is the sanctioned way to run several writes in one statement, and it is a single statement as far as the driver is concerned. If injection can reach the body of a batch, or turn a single write into one, additional writes ride along without needing statement stacking.

Injecting into an existing batch

Applications sometimes build a batch by concatenating per-item strings in a loop:

cql = "BEGIN BATCH\n"
for item in items:
    cql += "INSERT INTO cart (user, sku) VALUES ('%s','%s');\n" % (user, item)
cql += "APPLY BATCH;"
session.execute(cql)

An item value of:

x'); INSERT INTO users (id, role) VALUES ('attacker','admin') //

closes the current INSERT, adds a chosen one, and ends with a // line comment (CQL also accepts -- and /* */) to swallow the ') the template still appends. It expands to:

BEGIN BATCH
INSERT INTO cart (user, sku) VALUES ('alice','x'); INSERT INTO users (id,role) VALUES ('attacker','admin') //');
APPLY BATCH;

The comment discards the leftover ');, the smuggled INSERT writes an admin row, and the template's own APPLY BATCH terminates the block. Because statements inside a batch are separated by ; and the whole block is one driver statement, this works where standalone stacked queries do not.

Forming a batch from a single write

Where the sink is a single INSERT or UPDATE rather than an existing batch, injection can wrap it. Given:

q = "INSERT INTO audit (actor, note) VALUES ('%s','%s')" % (actor, note)

a crafted value can close the first write and append a second, provided the driver accepts the combined form as a batch. Prefixing the statement is rarely reachable, so the practical target is an application that already uses BEGIN BATCH, or an UPDATE whose SET or WHERE is injectable.

Overwriting and deleting

Inside a reachable batch, the full write surface is available:

'); UPDATE users SET role='admin' WHERE id='victim'
'); DELETE FROM sessions WHERE id='victim'
'); INSERT INTO users (id,password) VALUES ('bd','...')

An UPDATE in CQL is an upsert, so an UPDATE to a non-existent primary key creates the row. This makes UPDATE a dependable way to both modify an existing record and plant a new one. Batches also accept a USING TIMESTAMP clause, which can be set to a high value so the smuggled write wins conflict resolution against later legitimate writes:

'); UPDATE users USING TIMESTAMP 99999999999999 SET role='admin' WHERE id='victim'

Conditions and limits

Logged batches span partitions but are not isolated transactions, and lightweight-transaction conditions (IF) apply per statement. For injection the key fact is simpler: anything the application's database role is permitted to write, a smuggled batch statement can also write. Mapping that role's privileges, via Error-based schema probing, tells you which tables are reachable before you craft the extra writes.

References

Cookie Consent

We use cookies to enhance your experience. Learn more