0x01 Preface
I recently read P's discussion of delimiter security and a Django security advisory, so I took the opportunity to study delimiter-related vulnerabilities.
0x02 Delimiters
A delimiter marks boundaries. Suppose the delimiter is//, then//panda//means the computer starts at the first//begins, andpandastring, and ends at the next//end. Delimiters take many forms across languages: separators, comment markers, and more. In PHP, for example,<<<as a delimiter; MySQL's default statement delimiter is; . Python delimiter examples appear in the table below:
| ( | ) | [ | ] | { | } |
|---|---|---|---|---|---|
| , | : | . | ` | = | ; |
| += | -= | *= | /= | //= | %= |
| &= | |= | ^= | >>= | <<= | **= |
Delimiters are used widely, which is exactly why they can create security vulnerabilities.
0x03 Security Vulnerabilities Caused by Delimiters
1. Authentication Bypass
Suppose the PHP code is:
SELECT * FROM Users WHERE ((Username ='$username') AND (Password=MD5('$password')))
This is clearly login-validation code. Without checks for special characters—especially delimiters—authentication may be bypassed. In this example, set:
username = 1' or '1' = '1')) /*
password = test
The value passed into the SQL statement should therefore be:
SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND (Password=MD5('$password')))
This keeps the condition true and bypasses authentication.
2. Privilege Escalation
See this vulnerability:CVE-2003-1350 . If registration fields are insufficiently filtered and the application's database delimiter can be supplied, privilege escalation may result. List Site Pro uses |to delimit database fields without validating delimiters in user input, allowing an attacker to change any account's password.
A similar flaw also appeared in Poster V2, which contains a file namedmem.php, which stores user fields such as username, password, email, and privileges. It manages application users; an example follows:
<? panda|12345678|panda@cnpanda.net|admin| test|5211314|test@test.com|normal| ?>
panda is an administrator and test a normal user. When editing a profile, use index.phpOpen Edit Account and enter the user's login information.
The file example shows that its delimiter is|. If edited profile content is not filtered, a user can elevate their privileges to administrator through the edited field:
Username: test Password: 5211314 Email: test@test.com |admin|
When this information is stored inmem.phpfile, it becomes:
test|5211314|test@test.com|admin|normal|
This achieves privilege escalation. Applications rarely store user records in files this way anymore, but the technique remains instructive.
3. SQL Injection
A representative case is Django SQL injection, CVE-2020-7471. On February 3, 2020, Django published an advisory stating thatdjango.contrib.postgres.aggregates.StringAggThe aggregate function is vulnerable. A crafted delimiter enables SQL injection. Locate the function:
class StringAgg(OrderableAggMixin, Aggregate):
function = 'STRING_AGG'
template = "%(function)s(%(distinct)s%(expressions)s, '%(delimiter)s'%(ordering)s)"
allow_distinct = True
def __init__(self, expression, delimiter, **extra):
super().__init__(expression, delimiter=delimiter, **extra)
def convert_value(self, value, expression, connection):
if not value:
return ''
return value
The documentation explains the function as follows:
class StringAgg(expression, delimiter)
Returns the input values concatenated into a string, separated by the delimiter string.
delimiter
Required argument. Needs to be a string.
Put simply, the user supplies a delimiter and the function joins queried or supplied values with that custom delimiter.
For example, set the delimiter to-, with this table:
| uid | username | private |
|---|---|---|
| 1 | panda | admin |
| 2 | test | normal |
| 3 | hello | normal |
| 4 | world | normal |
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", '-') AS "mydefinedname" FROM "djsqltest_info" GROUP BY "djsqltest_info"."gender" LIMIT 1 OFFSET 1 --
Querying by private and aggregating username displays this result in Django:

{'private':'admin','username':'panda'}
{'private':'normal','username':'test-hello-world'}
The advisory states that a particular delimiter causes injection. Fuzzing identifies the single quote; setting it as the delimiter produces an error:

The query is:
SELECT "test_sql_userinfo"."private", STRING_AGG("test_sql_userinfo"."username", \'\'\') AS "username" FROM "test_sql_userinfo" GROUP BY "test_sql_userinfo"."private"
The supplied delimiter is escaped as\', which reaches PostgreSQL as:
SELECT "test_sql_userinfo"."private", STRING_AGG("test_sql_userinfo"."username", ''') AS "username" FROM "test_sql_userinfo" GROUP BY "test_sql_userinfo"."private"
Three single quotes make the SQL invalid. Because the supplied delimiter reaches the SQL statement, a carefully chosen delimiter may enable injection.
The vulnerability can now be demonstrated:
Define a database named django_sql
There is a table namedtest_sql_userinfo, containing:

There is a table namedsql_admin, containing:

Under normal conditions, set the delimiter to:-, producing:

But if the delimiter is:') AS "uname" FROM "test_sql_admin" group by "test_sql_admin"."id"--
The result is:

Other data is extracted successfully.
Although the chance of controlling this injection is small, it remains an SQL injection vulnerability and a classic delimiter-related case.
4. Denial of Service
In CVE-2008-5185, a delimiter fails to close a tag, causing an infinite loop and denial of service. The central code is:
function parse_code () {
...
$code = str_replace("\r\n", "\n", $this->source);
$code = str_replace("\r", "\n", $code);
// Add spaces for regular expression matching and line numbers
$code = "\n" . $code . "\n";
...
if ($this->strict_mode) {
// Break the source into bits. Each bit will be a portion of the code
// within script delimiters - for example, HTML between < and >
$parts = array(0 => array(0 => '', 1 => ''));
$k = 0;
for ($i = 0; $i < $length; ++$i) {
foreach ($this->language_data['SCRIPT_DELIMITERS'] as $delimiters) {
foreach ($delimiters as $open => $close) {
// Get the next little bit for this opening string
$open_strlen = strlen($open);
$check = substr($code, $i, $open_strlen);
// If it matches...
if ($check == $open) {
// We start a new block with the highlightable
// code in it
++$k;
$parts[$k][0] = $open;
$close_i = strpos($code, $close, $i + $open_strlen) + strlen($close);
if ($close_i === false) {
$close_i = $length - 1;
}
$parts[$k][7] = substr($code, $i, $close_i - $i);
$i = $close_i - 1;
++$k;
$parts[$k][0] = '';
$parts[$k][8] = '';
// No point going around again...
continue 3;
}
}
}
// only non-highlightable text reaches this point
$parts[$k][9] .= $code[$i];
}
}
...
Focus on the loop:$this->language_data['SCRIPT_DELIMITERS']defines an opening marker such as<) and an ending marker such as>) and assigns those characters to$openand$close, then reads the code block's first character and searches for an opening marker. If found, it evaluates:
$close_i = strpos($code, $close, $i + $open_strlen) + strlen($close);
$closeis the ending marker. If the block contains no ending marker, then$close_ibecomes 1 and is passed to: $i = $close_i - 1;, then$ibecomes 0, so the loop restarts indefinitely and causes denial of service.
0x04 Conclusion
Beyond these examples, delimiters can also cause code execution, as in CVE-2007-5178, but the source is too old to locate for analysis. In a broader sense, DOM XSS and CRLF injection can also be viewed as delimiter flaws. This is my interpretation; other techniques are welcome for discussion.
0x05 References
https://www.codeleading.com/article/8787703343/
https://owasp.org/www-community/attacks/Parameter_Delimiter
https://www.djangoproject.com/weblog/2020/feb/03/security-releases/
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5185
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2003-1350