Skip to main content

SQL Injection Lab: Think Like a Hacker

Security is IT’s top spending priority according to the 2017/2018 Computer Economics IT Spending & Staffing Benchmarks report*. Given the frequent changes and updates in vendor platforms, the pressure is on for IT teams who need to keep their infrastructures and data secure. As breaches and attacks become more sophisticated, teams will need to get creative to stay ahead of the next threat, and this includes putting yourself in a hacker’s shoes to see your system and its vulnerabilities in a new light. Following our previous hacking lab using VirtualBox and Vagrant, in this post, I will focus on how to use the lab to practice SQL injection attacks. SQL injection is often used by hackers to exploit security vulnerabilities in your software to ultimately gain access to your site’s database.

As with our previous lab, I recommend downloading and using the Kali Linux VM as your attack platform.

So, fire up your lab, set the difficulty to medium, and head to the SQL injection page!

SQL Injection

SQL Injection Submit Form

On the SQL injection page, you are presented with a User ID number selection input and a submit button.

Clicking submit returns some information about the user with the user ID selected in the input. In the example here, it’s for the admin user with ID of 1.
SQL Injection View Source
Check out the HTML source for the page by right-clicking anywhere on the page and select “view source.” Scroll down until you find the form element.

Here’s how this page works: When you select a value in the drop-down menu and submit it, the value for the selected item is sent as a POST request to the current page.

Depending on who you ask, the great or lousy thing about websites is that there isn’t a way to prevent users from sending whatever data they want.

For this reason, developers should consider all data from the browser to be malicious. This includes things like cookie values and request headers, as well as inputs. I like to consider the user interface as a nice way to use the site, though not a requirement.

SQL Injection Inspect Element

There are a lot of different ways that you can edit a value posted to a web page. One way is to use the developer tools built into the browser.

Try it out for yourself. Right-click on the input and select “Inspect Element.”

It should open the developer tools window and show the select element in the document object model, or DOM. If not, you can click the arrow beside the select element and expand it to show the options.

SQL Injection Edit Element
Double-click on the “1” in the value attribute for the first option in the select element. Edit the value by adding a space and then a single quote after the 1.

Once you’ve changed it, make sure the drop-down is set to 1 and click submit. Now, instead of sending a 1  it will send 1 ‘  which should throw an error. Because single quotes are part of the SQL syntax, adding one should throw off the query.

Throw an error, please!

SQL Injection Error
In this case, the error is returned for us to see. While this is not a best practice for developers, it is great for attackers.
The error here is basically saying that the change made to the posted value broke the query. Knowing this is your starting point for figuring out how to inject your own SQL.

Knowing what SQL to inject will initially be difficult. However, the more you learn about SQL and application development, the easier it will become.

For this example, my guess is that the query being executed is something like the following:

SELECT id, first_name, last_name FROM users WHERE id = 1 LIMIT 1

Why? I can only search for one record at the time; however, there are three fields displayed. The fields are for the user’s first name, last name, and the ID. Also, it’s a common SQL practice to use lower snake case for column names.

Another common practice is to have the table name in front of the ID. In this case, the ID column might be something like “user_id.” However, I’m not ready to look at the source code to find out.

In this case, the 1  in the “where” clause is the ID that’s being passed in from the POST body. This means that we can include the ID and some SQL, and it will be executed.

To start testing this, edit the value attribute for the option element again, setting it to 1 OR 1=1  and then submit it. If the query is similar to how I suspect it will look, then the end result will be like the following:

SELECT id, first_name, last_name FROM users WHERE id = 1 OR 1=1 LIMIT 1

The where clause here will evaluate true for all rows since the number 1 will always equal 1. Regardless of whether this query works or fails, it should provide some insight into both the application and the query design.

If multiple users are displayed on the page, then I know that there isn’t a limit of one. I’ll also know that the app just loops over all of the returned records and displays them.

Drumroll, please…

