学习qemu的搭建,就是来做这个题目的啦 )

用到QEMU是因为做到了2021年陕西省大学生网络安全技能大赛中的Findit4Me一题。

考察知识点

  • aarch64 架构逆向
  • 对 linux 系统调用机制的理解
  • 文件系统的制作及 qemu-system 的使用(运行及 gdb-multiarch 调试)
  • salsa20 流加密的识别与 CM 静态 patch(或动调 dump 内存)
  • sha-1 哈希算法的识别与 flag 的爆破

题解

题目提供了三个文件vmlinuxImageFindit4Me

在启动Image之前,我们需要构建一个文件系统

即在基本的文件系统中添加文件Findit4Me,打包后得到initramfs.cpio.gz

这里可以写一个start.sh

qemu-system-aarch64 \
    -machine virt -cpu cortex-a53 -smp 1 -m 2G \
    -kernel ./Image \
    -append "console=ttyAMA0" \
    -initrd ./initramfs.cpio.gz \
    -nographic

启动Image

image-20210813140939009

先看看.init_array

image-20210813203401961
  1. 调用mmap映射了一块内存到0x1000000
  2. /dev/urandom读取了0x20个随机字节到0x1000000

分析程序,我们需要满足的条件

  • 输入长度大于1 (只有回车)
  • write返回值等于0
image-20210813201727358

正常情况下write 会返回向终端输出的字符数,因此这两个条件是互斥的

write函数最终会通过内核对用户态的统一接口(系统调用)进入内核态,并根据系统调用号选择调用 syscall_table 中对应 write 的处理例程。

怀疑出题人修改了内核

翻阅一下Linux源代码

write函数在linux/fs/open.c

image-20210813153112908

反汇编vmlinux,查看ksys_write

image-20210813153404941

很快发现区别

image-20210813204105224

看到0x1000020,想到了之前_do_frame_dummy方法中向0x1000000写入的0x20个字节,但是又很奇怪,为什么是0x1000020?

看来是read也被魔改了

image-20210813205054353

而 read 的句柄又是从 open 拿到的,open在内核态对应do_sys_open

image-20210813205152671
image-20210813154401767
image-20210813154529493

很快发现open也同样被修改了:文件名等于/dev/urandom,则直接返回文件句柄为0xC0000005

那么也就是说,_do_frame_dummy方法根本不是随机读取了0x20个字节,而是固定的0x40个字节

那么下面就看crypt,标准的salsa20算法,不常见,如果识别不出来。

动态调试也不失为一种好办法,我们只需要知道它是一种流加密即可

