逆向分析
寄存器 : ax, bx, cx, dx
opcode:
mov指令
opcode == 0x00:
mov reg1, reg2
opcode == 0x01:
mov ax, imm
load指令
0x60
0x61
store指令
opcode==0x70
mov BYTE PTR [r8+reg1*1], reg2
opcode==0x71
mov WORD PTR [r8+reg1*1], reg2
jmp指令
opcode==0x50:
jmp offset #以4字节为单位jmp
opcode==0x51:
je 0x13
alu指令
neg 0x41
漏洞分析&&攻击思路
漏洞点出现在jmp指令,开始还感觉很奇怪,直接编译成了一个固定的偏移:
后来仔细观察发现,会在下面这个函数中进行偏移的修正
这里就要注意x86_64汇编中的跳转指令了,当距离在-128~127之间是短跳转指令,只占两个字节,当超过这个距离之后,就要用长跳转指令,其指令长度为5个字节,漏洞就出现在这里:
可以从下图中看到,该函数首先从指令中取出立即数并计算目标地址,如果偏移过大需要长跳转指令,则需要重新修改指令:
新的指令长度如下:
而原来的指令是2,也就是我们当前的指令长度凭空变长了3;
然后在该函数退出之后,会重新调整每条指令的地址:
也就是在每个指令检查过后(只检查jmp其实)都会调整指令的地址,同时由于跳转指令都是用的偏移地址,因此只要起始地址和目标地址的相对位置不变,就不会出错;但是漏洞还是发生在了一个细微之处,如果此时发生了一个长跳转,那么修改完指令之后,该jmp指令及其之前的指令的地址都是正确的,而其后边的指令地址目前来看都是少了3字节的;那么如果此时继续向后分析,遇到了一个新的跳转指令,那么在分析过程中,包括这个jmp指令在内的、向后的指令的地址都是错误的,而它前面的指令的地址都是正确的,那么如果此时这个指令的偏移是一个负数,也就是向前跳转,就会出现“从一个错误的地址跳转到一个正确的地址”的现象,那么就会发生偏移计算错误、最终跳转目标错误的情况;
正是利用这个问题,我们可以使得控制流跳转到某条指令的中间部分,而这个指令如果是包含立即数的,我们就可以在立即数中部署一些指令片段,从而使得其执行一些非法的指令;
由于是16位虚拟机,立即数只有2字节,因此我们的非法指令至多2字节,而由于起初大多数寄存器被清零了,同时r8为mmap的一片不可写的地址,但是通过调试可以发现r8+0x1000是可写的,我们在这里部署/bin/sh字符串,首先多次利用mov指令在这片内存中写入”/bin/sh”,然后通过跳转漏洞两次分别构造非法指令push r8,pop rax ,将r8放入rax中,然后利用虚拟机提供的指令使rax自增0x1000指向”/bin/sh”,然后再次利用跳转漏洞构造非法指令”push rax; pop rdi”(一次就行);之后继续利用跳转漏洞构造非法指令”xor rax, rax” (没办法,16位虚拟机清空不了)之后利用虚拟机提供的之后将rax赋值为0x3b(系统调用号),最后再利用因此跳转漏洞构造非法指令syscall,最终实现execve(“/bin/sh”, 0, 0)
攻击成功
SUCTF{Jump_T0_she11c0de_1n_1_byt3_di3Kj43jG1js}
exp
from pwn import * |