首页
社区
课程
招聘
[翻译]二进制漏洞利用(一)编写ARMshellcode&理解系统函数
发表于: 2018-7-28 18:17 7889

[翻译]二进制漏洞利用(一)编写ARMshellcode&理解系统函数

2018-7-28 18:17
7889

For this tutorial we will use the following tools (most of them should be installed by default on your Linux distribution):

这部分教程我们会使用以下工具(大部分是linux系统默认安装的)


GDB– our debugger of choice   

GDB-被我们选中的调试器


GEF–  GDB Enhanced Features, highly recommended (created by @_hugsy_

GEF-GDB的增强组件,强烈推荐(由hugsy创建的)


GCC– Gnu Compiler Collection   

GCC-Gnu编译器合集


as– assembler as 


ld– linker


strace– utility to trace system calls        

strace-用来追踪系统调用


objdump– to check for null-bytes in the disassembly   

objdump-检查反汇编中的空字节


objcopy– to extract raw shellcode from ELF binary   

objcopy-从ELF文件抽取出初步的shellcode


Make sure you compile and run all the examples in this tutorial in an ARM environment. 

Before you start writing your shellcode, make sure you are aware of some basic principles, such as:

在开始编写你的shellcode前,请确保你已经了解一些基本的原则,比如:


1.You want your shellcode to be compact and free of null-bytes

你想让自己的shellcode紧凑,没有空字节。

Reason: We are writing shellcode that we will use to exploit memory corruption vulnerabilities like buffer overflows. Some buffer overflows occur because of the use of the C function ‘strcpy’. Its job is to copy data until it receives a null-byte. We use the overflow to take control over the program flow and if strcpy hits a null-byte it will stop copying our shellcode and our exploit will not work

原因:我们正在编写shellcode,我们将利用它来开发内存损坏漏洞,比如缓冲区溢出。有些缓冲区溢出是因为使用C函数“strcpy”。该函数的工作原理是:拷贝数据直到遇到一个空字节。当我们使用溢出来控制程序流时,如果strcpy碰到一个空字节,它将停止复制我们的shellcode,导致我们的漏洞利用失败。


2.You also want to avoid library calls and absolute memory addresses

你想避免库调用和绝对内存地址

Reason: To make our shellcode as universal as possible, we can’t rely on library calls that require specific dependencies and absolute memory addresses that depend on specific environments.

The Process of writing shellcode involves the following steps:编写shellcode的过程包括以下几个步骤:


1.Knowing what system calls you want to use 

2.Figuring out the syscall number and the parameters your chosen syscall function requires

3.De-Nullifying your shellcode

4.Converting your shellcode into a Hex string


1.知道你想使用何种系统调用

2.计算出系统调用数字,以及系统调用函数需要的参数

Looking at this prototype, we can see that we need the following parameters:

fd– 1 for STDOUT

buf– pointer to a string

count– number of bytes to write -> 13

syscall number of write -> 0x4

Compile and execute:编译并执行

译者注:上述代码在linux中以 azeria@labs 执行,snip是跳过了命令显示结果,上述代码建议在原网站查看

先执行编译:

Turns out, the system function execve() is being invoked.结果是调用了系统函数execve()


The parameters execve() requires are: 

Pointer to a string specifying the path to a binary 

argv[] – array of command line variables

envp[] – array of environment variables

Which basically translates to: execve(*filename, *argv[], *envp[]) –> execve(*filename, 0, 0). The system call number of this function can be looked up with the following command:

基本可以翻译为: execve(*filename, *argv[], *envp[])    –>    execve(*filename, 0, 0)    这个函数的系统调用数可以用下面的命令来查看: 

Invoking system calls on x86 works as follows: First, you PUSH parameters on the stack. Then, the syscall number gets moved into EAX (MOV EAX, syscall_number). And lastly, you invoke the system call with SYSENTER / INT 80.

调用x86上的系统函数时,调用规则如下:首先,将参数压入堆栈。然后,将系统调用号移动到EAX中(MOV EAX,系统调用号)最后,用SYSENTER / INT 80 调用系统调用函数。


On ARM, syscall invocation works a little bit differently:

1.Move parameters into registers – R0, R1, ..

2.Move the syscall number into register R7

mov  r7, #<syscall_number>

3.Invoke the system call with

SVC #0 or

SVC #1

4.The return value ends up in R0

而在ARM中,系统调用有一些不同:

1.将参数放入寄存器R0,R1.。。,

2.将系统调用号放入R7

mov  r7, #<系统调用号>

3.调动系统函数:

SVC #0 或者

SVC #1

4.将返回值放入R0


This is how it looks like in ARM Assembly (Code uploaded to the azeria-labs Github account):

ARM汇编里看上去是这样的(代码已上传至girhub账号 azeria-labs )


As you can see in the picture above, we start with pointing R0 to our “/bin/sh” string by using PC-relative addressing (If you can’t remember why the effective PC starts two instructions ahead of the current one, go to ‘Part 2: Data Types and Registers‘ of the assembly basics tutorial and look at part where the PC register is explained along with an example). Then we move 0’s into R1 and R2 and move the syscall number 11 into R7. Looks easy, right? Let’s look at the disassembly of our first attempt using objdump:

正如您在上面的图片中看到的,我们首先用PC相对寻址将R0指向我们的“/bin /SH”字符串(如果您不记得为什么PC有效位指向当前指令两条指令之后的位置,那么转到《汇编基础教程的第2部分:数据类型和寄存器》。请看PC寄存器的那一部分以及那个示例。然后我们将0的放到R1和R2,并将系统调用号移动到R7。看起来很容易,对吧?让我们看看我们第一次尝试用objdump得到的反汇编:

Turns out we have quite a lot of null-bytes in our shellcode. The next step is to de-nullify the shellcode and replace all operations that involve.

事实证明,我们的shellcode中有很多空字节。下一步是确认shellcode并替换所有涉及的操作。


One of the techniques we can use to make null-bytes less likely to appear in our shellcode is to use Thumb mode. Using Thumb mode decreases the chances of having null-bytes, because Thumb instructions are 2 bytes long instead of 4. If you went through the ARM Assembly Basics tutorials you know how to switch from ARM to Thumb mode. If you haven’t I encourage you to read the chapter about the branching instructions “B / BX / BLX” in part 6 of the tutorial “Conditional Execution and Branching“.

我们可以通过使用Thumb模式来降低空字节出现在我们的代码中的可能性。这是我们能用的其中一种技术。使用Thumb模式减少了空字节出现的机会,因为Thumb指令是2字节长而不是4字节。如果你经过了ARM汇编基础教程的学习,你就知道如何从ARM切换到Thumb模式。如果没有,我鼓励您阅读教程的第六部分“条件执行和分支”中关于分支指令“B/BX/BLX”的章节。


In our second attempt we use Thumb mode and replace the operations containing #0’s with operations that result in 0’s by subtracting registers from each other or xor’ing them. For example, instead of using “mov  r1, #0”, use either “sub  r1, r1, r1” (r1 = r1 – r1) or “eor  r1, r1, r1” (r1 = r1 xor r1). Keep in mind that since we are now using Thumb mode (2 byte instructions) and our code must be 4 byte aligned, we need to add a NOP at the end (e.g. mov  r5, r5).

The result is that we only have one single null-byte that we need to get rid of. The part of our code that’s causing the null-byte is the null-terminated string “/bin/sh\0”. We can solve this issue with the following technique:

Replace “/bin/sh\0” with “/bin/shX”

Use the instruction strb (store byte) in combination with an existing zero-filled register to replace X with a null-byte


结果是只有一个空字节,我们需要去掉它。导致空字节的代码的片段是:用来终止字符串的空字节:“/bin/sh\0”。我们可以用以下技术解决这个问题:

用“/bin /shX”替换“/bin /sh\0”

使用指令strb(存储字节)与现有的零填充寄存器组合,用一个空字节替换x

(代码在Github账号:azeria_lib上提供下载   Code available on the azeria-labs Github account):

The shellcode we created can now be transformed into it’s hexadecimal representation. Before doing that, it is a good idea to check if the shellcode works as a standalone. But there’s a problem: if we compile our assembly file like we would normally do, it won’t work. The reason for this is that we use the strb operation to modify our code section (.text). This requires the code section to be writable and can be achieved by adding the -N flag during the linking process.

我们创建的shellcode现在可以转化为十六进制表示法。在这样做之前,检查一下shellcode是否作为一个独立的部分去生效是一个好主意。但是有一个问题:如果我们像通常编译程序文件那样编译我们的汇编文档,它就不起作用了。原因是我们使用strb指令来修改我们的代码段(.text)。这要求代码段是可写的,并且可以通过在连接过程中添加-N标志来实现。(见下图)



It works! Congratulations, you’ve written your first shellcode in ARM assembly.

它生效了!祝贺你,你已经在ARM汇编中编写了第一段代码。


To convert it into hex, use the following commands:

为了将他转换成十六进制,使用如下命令:


Instead of using the hexdump command above, you also do the same with a simple python script:

除了使用上面的hexdump命令之外,您还可以用简单的Python脚本进行同样的操作:


I hope you enjoyed this introduction into writing ARM shellcode. In the next part you will learn how to write shellcode in form of a reverse-shell, which is a little bit more complicated than the example above. After that we will dive into memory corruptions and learn how they occur and how to exploit them using our self-made shellcode.



The prerequisite for this part of the tutorial is a basic understanding of ARM assembly (covered in the first tutorial series “ARM Assembly Basics“). In this part, you will learn how to use your knowledge to create your first simple shellcode in ARM assembly. The examples used in this tutorial are compiled on an ARMv6 32-bit processor. If you don’t have access to an ARM device, you can create your own lab and emulate a Raspberry Pi distro in a VM by following this tutorial:Emulate Raspberry Pi with QEMU.
学习本教程该部分的先决条件是基本理解ARM汇编指令(在第一个教程系列“ARM汇编基础”中有介绍)。在这一部分,您将学习如何使用您ARM汇编的知识来编写第一个简单的ARM汇编指令的shellcode。本教程中使用的示例是在一个ARMv6 32位处理器上编译的。如果你没有一台ARM设备,你可以创建自己的实验环境,并且在根据这个教程(用QEUM模拟一个树莓派)在虚拟机中模拟一个树莓派。

This tutorial is for people who think beyond running automated shellcode generators and want to learn how to write shellcode in ARM assembly themselves. After all, knowing how it works under the hood and having full control over the result is much more fun than simply running a tool, isn’t it? Writing your own shellcode in assembly is a skill that can turn out to be very useful in scenarios where you need to bypass shellcode-detection algorithms or other restrictions where automated tools could turn out to be insufficient. The good news is, it’s a skill that can be learned quite easily once you are familiar with the process.
本教程是针对那些不仅仅满足于运行shellcode自动生成器,并且想学习如何在ARM程序集中编写自己的shellcode的人。毕竟,知道在底层软件是如何工作的,并完全控制运行结果,比简单地运行一个工具有趣的多,不是吗?编写自己的汇编shellcode是一种非常有用的技术,当自动化工具被证明不足以绕过检测shellcode算法或者其他限制算法时,它可以帮助你绕过他们。好消息是,这项技术是一种一旦你熟悉这个过程,就可以很容易地学会的一种技能。

For this tutorial we will use the following tools (most of them should be installed by default on your Linux distribution):

这部分教程我们会使用以下工具(大部分是linux系统默认安装的)


GDB– our debugger of choice   

GDB-被我们选中的调试器


GEF–  GDB Enhanced Features, highly recommended (created by @_hugsy_

GEF-GDB的增强组件,强烈推荐(由hugsy创建的)


GCC– Gnu Compiler Collection   


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2018-8-5 10:18 被r0Cat编辑 ,原因:
收藏
免费 1
支持
分享
打赏 + 5.00雪花
打赏次数 1 雪花 + 5.00
 
赞赏  junkboy   +5.00 2018/07/28
最新回复 (4)
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
......
最后于 2020-4-7 20:52 被junkboy编辑 ,原因: ......
2018-7-28 18:42
0
雪    币: 8715
活跃值: (8619)
能力值: ( LV13,RANK:570 )
在线值:
发帖
回帖
粉丝
3
junkboy 开新作啦 前排支持 ======== 提个建议:最好能用markdown来写,看雪支持,而且排版更友好,也更适合这种原文和译文对照的风格,超链、注之类的也不会混乱或者丢失,例如此贴原文中的很多超 ...
收到。链接已经补齐。但我之前没有用过markdown,等我学习一哈把它用起来
2018-7-28 19:16
0
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4

......

最后于 2020-4-7 20:52 被junkboy编辑 ,原因: ......
2018-7-28 19:23
0
雪    币: 8715
活跃值: (8619)
能力值: ( LV13,RANK:570 )
在线值:
发帖
回帖
粉丝
5
junkboy 哈哈赞,这语法还是挺方便的,主要可以写一份到处都能发,不用再去费心排版
好的建议越多越好,一起进步嘛
2018-7-28 19:32
0
游客
登录 | 注册 方可回帖
返回
//