2020RCTF,no_write
利用巧妙的gadget
实现在内存中 布置任意的 libc
地址,最终实现不泄露libc
地址即可完成orw
。vm
也是不能泄露地址,但是可以利用vm
自带的指令实现在内存中布置任意libc
地址,完成or
,通过exit
输出flag
的一字节;bf
类似 brainfuck
解释器,指针加存在off-by-one
漏洞;best_php
,webpwn 但是没环境,后续专门学习一下。
note
程序分析
这道题感觉我是通过非预期解做的,十分简单。
程序逻辑是个菜单题,但是edit
和 show
对 chunk
的 id
都没有检查下界,导致可以为负数,向上写和覆盖。
利用分析
那么非预期就是通过 edit
和 show
来 控制 chunk_list
达到任意地址写和覆盖。
EXP
1 | from pwn import * |
no_write
程序分析
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
程序漏洞很简单,就是一个简单的栈溢出。但是这道题启用了沙箱,只能执行 open read exit
三个函数。也就是没有输出函数。同时程序没有开启PIE
。
利用分析
这道题有两个很关键的点:
- 构造 任意 libc 地址
这道题没有输出函数,我们也就不能向往常一样 泄露得到libc
地址。但是这道题我们可以通过两个步骤,在内存 bss
中构造出 我们需要的 libc
地址。
首先是如下函数:
1 | .text:0000000000400520 _start proc near ; DATA XREF: LOAD:0000000000400018↑o |
在 start
函数中,会调用 __libc_start_main_ptr
函数,该函数会在 会在内存内写入initial
,exit_funcs_lock
的libc
地址,而执行的参数如下:
1 | ► 0x400544 <_start+36> call qword ptr [rip + 0x200aa6] <__libc_start_main> |
那么,我们这里只需要设置 rdi
为 readn
函数,迁移 rbp
到 bss
断, 那么即可再次执行 输入函数,同时也在 内存中留下了 libc
地址。
1 | 00:0000│ rsp 0x6013b8 ◂— 0x30000000000 |
然后,需要用到如下的 gadget
:
1 | 0x4005e8 <__do_global_dtors_aux+24>: add DWORD PTR [rbp-0x3d],ebx |
在这个 gadget
中,可以将 rbp-0x3d
的值与 ebx
相加,如果我们能够控制 rbp
和 rbx
,那么就可以实现任意内存改写。而结合上一步在 bss
中留下的 libc
地址,我们就可以实现在 bss
中构造任意 libc
地址。而通过 csu_init
我们刚好可以精确控制 rbp
和 rbx
。这里我们选择将 0x6013c8
的libc
修改为我们需要的 open
地址
- 得到 flag
通过第一步,我们解决了 libc
的问题。那么如何得到 flag
。方法其实在 天翼杯和蓝帽杯都有过类似思路。将 flag
通过 open
和 read
读入内存,再通过 strncmp
比较每一个字节,不断爆破得到flag
。那么就需要 strncmp
函数
注意,这里不能直接使用 strncmp
函数,而是要使用 strncmp_sse42
函数。原因是我们正常调用 strncmp
函数,系统并不会去延迟绑定 strncmp
,而是会使用 strncmp_sse42
。
然后,还有个关键问题,是如何判断我们 爆破到了 正确的 字符。这里有两种方法:
一种是,当 strncmp
比较正确时,会返回 rax=0
,而如果我们紧接着使用 syscall
调用,那么系统就会调用 read
函数,此时就会进入等待状态;而如果 rax!=0
时,syscall
就可能为非法调用。
另一种方法,曾经在 P4nda
学长的一道内核题目中看到,即将 我们的输入放入到 bss
的边界上,如果strncmp
判断正确,那么即继续比较下一地址的字符,而 bss
紧邻的地址 是不具有 可读权限的,此时程序会报错,那么即可认为此时 字符比较正确。
EXP
1 | from pwn import * |
VM
程序分析
程序是一个典型的 vm
题,不过这道题开启了 多线程,并且关闭了全部输出,所以需要我们自己在 内存中,布置我们需要的 libc
地址。并且开启了沙箱,我们只能通过 ROW
来读取 flag
。由于关闭了输出,但是可以看到父进程在子进程执行完毕之后输出子进程运行过程中的错误代码,因此我们可以在子进程中open,read
读取flag
,然后调用exit
以单字节的形式输出flag
中的一个字节,循环几次即可输出flag
。
这里程序主要有两个功能,一个是对用户输入 code
进行 check
,逻辑如下:
1 | __int64 __fastcall check(__int64 chunk1) |
然后,就是 vm
常见功能,对指令进行相应的执行:
1 | unsigned __int64 __fastcall funcs(__int64 chunk1, int num) |
这里稍微解释一下,代码中的注释, addr
代表当前的 0x71
的 堆地址, value
代表我们输入的值,bss
代表我们存储在 0x800
堆块的数据。可以看到上面对这三部分数据都有很详细的功能。
然后,看一下 0x71
chunk里存储的数据:
1 | 0x55e1baad2250: 0x0000000000000000 0x0000000000000071 //chunk1 |
可以看到 addr
中有存储堆块地址,功能中有 free
和 malloc
函数,那么就可以实现堆块泄露地址。
同时 12
指令中,可以将 rsp
便宜到 我们指定的 off+2
处,那么我们就可以利用此指令来 绕过 check
。
利用分析
- 绕过 check
在 check
指令中,我们首先必须将 指令个数 num
增加到一个合适的值,最后以 \xff
结尾。但是,为了将 我们在 funcs
中利用的指令不受影响,我们可以利用 funcs
12指令,布置如下指令:
1 | payload = p8(0x0c)+p8(0xfc) |
那么,在check
中,num
会持续增加到 64
才会结束。而在 funcs
中,首先进入12
指令,执行 rsp+0xfc+2
的功能,那么就会将 rsp
刚好移到 我们的 funcs_code
处,执行我们想利用的指令。
- 构造合适的 libc地址
由于程序关闭了所有输出,那么这道题的思路和上一题类似,就是在 内存中布置我们需要的 libc
地址。那么首先就要在 0x71
堆块中留下 libc
地址。
先将 chunk3
的 size
构造为 0x711
,然后释放 chunk3
到 unsortedbin
中,再申请一个小块为 chunk3
,那么此时新的 chunk3
就会留下 libc
地址。那么再将该 chunk3
的libc
地址 复制到 chunk1
中,那么此时完成了在 chunk1
中留下了 libc
地址。
那么随后就可以利用对 chunk1
的值的操作指令,将 留下的libc
地址,加上偏移构造为 free_hook
和 setcontext+53
的地址。
- get flag
当有了 free_hook
和 setcontext
的地址后,我们首先将 free_hook
复制到 chunk3
处,然后通过 修改 bss
指令将 free_hook
修改为 setcontext+53
地址。
然后,在堆上布置 libc
的基址,用来构造后面 SROP
里面 的 libc
地址。
然后,将 在堆上布置 SROP
来执行 or
,并单字节作为exit
的状态码返回,从而在父进程单字节得到flag。
EXP
1 | from pwn import * |
bf
程序分析
程序实现了一个类似 brainfuck
解释器的功能,指令功能如下:
1 | > ++ptr |
用户输入的 code
小于 0x10
,会被分配到栈上,如果大于 0x10
则会被分配到 堆上。同时还有一个 0x400
大小存储用户的输入,被放到栈上,而且其紧邻着 code
的地址。
而在代码中,指针加操作 ++ptr
,虽然会对指针的上界进行操作,但是却存储在 off-by-one
漏洞。当 v19
等于 v23
时,导致可以对 code
的地址的最后一字节进行修改。
1 | if ( *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](v23, j) == 62 ) |
利用分析
- 泄露地址
利用off-by-one
漏洞,将 code_addr
修改到 返回地址处,此时返回地址正好是 __libc_start_main+231
的位置,所以可以泄露出 libc
地址;
- orw
然后利用 读取用户输入到 code
处,我们可以将 orw
布置到 返回地址处,从而实现读取用户输入。
注意,最后需要将code
地址改为正确,完成析构函数。
EXP
1 | # encoding=utf-8 |
best_php
没有环境,希望后面可以复现一下
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2021/02/21/2020-RCTF/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!