R&D Shellcode series · 1 / 2

Crafting Shellcode: Techniques and Strategies for Bypassing Restricted Characters - 1

한국어로 읽기 →
SERIESShellcode for Bypassing Restricted Characters2 parts in total
  1. 01 Crafting Shellcode: Techniques and Strategies for Bypassing Restricted Characters - 1Now reading
  2. 02 Crafting Shellcode: Techniques and Strategies for Bypassing Restricted Characters - 2Read →
Contents
  1. 1☑️ Overview
  2. 2☑️ What is Shellcode?
  3. 3☑️ Minimum Requirements for Using Shellcode
  4. 4☑️ What are the main differences in creating shellcode for MIPS and ARM compared to x86_64?
  5. 5Differences in CPU Design Approach
  6. 6Registers Used
  7. 7System Calls

☑️ Overview

In 2024, we conducted an in-depth security analysis of 9 domestic(Korean) and international vehicle dashcams and made a startling discovery. We found that 7 out of these devices had Buffer Overflow vulnerabilities, and 4 of them had no protection mechanisms against Buffer Overflow attacks whatsoever. This means that many devices currently on the market can potentially allow malicious attackers to gain administrator privileges and take control of the system.

Among attacks exploiting Buffer Overflow, the most dangerous method is hijacking system control by executing unintended code within the device through Shellcode injection. While this Shellcode is frequently used by attackers, security experts must also possess knowledge about it and understand its creation methods to detect and analyze it preemptively.

This series has two parts. This first part covers the basic concepts of Shellcode and the conditions for writing it; strategies and techniques for bypassing restricted characters are covered in Part 2.

☑️ What is Shellcode?

Shellcode refers to a series of commands written by an attacker to execute a system's shell for a specific purpose. It is typically used as a type of malicious code, employed to take control of a system or perform additional malicious actions by exploiting vulnerable software.

Typically composed in low-level assembly language, shellcode is frequently deployed through security flaws like buffer overflows. While the term initially described code that spawned a command shell, it has since broadened to encompass various types of exploit payloads.

☑️ Minimum Requirements for Using Shellcode

  1. The attacker must be able to change the Return Address to a desired value, typically through vulnerabilities like Buffer Overflow or Format String. Fundamentally, to execute shellcode, it's necessary to modify the Return Address to change the value of the PC (Program Counter) register to the address where the shellcode is stored.

    Below is an example of exploiting a Buffer Overflow vulnerability in an IoT device using ARM architecture to overwrite registers R4 - R12 and manipulate the PC register.

  1. The executable file should run with administrator privileges or as the root account. Even without admin rights, it's possible to obtain a shell, but system access will be limited. From an attacker's perspective, targeting an executable with higher privileges is advantageous as it provides more control. In IoT and other embedded devices, it's common to use only a single root account. Even when multiple accounts are used to manage the device, the executables responsible for key functions typically run with administrator privileges, so this isn't a significant issue.

    The example below shows that in addition to the root account, the admin account is used to manage the accounts, but the main functions of the device can be executed with the admin privileges, so it is not necessary to obtain the root access.

  1. A memory region with both write and execute permissions is required. Specifically, when the NX (No-Execute) protection mechanism is not in place, it's possible to write data to the Stack or Heap area and then change the Return Address to this data area to execute the shellcode.

    When analyzing an executable file without NX protection, you can see areas that have both write (w) and execute (x) permissions, as shown below. By inserting shellcode into these areas and changing the Return Address, it's possible to execute the shellcode and cause abnormal behavior.

If the NX protection mechanism is activated, it removes execution permissions from areas with write permissions, as shown below. This prevents the execution of shellcode even if it's inserted. In this case, attacks must be carried out through techniques like RTL (Return to Library) or ROP (Return Oriented Programming) instead of shellcode insertion attacks.

If the above three conditions are met, it's possible to analyze the executable file, execute the shellcode, and cause abnormal behavior.

☑️ What are the main differences in creating shellcode for MIPS and ARM compared to x86_64?

Differences in CPU Design Approach

