目录
前言:
(一)整数溢出漏洞
0x01 整数溢出漏洞介绍
1.1 上界溢出
1.2 下界溢出
0x02 整数溢出漏洞修复
(二)硬编码密码漏洞
修复案例:
(三)不安全的随机数生成器
本次总结的漏洞在实战情况下,可能只是一个环节,包括我们经常遇到的WAF绕过,技术偏逆向破解,但是在web渗透测试中有一定的利用价值。
int 类型二进制存储的第一位为符号位,0 表示正数,1 表示负数,2147483647 这个数字的二进制表达为 01111111 11111111 11111111 11111111,加 1 以后的值为 10000000 00000000 00000000 00000000,发生上界溢出,而 10000000 00000000 00000000 00000000 表示的是-2147483648 这个数字
int 类型二进制存储的第一位为符号位,0 表示正数,1 表示负数,-2147483648 这个 数字的二进制表达为 10000000 00000000 00000000 00000000,减 1 以后的值为 01111111 11111111 11111111 11111111,发生下界溢出,而 01111111 11111111 11111111 11111111 表示 的是 2147483647 这个数字。Java 程序溢出运行的案例如图 1-1 所示:1
在 Java 8 版本中,可以使用新的“Math#addExact() and Math#subtractExact()”方法,该方法将监测“ArithmeticException”溢出
public static boolean willAdditionOverflow(int left, int right) { try { Math.addExact(left, right); return false; } catch (ArithmeticException e) { return true; } }
public static boolean willSubtractionOverflow(int left, int right) { try { Math.subtractExact(left, right); return false; } catch (ArithmeticException e) { return true; }
}
硬编码密码是指在系统中采用明文的形式存储密码,通常会导致严重的身份验证失败,这对于系统管理员而言可能很难检测到,一旦检测到,也很难修复。硬编码密码会造成密码泄露,其主要出现在以下几个方面
DriverManager.getConnection(url,"scott","tiger");
攻击者可以通过 javap –c 命令来访问反汇编的代码,反汇编的代码将包含所使用的密码值。
javap -c ConnMngr.class
22: ldc #36; //String jdbc:mysql://ixne.com/rxsql
24: ldc #38; //String scott
26: ldc #17; //String tiger
程序员在编写程序时应尽量避免对密码进行硬编码,而采用对密码加以模糊化或先经过 hash 处理再存储,或在外部资源文件中进行处理的方法。
然后通过代码读取数据库配置文件,最后进行数据库的连接,这样就避免了硬编码产生的漏洞。示例:读取 properties 属性文件到输入流中,从输入流中加载属性列表,获取数据库连接属性值,然后进行数据库连接,这样就不会出现硬编码漏洞了。
// 读取 properties 属性文件到输入流中
InputStream is = PropertiesTest.class.getResourceAsStream("/db.properties");
// 从输入流中加载属性列表
properties.load(is);
// 获取数据库连接属性值
DRIVER_CLASS = properties.getProperty("DRIVER_CLASS");
DB_URL = properties.getProperty("DB_URL");
DB_USER = properties.getProperty("DB_USER");
DB_PASSWORD = properties.getProperty("DB_PASSWORD");
// 加载数据库驱动类
Class.forName(DRIVER_CLASS);
随机数是专门的随机试验的结果。产生随机数有多种不同的方法,这些方法被称为随机数发生器。随机数最重要的特性是:它所产生的后面的那个数与前面的那个数毫无关系。根据密码学原理,随机数生成器分为以下三类。
Java 中的“java.util.Random”工具类 LCG 线性同余法伪随机数生成器,可以根据种子和算法生成随机数。此算法的缺陷就是可预测性,攻击者可能会猜测将要生成的下一个值,并使用此猜测来模拟其他用户或访问敏感信息。“java.util.Random”工具类没带参数构造函数生成的 Random 对象的种子默认是当前系统时间的毫秒数,故进入到 Random 类中查看其种子默认是当前的系统时间,如图 3-1 所示。使用计算机产生真随机数的方法是获取 CPU 频率与温度的不确定性,以及统计一段时间内的运算次数每次都会产生不同的值,系统时间的误差以及声卡的底噪等。在实际应用中往往使用伪随机数就足够了。计算机或计算器产生的随机数有很长的周期性。实际上它们不真正地随机,因为它们是可以计算出来的,但是它们具有类似于随机数的统计特征。这样的发生器称为伪随机数发器。
Random random = new Random();
int r = random.nextInt(); // 生成一个随机数
只要种子一样,其输出的随机序列也是一样的,如设置两个随机数列种子都是 1,则输出的随机数列也是完全相同的,如图 3-2 所示。
“java.util.Random”不是加密安全的。可以使用 SecureRandom 来获取密码安全的伪随机数生成器,以供对安全敏感的应用程序使用。“java.Security.SecureRandom”工具类提供加密的强随机数生成器(RNG),要求种子必须是不可预知的,产生非确定性输出。操作系统收集了一些随机事件,比如鼠标点击、键盘点击,等等。SecureRandom 使用这些随机事件作为种子。SecureRandom 函数的使用方式:
SecureRandom secureRandom2 = SecureRandom.getInstance("SHA1PRNG");
int r = secureRandom2.nextInt(); // 生成一个随机数
使用 SecureRandom 生成伪随机,因为 SecureRandom 使用鼠标点击、键盘点击等等这些随机事件作为种子,故其生成的随机数列完全不同,安全性要高,所以建议使用 “java.Security.SecureRandom”工具类来代替“java.util.Random”工具类,如图 3-3 所示。