QilingLab challenge writeup

Overview

qiling lab writeup

shielder 在 2021/7/21 發布了 QilingLab 來幫助學習 qiling framwork 的用法,剛好最近有用到,順手解了一下並寫了一下 writeup。

前情提要

Qiling 是一款功能強大的模擬框架,和 qemu user mode 類似,但可以做到更多功能,詳情請見他們的 github網站

他們有官方文件,解此題目前建議看一下。

我所解的為 aarch64 的 challenge,使用的 rootfs 為 qililng 所提供的 arm64_linux

逆向工具用 ghidra,因為我沒錢買 idapro。

First

先隨手寫個 python 用 qiling 執行 challenge binary。

import sys
from qiling import *
from qiling.const import QL_VERBOSE

sys.path.append("..")


if __name__ == "__main__":
    ql = Qiling(["qilinglab-aarch64"], "rootfs/arm64_linux",verbose=QL_VERBOSE.OFF)
    ql.run()

可以看到結果是 binary 會不正常執行,此為正常現象,有些 Challenge 沒解完會導致錯誤或是無窮迴圈。

Welcome to QilingLab.
Here is the list of challenges:
Challenge 1: Store 1337 at pointer 0x1337.
Challenge 2: Make the 'uname' syscall return the correct values.
Challenge 3: Make '/dev/urandom' and 'getrandom' "collide".
Challenge 4: Enter inside the "forbidden" loop.
Challenge 5: Guess every call to rand().
Challenge 6: Avoid the infinite loop.
Challenge 7: Don't waste time waiting for 'sleep'.
Challenge 8: Unpack the struct and write at the target address.
Challenge 9: Fix some string operation to make the iMpOsSiBlE come true.
Challenge 10: Fake the 'cmdline' line file to return the right content.
Challenge 11: Bypass CPUID/MIDR_EL1 checks.

Checking which challenge are solved...
Note: Some challenges will results in segfaults and infinite loops if they aren't solved.
[x]	

[x]	x0	:	 0x0
[x]	x1	:	 0x0
[x]	x2	:	 0x1
[x]	x3	:	 0x0
[x]	x4	:	 0x0

Challenge 1

把 0x1337 的位置的值改成 1337

