羊城杯2023部分题目复现
题目附件https://github.com/nyyyddddn/ctf/tree/main/%E7%BE%8A%E5%9F%8E%E6%9D%AF2023
risky_login
一道riscv64 小端序 栈溢出 ret2text的题目,最新的ida 9.0可以反编译riscv架构的程序,就不需要用难用的ghidra去分析了
https://ta0lve.github.io/posts/pwn/risc-v/0x01/#%E5%AF%84%E5%AD%98%E5%99%A8%E5%AD%A6%E4%B9%A0
在my input这里存在一个栈溢出,因为strlen只检查低一个字节的数据,那该溢出多少?通过阅读文档可以发现这个ret相当于jmp ra的作用,然后sd ra, 110h+var_s8(sp) 这个是存储返回地址,所以返回地址在栈底 - 0x8的位置,然后strcpy地址距离栈底 0x108,偏移0x100就刚刚好到返回地址那了
覆盖返回地址为backdoor,然后cat fl* 就能把flag读出来了
那riscv的程序如何调试呢?? qemu 有一个 -g的参数可以通过gdb-multiarch remote连上去打断点调试,在process开程序的时候加这个-g的参数就可以通过gdb连上去调试了
debug.sh
exp
shellcode
checksec 栈有rwx的权限
程序的逻辑是输入17个字节的数据(read 输入数据的逻辑有问题导致可以多输入一个字节) 然后判断输入的长度是否大于等于16个字节,判断每个字节是否是[79,95]这个范围的字节码,如果是就会执行这些shellcode
因为seccomp要满足一定的输入约束才会执行load bpf,所以直接seccomp dump是dump不出来沙箱规则的,得用python模拟符合输入约束的数据才能把沙箱规则dump出来,可以发现要用orw把flag读出来,然后rw对fd还有些约束,r的fd必须小于等于2,w的fd必须大于2,dup2(old_fd,new_fd)系统调用可以将一个fd重定向到一个新的fd,所以orw得用dup2重定向一下
test.py
限制的范围都是一些可以调整堆栈和寄存器的asm
通过yes和no那两个字节输入那 写一个syscall,push和pop调整堆栈,把syscall给读到一个寄存器里,然后通过push写满整个栈,这样在shellcode执行到结尾的时候就会取到这个syscall,syscall read一次后用dup2 orw的shellcode把flag读出来就行了
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
|
easy_vm
题目实现了一个简单的vm,有任意地址写的功能,然后stack_memroy残留了一个指向main_arena的指针,可以通过这个指针算出libc_base和ld_base,glibc 2.31下有一个exit hook,具体是exit - > __run_exit_handlers -> _dl_fini - > (rtld_lock_default_lock_recursive & rtld_lock_default_unlock_recursive ) lock 和 unlock这两个函数的调用方式是在rtld_global找这两个函数的指针然后call,然后rtld_global又在ld的数据段上,如果能知道ld_base就能算出这两个指针的地址,所以可以通过写这两个指针为one gadget去劫持控制流
exp