sqlmap源码入门笔记系列
sqlmap简介
sqlmap是一个开源的渗透测试工具,可以用来探测sql注入漏洞。
sqlmap的源码
sqlmap源码就像是九阴真经一样,梅超风只是偷看了九阴真经的皮毛,就可以在江湖上掀起一阵腥风血雨,哪怕只是掌握sqlmap的一点点精髓,也是可以益寿延年。sqlmap和九阴真经最大的不同是,sqlmap是开源的,学了sqlmap没有人会打断你的腿,只要你想要去学习,并付出努力就能够有收获。
我依然记得,当初看mycli的一小部分代码,学到click这个包的使用,然后自己写了一个基于click的命令行切换注册表工具,着实不错。
通过sqlmap这个工具的使用,结合sqlmap的源码,学习下sql是怎么注入的?这个工具又是如何实现的。
记得一次面试,我提到使用sqlmap来进行sql注入的尝试,面试官听到这里气色为之一振,我想这应该也是加分点吧。
言归正传,下面就是我阅读sqlmap源码的过程。
从sqlmap.py的main函数开始,往下阅读。
在banner函数中看到了如下的代码,给我看迷糊了
1、if not any 和 any的使用
两个列表,A和B,你想看看,A中的元素是否在B中出现过
if not any(_ in sys.argv for _ in ("--version", "--api"))
any(_ in sys.argv for _ in ("--disable-coloring", "--disable-colouring"))
2、globals()的使用
提升成为全局变量
for _ in ("cmdLineOptions", "conf", "kb"):
globals()[_] = getattr(sys.modules["lib.core.data"], _)
sqlmap是如何解析命令行参数的?
cmdLineParser这个函数是回答此问题的关键, This function parses the command line parameters and arguments
sys.argv
对应的解析这块
命令行的输出
对应的源码
如何使用 sqlmap的gui界面?
在没有看源码之前,我有想过,写个GUI界面,内部调用sqlmap,可是当我看到这里的时候,我笑了,原来sqlmap是自带gui界面的,差点就做了重复工作。
init()函数中做了哪些操作?
_useWizardInterface() 对应于sqlmap --wizard模式
sqlmap中的数据类型AttribDict
lib.core.datatype.py中定义了AttribDict,几十行代码。。。
class AttribDict(dict):
"""
This class defines the dictionary with added capability to access members as attributes
>>> foo = AttribDict()
>>> foo.bar = 1
>>> foo.bar
1
"""
def __init__(self, indict=None, attribute=None, keycheck=True):
if indict is None:
indict = {}
# Set any attributes here - before initialisation
# these remain as normal attributes
self.attribute = attribute
self.keycheck = keycheck
dict.__init__(self, indict)
self.__initialised = True
# After initialisation, setting attributes
# is the same as setting an item
def __getattr__(self, item):
"""
Maps values to attributes
Only called if there *is NOT* an attribute with this name
"""
try:
return self.__getitem__(item)
except KeyError:
if self.keycheck:
raise AttributeError("unable to access item '%s'" % item)
else:
return None
def __setattr__(self, item, value):
"""
Maps attributes to values
Only if we are initialised
"""
# This test allows attributes to be set in the __init__ method
if "_AttribDict__initialised" not in self.__dict__:
return dict.__setattr__(self, item, value)
# Any normal attributes are handled normally
elif item in self.__dict__:
dict.__setattr__(self, item, value)
else:
self.__setitem__(item, value)
def __getstate__(self):
return self.__dict__
def __setstate__(self, dict):
self.__dict__ = dict
def __deepcopy__(self, memo):
retVal = self.__class__()
memo[id(self)] = retVal
for attr in dir(self):
if not attr.startswith('_'):
value = getattr(self, attr)
if not isinstance(value, (types.BuiltinFunctionType, types.FunctionType, types.MethodType)):
setattr(retVal, attr, copy.deepcopy(value, memo))
for key, value in self.items():
retVal.__setitem__(key, copy.deepcopy(value, memo))
return retVal
sqlmap的logger是怎么使用的?
命令行中的-v参数,会决定logger的级别。
logger的定义在lib.core.data和lib.core.log中。
日志格式
FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", “%H:%M:%S”)
LOGGER = logging.getLogger(“sqlmapLog”)
LOGGER_HANDLER = logging.StreamHandler(sys.stdout)
LOGGER_HANDLER.setFormatter(FORMATTER)
LOGGER.addHandler(LOGGER_HANDLER)
LOGGER.setLevel(logging.INFO)
logger = LOGGER
这段代码看下来还是比较简单的,是一个比较基本的logging的使用。
参考文章
https://zhuanlan.zhihu.com/p/391930824
https://www.freebuf.com/column/232047.html
https://cloud.tencent.com/developer/article/1040203