Master the power of exploiting most complex SQL injections
Sometimes you need the power of the four elements to automate the exploitation of a SQL injection.
That’s what --eval
gives you, and here’s how to take over this power.
depending on the case, you may need to modify different parts of the request.SQLMap
uses python’s exec()
method to execute your script. the tool passes different parts of the request to this method as local variables. this way, you can access and modify them.
the post body of the request can be of different data types, like JSON, XML, etc. to provide access to the elements of each type, SQLMap
recognizes the type by these regexes. then asks if you want the tool to process the data:JSON/XML/... data found in POST/PUT/... body. Do you want to process it?
obviously, you need to answer YES if you want everything to work as intended.
URI
the URI of the request, without the query string, is accessible via uri
parameter.
if the user specifies a custom injection point in the path of the URL, the uri
parameter will contain the URL-encoded injected payload too.
example:
command:sqlmap -u “http://example.com/path?id=123"
local variable:uri --> http://example.com/path
command:sqlmap -u “http://example.com/path*?id=123"
local variable:uri --> http://example.com/path<URL-ENCODED PAYLOAD>
GET query
the value of each GET query parameter, plus the injected payload (if any), is accessible via local variables with the name of the original GET parameters.
example:
command:sqlmap -u "http://example.com/path?param1=123¶m2=456&class.id=789"
local variables:param1 --> 123
param2 --> 456
EVAL_636c6173732e6964 --> 789
(read the note below)
Note: if the parameter name contains special characters or is a reserved keyword in python, before passing the variable to the exec()
method, SQLMap
changes its name based on this pattern:EVAL_<HEX OF ORIGINAL NAME>
it applies to all the variables which are passed to the exec()
method.
including cookies and POST data.
cookies
the content of the Cookie
header, plus the injected payload (if any), is accessible via cookie
variable.
command:sqlmap -u "http://example.com/path?id=123" --cookie=”C1=123; C2=456"
local variable:cookie --> C1=123; C2=456
headers
there are two dictionaries available in exec()
method related to the request headers:_locals['headers']
_locals['auxHeaders']
you can access the request headers via the headers
dictionary, but modifying it will not affect the request.
to make changes to the headers of the final request, you need to update()
the auxHeaders
dictionary.
the value of auxHeaders
is None
by default. it means you need to add all the headers — modified or not, to the auxHeaders
dictionary.
example:
let’s say you want to modify the value of the User-Agent
header and add a custom authentication header X-Auth
.
this can be the code:
# _locals['auxHeaders'] is None here
_locals['headers']['User-Agent'] = "pentesting, no worries!!"
_locals['headers']['X-Auth'] = "My_Secret_Token"
_locals['auxHeaders'].update(_locals['headers'])
POST body
SQLMap
recognizes six types of POST data using regular expressions:
JSON
JSON-like (regex)
XML
Multipart
Array-like (regex)
Form-based
to see what JSON-like and array-like data types look like, refer to the regular expressions that recognize them.
at the time of writing this article SQLMap
only parses form-based
and json
data types.
for other data types, you don’t have access to the value of elements.
but you can still modify the value of the elements by declaring a variable with the same name as the element’s name. SQLMap
replaces the original value with the new one using a regex.
note this implementation is buggy with array-like
data types, but you can find a solution here.
payload
the SQL payload that SQLMap
has injected to the request is accessible via the _locals['payload']
variable.
long scripts
When the code you want to execute is a few lines, you can add the script as the value of --eval
in the terminal:sqlmap ... --eval="#first_line; #second_line; #third_line"
but if your script is longer, that’s not a good idea to write the entire script in the terminal. instead, you can write your code in a .py
file and import it as a module:sqlmap ... --eval="import myModule; #use_functions_of_myModule"
debugging
if you want to debug your code inside SQLMap
or want to get familiar with the environment, you can use the ipdp
module.
it gives you a debugging shell and you can move around and see what’s going on. the locals()
method can be a good starting point.sqlmap ... --eval=”import ipdb; ipdb.set_trace()”
A new implementation
as you can see there is room for improvement with the --eval
flag.
so I rewrote the code of --eval
flag. in this new implementation, all data types are supported in a unified way.
also, I made the raw GET
/POST
data accessible as local variables. so, if the user needs to modify the request, and the already local existing variables are not useful, he can use the raw data and parse it in the way he needs.
documentation:
user has access to these dictionaries as local variables:
headers
: all the request headers. modifying this dictionary will effect the request.get_data
: processed GET query datapost_data
: processed POST body. no matter what data type is, the user has access to ALL the elements and attributes of the data type.get_query
: raw GET query datapost_body
: raw POST body data
Note: modifying get_query
or post_body
will overwrite the changes made to get_data
and post_data
accordingly.
the code:
if conf.evalCode:
delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER
variables = {"uri": uri, "get_query": get, "headers": headers, "post_body": post, "get_data": {}, "post_data": {}, "lastPage": threadData.lastPage, "_locals": locals()}
original_get = get
original_post = post
if get:
for part in get.split(delimiter):
if '=' in part:
name, value = part.split('=', 1)
name = name.strip()
value = urldecode(value, convall=True)
variables['get_data'][name] = value
if kb.postHint:
if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP):
variables['post_data'] = xmltodict.parse(post)
if kb.postHint == POST_HINT.JSON:
variables['post_data'] = json.loads(post)
if kb.postHint == POST_HINT.JSON_LIKE:
if json_like_type == 3:
post = re.sub(r'(,|\{)\s*([^\'\s{,]+)\s*:', '\g<1>"\g<2>":', post)
if json_like_type == 4:
post = re.sub(r'(,|\{)\s*([^\'\s{,]+)\s*:', "\g<1>'\g<2>':", post)
if json_like_type in (2, 4):
post = post.replace("\\'", REPLACEMENT_MARKER).replace('\"','\\"').replace("'",'"').replace(REPLACEMENT_MARKER, "'")
variables['post_data'] = json.loads(post)
if kb.postHint == POST_HINT.MULTIPART:
multipart = MultipartDecoder(bytes(post, 'utf-8'), contentType)
boundary = '--' + multipart.boundary.decode('utf-8')
for part in multipart.parts:
name = re.search(r'"([^\"]*)"', part.headers._store[b'content-disposition'][1].decode('utf-8')).group(1)
value = part.text
variables['post_data'][name] = value
if kb.postHint == POST_HINT.ARRAY_LIKE:
post = re.sub(r"\A%s" % delimiter, "", post)
array_name = re.findall(r"%s(.*?)\[\]=" % delimiter, post)[0].strip()
variables['post_data'] = []
for value in post.split("%s[]=" % array_name)[1:]:
variables['post_data'].append(value.replace(delimiter, ""))
elif post:
for part in post.split(delimiter):
if '=' in part:
name, value = part.split('=', 1)
name = name.strip()
value = urldecode(value, convall=True, spaceplus=kb.postSpaceToPlus)
variables['post_data'][name] = valueevaluateCode(conf.evalCode, variables)if kb.postHint:
if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP):
post = xmltodict.unparse(variables['post_data'])
if kb.postHint == POST_HINT.JSON:
post = json.dumps(variables['post_data'])
if kb.postHint == POST_HINT.JSON_LIKE:
post = json.dumps(variables['post_data'])
if json_like_type in (3, 4):
post = re.sub(r'"([^"]+)":', '\g<1>:', post)
if json_like_type in (2, 4):
post = post.replace('\\"', REPLACEMENT_MARKER).replace("'", "\\'").replace('"', "'").replace(REPLACEMENT_MARKER, '"')
if kb.postHint == POST_HINT.MULTIPART:
for name, value in variables['post_data'].items():
post = re.sub(r"(?s)(name=\"%s\"(?:; ?filename=.+?)?\r\n\r\n).*?(%s)" % (name, boundary), r"\g<1>%s\r\n\g<2>" % value.replace('\\', r'\\'), post)
if kb.postHint == POST_HINT.ARRAY_LIKE:
post = array_name + "[]=" + (delimiter + array_name + "[]=").join(variables['post_data'])
else:
post = delimiter.join(f'{key}={value}' for key, value in variables['post_data'].items())uri = variables['uri']
get = delimiter.join(f'{key}={value}' for key, value in variables['get_data'].items())
auxHeaders.update(variables['headers'])
cookie = variables['headers']['Cookie'] if 'Cookie' in variables['headers'] else None
get = variables['get_query'] if variables['get_query'] != original_get else get
post = variables['post_body'] if variables['post_body'] != original_post else post
you need to add these libraries to the thirdparty
folder:
xmltodict
requests_toolblet
for more information about this implementation, refer to the pull request on Github.
was it helpful?
I don’t ask you to buy me a cup of coffee,
teach me something…
Discord: REND#9702