Java random要领的安全问题 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

Java random要领的安全问题

申博_安全防护 申博 72次浏览 未收录 0个评论

媒介

起源于强网杯的密码学题目random study

java.util.Random

题目中challenge two的重要代码以下:

o = subprocess.check_output(["java", "Main"])
tmp=[]
for i in o.split("\n")[0:3]:
    tmp.append(int(i.strip()))
v1=tmp[0] % 0xffffffff
v2=tmp[1] % 0xffffffff
v3=tmp[2] % 0xffffffff

还给了一个Main.class文件,翻开发现是字节码,用jd-gui反编译获得源码以下:

public class Main {
  public static void main(String[] paramArrayOfString) {
    Random random = new Random();
    System.out.println(random.nextInt());
    System.out.println(random.nextInt());
    System.out.println(random.nextInt());
  }
}

代码的意义很简朴,挪用random.nextInt要领天生三个一连的随机数,请求依据前两个随机数去展望第三个随机数

源码剖析

为了相识这个要领涌现的安全题目的道理,有必要去检察一下这个要领的源代码

在eclipse中将光标移动到nextInt处按F3能够追踪到jdk包里的详细代码

Java random要领的安全问题

能够看到它直接挪用了next要领,通报的参数是32。

继承追踪next要领

Java random要领的安全问题

能够看到前一个随机数种子和后一个随机数种子都是界说为long范例的,要领返回的值就是下一个种子右移16位然后强转为int的效果

while里的compareAndSet要领只是对照今后的种子值是不是为oldseed,如果是的话就更新为nextseed罢了,一样平常都邑返回true

而下一个种子的更新算法就在do-while轮回内里:nextseed = (oldseed * multiplier + addend) & mask,种子的初始值是将今后体系时刻带入运算的效果

能够在类界说的开首处看到这几个常量属性的值

Java random要领的安全问题

而这个种子的更新算法本质上就是一个线性同余天生器

线性同余天生器(LCG)

LCG是形如如许的式子:

Java random要领的安全问题

和上面的代码对照能够看出是基础一致的,因为和mask常量做与运算就相称因而舍弃高位,保存2进制的低47位,也就相称于模2的48次方

那末我们既然都有了常量的值了,我们就可以够去做第三个随机数的展望了

展望

要领很简朴,如果把天生第一个随机数的种子界说为seed1,seed2、seed3今后顺延的话

某CMS组合漏洞至Getshell

前言 首先这个文件上传的漏洞点只能上传压缩包,因此并不能直接getshell。 这里的组合原理是通过sql注入漏洞拿到数据库中存放的压缩包信息,然后利用压缩包信息去构造payload触发解压缩操作,最终实现getshell。 审计的cms名为5iSNS内容付费系统,代码规模并不大,但是漏洞点比较有趣,故分享此文。 源码的下载地址:http://down.chinaz.com/soft/39377.htm 源码的官网地址:http://www.imzaker.com/ 1. sql注入漏洞 其实乍一看他的数据库连接函数,都带有pdo的字样,但是跟进去具体瞧一瞧才发现其实还是用了很多的原生处理,如转义等操作,并没有完全做到预编译。 最初的入口漏洞代码位于5isns/basephp/func/db.func.php第241行 function db_find_one($table, $cond = array(), $orderby = array(), $col = array(), $d = NULL) {
$db = $_SERVER[‘db’];

// 高效写法,定参有利于编译器优化
$d = $d ? $d : $db;
if(!$d) return FALSE;

return $d->find_one($table, $cond, $orderby, $col);
}
这里对应的其实就是最简单的select功能,这里需要重点关注$cond这个参数,这其实就是我们用户传入的键值对。跟进到find_one函数中去,代码位于5isns/baseph

seed1右移16位就是第一个随机数的值,也就是说第一个随机数就丧失了16位,以是seed1就有2的16次方种能够,那末把这2的16次方种能够带入盘算下一个seed2,而且右移检察是不是和第二个随机数相称就可以晓得是不是正确找到了seed1了

先看一组简朴的测试样例,输出的三个随机数都是正数

Java random要领的安全问题

a = 0x5DEECE66DL
b = 0xBL
mask = (1L << 48) - 1

def findseed(x1, x2):
    seed = x1 << 16
    for i in range(2 ** 16):
        if ((a * seed + b) & mask) >> 16 == x2:
            return  seed
        seed += 1