SQL Injection 1=1
The results are interesting because there are multiple records that are being displayed. This is often just a bit easier to exploit because regardless of how many records are returned, they’ll be looped over and displayed.

Also, the ID for each record shows the value that I posted, which means that the query probably isn’t returning the ID. Now, it’s time to try and paint a clearer picture of the SQL query. To do that, set the value of the option element to 1 UNION ALL SELECT 1, 2

If this works, the query will return just two columns. If this fails, it should show an error indicating that the column count doesn’t match.
Testing how many columns in the query
It worked! This means that the query is returning two columns. This helps define the constraints, so it will make it easier for us to start creating queries.

The ID isn’t being fetched from the query, there doesn’t seem to be a limit, and there are only two columns. Therefore, my new hypothesis is that the query now looks something like this:

SELECT first_name, last_name FROM users WHERE id = 1;

Knowing this, I want to try and list all of the user tables to get an idea of what other data might be available. Set the value of the option element to:

1 UNION ALL SELECT table_schema,table_name FROM information_schema.tables where table_schema NOT IN ('mysql', 'information_schema', 'sys', 'performance_schema');

The error message returned wasn’t what I expected. Since I know the query should work, this makes me think that the posted ID is being passed through some function to sanitize the input.

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT table_schema,table_name FROM information_schema.tables where table_schema' at line 1

If the returned value is being sanitized, this means that any query with a single quote will fail. That’s only a problem for those who lack creativity.

I’ve created a crude bit of Python to be executed on the command line that will take a word as an argument and convert its ASCII value. Next, it wraps that in a char  function and concatenates the results.

python -c "import sys; print 'concat(' + ','.join(['char(' + str(ord(i)) + ')' for i in sys.argv[1]]) + ')'" someword

For example, passing in mysql  will return concat(char(109),char(121),char(115),char(113),char(108)) which MySQL will treat as the word mysql.

So the final query will be:

1 UNION ALL SELECT table_schema,table_name FROM information_schema.tables where table_schema NOT IN (concat(char(109),char(121),char(115),char(113),char(108)),concat(char(105),char(110),char(102),char(111),char(114),char(109),char(97),char(116),char(105),char(111),char(110),char(95),char(115),char(99),char(104),char(101),char(109),char(97)),concat(char(115),char(121),char(115)),concat(char(112),char(101),char(114),char(102),char(111),char(114),char(109),char(97),char(110),char(99),char(101),char(95),char(115),char(99),char(104),char(101),char(109),char(97)));

Successful query without quotes
Admittedly, that’s a pretty awful looking query, but it’s going for results rather than style. This returns a list of all the different user tables, although there aren’t many in this example.

Now that this is working, I want to try something a bit more interesting. This time, I will try to list off any MySQL usernames and password hashes.

For that, I’m going to set the value attribute to:

1 UNION ALL SELECT user, authentication_string FROM mysql.user

Running this is interesting since it returns results that include the username and password hash for MySQL users. With this, you could now take the results offline to crack them using a password cracker.

SQL Injection Union Users

All good things must come to an end

While there’s a lot more to do here, I’ll leave you here and let you keep learning on your own.

Don’t forget, user interfaces are just suggestions and errors let you know that you’ve found something interesting. If you want to explore SQL further, you can read another of our blog posts on How To Migrate Your SQL Server Database to Amazon RDS.

Happy hacking, and thanks for reading!

*Source: 2017/2018 Computer Economics IT Spending & Staffing Benchmarks report

Written by

Ben Lambert

Ben is a software engineer with years of experience building web and mobile apps. He learned about DevOps some time ago, and hasn’t stopped talking about it since. In addition to DevOps, he’s passionate about information security, as well as virtual and augmented reality systems. When he’s not working he’s hiking, camping, or creating video games.

Related Posts

— February 7, 2019

Measuring DevOps Success: What, Where, and How