解密脚本

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: salsa20.py
@Time: 2021-08-13 16:31
@Desc: It's all about getting better.
"""

from Crypto.Cipher import Salsa20
import struct

key = b"\x3A\xD6\xAB\x38\xB2\x5B\xDA\xB3\xD2\xA9\xB0\xB9\xDA\xB8\x0A\x59\x5C\x36\x9F\xFA\xCA\x0B\x46\xDB\x48\x3D\x8D" \
      b"\x8C\x7E\x6D\x99\xA9"
nonce = struct.pack("<Q", 0xBF9ECC32EB197C75)
value = b"\x13\xD3\x7E\xE2\xFD\x23\x21\xA4\xCF\x8A\x3D\xB3\xE2\x1B\xA3\x11\x41\x2A\xE0\xFF\xB8\xC8\xB9\x5C\x82\x35" \
        b"\x5F\x50\x07\xC1\xBC\xFD\xFD\xA8\x35\x50\xD9\xDE\x2D\xAE\xCE\x4E\xA3\x0B\x79\x65\x14\x3F\x61\x8F\x4B\x71" \
        b"\xA7\x2F\x40\xA5\x8D\x88\xA4\xE1\xAF\x4A\x46\x95\x86\xCF\xA2\xA8\x5C\x58\x55\x34\x03\xB7\xC8\x54\xA5\xE0" \
        b"\xF6\xB0\x53\xDC\x2C\xFC\x27\x92\x24\xAC\xEC\xAD\xAB\x50\x68\x59\xDD\x88\xBC\x58\xAF\x62\x5F\x13\x12\x1B" \
        b"\x3D\xD0\x13\x34\xB0\x2D\xD3\x86\x3F\xC8\x07\x25\xB0\x79\xD8\x45\x81\x6F\x90\xC1\x03\x24\x09\xCB\x3E\xC4" \
        b"\xA6\xE1\x42\x3B\xCD\xA8\x94\xDC\x3A\x79\xE2\xE0\x2B\x04\xEF\x25\x3C\x8A\xBE\x7D\x60\x8E\x21\xED\x2A\xBF" \
        b"\xE3\xAA\xD8\xA9\x2A\x0F\xCB\xA3\xA1\x80\x05\xCD\x2D\x8A\x1E\x12\x67\x34\xBA\x55\xBB\x9C\x12\x6B\x95\x48" \
        b"\x89\x12\xAF\xA0\x37\x71\x6A\x73\x7A\x7D\x0E\xAE\x8C\x03\x6F\xF2\xFD\x25\x4E\x95\xAC\xE3\xF4\x18\x19\xC9" \
        b"\xF0\x53\x07\xFF\x0A\x49\x73\xB0\x57\xB7\xF2\x52\x4A\x4A\x70\x03\x03\x89\xCB\xCC\xFD\xEC\xCB\x91\xE7\x84" \
        b"\xCA\x5E\x00\x22\x3B\x8D"

salsa = Salsa20.new(key, nonce)
data = salsa.decrypt(value)
print([i for i in data])

vfs_kibo解密出vfs_verify后,写个脚本patch一下

addr = 0xFFFFFFC01013AC60

ps = [253, 123, 182, 169, 253, 3, 0, 145, 160, 15, 0, 249, 160, 15, 64, 249, 0, 20, 0, 145, 160, 75, 0, 249, 191, 159, 0, 185, 47, 0, 0, 20, 160, 211, 0, 145, 167, 252, 255, 151, 160, 159, 128, 185, 161, 75, 64, 249, 33, 0, 0, 139, 160, 211, 0, 145, 130, 0, 128, 82, 175, 252, 255, 151, 161, 211, 0, 145, 160, 131, 0, 145, 242, 252, 255, 151, 191, 155, 0, 185, 28, 0, 0, 20, 160, 159, 64, 185, 1, 12, 0, 17, 31, 0, 0, 113, 32, 176, 128, 26, 0, 124, 2, 19, 225, 3, 0, 42, 64, 13, 0, 240, 3, 0, 13, 145, 162, 155, 128, 185, 33, 124, 64, 147, 224, 3, 1, 170, 0, 244, 126, 211, 0, 0, 1, 139, 0, 244, 126, 211, 96, 0, 0, 139, 0, 0, 2, 139, 1, 0, 64, 57, 160, 155, 128, 185, 162, 131, 0, 145, 64, 104, 96, 56, 63, 0, 0, 107, 96, 0, 0, 84, 192, 4, 128, 210, 14, 0, 0, 20, 160, 155, 64, 185, 0, 4, 0, 17, 160, 155, 0, 185, 160, 155, 64, 185, 31, 76, 0, 113, 109, 252, 255, 84, 160, 159, 64, 185, 0, 16, 0, 17, 160, 159, 0, 185, 160, 159, 64, 185, 31, 124, 0, 113, 13, 250, 255, 84, 0, 0, 128, 210, 253, 123, 202, 168, 192, 3, 95, 214]

for i, v in enumerate(ps):
    ida_bytes.patch_byte(addr+i, v)

之后就清晰多了,这一个加密,特征还是比较明显的,sha1算法

image-20210813200333998

对8组四位小写字母和数字的组合进行爆破

package main

import (
	"bytes"
	"crypto/sha1"
	"encoding/hex"
	"github.com/Mas0nShi/goConsole/console"
	"hash"
	"runtime"
	"sync"
	"time"
)

type _dict struct {
	upperAlpha string
	lowerAlpha string
	alnum	   string
}

var (
	wg        sync.WaitGroup

	classNew   func() hash.Hash

	dict = _dict{
		upperAlpha: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
		lowerAlpha: "abcdefghijklmnopqrstuvwxyz",
		alnum	  : "1234567890",
	}
)


// 爆破4位~
func fuzzing(s []byte, maps *[]byte, result *[]byte) {
	for _, ch1 := range s {
		for _, ch2 := range *maps {
			for _, ch3 := range *maps {
				for _, ch4 := range *maps {
					head := []byte{ch1, ch2, ch3, ch4}
					h := classNew()
					h.Write(head)
					//h.Write(tail)
					if bytes.Index(h.Sum(nil), *result) != -1 {
						console.Info("fuzz Result:", string(head))
					}
				}
			}
		}
	}
	wg.Done()
}

func fufufu(threads int, sthreads int, snum int, Maps *[]byte, result *[]byte) {
	wg.Add(threads)
	for i := 0; i < threads; i++ {
		if i < sthreads {
			go fuzzing((*Maps)[snum*i : snum*(i+1)], Maps, result)
		} else {
			base := snum * sthreads
			go fuzzing((*Maps)[base+(snum+1)*(i-sthreads) : base+(snum+1)*(i-sthreads+1)], Maps, result)
		}
	}
	wg.Wait()
}



func main() {
	threads := runtime.NumCPU()
	start := time.Now()

	// init
	classNew = sha1.New
	maps := []byte(dict.lowerAlpha + dict.alnum)

	snum := len(maps) / threads
	sthreads := threads*(1+snum) - len(maps)


	strss := "672C6AC4DB254CF02B28B9A3F41F49E3888898D96F02C552E15B60E1BB4D59E1F446B2EEAA8BF49DBE538A72101653B234255B6D3EF54C386247D3F37615504C247744B3C3394106FEAE7817893C8BD318D355396B49B59C855D68798AC3DB7558ED74AC49E9A128AB587D689EB2F179987FEF68387203156BE7996F0EE0B877CAD0AFC2235C193B6BDCEB0BE9A8751F3FF3D615F18E257FAA5B5492211910AA"
	fuzzArr := [8][]byte{}
	for i := 0; i < 8; i++ {
		fuzzArr[i], _ = hex.DecodeString(strss[0 + i*40:40+i*40])
	}

	for i := 0; i < 8; i++ {
		fufufu(threads, sthreads, snum, &maps, &fuzzArr[i])
	}

	end := time.Since(start)
	console.Log(end)
}

爆破出来

image-20210813200058591

sha1特征

魔数

  • 0xEFCDAB8967452301

  • 0x1032547698BADCFE

  • 0xC3D2E1F0

Linux源码查阅

LXR linux/fs/read_write.c (missinglinkelectronics.com)