java-sec-code学习
配合静态代码审计工具,学习一下。
git clone https://github.com/JoyChou93/java-sec-code
cd java-sec-code
# 生成jar包
mvn clean package
修改配置文件src/main/resources/application.properties:
:
将数据库名,账号密码修改为mysql中有的。
参考:mysql最常用最基础的命令
# 调试模式运行
java -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=12346 -jar /home/cqq/repos/java-sec-code/target/java-sec-code-1.0.0.jar
然后在IDEA中远程调试。
创建数据库
create user 'java_sec_code'@'192.168.17.1' identified by 'java_sec_code';
create user 'java_sec_code'@'localhost' identified by 'java_sec_code';
create database java_sec_code;
grant all privileges on java_sec_code.* to java_sec_code@192.168.17.1;
grant all privileges on java_sec_code.* to java_sec_code@localhost;alter user java_sec_code@192.168.17.1 identified with mysql_native_password by 'java_sec_code';
文件上传
- 有对文件内容开头做校验;
public static boolean isImage(File file) throws IOException {BufferedImage bi = ImageIO.read(file);if (bi == null) {return false;}return true;}
- 有对上传文件后缀名做校验(白名单:{“.jpg”, “.png”, “.jpeg”, “.gif”, “.bmp”});
- 上传的图片会通过uuid生成一个’/tmp’ + uuid + '.png’这样的文件名,然后最后删除掉
通过路径穿越修改filename
参数../../../../../home/cqq/repos/java-sec-code/1.png
可以将带有jsp代码的图片上传到任意目录
如果放在tomcat目录下,可以让tomcat解析?
相应的代码在src/main/java/org/joychou/controller/FileUpload.java
:
杂
碰到这种(状态码是404)就是Spring没有为这个path配置对应的回调方法。
碰到这种是505,服务端错误,可能是匹配到了path对应的回调方法,但是没有传入需要的参数。
检测fastjon反序列化漏洞的,可以看pom.xml文件中的版本:
全局的CSRF配置
在java-sec-code\src\main\resources\application.properties
配置文件中,
这有一行表示不用进行CSRF检测的url白名单:
joychou.security.csrf.exclude.url = /xxe/**, /fastjson/**, /xstream/**, /XmlDecoder/**
因为自己新加了一个类,并对 /XmlDecoder
进行了mapping,所以要手动添加到配置文件中,然后重新编译运行。
RememberMe Cookie反序列化代码执行
Cookie中的RememberMe字段被base64编码,然后服务端将用户Cookie中的RememberMe字段进行base64解码之后执行了readObejct操作。
代码:
/*** java -jar ysoserial.jar CommonsCollections5 "open -a Calculator" | base64* Add the result to rememberMe cookie.** http://localhost:8080/deserialize/rememberMe/vul*/@RequestMapping("/rememberMe/vul")public String rememberMeVul(HttpServletRequest request)throws IOException, ClassNotFoundException {Cookie cookie = getCookie(request, cookieName);if (null == cookie){return "No rememberMe cookie. Right?";}String rememberMe = cookie.getValue();byte[] decoded = Base64.getDecoder().decode(rememberMe);ByteArrayInputStream bytes = new ByteArrayInputStream(decoded);ObjectInputStream in = new ObjectInputStream(bytes);in.readObject(); //执行任意命令in.close();return "Are u ok?";}
这里用了CommonsCollections5 的链BadAttributeValueExpException
(JDK自带),然后需要CommonsCollection依赖。
演示:
但是为什么我调试的时候,还没有跟到执行任意方法的点,就弹出了计算器?
修复方式
升级到xlsx-streamer.jar到2.1.0版本及以上。
SQL注入
参考:
- http://rui0.cn/archives/823
Java提供了 Statement
、PreparedStatement
和 CallableStatement
三种方式来执行查询语句。
Statement
:普通查询;PreparedStatement
:参数化查询;CallableStatement
:用于存储过程。
简单来说,使用预编译语句(PreparedStatement#setString
)会对输入的字符进行转义。
调试验证一下:
在调用PreparedStatement#setString
的地方下断点:
java.sql.PreparedStatement
只是一个接口,具体的实现类需要调试时查看:
可以看到具体的实现类为:com.mysql.cj.jdbc.ClientPreparedStatement
然后最终在com.mysql.cj.ClientPreparedQueryBindings#setString(int parameterIndex, String x)
对特殊字符如'``和
"等进行了转义(加
`)
查看最终转义之后的结果:
源码在:
https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.12/mysql-connector-java-8.0.12-sources.jar
@Overridepublic void setString(int parameterIndex, String x) {if (x == null) {setNull(parameterIndex);} else {int stringLength = x.length();if (this.session.getServerSession().isNoBackslashEscapesSet()) {// Scan for any nasty charsboolean needsHexEscape = isEscapeNeededForString(x, stringLength);if (!needsHexEscape) {StringBuilder quotedString = new StringBuilder(x.length() + 2);quotedString.append('\'');quotedString.append(x);quotedString.append('\'');byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(quotedString.toString()): StringUtils.getBytes(quotedString.toString(), this.charEncoding);setValue(parameterIndex, parameterAsBytes);} else {byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(x) : StringUtils.getBytes(x, this.charEncoding);setBytes(parameterIndex, parameterAsBytes);}return;}String parameterAsString = x;boolean needsQuoted = true;if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {needsQuoted = false; // saves an allocation laterStringBuilder buf = new StringBuilder((int) (x.length() * 1.1));buf.append('\'');//// Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure...//for (int i = 0; i < stringLength; ++i) {char c = x.charAt(i);switch (c) {case 0: /* Must be escaped for 'mysql' */buf.append('\\');buf.append('0');break;case '\n': /* Must be escaped for logs */buf.append('\\');buf.append('n');break;case '\r':buf.append('\\');buf.append('r');break;case '\\':buf.append('\\');buf.append('\\');break;case '\'':buf.append('\\');buf.append('\'');break;case '"': /* Better safe than sorry */if (this.session.getServerSession().useAnsiQuotedIdentifiers()) {buf.append('\\');}buf.append('"');break;case '\032': /* This gives problems on Win32 */buf.append('\\');buf.append('Z');break;case '\u00a5':case '\u20a9':// escape characters interpreted as backslash by mysqlif (this.charsetEncoder != null) {CharBuffer cbuf = CharBuffer.allocate(1);ByteBuffer bbuf = ByteBuffer.allocate(1);cbuf.put(c);cbuf.position(0);this.charsetEncoder.encode(cbuf, bbuf, true);if (bbuf.get(0) == '\\') {buf.append('\\');}}buf.append(c);break;default:buf.append(c);}}buf.append('\'');parameterAsString = buf.toString();}byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(parameterAsString): (needsQuoted ? StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charEncoding): StringUtils.getBytes(parameterAsString, this.charEncoding));setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR);}}
【注意】占位符只能占位SQL语句中的普通值,决不能占位表名、列名、SQL关键字(select、insert等)。所以如果使用动态表名,字段,就只能向上面案例那样使用非预编译方法,不过这样显然很容易导致注入。
对于输入为int类型的参数userId
,使用参数类型绑定:
st.setInt(1, userId);
使用单引号时报错:
Failed to convert value of type 'java.lang.String' to required type 'int'
order by注入
参考:
- https://yang1k.github.io/post/sql%E6%B3%A8%E5%85%A5%E4%B9%8Border-by%E6%B3%A8%E5%85%A5/
order by是mysql中对查询数据进行排序的方法。(注意:没有查询结果的情况下无法进行order by的注入)
使用示例:
select * from 表名 order by 列名(或者数字) asc;升序(默认升序)
select * from 表名 order by 列名(或者数字) desc;降序
这里的重点是order by后接的参数可以是列名,也可以是参数。
由于id是user表的第一列的列名,那么如果想根据id来排序,有两种写法:
select * from users order by 1;
select * from users order by id;
盲注(可能有回显,但是不报错):
对应后端代码:
String sql = "select * from users where username = '" + username + "'" + " order by " + sort;
使用这个payload,第一个表达式为true时,会执行sleep(2)
if(1=1,SLEEP(2),1)
或者benchmark()
原理:
BENCHMARK(count,expr)
benchmark函数会重复计算expr表达式count次,所以我们可以尽可能多的增加计算的次数来增加时间延迟
这个函数是用来计算性能的,可以通过制定某运算的计算次数,来实现延时的效果:
参考:
- SQL注入有趣姿势总结
- https://phyb0x.github.io/2018/12/11/sql%E6%B3%A8%E5%85%A5%E5%B0%8F%E7%BB%93/
username=cqq查询到数据,才能进行order by注入;
username=111没有查询到数据,无法进行order by注入。
也可以通过order by的参数猜测列数,比如:
http://192.168.239.2:81/?order=11 错误
http://192.168.239.2:81/?order=10 正常
则表示有10列。
load_file通过dnslog带出数据
. 从payload看出load_file的路径是windows下的UNC路径,所以mysql带外注入只能发生在windows机器上
https://www.cnblogs.com/leixiao-/p/9876313.html
基于报错
后端代码:
String sql = "select * from users where username = '" + username + "'" + " order by " + sort;
catch (SQLException e) {throw e;
也可以用盲注的payload
if(1=1,SLEEP(2),1)
if(1=2,SLEEP(2),1)
但是有更好的为什么不用呢?
主要使用两个操作xml的函数:
updatexml(0,concat(0x7c,user()),1) # 接收三个参数
extractvalue(0, concat(0x7c,version())) # 接收两个参数
如果代码中写好了catch语句,没有throw则不能根据回显的报错进行注入,而如果这样写,即将报的异常抛出:
即便不像正常代码那样回显数据:
while(rs.next()){String res_name = rs.getString("username");String res_pwd = rs.getString("password");result += res_name + ": " + res_pwd + "\n";}return result;
也可进行报错注入:
使用updatexml
进行基于报错的注入,
payload:
username=cqq' or updatexml(0,concat(0x7c,user()),1) or'&password=socks
完整sql语句:
mysql> insert into users(username, password) values('cqq' or updatexml(0,concat(0x7c,version()),1) or'','socks');
ERROR 1105 (HY000): XPATH syntax error: '|5.7.26-0ubuntu0.18.04.1'
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
这里的0x7c
是|
用于拼接结果,其实可以换成其他的:
对mybatis的报错注入
/sqli/mybatis/vul01?username=cqq'+or+updatexml(0,concat(0x7c,version()),1);--+
漏洞代码是:
// SQLI.java@RequestMapping("/mybatis/vul01")public List<User> mybatis_vul1(@RequestParam("username") String username) {return userMapper.findByUserNameVul(username);}
// UserMapper.java@Select("select * from users where username = '${username}'")List<User> findByUserNameVul(@Param("username") String username);
第二处,
先用单引号测试,
/sqli/mybatis/vul02?username=cqq'
后端拼接出的sql语句是这样的:
select * from users where username like '%cqq'%'
同样的payload:
/sqli/mybatis/vul02?username=cqq'+or+updatexml(0,concat(0x7c,version()),1);--+
其漏洞代码为:
// SQLI.java@RequestMapping("/mybatis/vul02")public List<User> mybatis_vul2(@RequestParam("username") String username) {return userMapper.findByUserNameVul2(username);}
// UserMapper.java
List<User> findByUserNameVul2(String username);
<!-- UserMapper.xml--><select id="findByUserNameVul2" parameterType="String" resultMap="User">select * from users where username like '%${_parameter}%'</select>
#{}传过来的参数带单引号’', ${}传过来的参数不带单引号。
而使用安全的接口为:
一个单引号和两个单引号都返回null:
对应代码为:
//SQLI.java@GetMapping("/mybatis/sec01")public User mybatis_sec1(@RequestParam("username") String username) {return userMapper.findByUserName(username);}
// UserMapper.java@Select("select * from users where username = #{username}")User findByUserName(@Param("username") String username);
使用extractvalue()
报错注入
extractvalue()和updatexml()类似,只不过extractvalue()只需要两个参数。
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串).
select extractvalue(0, concat(0x7c,version()));
对比,
updatexml
使用第二个参数放注入的payload,第一、第三个参数放0占位即可;extractvalue()
使用第二个参数放注入的payload,第一个参数放0占位即可。
两个函数的本意本来都是写xpath的格式,这里故意不写xpath格式,而使用version(), user()等函数,将这个函数执行的结果通过报错显示出来。
参考:
http://www.admintony.com/SQL%E6%B3%A8%E5%85%A5-insert%E3%80%81update%E3%80%81delete%E6%B3%A8%E5%85%A5.html
updatexml
和extractvalue()
的用法参考官方文档:
- https://dev.mysql.com/doc/refman/8.0/en/xml-functions.html
故障排除
启动时碰到:
Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]
在pom中添加这个解决问题:
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version>
</dependency>
参考:
https://stackoverflow.com/questions/43041695/failed-to-start-component-standardenginetomcat-standardhostlocalhost-tomcat
https://www.cnblogs.com/hyy9527/p/13559442.html
java-sec-code学习相关推荐
- [转]java.lang.instrument 学习(一)
[转]java.lang.instrument 学习(一) 收藏 sunyh 发表于 10个月前 阅读 40 收藏 3 点赞 1 评论 0 转自:http://jiangbo.me/blog/201 ...
- Java IO流学习总结二:File
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/54581478 本文出自[赵彦军的博客] Java IO流学习总结一:输入输出流 J ...
- 20165235 Java第一周学习总结
(# 20165235 Java第一周学习总结 Ubuntu下git的安装与使用 首先Ubuntu下git的安装,使用sudo apt-get install git下载Ubuntu,下载完成后可以用 ...
- Java 8 实战学习笔记
Java 8 实战学习笔记 @(JAVASE)[java8, 实战, lambda] 文章目录 Java 8 实战学习笔记 参考内容 Lambda表达式 Lambda环绕执行模式(抽离步骤) 原始代码 ...
- 保存ResultSet中的数据(Java Source Code)
当我们在编写程序的时候,免不了要与数据库打交道,相信ResultSet大家也并不陌生,从数据库读取的数据将会存入其中. 操作结束关闭数据库连接以及ResultSet,否则保持数据库的连接并直接对Res ...
- 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor
[Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...
- Java云同桌学习系列(十五)——MySQL数据库
本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌". 每月预计保持 ...
- Java基础 快速学习(一)
注意:本系列博客是在作者已经有一年多的C++经验的基础上学习Java的个人总结,可能并不适合所有看到这篇博客的人. 一.数据类型 1.整数:byte,short,int,long 分别对应1,2,4 ...
- java和Android学习书籍
java书籍推荐: 入门 <Head First Java, 2nd Edition(中文版)> 这本书不仅内容丰富.有趣且通俗易懂,并且涵盖了所有 Java 相关的基础知识.如果你是 J ...
- 【Java】Java零基础学习笔记
文章目录 前言 思维导图 前期准备 卸载JDK 安装JDK Hello,world 可能遇到情况 java程序运行机制 IDEA的安装 java基础部分 基础语法 运算符 包机制 javaDoc文档手 ...
最新文章
- python3练习题:1-10
- HDLBits答案(20)_Verilog有限状态机(7)
- python install zabbix.4.0
- nginx配置多个站点共用80端口
- windows下使用cmake+mingw配置makefile(2)
- P2P穿透四种NAT类型
- 大量违规投放,青桔单车被紧急约谈
- 在CentOS 7中使用Sendmail通过PHP发送邮件
- HMM:隐马尔科夫模型 - 预测和解码
- RTCM 协议数据解析
- Cesium:加载OSGB倾斜摄影三维模型
- Error in cool_function[1] : object of type ‘closure‘ is not subsettable
- 论文阅读:Aspect-based Sentiment Classification with Aspect-specific Graph Convolutional Networks
- Liskov Substitution Principle
- Base64 UUE 文件编码解码工具及使用说明
- 自制typora主题
- Mysql生成UUID 亲测
- Oracle_1th_查询语句
- ByVal和ByRef的区别
- 基于区域生长的图像分割算法!