The most significant difference lies in the CPU design philosophy, specifically RISC vs CISC.

  • RISC (Reduced Instruction Set Computing)

    RISC follows a design philosophy that maximizes efficiency by using simple and fast instructions. Each instruction is designed to execute within the same time frame. The main goal of RISC is to keep each instruction simple and reduce the number of instructions to increase execution speed.

  • CISC (Complex Instruction Set Computing)

    CISC follows a design philosophy that uses complex and varied instructions to handle diverse tasks. It aims to perform complex operations with single instructions, thereby reducing the amount of code programmers need to write. Instructions can be complex, and execution time may vary between instructions.

RISC (ARM & MIPS)

MIPS and ARM are representative processors that follow the RISC architecture. The characteristics of RISC architecture are as follows:

  • Fixed instruction size

    Most instructions are of the same size (typically 4 bytes), making instruction decoding simple.

  • Simple instruction format

    The instruction format is simple and the number of instructions is small. Due to the architectural characteristics, memory access is minimized, and most operations occur between registers.

  • Reduced hardware complexity

    By avoiding complex instructions and using simple ones, hardware complexity is reduced, which in turn decreases power consumption and heat generation.

ADD r0, r1, r2 ; r0 = r1 + r2

CISC (x86)

x86 is a representative processor that follows the CISC architecture. The characteristics of CISC architecture are as follows:

  • Variable-length instructions

    Instruction lengths are variable, ranging from 1 byte to several dozen bytes. This makes instruction decoding more complex.

  • Complex instruction formats

    It supports various instruction formats and addressing modes, allowing complex tasks to be performed with a single instruction.

  • Increased hardware complexity

    More complex hardware is required to decode and execute complex instructions. This increases power consumption and heat generation.

    ADD EAX, EBX ; EAX = EAX + EBX

    ADD EAX, ECX ; EAX = EAX + ECX

It can be considered that using RISC architecture may reduce complexity compared to CISC.

Registers Used

ARM

16 general-purpose registers (R0-R15), stack pointer (R13), link register (R14), program counter (R15)

MIPS

32 general-purpose registers ($0-$31), each register has a specific purpose

x86

8 main general-purpose registers (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP), 32-bit size

x86_64

16 general-purpose registers (RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8-R15), 64-bit size, with access to the lower 32-bit, 16-bit, and 8-bit parts

System Calls

ARM

In the ARM architecture, system calls are performed using the software interrupt instruction (svc, formerly swi). The system call number and arguments are passed through registers.

  • System call number: r7

  • Arguments: r0 - r6

  • Return value: r0

    MOV r0, #1 ; exit code 1

    MOV r7, #1 ; syscall number for exit

    SVC 0 ; make the syscall

MIPS

In the MIPS architecture, system calls are made using the syscall instruction. The system call number and arguments are passed through registers.

  • System call number: $v0

  • Arguments: $a0 - $a3

  • Return value: $v0

    li $v0, 10 ; syscall number for exit

    syscall ; make the syscall

x86

In the x86 architecture, system calls are typically performed using the int 0x80 interrupt. The system call number and arguments are passed through registers.

  • System call number: eax

  • Arguments: ebx, ecx, edx, esi, edi

  • Return value: eax

    mov eax, 1 ; syscall number for exit

    mov ebx, 1 ; exit code 1

    int 0x80 ; make the syscall

x86_64

In the x86_64 architecture, the syscall instruction is used to perform system calls. The system call number and arguments are passed through registers.

  • System call number: rax

  • Arguments: rdi, rsi, rdx, r8, r9, r10

  • Return value: rax

    mov rax, 60 ; syscall number for exit

    mov rdi, 1 ; exit code 1

    syscall ; make the syscall

Strategies and techniques for bypassing restricted characters continue in Part 2.

Z ZIEN
{# 쿠키 수집 안내 팝업 — 선택 전 방문자에게만(cookie-consent '쿠키 안내 팝업'). 버튼은 폼 POST 라 JS 없이 동작하고, 닫기(×)는 '거부' 와 같이 저장된다. source·close 는 누른 버튼별 집계용 — 닫기를 거부와 따로 센다(cookie-consent '쿠키 선택 집계'). #}