X-NUCA'2020 rtos

卡住了…更新中…rt-thread的main函数栈溢出,不用getshell,ROP/shellcode读出根目录下的flag即可:

赛题仓库:XNUCA2020Qualifier,目前未更新此题。故给出题目附件如下,比赛时开始没符号,后来因为零解,然后放了有符号的ELF版,比赛最后服务器端的pow也直接去掉了:

参考:凝聚网络安全工作室 X-NUCA’2020 WriteUp: rtos

开发尝试

编译方法非常简单,进/rt-thread/bsp/qemu-vexpress-a9/,然后执行scons即可编译。业务代码可以修改/rt-thread/bsp/qemu-vexpress-a9/applications/main.c,尝试直接输出flag:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <dfs_posix.h> 

int main(void)
{
	char a[0x64];
	int fd;
    fd = open("/flag",O_RDONLY);
    read(fd,a,0x64);
    puts(a);
    return 0;
}

需要在sd卡中新建flag,可以进rt-thread后新建,也可以是用linux直接挂载文件系统新建

题目漏洞

一开始研究半天咋跟这玩意交互,直接qemu启动后,命令行里输入没有显示,后来发现使用文件加管道重定向给qemu可以获得比较正常的输入输出

就是个栈溢出,但是溢出点的数值不知道为啥被改了,看起来是数值加3:

$ python3 -c "print('1'*0x40)" | ./run.sh
WARNING: Image format was not specified for 'sd.bin' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument
qemu-system-arm: warning: hub 0 is not connected to host network

 \ | /
- RT -     Thread Operating System
 / | \     4.0.3 build Oct 30 2020
 2006 - 2020 Copyright by rt-thread team
lwIP-2.0.2 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/SDIO] SD card capacity 65536 KB.
file system initialization done!
welcome to simple rtos pwn level 1

read 1111111111111111111111111111111111111111111111111111111111111111
byebye
prefetch abort:
Execption:
r00:0x00000000 r01:0x07860785 r02:0x00000786 r03:0x00000000
r04:0xdeadbeef r05:0xdeadbeef r06:0xdeadbeef r07:0xdeadbeef
r08:0xdeadbeef r09:0xdeadbeef r10:0xdeadbeef
fp :0x31313131 ip :0x01010101
sp :0x6009ce78 lr :0x60012cbc pc :0x31313134
cpsr:0x60000033
shutdown...

修改run.sh开始调试:

qemu-system-arm -M vexpress-a9 -smp cpus=2 \
	-kernel binary.bin \
	-nographic -sd sd.bin -net nic \
	-serial mon:stdio \
	-monitor /dev/null \
	-gdb tcp::1234 -S

但是如果输入包含0x01,就会发生奇怪额现象….

from pwn import *
context(arch='arm',log_level='debug')

io = process(["bash","./run.sh"])
io.recvuntil("2006 - 2020 Copyright by rt-thread team") 
payload = 'A'*(0x20)
payload += p32(0x11223344) 
payload += p32(0x60012518)
io.sendline(payload)
io.interactive()

输入的0x60012518就会再过了scanf后直接在栈上变成0x60002518

───────────────────────────────────[ DISASM ]───────────────────────────────────
  0x60012558    pop    {fp, pc}
    
   0x60002518    andeq  r0, r0, r0
   0x6000251c    andeq  r0, r0, r0
   0x60002520    andeq  r0, r0, r0
   0x60002524    andeq  r0, r0, r0
   0x60002528    andeq  r0, r0, r0
   0x6000252c    andeq  r0, r0, r0
   0x60002530    andeq  r0, r0, r0
   0x60002534    andeq  r0, r0, r0
   0x60002538    andeq  r0, r0, r0
   0x6000253c    andeq  r0, r0, r0
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000 sp   0x6009ce70 —▸ 0x11223344 ◂— 0
01:0004 r11  0x6009ce74 —▸ 0x60002518 ◂— 0
02:0008      0x6009ce78 —▸ 0xdeadbeef ◂— 0
03:000c      0x6009ce7c ◂— 0
04:0010      0x6009ce80 —▸ 0xdeadbeef ◂— 0
05:0014      0x6009ce84 —▸ 0x6001ed64 ◂— push   {r4, fp, lr} /* 0xe92d4810 */
06:0018      0x6009ce88 —▸ 0x23232323 ◂— 0
07:001c      0x6009ce8c ◂— andeq  r1, r1, r0, lsr #29 /* 0x11ea0 */
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
  f 0 60012558
────────────────────────────────────────────────────────────────────────────────
Breakpoint * 0x60012558
pwndbg> 

进行不下去了…

引申阅读