配合静态代码审计工具,学习一下。

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';

文件上传

  1. 有对文件内容开头做校验;
    public static boolean isImage(File file) throws IOException {BufferedImage bi = ImageIO.read(file);if (bi == null) {return false;}return true;}
  1. 有对上传文件后缀名做校验(白名单:{“.jpg”, “.png”, “.jpeg”, “.gif”, “.bmp”});
  2. 上传的图片会通过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提供了 StatementPreparedStatementCallableStatement三种方式来执行查询语句。

  • 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

updatexmlextractvalue()的用法参考官方文档:

  • 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学习相关推荐

  1. [转]java.lang.instrument 学习(一)

    [转]java.lang.instrument 学习(一)  收藏 sunyh 发表于 10个月前 阅读 40 收藏 3 点赞 1 评论 0 转自:http://jiangbo.me/blog/201 ...

  2. Java IO流学习总结二:File

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/54581478 本文出自[赵彦军的博客] Java IO流学习总结一:输入输出流 J ...

  3. 20165235 Java第一周学习总结

    (# 20165235 Java第一周学习总结 Ubuntu下git的安装与使用 首先Ubuntu下git的安装,使用sudo apt-get install git下载Ubuntu,下载完成后可以用 ...

  4. Java 8 实战学习笔记

    Java 8 实战学习笔记 @(JAVASE)[java8, 实战, lambda] 文章目录 Java 8 实战学习笔记 参考内容 Lambda表达式 Lambda环绕执行模式(抽离步骤) 原始代码 ...

  5. 保存ResultSet中的数据(Java Source Code)

    当我们在编写程序的时候,免不了要与数据库打交道,相信ResultSet大家也并不陌生,从数据库读取的数据将会存入其中. 操作结束关闭数据库连接以及ResultSet,否则保持数据库的连接并直接对Res ...

  6. 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor

    [Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...

  7. Java云同桌学习系列(十五)——MySQL数据库

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为"云同桌". 每月预计保持 ...

  8. Java基础 快速学习(一)

    注意:本系列博客是在作者已经有一年多的C++经验的基础上学习Java的个人总结,可能并不适合所有看到这篇博客的人. 一.数据类型 1.整数:byte,short,int,long  分别对应1,2,4 ...

  9. java和Android学习书籍

    java书籍推荐: 入门 <Head First Java, 2nd Edition(中文版)> 这本书不仅内容丰富.有趣且通俗易懂,并且涵盖了所有 Java 相关的基础知识.如果你是 J ...

  10. 【Java】Java零基础学习笔记

    文章目录 前言 思维导图 前期准备 卸载JDK 安装JDK Hello,world 可能遇到情况 java程序运行机制 IDEA的安装 java基础部分 基础语法 运算符 包机制 javaDoc文档手 ...

最新文章

  1. python3练习题:1-10
  2. HDLBits答案(20)_Verilog有限状态机(7)
  3. python install zabbix.4.0
  4. nginx配置多个站点共用80端口
  5. windows下使用cmake+mingw配置makefile(2)
  6. P2P穿透四种NAT类型
  7. 大量违规投放,青桔单车被紧急约谈
  8. 在CentOS 7中使用Sendmail通过PHP发送邮件
  9. HMM:隐马尔科夫模型 - 预测和解码
  10. RTCM 协议数据解析
  11. Cesium:加载OSGB倾斜摄影三维模型
  12. Error in cool_function[1] : object of type ‘closure‘ is not subsettable
  13. 论文阅读:Aspect-based Sentiment Classification with Aspect-specific Graph Convolutional Networks
  14. Liskov Substitution Principle
  15. Base64 UUE 文件编码解码工具及使用说明
  16. 自制typora主题
  17. Mysql生成UUID 亲测
  18. Oracle_1th_查询语句
  19. ByVal和ByRef的区别
  20. 基于区域生长的图像分割算法!

热门文章

  1. redis下的cluste的脚本监控
  2. 摸鱼也有效率——8个python自动化脚本提高打工人幸福感~
  3. 合成大西瓜开发源码,手把手教你运行和部署大西瓜游戏项目
  4. 计算机能否代替老师英语作文,雅思大作文范文关于电脑替代老师
  5. win10不操作几分钟睡眠的解决
  6. AXWWriter兼容的Word处理元素
  7. 关于GIS原理的实际分析应用题的一些解法
  8. 正则表达式——Pattern.DOTALL
  9. 视频直播源码中主播印象功能开发
  10. 转战Linux 重装系统记录 windows - fedora