if __name__ == '__main__':
    x1 = 1564370740
    x2 = 2121441037

    seed1 = findseed(x1, x2)
    seed2 = (a * seed1 + b) & mask
    x3 = ((a * seed2 + b) & mask) >> 16
    print x3

经由过程测试,效果正确

然则你能够会猎奇为何测试的java代码有时刻会输出负数,因为右移1位是相称于除以2的,一个正数除以一个正数怎样会获得一个负数呢?

实际上这是因为java代码中的int强迫范例转换和>>>无标记右移所形成的

补码

先来回忆一下java的int范例,int范例占四个字节,也就是二进制的32位

盘算机中的数字通经常运用二进制补码透露表现,最高位为标记位,正数为0,负数为1,以是透露表现数值的一共有31位,故int范例的最小值为-2147483648(-2的31次方)最大值为 2147483647(2的31次方-1)

你能够会猎奇为何负数比正数多透露表现了1位,因为自然数0就是用全为0(包罗标记位)的二进制透露表现的,而到负数那边是没有负0的观点的,以是能够多透露表现一个数

接下来能够最先说>>>的意义了

java中有两种右移,一种是>>,代表逻辑上的右移(除以),高位补为标记位;一种是>>>代表无标记右移,高位直接补0

看一下这类状况:

Java random要领的安全问题

前两个为正数,然则第三个为负数,我们先按照上面的要领盘算出seed3和它右移16位的效果:

a = 0x5DEECE66DL
b = 0xBL
mask = (1L << 48) - 1

def findseed(x1, x2):
    seed = x1 << 16
    for i in range(2 ** 16):
        if ((a * seed + b) & mask) >> 16 == x2:
            return  seed
        seed += 1

if __name__ == '__main__':
    x1 = 1135971603
    x2 = 1130772191

    seed1 = findseed(x1, x2)
    seed2 = (a * seed1 + b) & mask
    seed3 = (a * seed2 + b) & mask
    print seed3
    print seed3.bit_length()
    print '{:064b}'.format(seed3)
    print '{:064b}'.format(seed3>>16)

输出效果为

141635148990318
48
0000000000000000100000001101000100000000101111100100011101101110
0000000000000000000000000000000010000000110100010000000010111110

如许就可以看出题目在哪了,因为seed3右移了16位今后除补0的高位就只有32位了,运用int强转今后java把它从long范例转换成了int,而且自动疏忽了32位今后的高位,这就相称于我们获得的第三个随机数用补码透露表现为10000000110100010000000010111110

能够看出来最高位为1,也就是说这个补码代表了一个负数,那末我们怎样经由过程补码找到这个负数的真值呢?很简朴,对补码再求一次补码就好了,也就是取反后加1。

01111111001011101111111101000010,对应的二进制为2133786434,以是第三个随机数应该为-2133786434,如此一来,我们就可以够经由过程负数找到其对应的seed了

exp

终究经由过程两个随机数展望第三个随机数的exp以下:

a = 0x5DEECE66DL
b = 0xBL
mask = (1L << 48) - 1

def n2p(x):
    y = -x
    y ^= 2 ** 32 - 1 #取反
    y += 1
    return y

def findseed(x1, x2):
    if x1 < 0:
        x1 = n2p(x1)

    if x2 < 0:
        x2 = n2p(x2)

    seed = x1 << 16
    for i in range(2 ** 16):
        if ((a * seed + b) & mask) >> 16 == x2:
            return seed
        seed += 1

def cal_x(seed):
    x = seed>>16
    if '{:032b}'.format(x).startswith('1'):
        x ^= 2 ** 32 - 1
        x += 1
        return -x
    return x

if __name__ == '__main__':
    x1 = 187562908
    x2 = 1663125607

    seed1 = findseed(x1, x2)
    seed2 = (a * seed1 + b) & mask
    seed3 = (a * seed2 + b) & mask
    x3 = cal_x(seed3)
    print x3

经由测试,不管x1或许x2是不是为负数,都能够正确展望

总结

之前进修LCG的时刻,只是晓得了它的道理,并没有接触到它在实际状况中的运用,经由过程此次竞赛,学到了java的random要领的安全漏洞,同时也非常谢谢出题人供应的进修时机


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明Java random要领的安全问题
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址