The DevOps methodology relates technical and organization practices so it's difficult to simply ascribe a number and say "our organization is a B+ on DevOps!" Things don't work that way. A better approach identifies intended outcomes and measurable characteristics for each outcome. Let'...

Read more
  • DevOps
— February 5, 2019

2019 DevOps and Automation Predictions

2019 DevOps and Automation PredictionsWe recently released our 2019 predictions for cloud computing and are doing the same here for DevOps and automation predictions.2018 was a great year for software, and DevOps falls somewhere on the slope of enlightenment on the Gartner Hype Cy...

Read more
  • DevOps
— January 17, 2019

Testing Through the Deployment Pipeline

Automated deployment pipelines empower teams to ship better software faster. The best pipelines do more than deploy software; they also ensure the entire system is regression-free. Our deployment pipelines must keep up with the shifting realities in software architecture. Applications a...

Read more
  • DevOps
— December 27, 2018

DevOps and Agile: Understanding the Relationship

Agile development used to be front and center in the conversation about software development. Now, DevOps has taken over the conversation. How do agile and DevOps relate? Both ideas began as ways to improve different aspects of software development. Agile embraced the changing nature of...

Read more
  • DevOps
— December 12, 2018

Getting Started With Site Reliability Engineering

Much has been written and discussed about SRE (Site Reliability Engineering) from what it is, how to do it, and how it's the same (or different) as DevOps. Google coined the term, defined the profession, and wrote the book on it. Their "Site Reliability Engineering" book covers the idea...

Read more
  • DevOps
  • SRE
— December 6, 2018

What DevOps Means for Risk Management

What Does DevOps Mean for Risk Management?Adopting DevOps makes the unfamiliar uneasy in two areas. One, they see an inherently risky choice between speed and quality and second, they are concerned that the quick iterations of DevOps may break compliance rules or introduce security vu...

Read more
  • DevOps
— October 25, 2018

How DevOps Transforms Software Testing

Testing is arguably the most important aspect of software development. Whether manual or automated, testing ensures the software works as expected. Broken software causes production outages, unsatisfied customers, refunds, decreased trust, or even complete financial collapse. Testing mi...

Read more
  • DevOps
— August 8, 2018

From Monolith to Serverless – The Evolving Cloudscape of Compute

Containers can help fragment monoliths into logical, easier to use workloads. The AWS Summit New York was held on July 17 and Cloud Academy sponsored my trip to the event. As someone who covers enterprise cloud technologies and services, the recent Amazon Web Services event was an insig...

Read more
  • AWS
  • AWS Summits
  • Containers
  • DevOps
  • serverless
Albert Qian
— August 6, 2018

Four Tactics for Cultural Change in DevOps Adoption

Many organizations approach digital transformation and DevOps adoption with the belief that simply by selecting and using the right tools, they will achieve higher levels of automation and gain massive efficiencies as a result. While DevOps adoption does require new tools and processes,...

Read more
  • DevOps
— July 24, 2018

Get Started with HashiCorp Vault

Ongoing threats of data breaches and cyber attacks remain top of mind for every team responsible for securing cloud workloads and applications, especially with the challenge of managing secrets including passwords, tokens, API keys, certificates, and more. Complexity is especially notab...

Read more
  • DevOps
  • HashiCorp Vault
— June 11, 2018

Open Source Software Security Risks and Best Practices

Enterprises are leveraging a variety of open source products including operating systems, code libraries, software, and applications for a range of business use cases. While using open source comes with cost, flexibility, and speed advantages, it can also pose some unique security chall...

Read more
  • DevOps
— June 5, 2018

What is Static Analysis Within CI/CD Pipelines?

Thanks to DevOps practices, enterprise IT is faster and more agile. Automation in the form of automated builds, tests, and releases plays a significant role in achieving those benefits and creates the foundation for Continuous Integration/Continuous Deployment (CI/CD) pipelines. However...

Read more
  • DevOps