用 qiling 把該位置的 memory 讀出來,在進行改寫,要注意 align 問題。詳情請見文件

    ql.mem.map(0x1337//4096*4096, 4096)
    ql.mem.write(0x1337,ql.pack16(1337) )

Challenge 2

改掉 uname 此 system call 的 return。

可以看到他去比對 uname.sysname 和 uname.version 是否為特定值。我採用對 system call 進行 hijack

去翻 linux 文件 可以看到 uname 回傳的格式為 :

struct utsname {
               char sysname[];    /* Operating system name (e.g., "Linux") */
               char nodename[];   /* Name within "some implementation-defined
                                     network" */
               char release[];    /* Operating system release
                                     (e.g., "2.6.28") */
               char version[];    /* Operating system version */
               char machine[];    /* Hardware identifier */
           #ifdef _GNU_SOURCE
               char domainname[]; /* NIS or YP domain name */
           #endif
};

依照此文件把相對應的位置改掉。注意如果 release 改太小或是沒給,會噴錯。

def my_syscall_uname(ql, write_buf, *args, **kw):
    buf = b'QilingOS\x00' # sysname
    ql.mem.write(write_buf, buf)
    buf = b'30000'.ljust(65, b'\x00') # important!! If not sat will `FATAL: kernel too old`
    ql.mem.write(write_buf+65*2, buf)
    buf = b'ChallengeStart'.ljust(65, b'\x00') # version
    ql.mem.write(write_buf+65*3, buf)
    regreturn = 0
    return regreturn

ql.set_syscall("uname", my_syscall_uname)

Challenge 3

/dev/random,從中讀取兩次,確保第一次的值和 getrandom 得到的值相同,且其中沒有第二次讀到值。

查了一下 getrandom 是一 system call。因此對 /dev/random 和 getrandom() 進行 hijack 即可

class Fake_urandom(QlFsMappedObject):
    def read(self, size):
        if(size > 1):
            return b"\x01" * size
        else:
            return b"\x02"
    def fstat(self): # syscall fstat will ignore it if return -1
        return -1
    def close(self):
        return 0

def my_syscall_getrandom(ql, write_buf, write_buf_size, flag , *args, **kw):
    buf = b"\x01" * write_buf_size
    ql.mem.write(write_buf, buf)
    regreturn = 0
    return regreturn
    
ql.add_fs_mapper('/dev/urandom', Fake_urandom())
ql.set_syscall("getrandom", my_syscall_getrandom)

Challenge 4

進入不能進去的迴圈

直接 hook cmp 的位置讓 reg w0 是 1 即可,位置記得要加上 pie。

    # 00100fd8 e0 1b 40 b9     ldr        w0,[sp, #local_8]
    # 00100fdc e1 1f 40 b9     ldr        w1,[sp, #local_4]
    # 00100fe0 3f 00 00 6b     cmp        w1,w0    <- hook         
def hook_cmp(ql):
    ql.reg.w0 = 1
    return

base_addr = ql.mem.get_lib_base(ql.path) # get pie_base addr
ql.hook_address(hook_cmp, base_addr + 0xfe0)

Challenge 5

rand() 出來的值和 0 比較要通過

直接 hijack rand() 讓他回傳都是 0 即可。

def hook_cmp(ql):
    ql.reg.w0 = 1
    return
    
ql.set_api("rand", hook_rand)

Challenge 6

解開無窮迴圈

和 Challenge 4 同想法,hook cmp。

def hook_cmp2(ql):
    ql.reg.w0 = 0
    return
    
ql.hook_address(hook_cmp2, base_addr + 0x001118)

Challenge 7

不要讓他 sleep。 解法很多,可以 hook sleep 這個 api,或是看 sleep linux 文件能知道內部處理是用 nanosleep,hook 他即可。

def hook_sleeptime(ql):
    ql.reg.w0 = 0
    return
ql.hook_address(hook_sleeptime, base_addr + 0x1154)

Challenge 8

裡面最難的一題,他是建立特殊一個結構長這個樣子。

struct something(0x18){ 
 string_ptr -> malloc (0x1e) ->  0x64206d6f646e6152
 long_int = 0x3DFCD6EA00000539
 check_addr -> check;
}  

由於他結構內部有 0x3DFCD6EA00000539 這個 magic byte,因此可以直接對此作搜尋並改寫內部記憶體。這邊要注意搜尋可能找到其他位置,因此前面可以加對 string_ptr 所在位置的判斷。

def find_and_patch(ql, *args, **kw):
    MAGIC = 0x3DFCD6EA00000539
    magic_addrs = ql.mem.search(ql.pack64(MAGIC)) 

    # check_all_magic
    for magic_addr in magic_addrs:
        # Dump and unpack the candidate structure
        malloc1_addr = magic_addr - 8
        malloc1_data = ql.mem.read(malloc1_addr, 24)
        # unpack three unsigned long
        string_addr, _ , check_addr = struct.unpack('QQQ', malloc1_data)

        # check string data        
        if ql.mem.string(string_addr) == "Random data":
            ql.mem.write(check_addr, b"\x01")
            break
    return
    
ql.hook_address(find_and_patch, base_addr + 0x011dc)

另一種解法則是由於該結構在 stack 上,因此直接讀 stack 即可。

Challenge 9

把一字串轉用tolower小寫,再用 strcmp 比較。

解法一樣很多種,我是 hijack tolower() 讓他啥事都不做。

def hook_tolower(ql):
    return
    
ql.set_api("tolower", hook_tolower)

Challenge 10

打開不存在的文件,讀取的值需要是 qilinglab

和 Challenge 3 作法一樣,這邊要注意的是 return 要是 byte,string 會出錯。 = =

class Fake_cmdline(QlFsMappedObject):

    def read(self, size):
        return b"qilinglab" # type should byte byte, string will error = =
    def fstat(self): # syscall fstat will ignore it if return -1
        return -1
    def close(self):
        return 0

ql.add_fs_mapper('/proc/self/cmdline', Fake_cmdline())

Challenge 11

可以看到他從 MIDR_EL1 取值,而此為特殊的暫存器。

這邊解法是去 hook code,我選擇 hook 這段

# 001013ec 00 00 38 d5     mrs        x0,midr_el1

去搜尋所有記憶體為 b"\x00\x00\x38\xD5" ,讓他執行時把 x0 暫存器改寫,並更改 pc。

def midr_el1_hook(ql, address, size):  
    if ql.mem.read(address, size) == b"\x00\x00\x38\xD5":
        # if any code is mrs        x0,midr_el1
        # Write the expected value to x0
        ql.reg.x0 = 0x1337 << 0x10
        # Go to next instruction
        ql.reg.arch_pc += 4
    # important !! Maybe hook library
    # see : https://joansivion.github.io/qilinglabs/
    return

ql.hook_code(midr_el1_hook)

Done

Thanks

Thanks MANSOUR Cyril release his writeup, help me alot.

You might also like...
Measuring Coding Challenge Competence With APPS

Measuring Coding Challenge Competence With APPS This is the repository for Measuring Coding Challenge Competence With APPS by Dan Hendrycks*, Steven B

 Waymo motion prediction challenge 2021: 3rd place solution
Waymo motion prediction challenge 2021: 3rd place solution

Waymo motion prediction challenge 2021: 3rd place solution 📜 Technical report 🗨️ Presentation 🎉 Announcement 🛆Motion Prediction Channel Website 🛆

4th place solution to datafactory challenge by Intermarché.

Solution to Datafactory challenge by Intermarché. 4th place solution to datafactory challenge by Intermarché. The objective of the challenge is to pre

Code for the Image similarity challenge.

ISC 2021 This repository contains code for the Image Similarity Challenge 2021. Getting started The docs subdirectory has step-by-step instructions on

Development Kit for the SoccerNet Challenge
Development Kit for the SoccerNet Challenge

SoccerNetv2-DevKit Welcome to the SoccerNet-V2 Development Kit for the SoccerNet Benchmark and Challenge. This kit is meant as a help to get started w

Kaggle | 9th place (part of) solution for the Bristol-Myers Squibb – Molecular Translation challenge

Part of the 9th place solution for the Bristol-Myers Squibb – Molecular Translation challenge translating images containing chemical structures into I

Kaggle | 9th place single model solution for TGS Salt Identification Challenge

UNet for segmenting salt deposits from seismic images with PyTorch. General We, tugstugi and xuyuan, have participated in the Kaggle competition TGS S

Malmo Collaborative AI Challenge - Team Pig Catcher
Malmo Collaborative AI Challenge - Team Pig Catcher

The Malmo Collaborative AI Challenge - Team Pig Catcher Approach The challenge involves 2 agents who can either cooperate or defect. The optimal polic

1st place solution to the Satellite Image Change Detection Challenge hosted by SenseTime
1st place solution to the Satellite Image Change Detection Challenge hosted by SenseTime

1st place solution to the Satellite Image Change Detection Challenge hosted by SenseTime

Owner
Yuan
Yuan
ManiSkill-Learn is a framework for training agents on SAPIEN Open-Source Manipulation Skill Challenge (ManiSkill Challenge), a large-scale learning-from-demonstrations benchmark for object manipulation.

ManiSkill-Learn ManiSkill-Learn is a framework for training agents on SAPIEN Open-Source Manipulation Skill Challenge, a large-scale learning-from-dem

Hao Su's Lab, UCSD 48 Dec 30, 2022
The comma.ai Calibration Challenge!

Welcome to the comma.ai Calibration Challenge! Your goal is to predict the direction of travel (in camera frame) from provided dashcam video. This rep

comma.ai 697 Jan 5, 2023
CVPR 2021 Challenge on Super-Resolution Space

Learning the Super-Resolution Space Challenge NTIRE 2021 at CVPR Learning the Super-Resolution Space challenge is held as a part of the 6th edition of

andreas 104 Oct 26, 2022
AI grand challenge 2020 Repo (Speech Recognition Track)

KorBERT를 활용한 한국어 텍스트 기반 위협 상황인지(2020 인공지능 그랜드 챌린지) 본 프로젝트는 ETRI에서 제공된 한국어 korBERT 모델을 활용하여 폭력 기반 한국어 텍스트를 분류하는 다양한 분류 모델들을 제공합니다. 본 개발자들이 참여한 2020 인공지

Young-Seok Choi 23 Jan 25, 2022
Code for 1st place solution in Sleep AI Challenge SNU Hospital

Sleep AI Challenge SNU Hospital 2021 Code for 1st place solution for Sleep AI Challenge (Note that the code is not fully organized) Refer to the notio

Saewon Yang 13 Jan 3, 2022
The challenge for Quantum Coalition Hackathon 2021

Qchack 2021 Google Challenge This is a challenge for the brave 2021 qchack.io participants. Instructions Hello, intrepid qchacker, welcome to the <G|o

quantumlib 18 May 4, 2022
This repo contains the official code of our work SAM-SLR which won the CVPR 2021 Challenge on Large Scale Signer Independent Isolated Sign Language Recognition.

Skeleton Aware Multi-modal Sign Language Recognition By Songyao Jiang, Bin Sun, Lichen Wang, Yue Bai, Kunpeng Li and Yun Fu. Smile Lab @ Northeastern

Isen (Songyao Jiang) 128 Dec 8, 2022
🏆 The 1st Place Submission to AICity Challenge 2021 Natural Language-Based Vehicle Retrieval Track (Alibaba-UTS submission)

AI City 2021: Connecting Language and Vision for Natural Language-Based Vehicle Retrieval ?? The 1st Place Submission to AICity Challenge 2021 Natural

null 82 Dec 29, 2022
The 1st place solution of track2 (Vehicle Re-Identification) in the NVIDIA AI City Challenge at CVPR 2021 Workshop.

AICITY2021_Track2_DMT The 1st place solution of track2 (Vehicle Re-Identification) in the NVIDIA AI City Challenge at CVPR 2021 Workshop. Introduction

Hao Luo 91 Dec 21, 2022
Starter kit for getting started in the Music Demixing Challenge.

Music Demixing Challenge - Starter Kit ?? Challenge page This repository is the Music Demixing Challenge Submission template and Starter kit! Clone th

AIcrowd 106 Dec 20, 2022